diff --git a/.github/file-filters.yml b/.github/file-filters.yml index 87e635bc..c0b9bb34 100644 --- a/.github/file-filters.yml +++ b/.github/file-filters.yml @@ -36,7 +36,8 @@ markdown_all: &markdown_all infrahub_reference_generated: &infrahub_reference_generated - "docs/docs/infrahubctl/*.mdx" - - "docs/docs/python-sdk/reference/config.mdx" + - "docs/docs/python-sdk/reference/*.mdx" + - "docs/docs/python-sdk/sdk_ref/**/*.mdx" documentation_all: - *development_files diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16d2de2d..faf53368 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,7 +108,7 @@ jobs: - name: "Linting: markdownlint" uses: DavidAnson/markdownlint-cli2-action@v22 with: - config: .markdownlint.yaml + config: docs/.markdownlint.yaml globs: | **/*.{md,mdx} !changelog/*.md @@ -176,7 +176,7 @@ jobs: uses: actions/setup-node@v5 with: node-version: 20 - cache: 'npm' + cache: "npm" cache-dependency-path: docs/package-lock.json - name: "Install dependencies" run: npm install @@ -207,6 +207,15 @@ jobs: uses: "actions/checkout@v6" with: submodules: true + - name: Install NodeJS + uses: actions/setup-node@v5 + with: + node-version: 20 + cache: "npm" + cache-dependency-path: docs/package-lock.json + - name: "Install npm dependencies" + run: npm install + working-directory: ./docs - name: Set up Python uses: actions/setup-python@v6 with: @@ -217,9 +226,11 @@ jobs: version: "${{ needs.prepare-environment.outputs.UV_VERSION }}" - name: Install dependencies run: uv sync --all-groups --all-extras + - name: Docs unit tests + run: npx --no-install vitest run + working-directory: ./docs - name: Validate generated documentation run: uv run invoke docs-validate - validate-documentation-style: if: | always() && !cancelled() && @@ -236,6 +247,7 @@ jobs: # The official GitHub Action for Vale doesn't work, installing manually instead: # https://github.com/errata-ai/vale-action/issues/103 + # cf -> https://github.com/nf-core/website/pull/3509 - name: Download Vale run: | curl -sL "https://github.com/errata-ai/vale/releases/download/v${VALE_VERSION}/vale_${VALE_VERSION}_Linux_64-bit.tar.gz" -o vale.tar.gz diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml index 65574ce7..67ff7ac9 100644 --- a/.github/workflows/sync-docs.yml +++ b/.github/workflows/sync-docs.yml @@ -8,8 +8,9 @@ on: - stable paths: - 'docs/docs/**' - - 'docs/sidebars-infrahubctl.ts' - - 'docs/sidebars-python-sdk.ts' + - 'docs/sidebars/sidebars-infrahubctl.ts' + - 'docs/sidebars/sidebars-python-sdk.ts' + - 'docs/sidebars/sidebar-utils.ts' jobs: sync: @@ -33,8 +34,9 @@ jobs: rm -f target-repo/docs/sidebars-python-sdk.ts rm -f target-repo/docs/sidebars-infrahubctl.ts cp -r source-repo/docs/docs/* target-repo/docs/docs-python-sdk/ - cp source-repo/docs/sidebars-infrahubctl.ts target-repo/docs/ - cp source-repo/docs/sidebars-python-sdk.ts target-repo/docs/ + cp source-repo/docs/sidebars/sidebars-infrahubctl.ts target-repo/docs/ + cp source-repo/docs/sidebars/sidebars-python-sdk.ts target-repo/docs/ + cp source-repo/docs/sidebars/sidebar-utils.ts target-repo/docs/ cd target-repo git config user.name github-actions git config user.email github-actions@github.com diff --git a/.vale.ini b/.vale.ini index 38608874..6133f5d4 100644 --- a/.vale.ini +++ b/.vale.ini @@ -23,3 +23,10 @@ BasedOnStyles = [*] BasedOnStyles = Infrahub + +# Generated API reference docs: use GeneratedRef spelling (allows snake_case) +# instead of global Infrahub spelling. Must be last to override [*]. +[docs/docs/python-sdk/sdk_ref/**/*.mdx] +BasedOnStyles = Infrahub, GeneratedRef +Infrahub.spelling = NO +BlockIgnores = (?s) *((import.*?\n)|(```.*?```\n)) diff --git a/.vale/styles/GeneratedRef/spelling.yml b/.vale/styles/GeneratedRef/spelling.yml new file mode 100644 index 00000000..52aea108 --- /dev/null +++ b/.vale/styles/GeneratedRef/spelling.yml @@ -0,0 +1,10 @@ +--- +extends: spelling +message: "Did you really mean '%s'?" +level: error +filters: + - '[pP]y.*\b' + - '\bimport_.*\b' # Ignore variables starting with 'import_' + - '\w+__value' # Skip Infrahub filters in documentation (name__value) + - '\b\w+_\w+\b' # Ignore snake_case identifiers in generated API reference docs +ignore: spelling-exceptions.txt diff --git a/.vale/styles/Infrahub/spelling.yml b/.vale/styles/Infrahub/spelling.yml index 5fbbbcdd..8f0bfa73 100644 --- a/.vale/styles/Infrahub/spelling.yml +++ b/.vale/styles/Infrahub/spelling.yml @@ -4,6 +4,6 @@ message: "Did you really mean '%s'?" level: error filters: - '[pP]y.*\b' - - '\bimport_.*\b' # New filter to ignore variables starting with 'import_' - - '\w+__value' # New filter to skip Infrahub filters in documentation (name__value) + - '\bimport_.*\b' # Ignore variables starting with 'import_' + - '\w+__value' # Skip Infrahub filters in documentation (name__value) ignore: spelling-exceptions.txt diff --git a/.vale/styles/spelling-exceptions.txt b/.vale/styles/spelling-exceptions.txt index 2da24e1f..0a4c0144 100644 --- a/.vale/styles/spelling-exceptions.txt +++ b/.vale/styles/spelling-exceptions.txt @@ -3,6 +3,7 @@ Alibaba Ansible append_git_suffix APIs +Args artifact_definitions artifact_name async diff --git a/changelog/201.added.md b/changelog/201.added.md new file mode 100644 index 00000000..bb3fbf00 --- /dev/null +++ b/changelog/201.added.md @@ -0,0 +1 @@ +Python SDK API documentation is now generated directly from the docstrings of the classes, functions, and methods contained in the code. \ No newline at end of file diff --git a/.markdownlint.yaml b/docs/.markdownlint.yaml similarity index 100% rename from .markdownlint.yaml rename to docs/.markdownlint.yaml diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 36021cbb..f869e84f 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -8,8 +8,10 @@ Docusaurus documentation following Diataxis framework. cd docs && npm install # Install deps cd docs && npm start # Dev server at localhost:3000 cd docs && npm run build # Build static site -uv run invoke docs # Generate auto-docs -uv run invoke docs-validate # Validate docs are current +cd docs && npm test # Run sidebar utility tests +uv run invoke docs # Build documentation website +uv run invoke docs-generate # Regenerate all docs (infrahubctl CLI + Python SDK) +uv run invoke docs-validate # Check that generated docs match committed files ``` ## Structure @@ -23,11 +25,19 @@ docs/docs/ └── infrahubctl/ # CLI docs (auto-generated) ``` +## Sidebars + +Sidebar navigation is dynamic: `sidebars-*.ts` files read the filesystem at build time via utility functions in `sidebar-utils.ts`. + +- **infrahubctl**: all `.mdx` files are discovered automatically and sorted alphabetically. +- **python-sdk**: guides, topics, and reference sections preserve a defined display order; new files are appended alphabetically at the end. + +No manual sidebar update is needed when adding a new `.mdx` file. However, to control the display order of a new page, add its doc ID to the ordered list in the corresponding `sidebars-*.ts` file. + ## Adding Documentation 1. Create MDX file in appropriate directory 2. Add frontmatter with `title` -3. Update `sidebars-*.ts` for navigation ## MDX Pattern @@ -52,7 +62,7 @@ Use callouts for important notes. ✅ **Always** - Include both async/sync examples using Tabs -- Run `uv run invoke docs-validate` after code changes +- Run `uv run invoke docs-validate` after code changes to verify generated docs are up to date 🚫 **Never** diff --git a/docs/__init__.py b/docs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs/_templates/sdk_config.j2 b/docs/_templates/sdk_config.j2 index 7922a363..4fbc9d39 100644 --- a/docs/_templates/sdk_config.j2 +++ b/docs/_templates/sdk_config.j2 @@ -31,8 +31,6 @@ The following settings can be defined in the `Config` class {% for property in properties %} ## {{ property.name }} - -**Property**: {{ property.name }}
**Description**: {% if '\n' in property.description %} {% endif %}{{ property.description }}
**Type**: `{{ property.type }}`
diff --git a/docs/docs/python-sdk/reference/config.mdx b/docs/docs/python-sdk/reference/config.mdx index 320aebb4..1b525389 100644 --- a/docs/docs/python-sdk/reference/config.mdx +++ b/docs/docs/python-sdk/reference/config.mdx @@ -30,8 +30,6 @@ The Python SDK (Async or Sync) client can be configured using an instance of the The following settings can be defined in the `Config` class ## address - -**Property**: address
**Description**: The URL to use when connecting to Infrahub.
**Type**: `string`
@@ -39,16 +37,12 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_ADDRESS`
## api_token - -**Property**: api_token
**Description**: API token for authentication against Infrahub.
**Type**: `string`
**Environment variable**: `INFRAHUB_API_TOKEN`
## echo_graphql_queries - -**Property**: echo_graphql_queries
**Description**: If set the GraphQL query and variables will be echoed to the screen
**Type**: `boolean`
@@ -56,24 +50,18 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_ECHO_GRAPHQL_QUERIES`
## username - -**Property**: username
**Description**: Username for accessing Infrahub
**Type**: `string`
**Environment variable**: `INFRAHUB_USERNAME`
## password - -**Property**: password
**Description**: Password for accessing Infrahub
**Type**: `string`
**Environment variable**: `INFRAHUB_PASSWORD`
## default_branch - -**Property**: default_branch
**Description**: Default branch to target if not specified for each request.
**Type**: `string`
@@ -81,8 +69,6 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_DEFAULT_BRANCH`
## default_branch_from_git - -**Property**: default_branch_from_git
**Description**: Indicates if the default Infrahub branch to target should come from the active branch in the local Git repository.
**Type**: `boolean`
@@ -90,16 +76,12 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_DEFAULT_BRANCH_FROM_GIT`
## identifier - -**Property**: identifier
**Description**: Tracker identifier
**Type**: `string`
**Environment variable**: `INFRAHUB_IDENTIFIER`
## insert_tracker - -**Property**: insert_tracker
**Description**: Insert a tracker on queries to the server
**Type**: `boolean`
@@ -107,8 +89,6 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_INSERT_TRACKER`
## max_concurrent_execution - -**Property**: max_concurrent_execution
**Description**: Max concurrent execution in batch mode
**Type**: `integer`
@@ -116,16 +96,12 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_MAX_CONCURRENT_EXECUTION`
## mode - -**Property**: mode
**Description**: Default mode for the client
**Type**: `object`
**Environment variable**: `INFRAHUB_MODE`
## pagination_size - -**Property**: pagination_size
**Description**: Page size for queries to the server
**Type**: `integer`
@@ -133,8 +109,6 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_PAGINATION_SIZE`
## retry_delay - -**Property**: retry_delay
**Description**: Number of seconds to wait until attempting a retry.
**Type**: `integer`
@@ -142,8 +116,6 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_RETRY_DELAY`
## retry_on_failure - -**Property**: retry_on_failure
**Description**: Retry operation in case of failure
**Type**: `boolean`
@@ -151,8 +123,6 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_RETRY_ON_FAILURE`
## max_retry_duration - -**Property**: max_retry_duration
**Description**: Maximum duration until we stop attempting to retry if enabled.
**Type**: `integer`
@@ -160,8 +130,6 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_MAX_RETRY_DURATION`
## schema_converge_timeout - -**Property**: schema_converge_timeout
**Description**: Number of seconds to wait for schema to have converged
**Type**: `integer`
@@ -169,8 +137,6 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_SCHEMA_CONVERGE_TIMEOUT`
## timeout - -**Property**: timeout
**Description**: Default connection timeout in seconds
**Type**: `integer`
@@ -178,32 +144,24 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_TIMEOUT`
## transport - -**Property**: transport
**Description**: Set an alternate transport using a predefined option
**Type**: `object`
**Environment variable**: `INFRAHUB_TRANSPORT`
## proxy - -**Property**: proxy
**Description**: Proxy address
**Type**: `string`
**Environment variable**: `INFRAHUB_PROXY`
## proxy_mounts - -**Property**: proxy_mounts
**Description**: Proxy mounts configuration
**Type**: `object`
**Environment variable**: `INFRAHUB_PROXY_MOUNTS`
## update_group_context - -**Property**: update_group_context
**Description**: Update GraphQL query groups
**Type**: `boolean`
@@ -211,8 +169,6 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_UPDATE_GROUP_CONTEXT`
## tls_insecure - -**Property**: tls_insecure
**Description**: Indicates if TLS certificates are verified. @@ -223,8 +179,6 @@ The following settings can be defined in the `Config` class **Environment variable**: `INFRAHUB_TLS_INSECURE`
## tls_ca_file - -**Property**: tls_ca_file
**Description**: File path to CA cert or bundle in PEM format
**Type**: `string`
diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/client.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/client.mdx new file mode 100644 index 00000000..7b47c99c --- /dev/null +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/client.mdx @@ -0,0 +1,942 @@ +--- +title: client +sidebarTitle: client +--- + +# `infrahub_sdk.client` + +## Classes + +### `InfrahubClient` + +GraphQL Client to interact with Infrahub. + +**Methods:** + +#### `get` + +```python +get(self, kind: type[SchemaType], raise_when_missing: Literal[False], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> SchemaType | None +``` + +
+Show 6 other overloads + +#### `get` + +```python +get(self, kind: type[SchemaType], raise_when_missing: Literal[True], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> SchemaType +``` + +#### `get` + +```python +get(self, kind: type[SchemaType], raise_when_missing: bool = ..., at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> SchemaType +``` + +#### `get` + +```python +get(self, kind: str, raise_when_missing: Literal[False], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> InfrahubNode | None +``` + +#### `get` + +```python +get(self, kind: str, raise_when_missing: Literal[True], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> InfrahubNode +``` + +#### `get` + +```python +get(self, kind: str, raise_when_missing: bool = ..., at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> InfrahubNode +``` + +#### `get` + +```python +get(self, kind: str | type[SchemaType], raise_when_missing: bool = True, at: Timestamp | None = None, branch: str | None = None, timeout: int | None = None, id: str | None = None, hfid: list[str] | None = None, include: list[str] | None = None, exclude: list[str] | None = None, populate_store: bool = True, fragment: bool = False, prefetch_relationships: bool = False, property: bool = False, include_metadata: bool = False, **kwargs: Any) -> InfrahubNode | SchemaType | None +``` + +
+#### `delete` + +```python +delete(self, kind: str | type[SchemaType], id: str, branch: str | None = None) -> None +``` + +#### `create` + +```python +create(self, kind: str | type[SchemaType], data: dict | None = None, branch: str | None = None, timeout: int | None = None, **kwargs: Any) -> InfrahubNode | SchemaType +``` + +
+Show 2 other overloads + +#### `create` + +```python +create(self, kind: str, data: dict | None = ..., branch: str | None = ..., **kwargs: Any) -> InfrahubNode +``` + +#### `create` + +```python +create(self, kind: type[SchemaType], data: dict | None = ..., branch: str | None = ..., **kwargs: Any) -> SchemaType +``` + +
+#### `get_version` + +```python +get_version(self) -> str +``` + +Return the Infrahub version. + +#### `get_user` + +```python +get_user(self) -> dict +``` + +Return user information + +#### `get_user_permissions` + +```python +get_user_permissions(self) -> dict +``` + +Return user permissions + +#### `count` + +```python +count(self, kind: str | type[SchemaType], at: Timestamp | None = None, branch: str | None = None, timeout: int | None = None, partial_match: bool = False, **kwargs: Any) -> int +``` + +Return the number of nodes of a given kind. + +#### `all` + +```python +all(self, kind: type[SchemaType], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., populate_store: bool = ..., offset: int | None = ..., limit: int | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., parallel: bool = ..., order: Order | None = ..., include_metadata: bool = ...) -> list[SchemaType] +``` + +
+Show 2 other overloads + +#### `all` + +```python +all(self, kind: str, at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., populate_store: bool = ..., offset: int | None = ..., limit: int | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., parallel: bool = ..., order: Order | None = ..., include_metadata: bool = ...) -> list[InfrahubNode] +``` + +#### `all` + +```python +all(self, kind: str | type[SchemaType], at: Timestamp | None = None, branch: str | None = None, timeout: int | None = None, populate_store: bool = True, offset: int | None = None, limit: int | None = None, include: list[str] | None = None, exclude: list[str] | None = None, fragment: bool = False, prefetch_relationships: bool = False, property: bool = False, parallel: bool = False, order: Order | None = None, include_metadata: bool = False) -> list[InfrahubNode] | list[SchemaType] +``` + +Retrieve all nodes of a given kind + +**Args:** + +- `kind`: kind of the nodes to query +- `at`: Time of the query. Defaults to Now. +- `branch`: Name of the branch to query from. Defaults to default_branch. +- `populate_store`: Flag to indicate whether to populate the store with the retrieved nodes. +- `timeout`: Overrides default timeout used when querying the GraphQL API. Specified in seconds. +- `offset`: The offset for pagination. +- `limit`: The limit for pagination. +- `include`: List of attributes or relationships to include in the query. +- `exclude`: List of attributes or relationships to exclude from the query. +- `fragment`: Flag to use GraphQL fragments for generic schemas. +- `prefetch_relationships`: Flag to indicate whether to pre-fetch related node data. +- `parallel`: Whether to use parallel processing for the query. +- `order`: Ordering related options. Setting `disable=True` enhances performances. +- `include_metadata`: If True, includes node_metadata and relationship_metadata in the query. + +**Returns:** + +- list\[InfrahubNode]: List of Nodes + +
+#### `filters` + +```python +filters(self, kind: type[SchemaType], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., populate_store: bool = ..., offset: int | None = ..., limit: int | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., fragment: bool = ..., prefetch_relationships: bool = ..., partial_match: bool = ..., property: bool = ..., parallel: bool = ..., order: Order | None = ..., include_metadata: bool = ..., **kwargs: Any) -> list[SchemaType] +``` + +
+Show 2 other overloads + +#### `filters` + +```python +filters(self, kind: str, at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., populate_store: bool = ..., offset: int | None = ..., limit: int | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., fragment: bool = ..., prefetch_relationships: bool = ..., partial_match: bool = ..., property: bool = ..., parallel: bool = ..., order: Order | None = ..., include_metadata: bool = ..., **kwargs: Any) -> list[InfrahubNode] +``` + +#### `filters` + +```python +filters(self, kind: str | type[SchemaType], at: Timestamp | None = None, branch: str | None = None, timeout: int | None = None, populate_store: bool = True, offset: int | None = None, limit: int | None = None, include: list[str] | None = None, exclude: list[str] | None = None, fragment: bool = False, prefetch_relationships: bool = False, partial_match: bool = False, property: bool = False, parallel: bool = False, order: Order | None = None, include_metadata: bool = False, **kwargs: Any) -> list[InfrahubNode] | list[SchemaType] +``` + +Retrieve nodes of a given kind based on provided filters. + +**Args:** + +- `kind`: kind of the nodes to query +- `at`: Time of the query. Defaults to Now. +- `branch`: Name of the branch to query from. Defaults to default_branch. +- `timeout`: Overrides default timeout used when querying the GraphQL API. Specified in seconds. +- `populate_store`: Flag to indicate whether to populate the store with the retrieved nodes. +- `offset`: The offset for pagination. +- `limit`: The limit for pagination. +- `include`: List of attributes or relationships to include in the query. +- `exclude`: List of attributes or relationships to exclude from the query. +- `fragment`: Flag to use GraphQL fragments for generic schemas. +- `prefetch_relationships`: Flag to indicate whether to pre-fetch related node data. +- `partial_match`: Allow partial match of filter criteria for the query. +- `parallel`: Whether to use parallel processing for the query. +- `order`: Ordering related options. Setting `disable=True` enhances performances. +- `include_metadata`: If True, includes node_metadata and relationship_metadata in the query. +- `**kwargs`: Additional filter criteria for the query. + +**Returns:** + +- list\[InfrahubNodeSync]: List of Nodes that match the given filters. + +
+#### `clone` + +```python +clone(self, branch: str | None = None) -> InfrahubClient +``` + +Return a cloned version of the client using the same configuration + +#### `execute_graphql` + +```python +execute_graphql(self, query: str, variables: dict | None = None, branch_name: str | None = None, at: str | Timestamp | None = None, timeout: int | None = None, raise_for_error: bool | None = None, tracker: str | None = None) -> dict +``` + +Execute a GraphQL query (or mutation). +If retry_on_failure is True, the query will retry until the server becomes reachable. + +**Args:** + +- `query`: GraphQL Query to execute, can be a query or a mutation +- `variables`: Variables to pass along with the GraphQL query. Defaults to None. +- `branch_name`: Name of the branch on which the query will be executed. Defaults to None. +- `at`: Time when the query should be executed. Defaults to None. +- `timeout`: Timeout in second for the query. Defaults to None. +- `raise_for_error`: Deprecated. Controls only HTTP status handling. +- None (default) or True\: HTTP errors raise via resp.raise_for_status(). +- False\: HTTP errors are not automatically raised. Defaults to None. + +**Raises:** + +- `GraphQLError`: When the GraphQL response contains errors. + +**Returns:** + +- The GraphQL data payload (response["data"]). + +#### `refresh_login` + +```python +refresh_login(self) -> None +``` + +#### `login` + +```python +login(self, refresh: bool = False) -> None +``` + +#### `query_gql_query` + +```python +query_gql_query(self, name: str, variables: dict | None = None, update_group: bool = False, subscribers: list[str] | None = None, params: dict | None = None, branch_name: str | None = None, at: str | None = None, timeout: int | None = None, tracker: str | None = None, raise_for_error: bool | None = None) -> dict +``` + +#### `create_diff` + +```python +create_diff(self, branch: str, name: str, from_time: datetime, to_time: datetime, wait_until_completion: bool = True) -> bool | str +``` + +#### `get_diff_summary` + +```python +get_diff_summary(self, branch: str, name: str | None = None, from_time: datetime | None = None, to_time: datetime | None = None, timeout: int | None = None, tracker: str | None = None, raise_for_error: bool | None = None) -> list[NodeDiff] +``` + +#### `get_diff_tree` + +```python +get_diff_tree(self, branch: str, name: str | None = None, from_time: datetime | None = None, to_time: datetime | None = None, timeout: int | None = None, tracker: str | None = None) -> DiffTreeData | None +``` + +Get complete diff tree with metadata and nodes. + +Returns None if no diff exists. + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNode, kind: type[SchemaType], identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[True] = True) -> SchemaType +``` + +
+Show 6 other overloads + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNode, kind: type[SchemaType], identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[False] = False) -> SchemaType | None +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNode, kind: type[SchemaType], identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: bool | None = ...) -> SchemaType +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNode, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[True] = True) -> CoreNode +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNode, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[False] = False) -> CoreNode | None +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNode, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: bool | None = ...) -> CoreNode | None +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNode, kind: type[SchemaType] | None = None, identifier: str | None = None, prefix_length: int | None = None, address_type: str | None = None, data: dict[str, Any] | None = None, branch: str | None = None, timeout: int | None = None, tracker: str | None = None, raise_for_error: bool | None = None) -> CoreNode | SchemaType | None +``` + +Allocate a new IP address by using the provided resource pool. + +**Args:** + +- `resource_pool`: Node corresponding to the pool to allocate resources from. +- `identifier`: Value to perform idempotent allocation, the same resource will be returned for a given identifier. +- `prefix_length`: Length of the prefix to set on the address to allocate. +- `address_type`: Kind of the address to allocate. +- `data`: A key/value map to use to set attributes values on the allocated address. +- `branch`: Name of the branch to allocate from. Defaults to default_branch. +- `timeout`: Flag to indicate whether to populate the store with the retrieved nodes. +- `tracker`: The offset for pagination. +- `raise_for_error`: Deprecated, raise an error if the HTTP status is not 2XX. + +Returns: + InfrahubNode: Node corresponding to the allocated resource. + +
+#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNode, kind: type[SchemaType], identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[True] = True) -> SchemaType +``` + +
+Show 6 other overloads + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNode, kind: type[SchemaType], identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[False] = False) -> SchemaType | None +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNode, kind: type[SchemaType], identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: bool | None = ...) -> SchemaType +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNode, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[True] = True) -> CoreNode +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNode, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[False] = False) -> CoreNode | None +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNode, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: bool | None = ...) -> CoreNode | None +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNode, kind: type[SchemaType] | None = None, identifier: str | None = None, prefix_length: int | None = None, member_type: str | None = None, prefix_type: str | None = None, data: dict[str, Any] | None = None, branch: str | None = None, timeout: int | None = None, tracker: str | None = None, raise_for_error: bool | None = None) -> CoreNode | SchemaType | None +``` + +Allocate a new IP prefix by using the provided resource pool. + +**Args:** + +- `resource_pool`: Node corresponding to the pool to allocate resources from. +- `identifier`: Value to perform idempotent allocation, the same resource will be returned for a given identifier. +- `prefix_length`: Length of the prefix to allocate. +- `member_type`: Member type of the prefix to allocate. +- `prefix_type`: Kind of the prefix to allocate. +- `data`: A key/value map to use to set attributes values on the allocated prefix. +- `branch`: Name of the branch to allocate from. Defaults to default_branch. +- `timeout`: Flag to indicate whether to populate the store with the retrieved nodes. +- `tracker`: The offset for pagination. +- `raise_for_error`: Deprecated, raise an error if the HTTP status is not 2XX. + +Returns: + InfrahubNode: Node corresponding to the allocated resource. + +
+#### `create_batch` + +```python +create_batch(self, return_exceptions: bool = False) -> InfrahubBatch +``` + +#### `get_list_repositories` + +```python +get_list_repositories(self, branches: dict[str, BranchData] | None = None, kind: str = 'CoreGenericRepository') -> dict[str, RepositoryData] +``` + +#### `repository_update_commit` + +```python +repository_update_commit(self, branch_name: str, repository_id: str, commit: str, is_read_only: bool = False) -> bool +``` + +#### `convert_object_type` + +```python +convert_object_type(self, node_id: str, target_kind: str, branch: str | None = None, fields_mapping: dict[str, ConversionFieldInput] | None = None) -> InfrahubNode +``` + +Convert a given node to another kind on a given branch. `fields_mapping` keys are target fields names +and its values indicate how to fill in these fields. Any mandatory field not having an equivalent field +in the source kind should be specified in this mapping. See https://docs.infrahub.app/guides/object-convert-type +for more information. + +### `InfrahubClientSync` + +**Methods:** + +#### `get` + +```python +get(self, kind: type[SchemaTypeSync], raise_when_missing: Literal[False], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> SchemaTypeSync | None +``` + +
+Show 6 other overloads + +#### `get` + +```python +get(self, kind: type[SchemaTypeSync], raise_when_missing: Literal[True], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> SchemaTypeSync +``` + +#### `get` + +```python +get(self, kind: type[SchemaTypeSync], raise_when_missing: bool = ..., at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> SchemaTypeSync +``` + +#### `get` + +```python +get(self, kind: str, raise_when_missing: Literal[False], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> InfrahubNodeSync | None +``` + +#### `get` + +```python +get(self, kind: str, raise_when_missing: Literal[True], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> InfrahubNodeSync +``` + +#### `get` + +```python +get(self, kind: str, raise_when_missing: bool = ..., at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., id: str | None = ..., hfid: list[str] | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., populate_store: bool = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., include_metadata: bool = ..., **kwargs: Any) -> InfrahubNodeSync +``` + +#### `get` + +```python +get(self, kind: str | type[SchemaTypeSync], raise_when_missing: bool = True, at: Timestamp | None = None, branch: str | None = None, timeout: int | None = None, id: str | None = None, hfid: list[str] | None = None, include: list[str] | None = None, exclude: list[str] | None = None, populate_store: bool = True, fragment: bool = False, prefetch_relationships: bool = False, property: bool = False, include_metadata: bool = False, **kwargs: Any) -> InfrahubNodeSync | SchemaTypeSync | None +``` + +
+#### `delete` + +```python +delete(self, kind: str | type[SchemaTypeSync], id: str, branch: str | None = None) -> None +``` + +#### `create` + +```python +create(self, kind: str | type[SchemaTypeSync], data: dict | None = None, branch: str | None = None, timeout: int | None = None, **kwargs: Any) -> InfrahubNodeSync | SchemaTypeSync +``` + +
+Show 2 other overloads + +#### `create` + +```python +create(self, kind: str, data: dict | None = ..., branch: str | None = ..., **kwargs: Any) -> InfrahubNodeSync +``` + +#### `create` + +```python +create(self, kind: type[SchemaTypeSync], data: dict | None = ..., branch: str | None = ..., **kwargs: Any) -> SchemaTypeSync +``` + +
+#### `get_version` + +```python +get_version(self) -> str +``` + +Return the Infrahub version. + +#### `get_user` + +```python +get_user(self) -> dict +``` + +Return user information + +#### `get_user_permissions` + +```python +get_user_permissions(self) -> dict +``` + +Return user permissions + +#### `clone` + +```python +clone(self, branch: str | None = None) -> InfrahubClientSync +``` + +Return a cloned version of the client using the same configuration + +#### `execute_graphql` + +```python +execute_graphql(self, query: str, variables: dict | None = None, branch_name: str | None = None, at: str | Timestamp | None = None, timeout: int | None = None, raise_for_error: bool | None = None, tracker: str | None = None) -> dict +``` + +Execute a GraphQL query (or mutation). +If retry_on_failure is True, the query will retry until the server becomes reachable. + +**Args:** + +- `query`: GraphQL Query to execute, can be a query or a mutation +- `variables`: Variables to pass along with the GraphQL query. Defaults to None. +- `branch_name`: Name of the branch on which the query will be executed. Defaults to None. +- `at`: Time when the query should be executed. Defaults to None. +- `timeout`: Timeout in second for the query. Defaults to None. +- `raise_for_error`: Deprecated. Controls only HTTP status handling. +- None (default) or True\: HTTP errors raise via `resp.raise_for_status()`. +- False\: HTTP errors are not automatically raised. +GraphQL errors always raise `GraphQLError`. Defaults to None. + +**Raises:** + +- `GraphQLError`: When the GraphQL response contains errors. + +**Returns:** + +- The GraphQL data payload (`response["data"]`). + +#### `count` + +```python +count(self, kind: str | type[SchemaType], at: Timestamp | None = None, branch: str | None = None, timeout: int | None = None, partial_match: bool = False, **kwargs: Any) -> int +``` + +Return the number of nodes of a given kind. + +#### `all` + +```python +all(self, kind: type[SchemaTypeSync], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., populate_store: bool = ..., offset: int | None = ..., limit: int | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., parallel: bool = ..., order: Order | None = ..., include_metadata: bool = ...) -> list[SchemaTypeSync] +``` + +
+Show 2 other overloads + +#### `all` + +```python +all(self, kind: str, at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., populate_store: bool = ..., offset: int | None = ..., limit: int | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., fragment: bool = ..., prefetch_relationships: bool = ..., property: bool = ..., parallel: bool = ..., order: Order | None = ..., include_metadata: bool = ...) -> list[InfrahubNodeSync] +``` + +#### `all` + +```python +all(self, kind: str | type[SchemaTypeSync], at: Timestamp | None = None, branch: str | None = None, timeout: int | None = None, populate_store: bool = True, offset: int | None = None, limit: int | None = None, include: list[str] | None = None, exclude: list[str] | None = None, fragment: bool = False, prefetch_relationships: bool = False, property: bool = False, parallel: bool = False, order: Order | None = None, include_metadata: bool = False) -> list[InfrahubNodeSync] | list[SchemaTypeSync] +``` + +Retrieve all nodes of a given kind + +**Args:** + +- `kind`: kind of the nodes to query +- `at`: Time of the query. Defaults to Now. +- `branch`: Name of the branch to query from. Defaults to default_branch. +- `timeout`: Overrides default timeout used when querying the GraphQL API. Specified in seconds. +- `populate_store`: Flag to indicate whether to populate the store with the retrieved nodes. +- `offset`: The offset for pagination. +- `limit`: The limit for pagination. +- `include`: List of attributes or relationships to include in the query. +- `exclude`: List of attributes or relationships to exclude from the query. +- `fragment`: Flag to use GraphQL fragments for generic schemas. +- `prefetch_relationships`: Flag to indicate whether to pre-fetch related node data. +- `parallel`: Whether to use parallel processing for the query. +- `order`: Ordering related options. Setting `disable=True` enhances performances. +- `include_metadata`: If True, includes node_metadata and relationship_metadata in the query. + +**Returns:** + +- list\[InfrahubNodeSync]: List of Nodes + +
+#### `filters` + +```python +filters(self, kind: type[SchemaTypeSync], at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., populate_store: bool = ..., offset: int | None = ..., limit: int | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., fragment: bool = ..., prefetch_relationships: bool = ..., partial_match: bool = ..., property: bool = ..., parallel: bool = ..., order: Order | None = ..., include_metadata: bool = ..., **kwargs: Any) -> list[SchemaTypeSync] +``` + +
+Show 2 other overloads + +#### `filters` + +```python +filters(self, kind: str, at: Timestamp | None = ..., branch: str | None = ..., timeout: int | None = ..., populate_store: bool = ..., offset: int | None = ..., limit: int | None = ..., include: list[str] | None = ..., exclude: list[str] | None = ..., fragment: bool = ..., prefetch_relationships: bool = ..., partial_match: bool = ..., property: bool = ..., parallel: bool = ..., order: Order | None = ..., include_metadata: bool = ..., **kwargs: Any) -> list[InfrahubNodeSync] +``` + +#### `filters` + +```python +filters(self, kind: str | type[SchemaTypeSync], at: Timestamp | None = None, branch: str | None = None, timeout: int | None = None, populate_store: bool = True, offset: int | None = None, limit: int | None = None, include: list[str] | None = None, exclude: list[str] | None = None, fragment: bool = False, prefetch_relationships: bool = False, partial_match: bool = False, property: bool = False, parallel: bool = False, order: Order | None = None, include_metadata: bool = False, **kwargs: Any) -> list[InfrahubNodeSync] | list[SchemaTypeSync] +``` + +Retrieve nodes of a given kind based on provided filters. + +**Args:** + +- `kind`: kind of the nodes to query +- `at`: Time of the query. Defaults to Now. +- `branch`: Name of the branch to query from. Defaults to default_branch. +- `timeout`: Overrides default timeout used when querying the GraphQL API. Specified in seconds. +- `populate_store`: Flag to indicate whether to populate the store with the retrieved nodes. +- `offset`: The offset for pagination. +- `limit`: The limit for pagination. +- `include`: List of attributes or relationships to include in the query. +- `exclude`: List of attributes or relationships to exclude from the query. +- `fragment`: Flag to use GraphQL fragments for generic schemas. +- `prefetch_relationships`: Flag to indicate whether to pre-fetch related node data. +- `partial_match`: Allow partial match of filter criteria for the query. +- `parallel`: Whether to use parallel processing for the query. +- `order`: Ordering related options. Setting `disable=True` enhances performances. +- `include_metadata`: If True, includes node_metadata and relationship_metadata in the query. +- `**kwargs`: Additional filter criteria for the query. + +**Returns:** + +- list\[InfrahubNodeSync]: List of Nodes that match the given filters. + +
+#### `create_batch` + +```python +create_batch(self, return_exceptions: bool = False) -> InfrahubBatchSync +``` + +Create a batch to execute multiple queries concurrently. + +Executing the batch will be performed using a thread pool, meaning it cannot guarantee the execution order. It is not recommended to use such +batch to manipulate objects that depend on each others. + +#### `get_list_repositories` + +```python +get_list_repositories(self, branches: dict[str, BranchData] | None = None, kind: str = 'CoreGenericRepository') -> dict[str, RepositoryData] +``` + +#### `query_gql_query` + +```python +query_gql_query(self, name: str, variables: dict | None = None, update_group: bool = False, subscribers: list[str] | None = None, params: dict | None = None, branch_name: str | None = None, at: str | None = None, timeout: int | None = None, tracker: str | None = None, raise_for_error: bool | None = None) -> dict +``` + +#### `create_diff` + +```python +create_diff(self, branch: str, name: str, from_time: datetime, to_time: datetime, wait_until_completion: bool = True) -> bool | str +``` + +#### `get_diff_summary` + +```python +get_diff_summary(self, branch: str, name: str | None = None, from_time: datetime | None = None, to_time: datetime | None = None, timeout: int | None = None, tracker: str | None = None, raise_for_error: bool | None = None) -> list[NodeDiff] +``` + +#### `get_diff_tree` + +```python +get_diff_tree(self, branch: str, name: str | None = None, from_time: datetime | None = None, to_time: datetime | None = None, timeout: int | None = None, tracker: str | None = None) -> DiffTreeData | None +``` + +Get complete diff tree with metadata and nodes. + +Returns None if no diff exists. + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNodeSync, kind: type[SchemaTypeSync], identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[True] = True) -> SchemaTypeSync +``` + +
+Show 6 other overloads + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNodeSync, kind: type[SchemaTypeSync], identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[False] = False) -> SchemaTypeSync | None +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNodeSync, kind: type[SchemaTypeSync], identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: bool | None = ...) -> SchemaTypeSync +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNodeSync, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[True] = True) -> CoreNodeSync +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNodeSync, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[False] = False) -> CoreNodeSync | None +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNodeSync, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., address_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: bool | None = ...) -> CoreNodeSync | None +``` + +#### `allocate_next_ip_address` + +```python +allocate_next_ip_address(self, resource_pool: CoreNodeSync, kind: type[SchemaTypeSync] | None = None, identifier: str | None = None, prefix_length: int | None = None, address_type: str | None = None, data: dict[str, Any] | None = None, branch: str | None = None, timeout: int | None = None, tracker: str | None = None, raise_for_error: bool | None = None) -> CoreNodeSync | SchemaTypeSync | None +``` + +Allocate a new IP address by using the provided resource pool. + +**Args:** + +- `resource_pool`: Node corresponding to the pool to allocate resources from. +- `identifier`: Value to perform idempotent allocation, the same resource will be returned for a given identifier. +- `prefix_length`: Length of the prefix to set on the address to allocate. +- `address_type`: Kind of the address to allocate. +- `data`: A key/value map to use to set attributes values on the allocated address. +- `branch`: Name of the branch to allocate from. Defaults to default_branch. +- `timeout`: Flag to indicate whether to populate the store with the retrieved nodes. +- `tracker`: The offset for pagination. +- `raise_for_error`: The limit for pagination. + +Returns: + InfrahubNodeSync: Node corresponding to the allocated resource. + +
+#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNodeSync, kind: type[SchemaTypeSync], identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[True] = True) -> SchemaTypeSync +``` + +
+Show 6 other overloads + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNodeSync, kind: type[SchemaTypeSync], identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[False] = False) -> SchemaTypeSync | None +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNodeSync, kind: type[SchemaTypeSync], identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: bool | None = ...) -> SchemaTypeSync +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNodeSync, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[True] = True) -> CoreNodeSync +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNodeSync, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: Literal[False] = False) -> CoreNodeSync | None +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNodeSync, kind: None = ..., identifier: str | None = ..., prefix_length: int | None = ..., member_type: str | None = ..., prefix_type: str | None = ..., data: dict[str, Any] | None = ..., branch: str | None = ..., timeout: int | None = ..., tracker: str | None = ..., raise_for_error: bool | None = ...) -> CoreNodeSync | None +``` + +#### `allocate_next_ip_prefix` + +```python +allocate_next_ip_prefix(self, resource_pool: CoreNodeSync, kind: type[SchemaTypeSync] | None = None, identifier: str | None = None, prefix_length: int | None = None, member_type: str | None = None, prefix_type: str | None = None, data: dict[str, Any] | None = None, branch: str | None = None, timeout: int | None = None, tracker: str | None = None, raise_for_error: bool | None = None) -> CoreNodeSync | SchemaTypeSync | None +``` + +Allocate a new IP prefix by using the provided resource pool. + +**Args:** + +- `resource_pool`: Node corresponding to the pool to allocate resources from. +- `identifier`: Value to perform idempotent allocation, the same resource will be returned for a given identifier. +- `size`: Length of the prefix to allocate. +- `member_type`: Member type of the prefix to allocate. +- `prefix_type`: Kind of the prefix to allocate. +- `data`: A key/value map to use to set attributes values on the allocated prefix. +- `branch`: Name of the branch to allocate from. Defaults to default_branch. +- `timeout`: Flag to indicate whether to populate the store with the retrieved nodes. +- `tracker`: The offset for pagination. +- `raise_for_error`: The limit for pagination. + +Returns: + InfrahubNodeSync: Node corresponding to the allocated resource. + +
+#### `repository_update_commit` + +```python +repository_update_commit(self, branch_name: str, repository_id: str, commit: str, is_read_only: bool = False) -> bool +``` + +#### `refresh_login` + +```python +refresh_login(self) -> None +``` + +#### `login` + +```python +login(self, refresh: bool = False) -> None +``` + +#### `convert_object_type` + +```python +convert_object_type(self, node_id: str, target_kind: str, branch: str | None = None, fields_mapping: dict[str, ConversionFieldInput] | None = None) -> InfrahubNodeSync +``` + +Convert a given node to another kind on a given branch. `fields_mapping` keys are target fields names +and its values indicate how to fill in these fields. Any mandatory field not having an equivalent field +in the source kind should be specified in this mapping. See https://docs.infrahub.app/guides/object-convert-type +for more information. + +### `ProcessRelationsNode` + +### `ProxyConfig` + +### `ProxyConfigSync` + +### `ProcessRelationsNodeSync` + +### `BaseClient` + +Base class for InfrahubClient and InfrahubClientSync + +**Methods:** + +#### `request_context` + +```python +request_context(self) -> RequestContext | None +``` + +#### `request_context` + +```python +request_context(self, request_context: RequestContext) -> None +``` + +#### `start_tracking` + +```python +start_tracking(self, identifier: str | None = None, params: dict[str, Any] | None = None, delete_unused_nodes: bool = False, group_type: str | None = None, group_params: dict[str, Any] | None = None, branch: str | None = None) -> Self +``` + +#### `set_context_properties` + +```python +set_context_properties(self, identifier: str, params: dict[str, str] | None = None, delete_unused_nodes: bool = True, reset: bool = True, group_type: str | None = None, group_params: dict[str, Any] | None = None, branch: str | None = None) -> None +``` + +## Functions + +### `handle_relogin` + +```python +handle_relogin(func: Callable[..., Coroutine[Any, Any, httpx.Response]]) -> Callable[..., Coroutine[Any, Any, httpx.Response]] +``` + +### `handle_relogin_sync` + +```python +handle_relogin_sync(func: Callable[..., httpx.Response]) -> Callable[..., httpx.Response] +``` + +### `raise_for_error_deprecation_warning` + +```python +raise_for_error_deprecation_warning(value: bool | None) -> None +``` diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/attribute.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/attribute.mdx new file mode 100644 index 00000000..a7b82ecb --- /dev/null +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/attribute.mdx @@ -0,0 +1,26 @@ +--- +title: attribute +sidebarTitle: attribute +--- + +# `infrahub_sdk.node.attribute` + +## Classes + +### `Attribute` + +Represents an attribute of a Node, including its schema, value, and properties. + +**Methods:** + +#### `value` + +```python +value(self) -> Any +``` + +#### `value` + +```python +value(self, value: Any) -> None +``` diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/constants.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/constants.mdx new file mode 100644 index 00000000..290f923e --- /dev/null +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/constants.mdx @@ -0,0 +1,8 @@ +--- +title: constants +sidebarTitle: constants +--- + +# `infrahub_sdk.node.constants` + +*This module is empty or contains only private/internal implementations.* diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/metadata.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/metadata.mdx new file mode 100644 index 00000000..6175236f --- /dev/null +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/metadata.mdx @@ -0,0 +1,16 @@ +--- +title: metadata +sidebarTitle: metadata +--- + +# `infrahub_sdk.node.metadata` + +## Classes + +### `NodeMetadata` + +Represents metadata about a node (created_at, created_by, updated_at, updated_by). + +### `RelationshipMetadata` + +Represents metadata about a relationship edge (updated_at, updated_by). diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/node.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/node.mdx new file mode 100644 index 00000000..e23120dd --- /dev/null +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/node.mdx @@ -0,0 +1,526 @@ +--- +title: node +sidebarTitle: node +--- + +# `infrahub_sdk.node.node` + +## Classes + +### `InfrahubNode` + +Represents a Infrahub node in an asynchronous context. + +**Methods:** + +#### `from_graphql` + +```python +from_graphql(cls, client: InfrahubClient, branch: str, data: dict, schema: MainSchemaTypesAPI | None = None, timeout: int | None = None) -> Self +``` + +#### `generate` + +```python +generate(self, nodes: list[str] | None = None) -> None +``` + +#### `artifact_generate` + +```python +artifact_generate(self, name: str) -> None +``` + +#### `artifact_fetch` + +```python +artifact_fetch(self, name: str) -> str | dict[str, Any] +``` + +#### `download_file` + +```python +download_file(self, dest: Path | None = None) -> bytes | int +``` + +Download the file content from this FileObject node. + +This method is only available for nodes that inherit from CoreFileObject. +The node must have been saved (have an id) before calling this method. + +**Args:** + +- `dest`: Optional destination path. If provided, the file will be streamed + directly to this path (memory-efficient for large files) and the + number of bytes written will be returned. If not provided, the + file content will be returned as bytes. + +**Returns:** + +- If ``dest`` is None: The file content as bytes. +- If ``dest`` is provided: The number of bytes written to the file. + +**Raises:** + +- `FeatureNotSupportedError`: If this node doesn't inherit from CoreFileObject. +- `ValueError`: If the node hasn't been saved yet or file not found. +- `AuthenticationError`: If authentication fails. + +**Examples:** + +```python +>>> # Download to memory +>>> content = await contract.download_file() +>>> # Stream to file (memory-efficient for large files) +>>> bytes_written = await contract.download_file(dest=Path("/tmp/contract.pdf")) +``` + +#### `delete` + +```python +delete(self, timeout: int | None = None, request_context: RequestContext | None = None) -> None +``` + +#### `save` + +```python +save(self, allow_upsert: bool = False, update_group_context: bool | None = None, timeout: int | None = None, request_context: RequestContext | None = None) -> None +``` + +#### `generate_query_data` + +```python +generate_query_data(self, filters: dict[str, Any] | None = None, offset: int | None = None, limit: int | None = None, include: list[str] | None = None, exclude: list[str] | None = None, fragment: bool = False, prefetch_relationships: bool = False, partial_match: bool = False, property: bool = False, order: Order | None = None, include_metadata: bool = False) -> dict[str, Any | dict] +``` + +#### `generate_query_data_node` + +```python +generate_query_data_node(self, include: list[str] | None = None, exclude: list[str] | None = None, inherited: bool = True, insert_alias: bool = False, prefetch_relationships: bool = False, property: bool = False, include_metadata: bool = False) -> dict[str, Any | dict] +``` + +Generate the node part of a GraphQL Query with attributes and nodes. + +**Args:** + +- `include`: List of attributes or relationships to include. Defaults to None. +- `exclude`: List of attributes or relationships to exclude. Defaults to None. +- `inherited`: Indicated of the attributes and the relationships inherited from generics should be included as well. + Defaults to True. +- `insert_alias`: If True, inserts aliases in the query for each attribute or relationship. +- `prefetch_relationships`: If True, pre-fetches relationship data as part of the query. +- `include_metadata`: If True, includes node_metadata and relationship_metadata in the query. + +**Returns:** + +- dict\[str, Union\[Any, Dict]]: GraphQL query in dictionary format + +#### `add_relationships` + +```python +add_relationships(self, relation_to_update: str, related_nodes: list[str]) -> None +``` + +#### `remove_relationships` + +```python +remove_relationships(self, relation_to_update: str, related_nodes: list[str]) -> None +``` + +#### `create` + +```python +create(self, allow_upsert: bool = False, timeout: int | None = None, request_context: RequestContext | None = None) -> None +``` + +#### `update` + +```python +update(self, do_full_update: bool = False, timeout: int | None = None, request_context: RequestContext | None = None) -> None +``` + +#### `get_pool_allocated_resources` + +```python +get_pool_allocated_resources(self, resource: InfrahubNode) -> list[InfrahubNode] +``` + +Fetch all nodes that were allocated for the pool and a given resource. + +**Args:** + +- `resource`: The resource from which the nodes were allocated. + +**Returns:** + +- list\[InfrahubNode]: The allocated nodes. + +#### `get_pool_resources_utilization` + +```python +get_pool_resources_utilization(self) -> list[dict[str, Any]] +``` + +Fetch the utilization of each resource for the pool. + +**Returns:** + +- list\[dict\[str, Any]]: A list containing the allocation numbers for each resource of the pool. + +#### `get_flat_value` + +```python +get_flat_value(self, key: str, separator: str = '__') -> Any +``` + +Query recursively a value defined in a flat notation (string), on a hierarchy of objects + +**Examples:** + +name__value +module.object.value + +#### `extract` + +```python +extract(self, params: dict[str, str]) -> dict[str, Any] +``` + +Extract some data points defined in a flat notation. + +### `InfrahubNodeSync` + +Represents a Infrahub node in a synchronous context. + +**Methods:** + +#### `from_graphql` + +```python +from_graphql(cls, client: InfrahubClientSync, branch: str, data: dict, schema: MainSchemaTypesAPI | None = None, timeout: int | None = None) -> Self +``` + +#### `generate` + +```python +generate(self, nodes: list[str] | None = None) -> None +``` + +#### `artifact_generate` + +```python +artifact_generate(self, name: str) -> None +``` + +#### `artifact_fetch` + +```python +artifact_fetch(self, name: str) -> str | dict[str, Any] +``` + +#### `download_file` + +```python +download_file(self, dest: Path | None = None) -> bytes | int +``` + +Download the file content from this FileObject node. + +This method is only available for nodes that inherit from CoreFileObject. +The node must have been saved (have an id) before calling this method. + +**Args:** + +- `dest`: Optional destination path. If provided, the file will be streamed + directly to this path (memory-efficient for large files) and the + number of bytes written will be returned. If not provided, the + file content will be returned as bytes. + +**Returns:** + +- If ``dest`` is None: The file content as bytes. +- If ``dest`` is provided: The number of bytes written to the file. + +**Raises:** + +- `FeatureNotSupportedError`: If this node doesn't inherit from CoreFileObject. +- `ValueError`: If the node hasn't been saved yet or file not found. +- `AuthenticationError`: If authentication fails. + +**Examples:** + +```python +>>> # Download to memory +>>> content = contract.download_file() +>>> # Stream to file (memory-efficient for large files) +>>> bytes_written = contract.download_file(dest=Path("/tmp/contract.pdf")) +``` + +#### `delete` + +```python +delete(self, timeout: int | None = None, request_context: RequestContext | None = None) -> None +``` + +#### `save` + +```python +save(self, allow_upsert: bool = False, update_group_context: bool | None = None, timeout: int | None = None, request_context: RequestContext | None = None) -> None +``` + +#### `generate_query_data` + +```python +generate_query_data(self, filters: dict[str, Any] | None = None, offset: int | None = None, limit: int | None = None, include: list[str] | None = None, exclude: list[str] | None = None, fragment: bool = False, prefetch_relationships: bool = False, partial_match: bool = False, property: bool = False, order: Order | None = None, include_metadata: bool = False) -> dict[str, Any | dict] +``` + +#### `generate_query_data_node` + +```python +generate_query_data_node(self, include: list[str] | None = None, exclude: list[str] | None = None, inherited: bool = True, insert_alias: bool = False, prefetch_relationships: bool = False, property: bool = False, include_metadata: bool = False) -> dict[str, Any | dict] +``` + +Generate the node part of a GraphQL Query with attributes and nodes. + +**Args:** + +- `include`: List of attributes or relationships to include. Defaults to None. +- `exclude`: List of attributes or relationships to exclude. Defaults to None. +- `inherited`: Indicated of the attributes and the relationships inherited from generics should be included as well. + Defaults to True. +- `insert_alias`: If True, inserts aliases in the query for each attribute or relationship. +- `prefetch_relationships`: If True, pre-fetches relationship data as part of the query. +- `include_metadata`: If True, includes node_metadata and relationship_metadata in the query. + +**Returns:** + +- dict\[str, Union\[Any, Dict]]: GraphQL query in dictionary format + +#### `add_relationships` + +```python +add_relationships(self, relation_to_update: str, related_nodes: list[str]) -> None +``` + +#### `remove_relationships` + +```python +remove_relationships(self, relation_to_update: str, related_nodes: list[str]) -> None +``` + +#### `create` + +```python +create(self, allow_upsert: bool = False, timeout: int | None = None, request_context: RequestContext | None = None) -> None +``` + +#### `update` + +```python +update(self, do_full_update: bool = False, timeout: int | None = None, request_context: RequestContext | None = None) -> None +``` + +#### `get_pool_allocated_resources` + +```python +get_pool_allocated_resources(self, resource: InfrahubNodeSync) -> list[InfrahubNodeSync] +``` + +Fetch all nodes that were allocated for the pool and a given resource. + +**Args:** + +- `resource`: The resource from which the nodes were allocated. + +**Returns:** + +- list\[InfrahubNodeSync]: The allocated nodes. + +#### `get_pool_resources_utilization` + +```python +get_pool_resources_utilization(self) -> list[dict[str, Any]] +``` + +Fetch the utilization of each resource for the pool. + +**Returns:** + +- list\[dict\[str, Any]]: A list containing the allocation numbers for each resource of the pool. + +#### `get_flat_value` + +```python +get_flat_value(self, key: str, separator: str = '__') -> Any +``` + +Query recursively a value defined in a flat notation (string), on a hierarchy of objects + +**Examples:** + +name__value +module.object.value + +#### `extract` + +```python +extract(self, params: dict[str, str]) -> dict[str, Any] +``` + +Extract some data points defined in a flat notation. + +### `InfrahubNodeBase` + +Base class for InfrahubNode and InfrahubNodeSync + +**Methods:** + +#### `get_branch` + +```python +get_branch(self) -> str +``` + +#### `get_path_value` + +```python +get_path_value(self, path: str) -> Any +``` + +#### `get_human_friendly_id` + +```python +get_human_friendly_id(self) -> list[str] | None +``` + +#### `get_human_friendly_id_as_string` + +```python +get_human_friendly_id_as_string(self, include_kind: bool = False) -> str | None +``` + +#### `hfid` + +```python +hfid(self) -> list[str] | None +``` + +#### `hfid_str` + +```python +hfid_str(self) -> str | None +``` + +#### `get_node_metadata` + +```python +get_node_metadata(self) -> NodeMetadata | None +``` + +Returns the node metadata (created_at, created_by, updated_at, updated_by) if fetched. + +#### `get_kind` + +```python +get_kind(self) -> str +``` + +#### `get_all_kinds` + +```python +get_all_kinds(self) -> list[str] +``` + +#### `is_ip_prefix` + +```python +is_ip_prefix(self) -> bool +``` + +#### `is_ip_address` + +```python +is_ip_address(self) -> bool +``` + +#### `is_resource_pool` + +```python +is_resource_pool(self) -> bool +``` + +#### `is_file_object` + +```python +is_file_object(self) -> bool +``` + +Check if this node inherits from CoreFileObject and supports file uploads. + +#### `upload_from_path` + +```python +upload_from_path(self, path: Path) -> None +``` + +Set a file from disk to be uploaded when saving this FileObject node. + +The file will be streamed during upload, avoiding loading the entire file into memory. + +**Args:** + +- `path`: Path to the file on disk. + +**Raises:** + +- `FeatureNotSupportedError`: If this node doesn't inherit from CoreFileObject. + +#### `upload_from_bytes` + +```python +upload_from_bytes(self, content: bytes | BinaryIO, name: str) -> None +``` + +Set content to be uploaded when saving this FileObject node. + +The content can be provided as bytes or a file-like object. +Using BinaryIO is recommended for large content to stream during upload. + +**Args:** + +- `content`: The file content as bytes or a file-like object. +- `name`: The filename to use for the uploaded file. + +**Raises:** + +- `FeatureNotSupportedError`: If this node doesn't inherit from CoreFileObject. + +**Examples:** + +```python +>>> # Using bytes (for small files) +>>> node.upload_from_bytes(content=b"file content", name="example.txt") +>>> # Using file-like object (for large files) +>>> with open("/path/to/file.bin", "rb") as f: +... node.upload_from_bytes(content=f, name="file.bin") +``` + +#### `clear_file` + +```python +clear_file(self) -> None +``` + +Clear any pending file content. + +#### `get_raw_graphql_data` + +```python +get_raw_graphql_data(self) -> dict | None +``` + +#### `generate_query_data_init` + +```python +generate_query_data_init(self, filters: dict[str, Any] | None = None, offset: int | None = None, limit: int | None = None, include: list[str] | None = None, exclude: list[str] | None = None, partial_match: bool = False, order: Order | None = None, include_metadata: bool = False) -> dict[str, Any | dict] +``` diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/parsers.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/parsers.mdx new file mode 100644 index 00000000..f70c6788 --- /dev/null +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/parsers.mdx @@ -0,0 +1,16 @@ +--- +title: parsers +sidebarTitle: parsers +--- + +# `infrahub_sdk.node.parsers` + +## Functions + +### `parse_human_friendly_id` + +```python +parse_human_friendly_id(hfid: str | list[str]) -> tuple[str | None, list[str]] +``` + +Parse a human-friendly ID into a kind and an identifier. diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/property.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/property.mdx new file mode 100644 index 00000000..a7400483 --- /dev/null +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/property.mdx @@ -0,0 +1,12 @@ +--- +title: property +sidebarTitle: property +--- + +# `infrahub_sdk.node.property` + +## Classes + +### `NodeProperty` + +Represents a property of a node, typically used for metadata like display labels. diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/related_node.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/related_node.mdx new file mode 100644 index 00000000..edc1112c --- /dev/null +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/related_node.mdx @@ -0,0 +1,126 @@ +--- +title: related_node +sidebarTitle: related_node +--- + +# `infrahub_sdk.node.related_node` + +## Classes + +### `RelatedNodeBase` + +Base class for representing a related node in a relationship. + +**Methods:** + +#### `id` + +```python +id(self) -> str | None +``` + +#### `hfid` + +```python +hfid(self) -> list[Any] | None +``` + +#### `hfid_str` + +```python +hfid_str(self) -> str | None +``` + +#### `is_resource_pool` + +```python +is_resource_pool(self) -> bool +``` + +#### `initialized` + +```python +initialized(self) -> bool +``` + +#### `display_label` + +```python +display_label(self) -> str | None +``` + +#### `typename` + +```python +typename(self) -> str | None +``` + +#### `kind` + +```python +kind(self) -> str | None +``` + +#### `is_from_profile` + +```python +is_from_profile(self) -> bool +``` + +Return whether this relationship was set from a profile. Done by checking if the source is of a profile kind. + +#### `get_relationship_metadata` + +```python +get_relationship_metadata(self) -> RelationshipMetadata | None +``` + +Returns the relationship metadata (updated_at, updated_by) if fetched. + +### `RelatedNode` + +Represents a RelatedNodeBase in an asynchronous context. + +**Methods:** + +#### `fetch` + +```python +fetch(self, timeout: int | None = None) -> None +``` + +#### `peer` + +```python +peer(self) -> InfrahubNode +``` + +#### `get` + +```python +get(self) -> InfrahubNode +``` + +### `RelatedNodeSync` + +Represents a related node in a synchronous context. + +**Methods:** + +#### `fetch` + +```python +fetch(self, timeout: int | None = None) -> None +``` + +#### `peer` + +```python +peer(self) -> InfrahubNodeSync +``` + +#### `get` + +```python +get(self) -> InfrahubNodeSync +``` diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/relationship.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/relationship.mdx new file mode 100644 index 00000000..567b7c8d --- /dev/null +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/relationship.mdx @@ -0,0 +1,114 @@ +--- +title: relationship +sidebarTitle: relationship +--- + +# `infrahub_sdk.node.relationship` + +## Classes + +### `RelationshipManagerBase` + +Base class for RelationshipManager and RelationshipManagerSync + +**Methods:** + +#### `peer_ids` + +```python +peer_ids(self) -> list[str] +``` + +#### `peer_hfids` + +```python +peer_hfids(self) -> list[list[Any]] +``` + +#### `peer_hfids_str` + +```python +peer_hfids_str(self) -> list[str] +``` + +#### `has_update` + +```python +has_update(self) -> bool +``` + +#### `is_from_profile` + +```python +is_from_profile(self) -> bool +``` + +Return whether this relationship was set from a profile. All its peers must be from a profile. + +### `RelationshipManager` + +Manages relationships of a node in an asynchronous context. + +**Methods:** + +#### `fetch` + +```python +fetch(self) -> None +``` + +#### `add` + +```python +add(self, data: str | RelatedNode | dict) -> None +``` + +Add a new peer to this relationship. + +#### `extend` + +```python +extend(self, data: Iterable[str | RelatedNode | dict]) -> None +``` + +Add new peers to this relationship. + +#### `remove` + +```python +remove(self, data: str | RelatedNode | dict) -> None +``` + +### `RelationshipManagerSync` + +Manages relationships of a node in a synchronous context. + +**Methods:** + +#### `fetch` + +```python +fetch(self) -> None +``` + +#### `add` + +```python +add(self, data: str | RelatedNodeSync | dict) -> None +``` + +Add a new peer to this relationship. + +#### `extend` + +```python +extend(self, data: Iterable[str | RelatedNodeSync | dict]) -> None +``` + +Add new peers to this relationship. + +#### `remove` + +```python +remove(self, data: str | RelatedNodeSync | dict) -> None +``` diff --git a/docs/docs_generation/__init__.py b/docs/docs_generation/__init__.py new file mode 100644 index 00000000..4b462a71 --- /dev/null +++ b/docs/docs_generation/__init__.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from .content_gen_methods import ( + ACommand, + ADocContentGenMethod, + CommandOutputDocContentGenMethod, + FilePrintingDocContentGenMethod, + Jinja2DocContentGenMethod, + MdxCodeDocumentation, + TyperGroupCommand, + TyperSingleCommand, +) +from .pages import DocPage, MDXDocPage + +__all__ = [ + "ACommand", + "ADocContentGenMethod", + "CommandOutputDocContentGenMethod", + "DocPage", + "FilePrintingDocContentGenMethod", + "Jinja2DocContentGenMethod", + "MDXDocPage", + "MdxCodeDocumentation", + "TyperGroupCommand", + "TyperSingleCommand", +] diff --git a/docs/docs_generation/content_gen_methods/__init__.py b/docs/docs_generation/content_gen_methods/__init__.py new file mode 100644 index 00000000..792539be --- /dev/null +++ b/docs/docs_generation/content_gen_methods/__init__.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from docs.docs_generation.content_gen_methods.command.command import ACommand +from docs.docs_generation.content_gen_methods.command.typer_command import TyperGroupCommand, TyperSingleCommand +from docs.docs_generation.content_gen_methods.mdx.mdx_code_doc import ACodeDocumentation, MdxCodeDocumentation +from docs.docs_generation.content_gen_methods.mdx.mdx_collapsed_overload_code_doc import ( + CollapsedOverloadCodeDocumentation, +) +from docs.docs_generation.content_gen_methods.mdx.mdx_ordered_code_doc import OrderedMdxCodeDocumentation + +from .base import ADocContentGenMethod +from .command_output_method import CommandOutputDocContentGenMethod +from .file_printing_method import FilePrintingDocContentGenMethod +from .jinja2_method import Jinja2DocContentGenMethod + +__all__ = [ + "ACodeDocumentation", + "ACommand", + "ADocContentGenMethod", + "CollapsedOverloadCodeDocumentation", + "CommandOutputDocContentGenMethod", + "FilePrintingDocContentGenMethod", + "Jinja2DocContentGenMethod", + "MdxCodeDocumentation", + "OrderedMdxCodeDocumentation", + "TyperGroupCommand", + "TyperSingleCommand", +] diff --git a/docs/docs_generation/content_gen_methods/base.py b/docs/docs_generation/content_gen_methods/base.py new file mode 100644 index 00000000..ddab2dde --- /dev/null +++ b/docs/docs_generation/content_gen_methods/base.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod + + +class ADocContentGenMethod(ABC): + """Strategy for producing documentation content as a string. + + Each subclass implements ``apply()`` for a specific content source + (Jinja2 template, CLI command, pre-generated file, ...). + """ + + @abstractmethod + def apply(self) -> str: + """Generate the documentation content.""" diff --git a/docs/docs_generation/content_gen_methods/command/__init__.py b/docs/docs_generation/content_gen_methods/command/__init__.py new file mode 100644 index 00000000..a1bc4e0a --- /dev/null +++ b/docs/docs_generation/content_gen_methods/command/__init__.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from .command import ACommand + +__all__ = [ + "ACommand", +] diff --git a/docs/docs_generation/content_gen_methods/command/command.py b/docs/docs_generation/content_gen_methods/command/command.py new file mode 100644 index 00000000..83a646c2 --- /dev/null +++ b/docs/docs_generation/content_gen_methods/command/command.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod + + +class ACommand(ABC): + """Abstract base for building a shell command string.""" + + @abstractmethod + def build(self) -> str: + """Return the full command string to execute.""" diff --git a/docs/docs_generation/content_gen_methods/command/typer_command.py b/docs/docs_generation/content_gen_methods/command/typer_command.py new file mode 100644 index 00000000..180f9b57 --- /dev/null +++ b/docs/docs_generation/content_gen_methods/command/typer_command.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from abc import ABC + +from .command import ACommand + + +class ATyperCommand(ACommand, ABC): + def __init__(self, name: str) -> None: + self.name = name + + +class TyperSingleCommand(ATyperCommand): + """A single (non-group) infrahubctl command.""" + + def build(self) -> str: + return ( + f'uv run typer --func {self.name} infrahub_sdk.ctl.cli_commands utils docs --name "infrahubctl {self.name}"' + ) + + +class TyperGroupCommand(ATyperCommand): + """An infrahubctl command group (e.g. ``branch``, ``schema``).""" + + def build(self) -> str: + return f'uv run typer infrahub_sdk.ctl.{self.name} utils docs --name "infrahubctl {self.name}"' diff --git a/docs/docs_generation/content_gen_methods/command_output_method.py b/docs/docs_generation/content_gen_methods/command_output_method.py new file mode 100644 index 00000000..bbb32f04 --- /dev/null +++ b/docs/docs_generation/content_gen_methods/command_output_method.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import tempfile +from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from invoke import Context + + from .command import ACommand + +from .base import ADocContentGenMethod + + +class CommandOutputDocContentGenMethod(ADocContentGenMethod): + """Run a command and return the content it writes to a temporary file. + + ``--output `` is appended to the command automatically. + + Args: + context: Invoke execution context. + working_directory: Directory in which the command is executed. + command: An ``ACommand`` whose ``build()`` returns the base command string. + + Example:: + + method = CommandOutputDocContentGenMethod( + context=ctx, + working_directory=project_root, + command=TyperCommand(module="infrahub_sdk.ctl.cli_commands", name="dump", app_name="infrahubctl", is_function=True), + ) + content = method.apply() + """ + + def __init__(self, context: Context, working_directory: Path, command: ACommand) -> None: + self.context = context + self.working_directory = working_directory + self.command = command + + def apply(self) -> str: + with tempfile.NamedTemporaryFile(mode="w", suffix=".mdx", delete=False, encoding="utf-8") as tmp: + tmp_path = Path(tmp.name) + + try: + full_cmd = f"{self.command.build()} --output {tmp_path}" + with self.context.cd(self.working_directory): + self.context.run(full_cmd) + + return tmp_path.read_text(encoding="utf-8") + finally: + tmp_path.unlink(missing_ok=True) diff --git a/docs/docs_generation/content_gen_methods/file_printing_method.py b/docs/docs_generation/content_gen_methods/file_printing_method.py new file mode 100644 index 00000000..1b07aa20 --- /dev/null +++ b/docs/docs_generation/content_gen_methods/file_printing_method.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .base import ADocContentGenMethod + +if TYPE_CHECKING: + from docs.docs_generation.content_gen_methods.mdx.mdx_code_doc import MdxFile + + +class FilePrintingDocContentGenMethod(ADocContentGenMethod): + """Return the content of an already-generated file as-is. + + Args: + file: The ``MdxFile`` whose content will be returned. + """ + + def __init__(self, file: MdxFile) -> None: + self.file = file + + def apply(self) -> str: + return self.file.content diff --git a/docs/docs_generation/content_gen_methods/jinja2_method.py b/docs/docs_generation/content_gen_methods/jinja2_method.py new file mode 100644 index 00000000..c91c0d7c --- /dev/null +++ b/docs/docs_generation/content_gen_methods/jinja2_method.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +import asyncio +from typing import TYPE_CHECKING, Any + +from .base import ADocContentGenMethod + +if TYPE_CHECKING: + from infrahub_sdk.template import Jinja2Template + + +class Jinja2DocContentGenMethod(ADocContentGenMethod): + """Render a template using a ``Jinja2Template``. + + The template engine is async; rendering is run synchronously via ``asyncio.run``. + + Args: + template: A ``Jinja2Template`` instance. + template_variables: Variables passed to the template during rendering. + + Example:: + + template = Jinja2Template( + template=Path("sdk_template_reference.j2"), + template_directory=docs_dir / "_templates", + ) + method = Jinja2DocContentGenMethod( + template=template, + template_variables={"builtin": BUILTIN_FILTERS}, + ) + content = method.apply() + """ + + def __init__(self, template: Jinja2Template, template_variables: dict[str, Any]) -> None: + self.template = template + self.template_variables = template_variables + + def apply(self) -> str: + return asyncio.run(self.template.render(variables=self.template_variables)) diff --git a/docs/docs_generation/content_gen_methods/mdx/__init__.py b/docs/docs_generation/content_gen_methods/mdx/__init__.py new file mode 100644 index 00000000..04032f7c --- /dev/null +++ b/docs/docs_generation/content_gen_methods/mdx/__init__.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from .mdx_code_doc import ACodeDocumentation, MdxCodeDocumentation, MdxFile +from .mdx_collapsed_overload_code_doc import CollapsedOverloadCodeDocumentation +from .mdx_collapsed_overload_section import CollapsedOverloadSection +from .mdx_ordered_code_doc import OrderedMdxCodeDocumentation +from .mdx_ordered_section import OrderedMdxSection +from .mdx_section import MdxSection + +__all__ = [ + "ACodeDocumentation", + "CollapsedOverloadCodeDocumentation", + "CollapsedOverloadSection", + "MdxCodeDocumentation", + "MdxFile", + "MdxSection", + "OrderedMdxCodeDocumentation", + "OrderedMdxSection", +] diff --git a/docs/docs_generation/content_gen_methods/mdx/mdx_code_doc.py b/docs/docs_generation/content_gen_methods/mdx/mdx_code_doc.py new file mode 100644 index 00000000..1fb3e8c9 --- /dev/null +++ b/docs/docs_generation/content_gen_methods/mdx/mdx_code_doc.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import tempfile +from abc import ABC, abstractmethod +from dataclasses import dataclass +from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from invoke import Context + + +def _wrap_doctest_examples(content: str) -> str: + """Wrap bare ``>>>`` doctest blocks in fenced code blocks for MDX compatibility. + + mdxify does not fence doctest examples, so curly braces and brackets + in those lines cause MDX/acorn parse errors. + """ + lines = content.split("\n") + result: list[str] = [] + in_fence = False + in_doctest = False + + for line in lines: + if line.startswith("```"): + if in_doctest: + result.append("```") + in_doctest = False + in_fence = not in_fence + result.append(line) + continue + + if in_fence: + result.append(line) + continue + + if line.startswith(">>>"): + if not in_doctest: + result.append("```python") + in_doctest = True + result.append(line) + elif in_doctest: + if not line.strip() or line.startswith("#"): + result.append("```") + in_doctest = False + result.append(line) + else: + result.append(line) + else: + result.append(line) + + if in_doctest: + result.append("```") + + return "\n".join(result) + + +def _source_path_from_mdx_name(mdx_filename: str) -> Path: + """Derive the Python source file path from an mdxify output filename. + + mdxify names output files using ``-`` as a path separator, e.g. + ``infrahub_sdk-node-node.mdx`` comes from ``infrahub_sdk/node/node.py``. + """ + stem = Path(mdx_filename).stem + return Path(stem.replace("-", "/")).with_suffix(".py") + + +@dataclass +class MdxFile: + """Content of a single ``.mdx`` file produced by mdxify.""" + + name: str + content: str + source_path: Path + + +class ACodeDocumentation(ABC): + """Abstract base for code documentation generators.""" + + @abstractmethod + def generate(self, context: Context, modules_to_document: list[str]) -> dict[str, MdxFile]: ... + + +class MdxCodeDocumentation(ACodeDocumentation): + """Run mdxify once and cache the resulting files. + + Args: + file_filters: Substrings to exclude from output filenames. + Defaults to ``["__init__"]``. + + Example:: + + doc = MdxCodeDocumentation() + files = doc.generate(context=ctx, modules_to_document=["infrahub_sdk.node"]) + """ + + def __init__( + self, + file_filters: list[str] | None = None, + ) -> None: + self.file_filters = file_filters or ["__init__"] + self._cache: dict[frozenset[str], dict[str, MdxFile]] = {} + + def generate(self, context: Context, modules_to_document: list[str]) -> dict[str, MdxFile]: + """Return mdxify results, re-running the tool when *modules_to_document* changes.""" + key = frozenset(modules_to_document) + if key not in self._cache: + self._cache[key] = self._execute_mdxify(context, modules_to_document) + return self._cache[key] + + def _execute_mdxify(self, context: Context, modules_to_document: list[str]) -> dict[str, MdxFile]: + with tempfile.TemporaryDirectory() as tmp_dir: + exec_cmd = f"mdxify {' '.join(modules_to_document)} --output-dir {tmp_dir}" + context.run(exec_cmd, pty=True) + + results: dict[str, MdxFile] = {} + for mdx_file in Path(tmp_dir).glob("*.mdx"): + if any(f.lower() in mdx_file.name for f in self.file_filters): + continue + content = _wrap_doctest_examples(mdx_file.read_text(encoding="utf-8")) + results[mdx_file.name] = MdxFile( + name=mdx_file.name, + content=content, + source_path=_source_path_from_mdx_name(mdx_file.name), + ) + + return results diff --git a/docs/docs_generation/content_gen_methods/mdx/mdx_collapsed_overload_code_doc.py b/docs/docs_generation/content_gen_methods/mdx/mdx_collapsed_overload_code_doc.py new file mode 100644 index 00000000..1f680d15 --- /dev/null +++ b/docs/docs_generation/content_gen_methods/mdx/mdx_collapsed_overload_code_doc.py @@ -0,0 +1,133 @@ +from __future__ import annotations + +from dataclasses import dataclass +from itertools import groupby +from typing import TYPE_CHECKING + +from .mdx_code_doc import ACodeDocumentation, MdxFile +from .mdx_collapsed_overload_section import CollapsedOverloadSection, MethodSignature +from .mdx_section import ASection, MdxSection, _parse_sections + +if TYPE_CHECKING: + from invoke import Context + + +@dataclass +class CollapsedOverloadCodeDocumentation(ACodeDocumentation): + """Decorator around :class:`ACodeDocumentation` that collapses overloaded methods. + + Delegates generation to the wrapped *documentation* instance, then + replaces groups of same-name H4 method sections within each class + with a :class:`CollapsedOverloadSection` showing the primary overload + and a collapsible ``
`` block for the rest. + """ + + documentation: ACodeDocumentation + + def generate(self, context: Context, modules_to_document: list[str]) -> dict[str, MdxFile]: + """Generate MDX files and collapse overloaded methods in each one.""" + files = self.documentation.generate(context, modules_to_document) + return {name: self._collapse_overloads(mdx_file) for name, mdx_file in files.items()} + + def _collapse_overloads(self, mdx_file: MdxFile) -> MdxFile: + """Return a copy of *mdx_file* with overloaded methods collapsed.""" + lines = mdx_file.content.split("\n") + parsed_h2 = _parse_sections(lines, heading_level=2) + + processed_h2: list[ASection] = [] + for h2 in parsed_h2.sections: + processed_h3 = self._process_class_sections(h2.content) + if processed_h3 is None: + # No subsection means no method to manage + processed_h2.append(h2) + else: + h3_parsed = _parse_sections(h2.content, heading_level=3) + new_lines = h3_parsed.reassembled(processed_h3) + processed_h2.append( + MdxSection(name=h2.name, heading_level=h2.heading_level, _lines=[h2.heading, *new_lines]) + ) + + new_content = "\n".join(parsed_h2.reassembled(processed_h2)) + return MdxFile(name=mdx_file.name, content=new_content, source_path=mdx_file.source_path) + + def _process_class_sections(self, h2_content: list[str]) -> list[ASection] | None: + """Collapse overloads inside each H3 class section, or return ``None`` if nothing changed.""" + h3_parsed = _parse_sections(h2_content, heading_level=3) + if not h3_parsed.sections: + return None + + any_collapsed = False + processed: list[ASection] = [] + for h3 in h3_parsed.sections: + collapsed_methods = self._collapse_methods_in_class(h3.content) + if collapsed_methods is None: + processed.append(h3) + else: + any_collapsed = True + h4_parsed = _parse_sections(h3.content, heading_level=4) + new_lines = h4_parsed.reassembled(collapsed_methods) + processed.append( + MdxSection(name=h3.name, heading_level=h3.heading_level, _lines=[h3.heading, *new_lines]) + ) + + return processed if any_collapsed else None + + def _collapse_methods_in_class(self, h3_content: list[str]) -> list[ASection] | None: + """Collapse consecutive same-name H4 methods, or return ``None`` if no overloads found.""" + h4_parsed = _parse_sections(h3_content, heading_level=4) + if not h4_parsed.sections: + return None + + groups = self._group_consecutive_overloads(h4_parsed.sections) + has_overloads = any(len(group) > 1 for group in groups) + if not has_overloads: + return None + + collapsed: list[ASection] = [] + for group in groups: + if len(group) == 1: + collapsed.append(group[0]) + else: + accessors, overloads = _split_property_accessors(group) + collapsed.extend(accessors) + if len(overloads) > 1: + collapsed.append(CollapsedOverloadSection.from_overloads(overloads)) + else: + collapsed.extend(overloads) + return collapsed + + @staticmethod + def _group_consecutive_overloads(sections: list[MdxSection]) -> list[list[MdxSection]]: + """Group consecutive sections sharing the same name.""" + return [list(group) for _, group in groupby(sections, key=lambda s: s.name)] + + +def _split_property_accessors(sections: list[MdxSection]) -> tuple[list[MdxSection], list[MdxSection]]: + """Partition *sections* into property accessors and remaining overloads. + + Recognised accessor patterns: + + * **getter** — 0 params, non-``None`` return + * **setter** — 1 param, ``None`` return + * **deleter** — 0 params, ``None`` return + """ + sigs = [(section, MethodSignature(section)) for section in sections] + + def is_accessor(sig: MethodSignature) -> bool: + return getter_sig(sig) or setter_sig(sig) or deleter_sig(sig) + + accessors = [section for section, sig in sigs if is_accessor(sig)] + overloads = [section for section, sig in sigs if not is_accessor(sig)] + return accessors, overloads + + +def deleter_sig(sig: MethodSignature) -> bool: + return sig.param_count() == 0 and sig.return_type() == "None" + + +def setter_sig(sig: MethodSignature) -> bool: + return sig.param_count() == 1 and sig.return_type() == "None" + + +def getter_sig(sig: MethodSignature) -> bool: + return sig.param_count() == 0 and sig.return_type() != "None" diff --git a/docs/docs_generation/content_gen_methods/mdx/mdx_collapsed_overload_section.py b/docs/docs_generation/content_gen_methods/mdx/mdx_collapsed_overload_section.py new file mode 100644 index 00000000..85b6339d --- /dev/null +++ b/docs/docs_generation/content_gen_methods/mdx/mdx_collapsed_overload_section.py @@ -0,0 +1,149 @@ +from __future__ import annotations + +import re +from dataclasses import dataclass, field + +from .mdx_section import ASection, MdxSection + + +@dataclass +class CollapsedOverloadSection(ASection): + """Collapses a group of overloaded method sections into one primary entry + followed by a collapsible ``
`` block with the remaining overloads. + + The *primary* overload is the one with the most parameters (excluding + ``self``). On ties, the first in source order wins. + + Example:: + + >>> section = CollapsedOverloadSection.from_overloads(overload_sections) + >>> section.heading # delegates to primary + '#### `get`' + """ + + primary: MdxSection + others: list[MdxSection] = field(default_factory=list) + + @property + def heading(self) -> str: + """Return the heading of the primary overload.""" + return self.primary.heading + + @property + def content(self) -> list[str]: + """Return primary content followed by a ``
`` block for the other overloads.""" + if not self.others: + return self.primary.content + + result = list(self.primary.content) + inner = [line for other in self.others for line in other.lines] + count = len(self.others) + noun = "overload" if count == 1 else "overloads" + result.extend(_HtmlDetailsBlock(f"Show {count} other {noun}", inner).lines()) + return result + + @classmethod + def from_overloads(cls, sections: list[MdxSection]) -> CollapsedOverloadSection: + """Create from a list of overloaded :class:`MdxSection` objects. + + Selects the overload with the most parameters as *primary*. + On ties, the first in source order wins. + """ + if not sections: + raise ValueError("Cannot create CollapsedOverloadSection from an empty list") + + primary = max(sections, key=lambda s: MethodSignature(s).param_count()) + others = [s for s in sections if s is not primary] + return cls(primary=primary, others=others) + + +# --- Private collaborators --- + + +@dataclass(frozen=True) +class _HtmlDetailsBlock: + """A collapsible HTML ``
`` block.""" + + summary: str + inner_lines: list[str] + + def lines(self) -> list[str]: + """Return the full block as a list of MDX lines.""" + return ["", "
", f"{self.summary}", "", *self.inner_lines, "", "
"] + + +_CODE_FENCE_PATTERN = re.compile(r"^```python\s*$") +_CODE_FENCE_END = re.compile(r"^```\s*$") + + +def _extract_text(section: MdxSection) -> str: + """Extract the signature from the first code fence in *section*.""" + in_fence = False + sig_lines: list[str] = [] + for line in section.content: + if not in_fence and _CODE_FENCE_PATTERN.match(line): + in_fence = True + continue + if in_fence: + if _CODE_FENCE_END.match(line): + break + sig_lines.append(line) + return " ".join(sig_lines).strip() + + +def _split_params(text: str) -> list[str]: + """Split *text* on commas that are not inside brackets.""" + depth = 0 + tokens: list[str] = [] + current: list[str] = [] + for char in text: + if char in {"[", "(", "{"}: + depth += 1 + current.append(char) + elif char in {"]", ")", "}"}: + depth -= 1 + current.append(char) + elif char == "," and depth == 0: + tokens.append("".join(current)) + current = [] + else: + current.append(char) + if current: + tokens.append("".join(current)) + return tokens + + +class MethodSignature: + """A Python method signature extracted from an MDX code fence. + + Parses the raw signature text and counts comma-separated parameters + at the top level, respecting bracket nesting for generic types + like ``dict[str, int]``. + """ + + def __init__(self, section: MdxSection) -> None: + self._text = _extract_text(section) + + def param_count(self) -> int: + """Return the number of parameters excluding ``self``.""" + params_text = self._extract_params_text() + if not params_text.strip(): + return 0 + tokens = _split_params(params_text) + return len([t for t in tokens if t.strip() and t.strip() != "self"]) + + def return_type(self) -> str: + """Return the return-type annotation (e.g. ``"None"``), or ``""`` if absent.""" + _, sep, ret = self._text.rpartition(")") + if not sep: + return "" + _, arrow, after_arrow = ret.partition("->") + return after_arrow.strip() if arrow else "" + + def _extract_params_text(self) -> str: + """Extract the text between the first ``(`` and its last ``)``.""" + _, sep, after_open = self._text.partition("(") + if not sep: + return "" + params, _, _ = after_open.rpartition(")") + return params diff --git a/docs/docs_generation/content_gen_methods/mdx/mdx_ordered_code_doc.py b/docs/docs_generation/content_gen_methods/mdx/mdx_ordered_code_doc.py new file mode 100644 index 00000000..0074c4bb --- /dev/null +++ b/docs/docs_generation/content_gen_methods/mdx/mdx_ordered_code_doc.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +from dataclasses import dataclass +from functools import cached_property +from typing import TYPE_CHECKING + +from .mdx_code_doc import ACodeDocumentation, MdxFile +from .mdx_ordered_section import OrderedMdxSection +from .mdx_priority import SectionPriority +from .mdx_section import ASection, MdxSection, _parse_sections + +if TYPE_CHECKING: + from invoke import Context + + from .mdx_priority import PagePriority + + +@dataclass +class PageHeadings: + """Heading structure extracted from an :class:`MdxFile`. + + Parses lazily on first access and caches results. + """ + + mdx_file: MdxFile + + @cached_property + def h2_names(self) -> set[str]: + return {s.name for s in self._h2_sections} + + @cached_property + def h3_names(self) -> set[str]: + names, _ = self._h3_structure + return names + + @cached_property + def h3_to_h4_names(self) -> dict[str, set[str]]: + _, mapping = self._h3_structure + return mapping + + @cached_property + def _h2_sections(self) -> list[MdxSection]: + lines = self.mdx_file.content.split("\n") + return _parse_sections(lines, heading_level=2).sections + + @cached_property + def _h3_structure(self) -> tuple[set[str], dict[str, set[str]]]: + h3_names: set[str] = set() + h3_to_h4: dict[str, set[str]] = {} + for h2 in self._h2_sections: + for h3 in _parse_sections(h2.content, heading_level=3).sections: + h3_names.add(h3.name) + h3_to_h4[h3.name] = {h4.name for h4 in _parse_sections(h3.content, heading_level=4).sections} + return h3_names, h3_to_h4 + + def reference_errors(self, file_key: str, priority: PagePriority) -> list[str]: + """Return error messages for priority references not found in this file's headings.""" + errors: list[str] = [] + + errors.extend( + f"Priority section '{section}' not found as heading in '{file_key}'" + for section in priority.sections + if section not in self.h2_names + ) + + errors.extend( + f"Priority class '{cls}' not found as heading in '{file_key}'" + for cls in priority.classes + if cls not in self.h3_names + ) + + for cls_name, methods in priority.methods.items(): + if cls_name not in self.h3_names: + errors.append(f"Priority methods reference unknown class '{cls_name}' in '{file_key}'") + continue + errors.extend( + f"Priority method '{method}' not found under class '{cls_name}' in '{file_key}'" + for method in methods + if method not in self.h3_to_h4_names.get(cls_name, set()) + ) + + return errors + + +@dataclass +class OrderedMdxCodeDocumentation(ACodeDocumentation): + """Decorator around :class:`ACodeDocumentation` that reorders sections by priority. + + Delegates generation to the wrapped *documentation* instance, then applies + :class:`OrderedMdxSection` reordering to pages that have a corresponding + :class:`PagePriority` entry. + """ + + documentation: ACodeDocumentation + page_priorities: dict[str, PagePriority] + + def generate(self, context: Context, modules_to_document: list[str]) -> dict[str, MdxFile]: + files = self.documentation.generate(context, modules_to_document) + self._validate_references(files) + return {name: self._apply_priority(name, mdx_file) for name, mdx_file in files.items()} + + def _validate_references(self, files: dict[str, MdxFile]) -> None: + errors: list[str] = [] + for file_key, priority in self.page_priorities.items(): + if file_key not in files: + errors.append(f"Priority references unknown file key '{file_key}'") + continue + errors.extend(PageHeadings(files[file_key]).reference_errors(file_key, priority)) + if errors: + raise ValueError("Invalid priority configuration:\n" + "\n".join(f" - {e}" for e in errors)) + + def _apply_priority(self, name: str, mdx_file: MdxFile) -> MdxFile: + if name not in self.page_priorities: + return mdx_file + + priority = self.page_priorities[name] + if not priority.sections and not priority.classes and not priority.methods: + return mdx_file + + lines = mdx_file.content.split("\n") + parsed = _parse_sections(lines, heading_level=2).reordered(priority.sections) + + section_priority = SectionPriority(names=priority.classes, sub_priorities=priority.methods) + reordered: list[ASection] = [ + OrderedMdxSection(section=h2, priority=section_priority, child_heading_level=3) for h2 in parsed.sections + ] + + return MdxFile( + name=mdx_file.name, content="\n".join(parsed.reassembled(reordered)), source_path=mdx_file.source_path + ) diff --git a/docs/docs_generation/content_gen_methods/mdx/mdx_ordered_section.py b/docs/docs_generation/content_gen_methods/mdx/mdx_ordered_section.py new file mode 100644 index 00000000..e8ad4aa0 --- /dev/null +++ b/docs/docs_generation/content_gen_methods/mdx/mdx_ordered_section.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from .mdx_priority import SectionPriority +from .mdx_section import ASection, MdxSection, _parse_sections + + +@dataclass +class OrderedMdxSection(ASection): + """Decorator around MdxSection that reorders child sections by priority. + + Reorders immediate children at ``child_heading_level`` according to + ``priority.names``. Children whose name appears in + ``priority.sub_priorities`` are themselves wrapped in a nested + :class:`OrderedMdxSection` that reorders *their* children one + heading level deeper. + """ + + section: MdxSection + priority: SectionPriority + child_heading_level: int + + @property + def heading(self) -> str: + return self.section.heading + + @property + def content(self) -> list[str]: + if not self.priority.names and not self.priority.sub_priorities: + return self.section.content + parsed = _parse_sections(self.section.content, heading_level=self.child_heading_level) + if not parsed.sections: + return self.section.content + parsed = parsed.reordered(self.priority.names) + children = self._apply_sub_priorities(parsed.sections) + return parsed.reassembled(children) + + def _apply_sub_priorities(self, sections: list[MdxSection]) -> list[ASection]: + """Wrap children that have sub-priorities in nested :class:`OrderedMdxSection`.""" + if not self.priority.sub_priorities: + return list(sections) + result: list[ASection] = [] + for section in sections: + if section.name in self.priority.sub_priorities: + result.append( + OrderedMdxSection( + section=section, + priority=SectionPriority(names=self.priority.sub_priorities[section.name]), + child_heading_level=self.child_heading_level + 1, + ) + ) + else: + result.append(section) + return result diff --git a/docs/docs_generation/content_gen_methods/mdx/mdx_priority.py b/docs/docs_generation/content_gen_methods/mdx/mdx_priority.py new file mode 100644 index 00000000..20b55513 --- /dev/null +++ b/docs/docs_generation/content_gen_methods/mdx/mdx_priority.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from dataclasses import dataclass, field + + +def _duplicates(items: list[str]) -> list[str]: + """Return items that appear more than once, in order of first extra occurrence.""" + seen: set[str] = set() + dupes: list[str] = [] + for item in items: + if item in seen and item not in dupes: + dupes.append(item) + seen.add(item) + return dupes + + +@dataclass(frozen=True) +class PagePriority: + """Priority ordering configuration for a single documentation page. + + Attributes: + sections: Ordered list of H2 section names to appear first (e.g. ``"Classes"``). + classes: Ordered list of class/function names to appear first on the page. + methods: Per-class ordered list of method names to appear first. + Key is class name, value is ordered method name list. + """ + + sections: list[str] = field(default_factory=list) + classes: list[str] = field(default_factory=list) + methods: dict[str, list[str]] = field(default_factory=dict) + + def __post_init__(self) -> None: + errors = [f"Duplicate section '{s}'" for s in _duplicates(self.sections)] + errors.extend(f"Duplicate class '{c}'" for c in _duplicates(self.classes)) + for cls_name, methods in self.methods.items(): + errors.extend(f"Duplicate method '{m}' for class '{cls_name}'" for m in _duplicates(methods)) + if errors: + raise ValueError("Invalid priority configuration:\n" + "\n".join(f" - {e}" for e in errors)) + + +@dataclass(frozen=True) +class SectionPriority: + """Priority configuration for reordering child sections. + + Attributes: + names: Ordered list of child section names to appear first. + sub_priorities: Per-child priorities for deeper nesting. + Key is child name, value is ordered subsection name list. + """ + + names: list[str] = field(default_factory=list) + sub_priorities: dict[str, list[str]] = field(default_factory=dict) diff --git a/docs/docs_generation/content_gen_methods/mdx/mdx_section.py b/docs/docs_generation/content_gen_methods/mdx/mdx_section.py new file mode 100644 index 00000000..ed4e25fa --- /dev/null +++ b/docs/docs_generation/content_gen_methods/mdx/mdx_section.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +import re +from abc import ABC, abstractmethod +from collections.abc import Sequence +from dataclasses import dataclass, field + + +class ASection(ABC): + """Abstract base for MDX section types.""" + + @property + @abstractmethod + def heading(self) -> str: ... + + @property + @abstractmethod + def content(self) -> list[str]: ... + + @property + def lines(self) -> list[str]: + return [self.heading, *self.content] + + +@dataclass +class MdxSection(ASection): + """A parsed section of MDX content delimited by a heading. + + Attributes: + name: Item name extracted from the heading (e.g. class or method name). + heading_level: Markdown heading level (2, 3, or 4). + _lines: All lines belonging to this section, including the heading. + """ + + name: str + heading_level: int + _lines: list[str] = field(default_factory=list) + + @property + def heading(self) -> str: + return self._lines[0] + + @property + def content(self) -> list[str]: + return self._lines[1:] + + @property + def lines(self) -> list[str]: + return self._lines + + +def _heading_level(line: str) -> int | None: + """Return the heading level (1-6) if *line* is a Markdown heading, else ``None``.""" + match = re.match(r"^(#{1,6})\s", line) + return len(match.group(1)) if match else None + + +def _extract_heading_name(line: str) -> str: + """Extract the bare name from a heading, stripping backtick quoting. + + Handles ``### `ClassName` `` → ``ClassName`` as well as plain headings. + """ + match = re.match(r"^#{1,6}\s+`([^`]+)`", line) + if match: + return match.group(1) + match = re.match(r"^#{1,6}\s+(.+)", line) + if match: + return match.group(1).strip() + return "" + + +@dataclass +class ParsedContent: + """Result of splitting MDX lines at a heading level. + + Holds the *preamble* (lines before the first heading) and the + *sections* found at that level. Provides reordering and reassembly + as instance methods so these operations stay with the data they act on. + """ + + preamble: list[str] + sections: list[MdxSection] + + @property + def lines(self) -> list[str]: + """Reassemble preamble and sections into a flat line list.""" + return self.reassembled(self.sections) + + def reassembled(self, sections: Sequence[ASection]) -> list[str]: + """Combine this preamble with *sections* into a flat line list.""" + result = list(self.preamble) + for section in sections: + result.extend(section.lines) + return result + + def reordered(self, names: list[str]) -> ParsedContent: + """Return a new :class:`ParsedContent` with sections reordered by *names*. + + Priority sections appear in the order given by *names*. + Non-priority sections retain their original relative order. + Handles overloaded names (multiple sections sharing a name) by grouping them. + Names not found in *sections* are silently skipped. + """ + if not names: + return self + by_name: dict[str, list[MdxSection]] = {} + for section in self.sections: + by_name.setdefault(section.name, []).append(section) + ordered: list[MdxSection] = [] + used: set[str] = set() + for name in names: + if name in by_name and name not in used: + ordered.extend(by_name[name]) + used.add(name) + ordered.extend(s for s in self.sections if s.name not in used) + return ParsedContent(preamble=self.preamble, sections=ordered) + + +def _parse_sections(lines: list[str], heading_level: int) -> ParsedContent: + """Split *lines* into a preamble and sections at *heading_level*. + + Returns a :class:`ParsedContent` where *preamble* contains every line + before the first heading at the target level, and each + :class:`MdxSection` runs from its heading until the next heading at the + same level (or the end of the input). + """ + preamble: list[str] = [] + sections: list[MdxSection] = [] + current_section: MdxSection | None = None + + for line in lines: + level = _heading_level(line) + if level == heading_level: + if current_section is not None: + # current_section is now complete + sections.append(current_section) + name = _extract_heading_name(line) + current_section = MdxSection(name=name, heading_level=heading_level, _lines=[line]) + elif current_section is not None: + # If this not the beginning of a new section of the same heading level, this means this line is a part of + # the current section + current_section.lines.append(line) + else: + # There is no current section, these lines are part of preamble + preamble.append(line) + + if current_section is not None: + sections.append(current_section) + + return ParsedContent(preamble=preamble, sections=sections) diff --git a/docs/docs_generation/helpers.py b/docs/docs_generation/helpers.py new file mode 100644 index 00000000..9951a80c --- /dev/null +++ b/docs/docs_generation/helpers.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from collections import defaultdict +from typing import Any + +from pydantic_settings import EnvSettingsSource + +from infrahub_sdk.config import ConfigBase + + +def get_env_vars() -> dict[str, list[str]]: + """Extract environment variable names for each field of ``ConfigBase``. + + Returns: + Mapping of field name to list of upper-cased environment variable names. + """ + env_vars: dict[str, list[str]] = defaultdict(list) + settings = ConfigBase() + env_settings = EnvSettingsSource(settings.__class__, env_prefix=settings.model_config.get("env_prefix", "")) + + for field_name, field in settings.model_fields.items(): + for field_key, field_env_name, _ in env_settings._extract_field_info(field, field_name): + env_vars[field_key].append(field_env_name.upper()) + + return env_vars + + +def _resolve_allof(prop: dict[str, Any], definitions: dict[str, Any]) -> tuple[list[Any], str]: + """Resolve an ``allOf`` JSON Schema reference to extract enum choices and type.""" + if "allOf" not in prop: + return [], "" + ref_name = prop["allOf"][0]["$ref"].split("/")[-1] + ref_def = definitions.get(ref_name, {}) + return ref_def.get("enum", []), ref_def.get("type", "") + + +def _resolve_anyof_type(prop: dict[str, Any]) -> str: + """Resolve an ``anyOf`` to a comma-separated type string, excluding ``null``.""" + if "anyOf" not in prop: + return "" + return ", ".join(i["type"] for i in prop["anyOf"] if "type" in i and i["type"] != "null") + + +def build_config_properties() -> list[dict[str, Any]]: + """Build the list of configuration properties for SDK config documentation. + + Returns: + List of dicts with keys: ``name``, ``description``, ``type``, + ``choices``, ``default``, ``env_vars``. + """ + schema = ConfigBase.model_json_schema() + env_vars = get_env_vars() + definitions = schema.get("$defs", {}) + + properties = [] + for name, prop in schema["properties"].items(): + choices, kind = _resolve_allof(prop, definitions) + composed_type = _resolve_anyof_type(prop) + + properties.append( + { + "name": name, + "description": prop.get("description", ""), + "type": prop.get("type", kind) or composed_type or "object", + "choices": choices, + "default": prop.get("default", ""), + "env_vars": env_vars.get(name, []), + } + ) + + return properties diff --git a/docs/docs_generation/pages/__init__.py b/docs/docs_generation/pages/__init__.py new file mode 100644 index 00000000..58211bd4 --- /dev/null +++ b/docs/docs_generation/pages/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .base import DocPage, MDXDocPage + +__all__ = [ + "DocPage", + "MDXDocPage", +] diff --git a/docs/docs_generation/pages/base.py b/docs/docs_generation/pages/base.py new file mode 100644 index 00000000..6527d1b8 --- /dev/null +++ b/docs/docs_generation/pages/base.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from docs.docs_generation.content_gen_methods import ADocContentGenMethod + + +class DocPage: + """A documentation page whose content is produced by an injected generation method. + + Args: + content_gen_method: Strategy that produces the page content as a string. + + Example:: + + page = DocPage(content_gen_method=Jinja2DocContentGenMethod(...)) + print(page.content()) + """ + + def __init__(self, content_gen_method: ADocContentGenMethod) -> None: + self.content_gen_method = content_gen_method + + def content(self) -> str: + return self.content_gen_method.apply() + + +class MDXDocPage: + """Decorator which is a documentation page that can be written in an ``.mdx`` file. + + Args: + page: The documentation page whose content will be rendered. + output_path: File path where the ``.mdx`` output will be written. + + Example:: + + mdx = MDXDocPage(page=my_page, output_path=Path("docs/ref/client.mdx")) + mdx.to_mdx() + """ + + def __init__(self, page: DocPage, output_path: Path) -> None: + self.page = page + self.output_path = output_path + + def to_mdx(self) -> None: + rendered = self.page.content() + self.output_path.parent.mkdir(parents=True, exist_ok=True) + self.output_path.write_text(rendered, encoding="utf-8") + print(f"Docs saved to: {self.output_path}") diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index c9f85753..e3a23cca 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -37,7 +37,7 @@ const config: Config = { editUrl: "https://github.com/opsmill/infrahub-sdk-python/tree/stable/docs", path: 'docs/python-sdk', routeBasePath: 'python-sdk', - sidebarPath: './sidebars-python-sdk.ts', + sidebarPath: './sidebars/sidebars-python-sdk.ts', sidebarCollapsed: true, }, blog: false, @@ -55,7 +55,7 @@ const config: Config = { path: 'docs/infrahubctl', routeBasePath: 'infrahubctl', sidebarCollapsed: false, - sidebarPath: './sidebars-infrahubctl.ts', + sidebarPath: './sidebars/sidebars-infrahubctl.ts', }, ], ], diff --git a/docs/package-lock.json b/docs/package-lock.json index 6a594bda..c7241869 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,10 +8,12 @@ "name": "docs", "version": "0.0.0", "dependencies": { - "@docusaurus/core": "^3.8.1", - "@docusaurus/preset-classic": "^3.8.1", + "@docusaurus/core": "^3.9.2", + "@docusaurus/preset-classic": "^3.9.2", + "@iconify/react": "^6.0.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", + "markdownlint-cli2": "^0.20.0", "prism-react-renderer": "^2.3.0", "raw-loader": "^4.0.2", "react": "^18.0.0", @@ -19,95 +21,26 @@ "react-player": "^3.3.2" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^3.8.1", - "@docusaurus/tsconfig": "^3.8.1", - "@docusaurus/types": "^3.8.1", - "typescript": "~5.5.2" + "@docusaurus/module-type-aliases": "^3.9.2", + "@docusaurus/tsconfig": "^3.9.2", + "@docusaurus/types": "^3.9.2", + "typescript": "~5.5.2", + "vitest": "^4.0.17" }, "engines": { "node": ">=18.0" } }, - "node_modules/@ai-sdk/gateway": { - "version": "1.0.33", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-1.0.33.tgz", - "integrity": "sha512-v9i3GPEo4t3fGcSkQkc07xM6KJN75VUv7C1Mqmmsu2xD8lQwnQfsrgAXyNuWe20yGY0eHuheSPDZhiqsGKtH1g==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "2.0.0", - "@ai-sdk/provider-utils": "3.0.10", - "@vercel/oidc": "^3.0.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@ai-sdk/provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", - "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/provider-utils": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.10.tgz", - "integrity": "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "2.0.0", - "@standard-schema/spec": "^1.0.0", - "eventsource-parser": "^3.0.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@ai-sdk/react": { - "version": "2.0.60", - "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-2.0.60.tgz", - "integrity": "sha512-Ev0MC0I7eDcCH4FnrHzK48g9bJjyF3F67MMq76qoVsbtcs6fGIO5RjmYgPoFeSo8/yQ5EM6i/14yfcD0oB+moA==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider-utils": "3.0.10", - "ai": "5.0.60", - "swr": "^2.2.5", - "throttleit": "2.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "zod": "^3.25.76 || ^4.1.8" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, "node_modules/@algolia/abtesting": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.5.0.tgz", - "integrity": "sha512-W/ohRkbKQsqDWALJg28X15KF7Tcyg53L1MfdOkLgvkcCcofdzGHSimHHeNG05ojjFw9HK8+VPhe/Vwq4MozIJg==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.14.0.tgz", + "integrity": "sha512-cZfj+1Z1dgrk3YPtNQNt0H9Rr67P8b4M79JjUKGS0d7/EbFbGxGgSu6zby5f22KXo3LT0LZa4O2c6VVbupJuDg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" @@ -146,99 +79,99 @@ } }, "node_modules/@algolia/client-abtesting": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.39.0.tgz", - "integrity": "sha512-Vf0ZVe+qo3sHDrCinouJqlg8VoxM4Qo/KxNIqMYybkuctutfnp3kIY9OmESplOQ/9NGBthU9EG+4d5fBibWK/A==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.48.0.tgz", + "integrity": "sha512-n17WSJ7vazmM6yDkWBAjY12J8ERkW9toOqNgQ1GEZu/Kc4dJDJod1iy+QP5T/UlR3WICgZDi/7a/VX5TY5LAPQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.39.0.tgz", - "integrity": "sha512-V16ITZxYIwcv1arNce65JZmn94Ft6vKlBZ//gXw8AvIH32glJz1KcbaVAUr9p7PYlGZ/XVHP6LxDgrpNdtwgcA==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.48.0.tgz", + "integrity": "sha512-v5bMZMEqW9U2l40/tTAaRyn4AKrYLio7KcRuHmLaJtxuJAhvZiE7Y62XIsF070juz4MN3eyvfQmI+y5+OVbZuA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.39.0.tgz", - "integrity": "sha512-UCJTuwySEQeiKPWV3wruhuI/wHbDYenHzgL9pYsvh6r/u5Z+g61ip1iwdAlFp02CnywzI9O7+AQPh2ManYyHmQ==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.48.0.tgz", + "integrity": "sha512-7H3DgRyi7UByScc0wz7EMrhgNl7fKPDjKX9OcWixLwCj7yrRXDSIzwunykuYUUO7V7HD4s319e15FlJ9CQIIFQ==", "license": "MIT", "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-insights": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.39.0.tgz", - "integrity": "sha512-s0ia8M/ZZR+iO2uLNTBrlQdEb6ZMAMcKMHckp5mcoglxrf8gHifL4LmdhGKdAxAn3UIagtqIP0RCnIymHUbm7A==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.48.0.tgz", + "integrity": "sha512-tXmkB6qrIGAXrtRYHQNpfW0ekru/qymV02bjT0w5QGaGw0W91yT+53WB6dTtRRsIrgS30Al6efBvyaEosjZ5uw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.39.0.tgz", - "integrity": "sha512-vZPIt7Lw+toNsHZUiPhNIc1Z3vUjDp7nzn6AMOaPC73gEuTq2iLPNvM06CSB6aHePo5eMeJIP5YEKBUQUA/PJA==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.48.0.tgz", + "integrity": "sha512-4tXEsrdtcBZbDF73u14Kb3otN+xUdTVGop1tBjict+Rc/FhsJQVIwJIcTrOJqmvhtBfc56Bu65FiVOnpAZCxcw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.39.0.tgz", - "integrity": "sha512-jcPQr3iKTWNVli2NYHPv02aNLwixDjPCpOgMp9CZTvEiPI6Ec4jHX+oFr3LDZagOFY9e1xJhc/JrgMGGW1sHnw==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.48.0.tgz", + "integrity": "sha512-unzSUwWFpsDrO8935RhMAlyK0Ttua/5XveVIwzfjs5w+GVBsHgIkbOe8VbBJccMU/z1LCwvu1AY3kffuSLAR5Q==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.39.0.tgz", - "integrity": "sha512-/IYpF10BpthGZEJQZMhMqV4AqWr5avcWfZm/SIKK1RvUDmzGqLoW/+xeJVX9C8ZnNkIC8hivbIQFaNaRw0BFZQ==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.48.0.tgz", + "integrity": "sha512-RB9bKgYTVUiOcEb5bOcZ169jiiVW811dCsJoLT19DcbbFmU4QaK0ghSTssij35QBQ3SCOitXOUrHcGgNVwS7sQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" @@ -251,93 +184,93 @@ "license": "MIT" }, "node_modules/@algolia/ingestion": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.39.0.tgz", - "integrity": "sha512-IgSHKUiuecqLfBlXiuCSdRTdsO3/yvpmXrMFz8fAJ8M4QmDtHkOuD769dmybRYqsbYMHivw+lir4BgbRGMtOIQ==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.48.0.tgz", + "integrity": "sha512-rhoSoPu+TDzDpvpk3cY/pYgbeWXr23DxnAIH/AkN0dUC+GCnVIeNSQkLaJ+CL4NZ51cjLIjksrzb4KC5Xu+ktw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.39.0.tgz", - "integrity": "sha512-8Xnd4+609SKC/hqVsuFc4evFBmvA2765/4NcH+Dpr756SKPbL1BY0X8kVxlmM3YBLNqnduSQxHxpDJUK58imCA==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.48.0.tgz", + "integrity": "sha512-aSe6jKvWt+8VdjOaq2ERtsXp9+qMXNJ3mTyTc1VMhNfgPl7ArOhRMRSQ8QBnY8ZL4yV5Xpezb7lAg8pdGrrulg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.39.0.tgz", - "integrity": "sha512-D7Ye2Ss/5xqUkQUxKm/VqEJLt5kARd9IMmjdzlxaKhGgNlOemTay0lwBmOVFuJRp7UODjp5c9+K+B8g0ORObIw==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.48.0.tgz", + "integrity": "sha512-p9tfI1bimAaZrdiVExL/dDyGUZ8gyiSHsktP1ZWGzt5hXpM3nhv4tSjyHtXjEKtA0UvsaHKwSfFE8aAAm1eIQA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "@algolia/client-common": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.39.0.tgz", - "integrity": "sha512-mgPte1ZJqpk9dkVs44J3wKAbHATvHZNlSpzhMdjMLIg/3qTycSZyDiomLiSlxE8CLsxyBAOJWnyKRHfom+Z1rg==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.48.0.tgz", + "integrity": "sha512-XshyfpsQB7BLnHseMinp3fVHOGlTv6uEHOzNK/3XrEF9mjxoZAcdVfY1OCXObfwRWX5qXZOq8FnrndFd44iVsQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0" + "@algolia/client-common": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.39.0.tgz", - "integrity": "sha512-LIrCkrxu1WnO3ev1+w6NnZ12JZL/o+2H9w6oWnZAjQZIlA/Ym6M9QHkt+OQ/SwkuoiNkW3DAo+Pi4A2V9FPtqg==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.48.0.tgz", + "integrity": "sha512-Q4XNSVQU89bKNAPuvzSYqTH9AcbOOiIo6AeYMQTxgSJ2+uvT78CLPMG89RIIloYuAtSfE07s40OLV50++l1Bbw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0" + "@algolia/client-common": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.39.0.tgz", - "integrity": "sha512-6beG+egPwXmvhAg+m0STCj+ZssDcjrLzf4L05aKm2nGglMXSSPz0cH/rM+kVD9krNfldiMctURd4wjojW1fV0w==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.48.0.tgz", + "integrity": "sha512-ZgxV2+5qt3NLeUYBTsi6PLyHcENQWC0iFppFZekHSEDA2wcLdTUjnaJzimTEULHIvJuLRCkUs4JABdhuJktEag==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.39.0" + "@algolia/client-common": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -346,29 +279,29 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", - "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.4", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.4", - "@babel/types": "^7.28.4", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -394,13 +327,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -422,12 +355,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -447,17 +380,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", - "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.3", + "@babel/traverse": "^7.28.6", "semver": "^6.3.1" }, "engines": { @@ -477,13 +410,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "engines": { @@ -503,16 +436,16 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", - "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz", + "integrity": "sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==", "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "debug": "^4.4.1", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", "lodash.debounce": "^4.0.8", - "resolve": "^1.22.10" + "resolve": "^1.22.11" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -528,40 +461,40 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -583,9 +516,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -609,14 +542,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -648,9 +581,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -666,39 +599,39 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", - "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2" + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -708,13 +641,13 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", - "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -771,13 +704,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", - "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -811,12 +744,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -826,12 +759,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -841,12 +774,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -856,12 +789,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -902,14 +835,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", - "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", + "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -919,13 +852,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { @@ -951,12 +884,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.4.tgz", - "integrity": "sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -966,13 +899,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", - "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -982,13 +915,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", - "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.3", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -998,17 +931,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", - "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.4" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1018,13 +951,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1034,13 +967,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1050,13 +983,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1081,13 +1014,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1112,13 +1045,13 @@ } }, "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", - "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1128,12 +1061,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1191,12 +1124,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", - "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1221,12 +1154,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", - "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1267,13 +1200,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1283,15 +1216,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", + "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -1317,13 +1250,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1348,12 +1281,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", - "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1363,12 +1296,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", - "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1378,16 +1311,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz", - "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.4" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1413,12 +1346,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", - "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1428,12 +1361,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { @@ -1459,13 +1392,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", - "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1475,14 +1408,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", - "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1537,16 +1470,16 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", - "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", + "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-jsx": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1587,12 +1520,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", - "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", + "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1602,13 +1535,13 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", - "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1633,13 +1566,13 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz", - "integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", + "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", @@ -1677,12 +1610,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { @@ -1738,16 +1671,16 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" + "@babel/plugin-syntax-typescript": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1772,13 +1705,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", - "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1804,13 +1737,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", - "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1820,80 +1753,80 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", - "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.0.tgz", + "integrity": "sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/compat-data": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.27.1", - "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.28.0", - "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.29.0", + "@babel/plugin-transform-async-to-generator": "^7.28.6", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.0", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-class-static-block": "^7.28.3", - "@babel/plugin-transform-classes": "^7.28.3", - "@babel/plugin-transform-computed-properties": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", - "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.28.6", "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", + "@babel/plugin-transform-modules-systemjs": "^7.29.0", "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.27.1", - "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.3", - "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.29.0", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.14", - "babel-plugin-polyfill-corejs3": "^0.13.0", - "babel-plugin-polyfill-regenerator": "^0.6.5", - "core-js-compat": "^3.43.0", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", "semver": "^6.3.1" }, "engines": { @@ -1903,6 +1836,19 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.0.tgz", + "integrity": "sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.6", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1927,14 +1873,14 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.27.1.tgz", - "integrity": "sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", + "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.27.1", + "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" @@ -1947,16 +1893,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", - "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", + "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.27.1" + "@babel/plugin-transform-typescript": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1975,43 +1921,43 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.28.4.tgz", - "integrity": "sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz", + "integrity": "sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA==", "license": "MIT", "dependencies": { - "core-js-pure": "^3.43.0" + "core-js-pure": "^3.48.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -2019,13 +1965,13 @@ } }, "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2275,9 +2221,9 @@ } }, "node_modules/@csstools/postcss-cascade-layers/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -2696,9 +2642,9 @@ } }, "node_modules/@csstools/postcss-is-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -2935,9 +2881,9 @@ } }, "node_modules/@csstools/postcss-normalize-display-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.0.tgz", - "integrity": "sha512-HlEoG0IDRoHXzXnkV4in47dzsxdsjdz6+j7MLjaACABX2NfvjFS6XVAnpaDyGesz9gK2SC7MbNwdCHusObKJ9Q==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.1.tgz", + "integrity": "sha512-TQUGBuRvxdc7TgNSTevYqrL8oItxiwPDixk20qCB5me/W8uF7BPbhRrAvFuhEoywQp/woRsUZ6SJ+sU5idZAIA==", "funding": [ { "type": "github", @@ -2988,6 +2934,28 @@ "postcss": "^8.4" } }, + "node_modules/@csstools/postcss-position-area-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-position-area-property/-/postcss-position-area-property-1.0.0.tgz", + "integrity": "sha512-fUP6KR8qV2NuUZV3Cw8itx0Ep90aRjAZxAEzC3vrl6yjFv+pFsQbR18UuQctEKmA72K9O27CoYiKEgXxkqjg8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/@csstools/postcss-progressive-custom-properties": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.2.1.tgz", @@ -3013,6 +2981,32 @@ "postcss": "^8.4" } }, + "node_modules/@csstools/postcss-property-rule-prelude-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-property-rule-prelude-list/-/postcss-property-rule-prelude-list-1.0.0.tgz", + "integrity": "sha512-IxuQjUXq19fobgmSSvUDO7fVwijDJaZMvWQugxfEUxmjBeDCVaDuMpsZ31MsTm5xbnhA+ElDi0+rQ7sQQGisFA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/@csstools/postcss-random-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz", @@ -3095,9 +3089,9 @@ } }, "node_modules/@csstools/postcss-scope-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -3161,10 +3155,10 @@ "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz", - "integrity": "sha512-KSkGgZfx0kQjRIYnpsD7X2Om9BUXX/Kii77VBifQW9Ih929hK0KNjVngHDH0bFB9GmfWcR9vJYJJRvw/NQjkrA==", + "node_modules/@csstools/postcss-syntax-descriptor-syntax-production": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-syntax-descriptor-syntax-production/-/postcss-syntax-descriptor-syntax-production-1.0.1.tgz", + "integrity": "sha512-GneqQWefjM//f4hJ/Kbox0C6f2T7+pi4/fqTqOFGTL3EjnvOReTqO1qUQ30CaUjkwjYq9qZ41hzarrAxCc4gow==", "funding": [ { "type": "github", @@ -3177,8 +3171,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/color-helpers": "^5.1.0", - "postcss-value-parser": "^4.2.0" + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -3187,10 +3180,10 @@ "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz", - "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==", + "node_modules/@csstools/postcss-system-ui-font-family": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-system-ui-font-family/-/postcss-system-ui-font-family-1.0.0.tgz", + "integrity": "sha512-s3xdBvfWYfoPSBsikDXbuorcMG1nN1M6GdU0qBsGfcmNR0A/qhloQZpTxjA3Xsyrk1VJvwb2pOfiOT3at/DuIQ==", "funding": [ { "type": "github", @@ -3203,7 +3196,6 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.4", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" }, @@ -3214,10 +3206,10 @@ "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-unset-value": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz", - "integrity": "sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==", + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz", + "integrity": "sha512-KSkGgZfx0kQjRIYnpsD7X2Om9BUXX/Kii77VBifQW9Ih929hK0KNjVngHDH0bFB9GmfWcR9vJYJJRvw/NQjkrA==", "funding": [ { "type": "github", @@ -3229,6 +3221,10 @@ } ], "license": "MIT-0", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "postcss-value-parser": "^4.2.0" + }, "engines": { "node": ">=18" }, @@ -3236,10 +3232,59 @@ "postcss": "^8.4" } }, - "node_modules/@csstools/utilities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz", - "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==", + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz", + "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz", + "integrity": "sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/utilities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz", + "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==", "funding": [ { "type": "github", @@ -3267,25 +3312,43 @@ "node": ">=10.0.0" } }, + "node_modules/@docsearch/core": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.5.4.tgz", + "integrity": "sha512-DbkfZbJyYAPFJtF71eAFOTQSy5z5c/hdSN0UrErORKDwXKLTJBR0c+5WxE5l+IKZx4xIaEa8RkrL7T28DTCOYw==", + "license": "MIT", + "peerDependencies": { + "@types/react": ">= 16.8.0 < 20.0.0", + "react": ">= 16.8.0 < 20.0.0", + "react-dom": ">= 16.8.0 < 20.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/@docsearch/css": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.1.0.tgz", - "integrity": "sha512-nuNKGjHj/FQeWgE9t+i83QD/V67QiaAmGY7xS9TVCRUiCqSljOgIKlsLoQZKKVwEG8f+OWKdznzZkJxGZ7d06A==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.5.4.tgz", + "integrity": "sha512-gzO4DJwyM9c4YEPHwaLV1nUCDC2N6yoh0QJj44dce2rcfN71mB+jpu3+F+Y/KMDF1EKV0C3m54leSWsraE94xg==", "license": "MIT" }, "node_modules/@docsearch/react": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.1.0.tgz", - "integrity": "sha512-4GHI7TT3sJZ2Vs4Kjadv7vAkMrTsJqHvzvxO3JA7UT8iPRKaDottG5o5uNshPWhVVaBYPC35Ukf8bfCotGpjSg==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.5.4.tgz", + "integrity": "sha512-iBNFfvWoUFRUJmGQ/r+0AEp2OJgJMoYIKRiRcTDON0hObBRSLlrv2ktb7w3nc1MeNm1JIpbPA99i59TiIR49fA==", "license": "MIT", "dependencies": { - "@ai-sdk/react": "^2.0.30", "@algolia/autocomplete-core": "1.19.2", - "@docsearch/css": "4.1.0", - "ai": "^5.0.30", - "algoliasearch": "^5.28.0", - "marked": "^16.3.0", - "zod": "^4.1.8" + "@docsearch/core": "4.5.4", + "@docsearch/css": "4.5.4" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", @@ -3309,9 +3372,9 @@ } }, "node_modules/@docusaurus/babel": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.9.1.tgz", - "integrity": "sha512-/uoi3oG+wvbVWNBRfPrzrEslOSeLxrQEyWMywK51TLDFTANqIRivzkMusudh5bdDty8fXzCYUT+tg5t697jYqg==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.9.2.tgz", + "integrity": "sha512-GEANdi/SgER+L7Japs25YiGil/AUDnFFHaCGPBbundxoWtCkA2lmy7/tFmgED4y1htAy6Oi4wkJEQdGssnw9MA==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", @@ -3324,8 +3387,8 @@ "@babel/runtime": "^7.25.9", "@babel/runtime-corejs3": "^7.25.9", "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.9.1", - "@docusaurus/utils": "3.9.1", + "@docusaurus/logger": "3.9.2", + "@docusaurus/utils": "3.9.2", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -3335,17 +3398,17 @@ } }, "node_modules/@docusaurus/bundler": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.9.1.tgz", - "integrity": "sha512-E1c9DgNmAz4NqbNtiJVp4UgjLtr8O01IgtXD/NDQ4PZaK8895cMiTOgb3k7mN0qX8A3lb8vqyrPJ842+yMpuUg==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.9.2.tgz", + "integrity": "sha512-ZOVi6GYgTcsZcUzjblpzk3wH1Fya2VNpd5jtHoCCFcJlMQ1EYXZetfAnRHLcyiFeBABaI1ltTYbOBtH/gahGVA==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.9.1", - "@docusaurus/cssnano-preset": "3.9.1", - "@docusaurus/logger": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils": "3.9.1", + "@docusaurus/babel": "3.9.2", + "@docusaurus/cssnano-preset": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", "babel-loader": "^9.2.1", "clean-css": "^5.3.3", "copy-webpack-plugin": "^11.0.0", @@ -3378,18 +3441,18 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.9.1.tgz", - "integrity": "sha512-FWDk1LIGD5UR5Zmm9rCrXRoxZUgbwuP6FBA7rc50DVfzqDOMkeMe3NyJhOsA2dF0zBE3VbHEIMmTjKwTZJwbaA==", - "license": "MIT", - "dependencies": { - "@docusaurus/babel": "3.9.1", - "@docusaurus/bundler": "3.9.1", - "@docusaurus/logger": "3.9.1", - "@docusaurus/mdx-loader": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-common": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.9.2.tgz", + "integrity": "sha512-HbjwKeC+pHUFBfLMNzuSjqFE/58+rLVKmOU3lxQrpsxLBOGosYco/Q0GduBb0/jEMRiyEqjNT/01rRdOMWq5pw==", + "license": "MIT", + "dependencies": { + "@docusaurus/babel": "3.9.2", + "@docusaurus/bundler": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -3439,9 +3502,9 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.9.1.tgz", - "integrity": "sha512-2y7+s7RWQMqBg+9ejeKwvZs7Bdw/hHIVJIodwMXbs2kr+S48AhcmAfdOh6Cwm0unJb0hJUshN0ROwRoQMwl3xg==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.9.2.tgz", + "integrity": "sha512-8gBKup94aGttRduABsj7bpPFTX7kbwu+xh3K9NMCF5K4bWBqTFYW+REKHF6iBVDHRJ4grZdIPbvkiHd/XNKRMQ==", "license": "MIT", "dependencies": { "cssnano-preset-advanced": "^6.1.2", @@ -3454,9 +3517,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.9.1.tgz", - "integrity": "sha512-C9iFzXwHzwvGlisE4bZx+XQE0JIqlGAYAd5LzpR7fEDgjctu7yL8bE5U4nTNywXKHURDzMt4RJK8V6+stFHVkA==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.9.2.tgz", + "integrity": "sha512-/SVCc57ByARzGSU60c50rMyQlBuMIJCjcsJlkphxY6B0GV4UH3tcA1994N8fFfbJ9kX3jIBe/xg3XP5qBtGDbA==", "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -3467,14 +3530,14 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.9.1.tgz", - "integrity": "sha512-/1PY8lqry8jCt0qZddJSpc0U2sH6XC27kVJZfpA7o2TiQ3mdBQyH5AVbj/B2m682B1ounE+XjI0LdpOkAQLPoA==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.9.2.tgz", + "integrity": "sha512-wiYoGwF9gdd6rev62xDU8AAM8JuLI/hlwOtCzMmYcspEkzecKrP8J8X+KpYnTlACBUUtXNJpSoCwFWJhLRevzQ==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "@docusaurus/logger": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -3506,12 +3569,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.9.1.tgz", - "integrity": "sha512-YBce3GbJGGcMbJTyHcnEOMvdXqg41pa5HsrMCGA5Rm4z0h0tHS6YtEldj0mlfQRhCG7Y0VD66t2tb87Aom+11g==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.9.2.tgz", + "integrity": "sha512-8qVe2QA9hVLzvnxP46ysuofJUIc/yYQ82tvA/rBTrnpXtCjNSFLxEZfd5U8cYZuJIVlkPxamsIgwd5tGZXfvew==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.9.1", + "@docusaurus/types": "3.9.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3525,19 +3588,19 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.9.1.tgz", - "integrity": "sha512-vT6kIimpJLWvW9iuWzH4u7VpTdsGlmn4yfyhq0/Kb1h4kf9uVouGsTmrD7WgtYBUG1P+TSmQzUUQa+ALBSRTig==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/logger": "3.9.1", - "@docusaurus/mdx-loader": "3.9.1", - "@docusaurus/theme-common": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-common": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.9.2.tgz", + "integrity": "sha512-3I2HXy3L1QcjLJLGAoTvoBnpOwa6DPUa3Q0dMK19UTY9mhPkKQg/DYhAGTiBUKcTR0f08iw7kLPqOhIgdV3eVQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "cheerio": "1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", @@ -3559,20 +3622,20 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.1.tgz", - "integrity": "sha512-DyLk9BIA6I9gPIuia8XIL+XIEbNnExam6AHzRsfrEq4zJr7k/DsWW7oi4aJMepDnL7jMRhpVcdsCxdjb0/A9xg==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/logger": "3.9.1", - "@docusaurus/mdx-loader": "3.9.1", - "@docusaurus/module-type-aliases": "3.9.1", - "@docusaurus/theme-common": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-common": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz", + "integrity": "sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/module-type-aliases": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -3592,16 +3655,16 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.9.1.tgz", - "integrity": "sha512-/1wFzRnXYASI+Nv9ck9IVPIMw0O5BGQ8ZVhDzEwhkL+tl44ycvSnY6PIe6rW2HLxsw61Z3WFwAiU8+xMMtMZpg==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.9.2.tgz", + "integrity": "sha512-s4849w/p4noXUrGpPUF0BPqIAfdAe76BLaRGAGKZ1gTDNiGxGcpsLcwJ9OTi1/V8A+AzvsmI9pkjie2zjIQZKA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/mdx-loader": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "@docusaurus/core": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -3615,15 +3678,15 @@ } }, "node_modules/@docusaurus/plugin-css-cascade-layers": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.9.1.tgz", - "integrity": "sha512-/QyW2gRCk/XE3ttCK/ERIgle8KJ024dBNKMu6U5SmpJvuT2il1n5jR/48Pp/9wEwut8WVml4imNm6X8JsL5A0Q==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.9.2.tgz", + "integrity": "sha512-w1s3+Ss+eOQbscGM4cfIFBlVg/QKxyYgj26k5AnakuHkKxH6004ZtuLe5awMBotIYF2bbGDoDhpgQ4r/kcj4rQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "tslib": "^2.6.0" }, "engines": { @@ -3631,14 +3694,14 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.9.1.tgz", - "integrity": "sha512-qPeAuk0LccC251d7jg2MRhNI+o7niyqa924oEM/AxnZJvIpMa596aAxkRImiAqNN6+gtLE1Hkrz/RHUH2HDGsA==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.9.2.tgz", + "integrity": "sha512-j7a5hWuAFxyQAkilZwhsQ/b3T7FfHZ+0dub6j/GxKNFJp2h9qk/P1Bp7vrGASnvA9KNQBBL1ZXTe7jlh4VdPdA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils": "3.9.1", + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", "fs-extra": "^11.1.1", "react-json-view-lite": "^2.3.0", "tslib": "^2.6.0" @@ -3652,14 +3715,14 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.9.1.tgz", - "integrity": "sha512-k4Qq2HphqOrIU/CevGPdEO1yJnWUI8m0zOJsYt5NfMJwNsIn/gDD6gv/DKD+hxHndQT5pacsfBd4BWHZVNVroQ==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.9.2.tgz", + "integrity": "sha512-mAwwQJ1Us9jL/lVjXtErXto4p4/iaLlweC54yDUK1a97WfkC6Z2k5/769JsFgwOwOP+n5mUQGACXOEQ0XDuVUw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "tslib": "^2.6.0" }, "engines": { @@ -3671,14 +3734,14 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.9.1.tgz", - "integrity": "sha512-n9BURBiQyJKI/Ecz35IUjXYwXcgNCSq7/eA07+ZYcDiSyH2p/EjPf8q/QcZG3CyEJPZ/SzGkDHePfcVPahY4Gg==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.9.2.tgz", + "integrity": "sha512-YJ4lDCphabBtw19ooSlc1MnxtYGpjFV9rEdzjLsUnBCeis2djUyCozZaFhCg6NGEwOn7HDDyMh0yzcdRpnuIvA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -3691,14 +3754,14 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.9.1.tgz", - "integrity": "sha512-rZAQZ25ZuXaThBajxzLjXieTDUCMmBzfAA6ThElQ3o7Q+LEpOjCIrwGFau0KLY9HeG6x91+FwwsAM8zeApYDrg==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.9.2.tgz", + "integrity": "sha512-LJtIrkZN/tuHD8NqDAW1Tnw0ekOwRTfobWPsdO15YxcicBo2ykKF0/D6n0vVBfd3srwr9Z6rzrIWYrMzBGrvNw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "tslib": "^2.6.0" }, "engines": { @@ -3710,17 +3773,17 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.9.1.tgz", - "integrity": "sha512-k/bf5cXDxAJUYTzqatgFJwmZsLUbIgl6S8AdZMKGG2Mv2wcOHt+EQNN9qPyWZ5/9cFj+Q8f8DN+KQheBMYLong==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.9.2.tgz", + "integrity": "sha512-WLh7ymgDXjG8oPoM/T4/zUP7KcSuFYRZAUTl8vR6VzYkfc18GBM4xLhcT+AKOwun6kBivYKUJf+vlqYJkm+RHw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/logger": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-common": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -3734,15 +3797,15 @@ } }, "node_modules/@docusaurus/plugin-svgr": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.9.1.tgz", - "integrity": "sha512-TeZOXT2PSdTNR1OpDJMkYqFyX7MMhbd4t16hQByXksgZQCXNyw3Dio+KaDJ2Nj+LA4WkOvsk45bWgYG5MAaXSQ==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.9.2.tgz", + "integrity": "sha512-n+1DE+5b3Lnf27TgVU5jM1d4x5tUh2oW5LTsBxJX4PsAPV0JGcmI6p3yLYtEY0LRVEIJh+8RsdQmRE66wSV8mw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "@svgr/core": "8.1.0", "@svgr/webpack": "^8.1.0", "tslib": "^2.6.0", @@ -3757,26 +3820,26 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.9.1.tgz", - "integrity": "sha512-ZHga2xsxxsyd0dN1BpLj8S889Eu9eMBuj2suqxdw/vaaXu/FjJ8KEGbcaeo6nHPo8VQcBBnPEdkBtSDm2TfMNw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/plugin-content-blog": "3.9.1", - "@docusaurus/plugin-content-docs": "3.9.1", - "@docusaurus/plugin-content-pages": "3.9.1", - "@docusaurus/plugin-css-cascade-layers": "3.9.1", - "@docusaurus/plugin-debug": "3.9.1", - "@docusaurus/plugin-google-analytics": "3.9.1", - "@docusaurus/plugin-google-gtag": "3.9.1", - "@docusaurus/plugin-google-tag-manager": "3.9.1", - "@docusaurus/plugin-sitemap": "3.9.1", - "@docusaurus/plugin-svgr": "3.9.1", - "@docusaurus/theme-classic": "3.9.1", - "@docusaurus/theme-common": "3.9.1", - "@docusaurus/theme-search-algolia": "3.9.1", - "@docusaurus/types": "3.9.1" + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.9.2.tgz", + "integrity": "sha512-IgyYO2Gvaigi21LuDIe+nvmN/dfGXAiMcV/murFqcpjnZc7jxFAxW+9LEjdPt61uZLxG4ByW/oUmX/DDK9t/8w==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/plugin-content-blog": "3.9.2", + "@docusaurus/plugin-content-docs": "3.9.2", + "@docusaurus/plugin-content-pages": "3.9.2", + "@docusaurus/plugin-css-cascade-layers": "3.9.2", + "@docusaurus/plugin-debug": "3.9.2", + "@docusaurus/plugin-google-analytics": "3.9.2", + "@docusaurus/plugin-google-gtag": "3.9.2", + "@docusaurus/plugin-google-tag-manager": "3.9.2", + "@docusaurus/plugin-sitemap": "3.9.2", + "@docusaurus/plugin-svgr": "3.9.2", + "@docusaurus/theme-classic": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/theme-search-algolia": "3.9.2", + "@docusaurus/types": "3.9.2" }, "engines": { "node": ">=20.0" @@ -3787,24 +3850,24 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.9.1.tgz", - "integrity": "sha512-LrAIu/mQ04nG6s1cssC0TMmICD8twFIIn/hJ5Pd9uIPQvtKnyAKEn12RefopAul5KfMo9kixPaqogV5jIJr26w==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.9.1", - "@docusaurus/logger": "3.9.1", - "@docusaurus/mdx-loader": "3.9.1", - "@docusaurus/module-type-aliases": "3.9.1", - "@docusaurus/plugin-content-blog": "3.9.1", - "@docusaurus/plugin-content-docs": "3.9.1", - "@docusaurus/plugin-content-pages": "3.9.1", - "@docusaurus/theme-common": "3.9.1", - "@docusaurus/theme-translations": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-common": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.9.2.tgz", + "integrity": "sha512-IGUsArG5hhekXd7RDb11v94ycpJpFdJPkLnt10fFQWOVxAtq5/D7hT6lzc2fhyQKaaCE62qVajOMKL7OiAFAIA==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/module-type-aliases": "3.9.2", + "@docusaurus/plugin-content-blog": "3.9.2", + "@docusaurus/plugin-content-docs": "3.9.2", + "@docusaurus/plugin-content-pages": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/theme-translations": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "infima": "0.2.0-alpha.45", @@ -3827,15 +3890,15 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.9.1.tgz", - "integrity": "sha512-j9adi961F+6Ps9d0jcb5BokMcbjXAAJqKkV43eo8nh4YgmDj7KUNDX4EnOh/MjTQeO06oPY5cxp3yUXdW/8Ggw==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.9.2.tgz", + "integrity": "sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag==", "license": "MIT", "dependencies": { - "@docusaurus/mdx-loader": "3.9.1", - "@docusaurus/module-type-aliases": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-common": "3.9.1", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/module-type-aliases": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3855,19 +3918,19 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.9.1.tgz", - "integrity": "sha512-WjM28bzlgfT6nHlEJemkwyGVpvGsZWPireV/w+wZ1Uo64xCZ8lNOb4xwQRukDaLSed3oPBN0gSnu06l5VuCXHg==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.9.2.tgz", + "integrity": "sha512-GBDSFNwjnh5/LdkxCKQHkgO2pIMX1447BxYUBG2wBiajS21uj64a+gH/qlbQjDLxmGrbrllBrtJkUHxIsiwRnw==", "license": "MIT", "dependencies": { "@docsearch/react": "^3.9.0 || ^4.1.0", - "@docusaurus/core": "3.9.1", - "@docusaurus/logger": "3.9.1", - "@docusaurus/plugin-content-docs": "3.9.1", - "@docusaurus/theme-common": "3.9.1", - "@docusaurus/theme-translations": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-validation": "3.9.1", + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/plugin-content-docs": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/theme-translations": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", "algoliasearch": "^5.37.0", "algoliasearch-helper": "^3.26.0", "clsx": "^2.0.0", @@ -3886,9 +3949,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.9.1.tgz", - "integrity": "sha512-mUQd49BSGKTiM6vP9+JFgRJL28lMIN3PUvXjF3rzuOHMByUZUBNwCt26Z23GkKiSIOrRkjKoaBNTipR/MHdYSQ==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.9.2.tgz", + "integrity": "sha512-vIryvpP18ON9T9rjgMRFLr2xJVDpw1rtagEGf8Ccce4CkTrvM/fRB8N2nyWYOW5u3DdjkwKw5fBa+3tbn9P4PA==", "license": "MIT", "dependencies": { "fs-extra": "^11.1.1", @@ -3899,16 +3962,16 @@ } }, "node_modules/@docusaurus/tsconfig": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.9.1.tgz", - "integrity": "sha512-stdzM1dNDgRO0OvxeznXlE3N1igUoeHPNJjiKqyffLizgpVgNXJBAWeG6fuoYiCH4udGUBqy2dyM+1+kG2/UPQ==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.9.2.tgz", + "integrity": "sha512-j6/Fp4Rlpxsc632cnRnl5HpOWeb6ZKssDj6/XzzAzVGXXfm9Eptx3rxCC+fDzySn9fHTS+CWJjPineCR1bB5WQ==", "dev": true, "license": "MIT" }, "node_modules/@docusaurus/types": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.9.1.tgz", - "integrity": "sha512-ElekJ29sk39s5LTEZMByY1c2oH9FMtw7KbWFU3BtuQ1TytfIK39HhUivDEJvm5KCLyEnnfUZlvSNDXeyk0vzAA==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.9.2.tgz", + "integrity": "sha512-Ux1JUNswg+EfUEmajJjyhIohKceitY/yzjRUpu04WXgvVz+fbhVC0p+R0JhvEu4ytw8zIAys2hrdpQPBHRIa8Q==", "license": "MIT", "dependencies": { "@mdx-js/mdx": "^3.0.0", @@ -3942,14 +4005,14 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.9.1.tgz", - "integrity": "sha512-YAL4yhhWLl9DXuf5MVig260a6INz4MehrBGFU/CZu8yXmRiYEuQvRFWh9ZsjfAOyaG7za1MNmBVZ4VVAi/CiJA==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.9.2.tgz", + "integrity": "sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.9.1", - "@docusaurus/types": "3.9.1", - "@docusaurus/utils-common": "3.9.1", + "@docusaurus/logger": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils-common": "3.9.2", "escape-string-regexp": "^4.0.0", "execa": "5.1.1", "file-loader": "^6.2.0", @@ -3974,12 +4037,12 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.9.1.tgz", - "integrity": "sha512-4M1u5Q8Zn2CYL2TJ864M51FV4YlxyGyfC3x+7CLuR6xsyTVNBNU4QMcPgsTHRS9J2+X6Lq7MyH6hiWXyi/sXUQ==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.9.2.tgz", + "integrity": "sha512-I53UC1QctruA6SWLvbjbhCpAw7+X7PePoe5pYcwTOEXD/PxeP8LnECAhTHHwWCblyUX5bMi4QLRkxvyZ+IT8Aw==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.9.1", + "@docusaurus/types": "3.9.2", "tslib": "^2.6.0" }, "engines": { @@ -3987,14 +4050,14 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.9.1.tgz", - "integrity": "sha512-5bzab5si3E1udrlZuVGR17857Lfwe8iFPoy5AvMP9PXqDfoyIKT7gDQgAmxdRDMurgHaJlyhXEHHdzDKkOxxZQ==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.9.2.tgz", + "integrity": "sha512-l7yk3X5VnNmATbwijJkexdhulNsQaNDwoagiwujXoxFbWLcxHQqNQ+c/IAlzrfMMOfa/8xSBZ7KEKDesE/2J7A==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.9.1", - "@docusaurus/utils": "3.9.1", - "@docusaurus/utils-common": "3.9.1", + "@docusaurus/logger": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -4005,6 +4068,448 @@ "node": ">=20.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -4020,6 +4525,27 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@iconify/react": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@iconify/react/-/react-6.0.0.tgz", + "integrity": "sha512-eqNscABVZS8eCpZLU/L5F5UokMS9mnCf56iS1nM9YYHdH8ZxqZL9zyjSwW60IOQFsXZkilbBiv+1paMXBhSQnw==", + "license": "MIT", + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -4380,15 +4906,6 @@ "node": ">= 8" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -4436,6 +4953,356 @@ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "license": "MIT" }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -4458,9 +5325,9 @@ "license": "BSD-3-Clause" }, "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", "license": "MIT" }, "node_modules/@sindresorhus/is": { @@ -4475,6 +5342,18 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@slorber/remark-comment": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", @@ -4490,6 +5369,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, "license": "MIT" }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { @@ -4795,6 +5675,17 @@ "@types/node": "*" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -4823,6 +5714,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -4960,6 +5858,12 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "license": "MIT" }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -5143,9 +6047,9 @@ } }, "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -5163,15 +6067,6 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, - "node_modules/@vercel/oidc": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.0.1.tgz", - "integrity": "sha512-V/YRVrJDqM6VaMBjRUrd6qRMrTKvZjHdVdEmdXsOZMulTa3iK98ijKTc3wldBmst6W5rHpqMoKllKcBAHgN7GQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 20" - } - }, "node_modules/@vimeo/player": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/@vimeo/player/-/player-2.29.0.tgz", @@ -5182,6 +6077,117 @@ "weakmap-polyfill": "2.0.4" } }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -5450,24 +6456,6 @@ "node": ">=8" } }, - "node_modules/ai": { - "version": "5.0.60", - "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.60.tgz", - "integrity": "sha512-80U/3kmdBW6g+JkLXpz/P2EwkyEaWlPlYtuLUpx/JYK9F7WZh9NnkYoh1KvUi1Sbpo0NyurBTvX0a2AG9mmbDA==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/gateway": "1.0.33", - "@ai-sdk/provider": "2.0.0", - "@ai-sdk/provider-utils": "3.0.10", - "@opentelemetry/api": "1.9.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -5514,34 +6502,34 @@ } }, "node_modules/algoliasearch": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.39.0.tgz", - "integrity": "sha512-DzTfhUxzg9QBNGzU/0kZkxEV72TeA4MmPJ7RVfLnQwHNhhliPo7ynglEWJS791rNlLFoTyrKvkapwr/P3EXV9A==", - "license": "MIT", - "dependencies": { - "@algolia/abtesting": "1.5.0", - "@algolia/client-abtesting": "5.39.0", - "@algolia/client-analytics": "5.39.0", - "@algolia/client-common": "5.39.0", - "@algolia/client-insights": "5.39.0", - "@algolia/client-personalization": "5.39.0", - "@algolia/client-query-suggestions": "5.39.0", - "@algolia/client-search": "5.39.0", - "@algolia/ingestion": "1.39.0", - "@algolia/monitoring": "1.39.0", - "@algolia/recommend": "5.39.0", - "@algolia/requester-browser-xhr": "5.39.0", - "@algolia/requester-fetch": "5.39.0", - "@algolia/requester-node-http": "5.39.0" + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.48.0.tgz", + "integrity": "sha512-aD8EQC6KEman6/S79FtPdQmB7D4af/etcRL/KwiKFKgAE62iU8c5PeEQvpvIcBPurC3O/4Lj78nOl7ZcoazqSw==", + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.14.0", + "@algolia/client-abtesting": "5.48.0", + "@algolia/client-analytics": "5.48.0", + "@algolia/client-common": "5.48.0", + "@algolia/client-insights": "5.48.0", + "@algolia/client-personalization": "5.48.0", + "@algolia/client-query-suggestions": "5.48.0", + "@algolia/client-search": "5.48.0", + "@algolia/ingestion": "1.48.0", + "@algolia/monitoring": "1.48.0", + "@algolia/recommend": "5.48.0", + "@algolia/requester-browser-xhr": "5.48.0", + "@algolia/requester-fetch": "5.48.0", + "@algolia/requester-node-http": "5.48.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.26.0.tgz", - "integrity": "sha512-Rv2x3GXleQ3ygwhkhJubhhYGsICmShLAiqtUuJTUkr9uOCOXyF2E71LVT4XDnVffbknv8XgScP4U0Oxtgm+hIw==", + "version": "3.27.1", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.27.1.tgz", + "integrity": "sha512-XXGr02Cz285vLbqM6vPfb39xqV1ptpFr1xn9mqaW+nUvYTvFTdKgYTC/Cg1VzgRTQqNkq9+LlUVv8cfCeOoKig==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1" @@ -5682,6 +6670,16 @@ "node": ">=8" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/astring": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", @@ -5692,9 +6690,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "version": "10.4.24", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", + "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", "funding": [ { "type": "opencollective", @@ -5711,10 +6709,9 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001766", + "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, @@ -5755,13 +6752,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", - "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz", + "integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.7", - "@babel/helper-define-polyfill-provider": "^0.6.5", + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.6", "semver": "^6.3.1" }, "peerDependencies": { @@ -5791,12 +6788,12 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", - "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz", + "integrity": "sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==", "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5" + "@babel/helper-define-polyfill-provider": "^0.6.6" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -5819,9 +6816,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.12.tgz", - "integrity": "sha512-vAPMQdnyKCBtkmQA6FMCBvU9qFIppS3nzyXnEM+Lo2IAhG4Mpjv9cCxMudhgV3YdNNJv6TNqXy97dfRVL2LmaQ==", + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -6002,9 +6999,9 @@ } }, "node_modules/browserslist": { - "version": "4.26.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", - "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "funding": [ { "type": "opencollective", @@ -6021,11 +7018,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.9", - "caniuse-lite": "^1.0.30001746", - "electron-to-chromium": "^1.5.227", - "node-releases": "^2.0.21", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -6182,9 +7179,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001748", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001748.tgz", - "integrity": "sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==", + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", "funding": [ { "type": "opencollective", @@ -6229,6 +7226,16 @@ "react": ">=17.0.0" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -6806,12 +7813,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", - "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.48.0.tgz", + "integrity": "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==", "license": "MIT", "dependencies": { - "browserslist": "^4.25.3" + "browserslist": "^4.28.1" }, "funding": { "type": "opencollective", @@ -6819,9 +7826,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.45.1.tgz", - "integrity": "sha512-OHnWFKgTUshEU8MK+lOs1H8kC8GkTi9Z1tvNkxrCcw9wl3MJIO7q2ld77wjWn4/xuGrVu2X+nME1iIIPBSdyEQ==", + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.48.0.tgz", + "integrity": "sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -6928,9 +7935,9 @@ } }, "node_modules/css-blank-pseudo/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -6941,9 +7948,9 @@ } }, "node_modules/css-declaration-sorter": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.3.0.tgz", - "integrity": "sha512-LQF6N/3vkAMYF4xoHLJfG718HRJh34Z8BnNhd6bosOMIVjMlhuZK5++oZa3uYAgrI5+7x2o27gUqTR2U/KjUOQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.3.1.tgz", + "integrity": "sha512-gz6x+KkgNCjxq3Var03pRYLhyNfwhkKF1g/yoLgDNtFvVu0/fOLV9C8fFEZRjACp/XQLumjAYo7JVjzH3wLbxA==", "license": "ISC", "engines": { "node": "^14 || ^16 || >=18" @@ -7002,9 +8009,9 @@ } }, "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -7157,9 +8164,9 @@ } }, "node_modules/cssdb": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.4.2.tgz", - "integrity": "sha512-PzjkRkRUS+IHDJohtxkIczlxPPZqRo0nXplsYXOMBRPjcVRjj1W4DfvRgshUYTVuUigU7ptVYkFJQ7abUB0nyg==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.7.1.tgz", + "integrity": "sha512-+F6LKx48RrdGOtE4DT5jz7Uo+VeyKXpK797FAevIkzjV8bMHz6xTO5F7gNDcRCHmPgD5jj2g6QCsY9zmVrh38A==", "funding": [ { "type": "opencollective", @@ -7735,9 +8742,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.232", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.232.tgz", - "integrity": "sha512-ENirSe7wf8WzyPCibqKUG1Cg43cPaxH4wRR7AJsX7MCABCHBIOFqvaYODSLKUuZdraxUTHRE/0A2Aq8BYKEHOg==", + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7882,6 +8889,48 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -8046,9 +9095,9 @@ } }, "node_modules/estree-util-value-to-estree": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.4.0.tgz", - "integrity": "sha512-Zlp+gxis+gCfK12d3Srl2PdX2ybsEA8ZYy6vQGVQTNNYLEGRQQ56XB64bjemN8kxIKXP1nC9ip4Z+ILy9LGzvQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.5.0.tgz", + "integrity": "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" @@ -8137,15 +9186,6 @@ "node": ">=0.8.x" } }, - "node_modules/eventsource-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", - "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -8169,6 +9209,16 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", @@ -8591,15 +9641,15 @@ } }, "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", + "type": "github", "url": "https://github.com/sponsors/rawify" } }, @@ -8658,6 +9708,18 @@ "node": ">=6.9.0" } }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -8868,9 +9930,9 @@ } }, "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -9072,15 +10134,15 @@ } }, "node_modules/hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", - "property-information": "^6.0.0", + "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" @@ -9090,16 +10152,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-parse5/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -9625,9 +10677,9 @@ } }, "node_modules/inline-style-parser": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", "license": "MIT" }, "node_modules/invariant": { @@ -10060,9 +11112,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -10095,12 +11147,6 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "license": "MIT" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -10119,6 +11165,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", @@ -10131,6 +11183,31 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/katex": { + "version": "0.16.28", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.28.tgz", + "integrity": "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10219,6 +11296,15 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -10296,84 +11382,329 @@ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", "license": "MIT", "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/markdownlint": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.40.0.tgz", + "integrity": "sha512-UKybllYNheWac61Ia7T6fzuQNDZimFIpCg2w6hHjgV1Qu0w1TV0LlSgryUGzM0bkKQCBhy2FDhEELB73Kb0kAg==", + "license": "MIT", + "dependencies": { + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.2", + "string-width": "8.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/markdownlint-cli2": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.20.0.tgz", + "integrity": "sha512-esPk+8Qvx/f0bzI7YelUeZp+jCtFOk3KjZ7s9iBQZ6HlymSXoTtWGiIRZP05/9Oy2ehIoIjenVwndxGtxOIJYQ==", "license": "MIT", "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "globby": "15.0.0", + "js-yaml": "4.1.1", + "jsonc-parser": "3.3.1", + "markdown-it": "14.1.0", + "markdownlint": "0.40.0", + "markdownlint-cli2-formatter-default": "0.0.6", + "micromatch": "4.0.8" }, "bin": { - "loose-envify": "cli.js" + "markdownlint-cli2": "markdownlint-cli2-bin.mjs" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" } }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "node_modules/markdownlint-cli2-formatter-default": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.6.tgz", + "integrity": "sha512-VVDGKsq9sgzu378swJ0fcHfSicUnMxnL8gnLm/Q4J/xsNJ4e5bA6lvAz7PCzIl0/No0lHyaWdqVD2jotxOSFMQ==", "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + }, + "peerDependencies": { + "markdownlint-cli2": ">=0.0.4" } }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "node_modules/markdownlint-cli2/node_modules/globby": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-15.0.0.tgz", + "integrity": "sha512-oB4vkQGqlMl682wL1IlWd02tXCbquGWM4voPEI85QmNKCaw8zGTm1f1rubFgkg3Eli2PtKlFgrnmUqasbQWlkw==", "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.5", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" + "node_modules/markdownlint-cli2/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" } }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "node_modules/markdownlint-cli2/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "node_modules/markdownlint-cli2/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "license": "MIT", + "engines": { + "node": ">=14.16" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/marked": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.0.tgz", - "integrity": "sha512-CTPAcRBq57cn3R8n3hwc2REddc28hjR7RzDXQ+lXLmMJYqn20BaI2cGw6QjgZGIgVfp2Wdfw4aMzgNteQ6qJgQ==", + "node_modules/markdownlint/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", - "bin": { - "marked": "bin/marked.js" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/markdownlint/node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/markdownlint/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/markdownlint/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/markdownlint/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/markdownlint/node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdownlint/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 20" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/math-intrinsics": { @@ -10733,9 +12064,9 @@ } }, "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -10793,6 +12124,12 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "license": "CC0-1.0" }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, "node_modules/media-chrome": { "version": "4.14.0", "resolved": "https://registry.npmjs.org/media-chrome/-/media-chrome-4.14.0.tgz", @@ -11461,6 +12798,81 @@ ], "license": "MIT" }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromark-extension-mdx-expression": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", @@ -12720,9 +14132,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.4.tgz", - "integrity": "sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.0.tgz", + "integrity": "sha512-540P2c5dYnJlyJxTaSloliZexv8rji6rY8FhQN+WF/82iHQfA23j/xtJx97L+mXOML27EqksSek/g4eK7jaL3g==", "license": "MIT", "dependencies": { "schema-utils": "^4.0.0", @@ -12874,9 +14286,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.23", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", - "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "license": "MIT" }, "node_modules/normalize-path": { @@ -12888,15 +14300,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/normalize-url": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.0.tgz", @@ -13064,6 +14467,17 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "license": "MIT" }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -13433,6 +14847,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -13545,9 +14966,9 @@ } }, "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -13789,9 +15210,9 @@ } }, "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -13827,9 +15248,9 @@ } }, "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -13955,9 +15376,9 @@ } }, "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -13993,9 +15414,9 @@ } }, "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -14282,9 +15703,9 @@ } }, "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -14310,9 +15731,9 @@ } }, "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -14409,9 +15830,9 @@ } }, "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -14652,9 +16073,9 @@ } }, "node_modules/postcss-preset-env": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.4.0.tgz", - "integrity": "sha512-2kqpOthQ6JhxqQq1FSAAZGe9COQv75Aw8WbsOvQVNJ2nSevc9Yx/IKZGuZ7XJ+iOTtVon7LfO7ELRzg8AZ+sdw==", + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.6.1.tgz", + "integrity": "sha512-yrk74d9EvY+W7+lO9Aj1QmjWY9q5NsKjK2V9drkOPZB/X6KZ0B3igKsHUYakb7oYVhnioWypQX3xGuePf89f3g==", "funding": [ { "type": "github", @@ -14692,23 +16113,27 @@ "@csstools/postcss-media-minmax": "^2.0.9", "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5", "@csstools/postcss-nested-calc": "^4.0.0", - "@csstools/postcss-normalize-display-values": "^4.0.0", + "@csstools/postcss-normalize-display-values": "^4.0.1", "@csstools/postcss-oklab-function": "^4.0.12", + "@csstools/postcss-position-area-property": "^1.0.0", "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/postcss-property-rule-prelude-list": "^1.0.0", "@csstools/postcss-random-function": "^2.0.1", "@csstools/postcss-relative-color-syntax": "^3.0.12", "@csstools/postcss-scope-pseudo-class": "^4.0.1", "@csstools/postcss-sign-functions": "^1.1.4", "@csstools/postcss-stepped-value-functions": "^4.0.9", + "@csstools/postcss-syntax-descriptor-syntax-production": "^1.0.1", + "@csstools/postcss-system-ui-font-family": "^1.0.0", "@csstools/postcss-text-decoration-shorthand": "^4.0.3", "@csstools/postcss-trigonometric-functions": "^4.0.9", "@csstools/postcss-unset-value": "^4.0.0", - "autoprefixer": "^10.4.21", - "browserslist": "^4.26.0", + "autoprefixer": "^10.4.23", + "browserslist": "^4.28.1", "css-blank-pseudo": "^7.0.1", "css-has-pseudo": "^7.0.3", "css-prefers-color-scheme": "^10.0.0", - "cssdb": "^8.4.2", + "cssdb": "^8.6.0", "postcss-attribute-case-insensitive": "^7.0.1", "postcss-clamp": "^4.1.0", "postcss-color-functional-notation": "^7.0.12", @@ -14768,9 +16193,9 @@ } }, "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -14861,9 +16286,9 @@ } }, "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -15068,6 +16493,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pupa": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.3.0.tgz", @@ -15914,12 +17348,12 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -15988,6 +17422,51 @@ "node": ">=0.10.0" } }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, "node_modules/rtlcss": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", @@ -16499,6 +17978,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -16551,10 +18037,13 @@ "license": "MIT" }, "node_modules/sitemap/node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", + "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } }, "node_modules/skin-tone": { "version": "2.0.0", @@ -16708,6 +18197,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -16718,9 +18214,9 @@ } }, "node_modules/std-env": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "license": "MIT" }, "node_modules/string_decoder": { @@ -16847,21 +18343,21 @@ } }, "node_modules/style-to-js": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz", - "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", "license": "MIT", "dependencies": { - "style-to-object": "1.0.9" + "style-to-object": "1.0.14" } }, "node_modules/style-to-object": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", - "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", "license": "MIT", "dependencies": { - "inline-style-parser": "0.2.4" + "inline-style-parser": "0.2.7" } }, "node_modules/stylehacks": { @@ -16950,19 +18446,6 @@ "node": ">= 10" } }, - "node_modules/swr": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz", - "integrity": "sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.3", - "use-sync-external-store": "^1.4.0" - }, - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -17079,18 +18562,6 @@ "tslib": "^2" } }, - "node_modules/throttleit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", - "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -17115,6 +18586,71 @@ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "license": "MIT" }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", @@ -17124,6 +18660,16 @@ "node": "^18.0.0 || >=20.0.0" } }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -17297,6 +18843,12 @@ "node": "*" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, "node_modules/undici-types": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", @@ -17352,6 +18904,18 @@ "node": ">=4" } }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", @@ -17387,9 +18951,9 @@ } }, "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -17439,9 +19003,9 @@ } }, "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -17454,9 +19018,9 @@ } }, "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -17486,9 +19050,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "funding": [ { "type": "opencollective", @@ -17695,15 +19259,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -17809,6 +19364,203 @@ "@vimeo/player": "2.29.0" } }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" + }, + "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/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/watchpack": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", @@ -18254,6 +20006,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -18428,10 +20197,13 @@ } }, "node_modules/xml-js/node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", + "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } }, "node_modules/yallist": { "version": "3.1.1", @@ -18440,9 +20212,9 @@ "license": "ISC" }, "node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", "license": "MIT", "engines": { "node": ">=12.20" @@ -18457,15 +20229,6 @@ "integrity": "sha512-YHDIOAqgRpfl1Ois9HcB8UFtWOxK8KJrV5TXpImj4BKYP1rWT04f/fMM9tQ9SYZlBKukT7NR+9wcI3UpB5BMDQ==", "license": "MIT" }, - "node_modules/zod": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", - "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/docs/package.json b/docs/package.json index 0dc1e714..c69d436e 100644 --- a/docs/package.json +++ b/docs/package.json @@ -12,13 +12,17 @@ "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" + "typecheck": "tsc", + "test": "vitest run", + "test:watch": "vitest watch" }, "dependencies": { - "@docusaurus/core": "^3.8.1", - "@docusaurus/preset-classic": "^3.8.1", + "@docusaurus/core": "^3.9.2", + "@docusaurus/preset-classic": "^3.9.2", + "@iconify/react": "^6.0.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", + "markdownlint-cli2": "^0.20.0", "prism-react-renderer": "^2.3.0", "raw-loader": "^4.0.2", "react": "^18.0.0", @@ -26,10 +30,11 @@ "react-player": "^3.3.2" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^3.8.1", - "@docusaurus/tsconfig": "^3.8.1", - "@docusaurus/types": "^3.8.1", - "typescript": "~5.5.2" + "@docusaurus/module-type-aliases": "^3.9.2", + "@docusaurus/tsconfig": "^3.9.2", + "@docusaurus/types": "^3.9.2", + "typescript": "~5.5.2", + "vitest": "^4.0.17" }, "browserslist": { "production": [ diff --git a/docs/sidebars-infrahubctl.ts b/docs/sidebars-infrahubctl.ts deleted file mode 100644 index c50587f8..00000000 --- a/docs/sidebars-infrahubctl.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; - -const sidebars: SidebarsConfig = { - infrahubctlSidebar: [ - { - type: 'doc', - id: 'infrahubctl', - label: 'Infrahubctl CLI Tool', - }, - { - type: 'category', - label: 'Commands', - items: [ - 'infrahubctl-branch', - 'infrahubctl-check', - 'infrahubctl-dump', - 'infrahubctl-generator', - 'infrahubctl-info', - 'infrahubctl-load', - 'infrahubctl-menu', - 'infrahubctl-object', - 'infrahubctl-protocols', - 'infrahubctl-render', - 'infrahubctl-repository', - 'infrahubctl-run', - 'infrahubctl-schema', - 'infrahubctl-task', - 'infrahubctl-transform', - 'infrahubctl-validate', - 'infrahubctl-version' - ], - }, - ], -}; - -export default sidebars; \ No newline at end of file diff --git a/docs/sidebars-python-sdk.ts b/docs/sidebars-python-sdk.ts deleted file mode 100644 index e2fc932c..00000000 --- a/docs/sidebars-python-sdk.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; - -const sidebars: SidebarsConfig = { - pythonSdkSidebar: [ - { - type: 'category', - label: 'Python SDK docs', - link: { - type: 'doc', - id: 'introduction', - }, - items: [ - { - type: 'category', - label: 'Guides', - items: [ - 'guides/installation', - 'guides/client', - 'guides/query_data', - 'guides/create_update_delete', - 'guides/branches', - 'guides/store', - 'guides/tracking', - 'guides/python-typing', - 'guides/batch', - 'guides/object-storage', - 'guides/resource-manager', - ], - }, - { - type: 'category', - label: 'Topics', - items: [ - 'topics/tracking', - 'topics/object_file', - ], - }, - { - type: 'category', - label: 'Reference', - items: [ - 'reference/config', - 'reference/templating', - ], - }, - ], - }, - ], -}; - -export default sidebars; \ No newline at end of file diff --git a/docs/sidebars/sidebar-utils.test.ts b/docs/sidebars/sidebar-utils.test.ts new file mode 100644 index 00000000..010a9552 --- /dev/null +++ b/docs/sidebars/sidebar-utils.test.ts @@ -0,0 +1,119 @@ +import { describe, expect, it } from "vitest"; + +import { getCommandItems, getItemsWithOrder } from "./sidebar-utils"; + +describe("getCommandItems", () => { + it("should filter and sort mdx command files", () => { + const files = [ + "infrahubctl.mdx", + "infrahubctl-branch.mdx", + "infrahubctl-validate.mdx", + "infrahubctl-check.mdx", + ]; + + const result = getCommandItems(files); + + expect(result).toStrictEqual([ + "infrahubctl-branch", + "infrahubctl-check", + "infrahubctl-validate", + ]); + }); + + it("should exclude the index file", () => { + const files = ["infrahubctl.mdx", "infrahubctl-branch.mdx"]; + + const result = getCommandItems(files); + + expect(result).toStrictEqual(["infrahubctl-branch"]); + }); + + it("should ignore non-mdx files", () => { + const files = ["infrahubctl-branch.mdx", "README.md", ".DS_Store", "image.png"]; + + const result = getCommandItems(files); + + expect(result).toStrictEqual(["infrahubctl-branch"]); + }); + + it("should return an empty array when only the index file exists", () => { + const result = getCommandItems(["infrahubctl.mdx"]); + + expect(result).toStrictEqual([]); + }); + + it("should return an empty array for an empty directory", () => { + const result = getCommandItems([]); + + expect(result).toStrictEqual([]); + }); + + it("should support a custom index file name", () => { + const files = ["index.mdx", "command-a.mdx", "command-b.mdx"]; + + const result = getCommandItems(files, "index.mdx"); + + expect(result).toStrictEqual(["command-a", "command-b"]); + }); +}); + +describe("getItemsWithOrder", () => { + it("should preserve the defined order for known items", () => { + const files = ["client.mdx", "installation.mdx", "batch.mdx"]; + const orderedIds = ["guides/installation", "guides/client", "guides/batch"]; + + const result = getItemsWithOrder(files, orderedIds, "guides"); + + expect(result).toStrictEqual(["guides/installation", "guides/client", "guides/batch"]); + }); + + it("should append new files sorted alphabetically after ordered items", () => { + const files = ["client.mdx", "installation.mdx", "batch.mdx", "new-guide.mdx", "advanced.mdx"]; + const orderedIds = ["guides/installation", "guides/client", "guides/batch"]; + + const result = getItemsWithOrder(files, orderedIds, "guides"); + + expect(result).toStrictEqual([ + "guides/installation", + "guides/client", + "guides/batch", + "guides/advanced", + "guides/new-guide", + ]); + }); + + it("should skip ordered items that no longer exist on disk", () => { + const files = ["installation.mdx", "batch.mdx"]; + const orderedIds = ["guides/installation", "guides/client", "guides/batch"]; + + const result = getItemsWithOrder(files, orderedIds, "guides"); + + expect(result).toStrictEqual(["guides/installation", "guides/batch"]); + }); + + it("should ignore non-mdx files", () => { + const files = ["installation.mdx", "README.md", ".DS_Store"]; + const orderedIds = ["guides/installation"]; + + const result = getItemsWithOrder(files, orderedIds, "guides"); + + expect(result).toStrictEqual(["guides/installation"]); + }); + + it("should work without a prefix", () => { + const files = ["tracking.mdx", "object_file.mdx", "new-topic.mdx"]; + const orderedIds = ["tracking", "object_file"]; + + const result = getItemsWithOrder(files, orderedIds); + + expect(result).toStrictEqual(["tracking", "object_file", "new-topic"]); + }); + + it("should return all files sorted when no ordered ids are provided", () => { + const files = ["batch.mdx", "installation.mdx", "client.mdx"]; + + const result = getItemsWithOrder(files, [], "guides"); + + expect(result).toStrictEqual(["guides/batch", "guides/client", "guides/installation"]); + }); +}); diff --git a/docs/sidebars/sidebar-utils.ts b/docs/sidebars/sidebar-utils.ts new file mode 100644 index 00000000..0860904c --- /dev/null +++ b/docs/sidebars/sidebar-utils.ts @@ -0,0 +1,23 @@ +export function getCommandItems(files: string[], indexFile: string = 'infrahubctl.mdx'): string[] { + return files + .filter(file => file.endsWith('.mdx') && file !== indexFile) + .map(file => file.replace('.mdx', '')) + .sort(); +} + +export function getItemsWithOrder(files: string[], orderedIds: string[], prefix: string = ''): string[] { + const allIds = files + .filter(file => file.endsWith('.mdx')) + .map(file => { + const base = file.replace('.mdx', ''); + return prefix ? `${prefix}/${base}` : base; + }); + + const existingIds = new Set(allIds); + const ordered = orderedIds.filter(id => existingIds.has(id)); + + const orderedSet = new Set(orderedIds); + const remaining = allIds.filter(id => !orderedSet.has(id)).sort(); + + return [...ordered, ...remaining]; +} diff --git a/docs/sidebars/sidebars-infrahubctl.ts b/docs/sidebars/sidebars-infrahubctl.ts new file mode 100644 index 00000000..bd92511d --- /dev/null +++ b/docs/sidebars/sidebars-infrahubctl.ts @@ -0,0 +1,24 @@ +import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; +import {readdirSync} from 'fs'; +import {join} from 'path'; +import {getCommandItems} from './sidebar-utils'; + +const docsDir = join(__dirname, '..', 'docs', 'infrahubctl'); +const commandItems = getCommandItems(readdirSync(docsDir)); + +const sidebars: SidebarsConfig = { + infrahubctlSidebar: [ + { + type: 'doc', + id: 'infrahubctl', + label: 'Infrahubctl CLI Tool', + }, + { + type: 'category', + label: 'Commands', + items: commandItems, + }, + ], +}; + +export default sidebars; \ No newline at end of file diff --git a/docs/sidebars/sidebars-python-sdk.ts b/docs/sidebars/sidebars-python-sdk.ts new file mode 100644 index 00000000..3adbac3e --- /dev/null +++ b/docs/sidebars/sidebars-python-sdk.ts @@ -0,0 +1,86 @@ +import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'; +import { readdirSync } from 'fs'; +import { join } from 'path'; +import { getItemsWithOrder } from './sidebar-utils'; + +const pythonSdkDocsDir = join(__dirname, '..', 'docs', 'python-sdk'); + +const guidesItems = getItemsWithOrder( + readdirSync(join(pythonSdkDocsDir, 'guides')), + [ + 'guides/installation', + 'guides/client', + 'guides/query_data', + 'guides/create_update_delete', + 'guides/branches', + 'guides/store', + 'guides/tracking', + 'guides/python-typing', + 'guides/batch', + 'guides/object-storage', + 'guides/resource-manager', + ], + 'guides', +); + +const topicsItems = getItemsWithOrder( + readdirSync(join(pythonSdkDocsDir, 'topics')), + [ + 'topics/tracking', + 'topics/object_file', + ], + 'topics', +); + +const referenceItems = getItemsWithOrder( + readdirSync(join(pythonSdkDocsDir, 'reference')), + [ + 'reference/config', + 'reference/templating', + ], + 'reference', +); + +const sidebars: SidebarsConfig = { + pythonSdkSidebar: [ + { + type: 'category', + label: 'Python SDK docs', + link: { + type: 'doc', + id: 'introduction', + }, + items: [ + { + type: 'category', + label: 'Guides', + items: guidesItems, + }, + { + type: 'category', + label: 'Topics', + items: topicsItems, + }, + { + type: 'category', + label: 'Reference', + items: [ + { + type: 'category', + label: 'Python SDK API', + items: [ + { + type: 'autogenerated', + dirName: 'sdk_ref', + }, + ], + }, + ...referenceItems, + ], + }, + ], + }, + ], +}; + +export default sidebars; \ No newline at end of file diff --git a/docs/src/theme/MDXComponents.js b/docs/src/theme/MDXComponents.js new file mode 100644 index 00000000..682ed38f --- /dev/null +++ b/docs/src/theme/MDXComponents.js @@ -0,0 +1,10 @@ +import React from 'react'; +// Import the original mapper +import MDXComponents from '@theme-original/MDXComponents'; +import { Icon } from '@iconify/react'; // Import the entire Iconify library. + +export default { + // Re-use the default mapping + ...MDXComponents, + Icon: Icon, // Make the iconify Icon component available in MDX as . +}; \ No newline at end of file diff --git a/docs/vitest.config.ts b/docs/vitest.config.ts new file mode 100644 index 00000000..7d2a0b3b --- /dev/null +++ b/docs/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["**/*.test.ts"], + exclude: ["**/node_modules/**", "**/build/**"], + }, +}); diff --git a/infrahub_sdk/client.py b/infrahub_sdk/client.py index c924ab41..70b026af 100644 --- a/infrahub_sdk/client.py +++ b/infrahub_sdk/client.py @@ -568,7 +568,7 @@ async def _process_nodes_and_relationships( response (dict[str, Any]): The response from the GraphQL query. schema_kind (str): The kind of schema being queried. branch (str): The branch name. - prefetch_relationships (bool): Flag to indicate whether to prefetch relationship data. + prefetch_relationships (bool): Flag to indicate whether to pre-fetch relationship data. timeout (int, optional): Overrides default timeout used when querying the GraphQL API. Specified in seconds. Returns: @@ -698,7 +698,7 @@ async def all( include (list[str], optional): List of attributes or relationships to include in the query. exclude (list[str], optional): List of attributes or relationships to exclude from the query. fragment (bool, optional): Flag to use GraphQL fragments for generic schemas. - prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data. + prefetch_relationships (bool, optional): Flag to indicate whether to pre-fetch related node data. parallel (bool, optional): Whether to use parallel processing for the query. order (Order, optional): Ordering related options. Setting `disable=True` enhances performances. include_metadata (bool, optional): If True, includes node_metadata and relationship_metadata in the query. @@ -801,7 +801,7 @@ async def filters( include (list[str], optional): List of attributes or relationships to include in the query. exclude (list[str], optional): List of attributes or relationships to exclude from the query. fragment (bool, optional): Flag to use GraphQL fragments for generic schemas. - prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data. + prefetch_relationships (bool, optional): Flag to indicate whether to pre-fetch related node data. partial_match (bool, optional): Allow partial match of filter criteria for the query. parallel (bool, optional): Whether to use parallel processing for the query. order (Order, optional): Ordering related options. Setting `disable=True` enhances performances. @@ -919,7 +919,7 @@ async def execute_graphql( tracker: str | None = None, ) -> dict: """Execute a GraphQL query (or mutation). - If retry_on_failure is True, the query will retry until the server becomes reacheable. + If retry_on_failure is True, the query will retry until the server becomes reachable. Args: query (_type_): GraphQL Query to execute, can be a query or a mutation @@ -1975,7 +1975,7 @@ def execute_graphql( tracker: str | None = None, ) -> dict: """Execute a GraphQL query (or mutation). - If retry_on_failure is True, the query will retry until the server becomes reacheable. + If retry_on_failure is True, the query will retry until the server becomes reachable. Args: query (str): GraphQL Query to execute, can be a query or a mutation @@ -2275,7 +2275,7 @@ def all( include (list[str], optional): List of attributes or relationships to include in the query. exclude (list[str], optional): List of attributes or relationships to exclude from the query. fragment (bool, optional): Flag to use GraphQL fragments for generic schemas. - prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data. + prefetch_relationships (bool, optional): Flag to indicate whether to pre-fetch related node data. parallel (bool, optional): Whether to use parallel processing for the query. order (Order, optional): Ordering related options. Setting `disable=True` enhances performances. include_metadata (bool, optional): If True, includes node_metadata and relationship_metadata in the query. @@ -2316,7 +2316,7 @@ def _process_nodes_and_relationships( response (dict[str, Any]): The response from the GraphQL query. schema_kind (str): The kind of schema being queried. branch (str): The branch name. - prefetch_relationships (bool): Flag to indicate whether to prefetch relationship data. + prefetch_relationships (bool): Flag to indicate whether to pre-fetch relationship data. timeout (int, optional): Overrides default timeout used when querying the GraphQL API. Specified in seconds. Returns: @@ -2419,7 +2419,7 @@ def filters( include (list[str], optional): List of attributes or relationships to include in the query. exclude (list[str], optional): List of attributes or relationships to exclude from the query. fragment (bool, optional): Flag to use GraphQL fragments for generic schemas. - prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data. + prefetch_relationships (bool, optional): Flag to indicate whether to pre-fetch related node data. partial_match (bool, optional): Allow partial match of filter criteria for the query. parallel (bool, optional): Whether to use parallel processing for the query. order (Order, optional): Ordering related options. Setting `disable=True` enhances performances. diff --git a/infrahub_sdk/ctl/utils.py b/infrahub_sdk/ctl/utils.py index 968f6093..7130ea80 100644 --- a/infrahub_sdk/ctl/utils.py +++ b/infrahub_sdk/ctl/utils.py @@ -51,7 +51,7 @@ def init_logging(debug: bool = False) -> None: def handle_exception(exc: Exception, console: Console, exit_code: int) -> NoReturn: - """Handle exeception in a different fashion based on its type.""" + """Handle exception in a different fashion based on its type.""" if isinstance(exc, Exit): raise typer.Exit(code=exc.exit_code) if isinstance(exc, AuthenticationError): diff --git a/infrahub_sdk/jinja2.py b/infrahub_sdk/jinja2.py index 29afbf06..d64d22c1 100644 --- a/infrahub_sdk/jinja2.py +++ b/infrahub_sdk/jinja2.py @@ -7,7 +7,7 @@ def identify_faulty_jinja_code(traceback: Traceback, nbr_context_lines: int = 3) -> list[tuple[Frame, Syntax]]: """This function identifies the faulty Jinja2 code and beautify it to provide meaningful information to the user. - We use the rich's Traceback to parse the complete stack trace and extract Frames for each expection found in the trace. + We use the rich's Traceback to parse the complete stack trace and extract Frames for each exception found in the trace. """ response = [] diff --git a/infrahub_sdk/node/node.py b/infrahub_sdk/node/node.py index 2768c10d..bbc14304 100644 --- a/infrahub_sdk/node/node.py +++ b/infrahub_sdk/node/node.py @@ -256,12 +256,12 @@ def upload_from_bytes(self, content: bytes | BinaryIO, name: str) -> None: FeatureNotSupportedError: If this node doesn't inherit from CoreFileObject. Examples: - # Using bytes (for small files) - node.upload_from_bytes(content=b"file content", name="example.txt") + >>> # Using bytes (for small files) + >>> node.upload_from_bytes(content=b"file content", name="example.txt") - # Using file-like object (for large files) - with open("/path/to/file.bin", "rb") as f: - node.upload_from_bytes(content=f, name="file.bin") + >>> # Using file-like object (for large files) + >>> with open("/path/to/file.bin", "rb") as f: + ... node.upload_from_bytes(content=f, name="file.bin") """ if not self._file_object_support: raise FeatureNotSupportedError( @@ -794,8 +794,8 @@ async def download_file(self, dest: Path | None = None) -> bytes | int: file content will be returned as bytes. Returns: - If dest is None: The file content as bytes. - If dest is provided: The number of bytes written to the file. + If ``dest`` is None: The file content as bytes. + If ``dest`` is provided: The number of bytes written to the file. Raises: FeatureNotSupportedError: If this node doesn't inherit from CoreFileObject. @@ -803,11 +803,11 @@ async def download_file(self, dest: Path | None = None) -> bytes | int: AuthenticationError: If authentication fails. Examples: - # Download to memory - content = await contract.download_file() + >>> # Download to memory + >>> content = await contract.download_file() - # Stream to file (memory-efficient for large files) - bytes_written = await contract.download_file(dest=Path("/tmp/contract.pdf")) + >>> # Stream to file (memory-efficient for large files) + >>> bytes_written = await contract.download_file(dest=Path("/tmp/contract.pdf")) """ self._validate_file_object_support(message=FILE_DOWNLOAD_FEATURE_NOT_SUPPORTED_MESSAGE) @@ -1447,7 +1447,7 @@ async def get_flat_value(self, key: str, separator: str = "__") -> Any: return await related_node.peer.get_flat_value(key=remaining, separator=separator) async def extract(self, params: dict[str, str]) -> dict[str, Any]: - """Extract some datapoints defined in a flat notation.""" + """Extract some data points defined in a flat notation.""" result: dict[str, Any] = {} for key, value in params.items(): result[key] = await self.get_flat_value(key=value) @@ -1682,8 +1682,8 @@ def download_file(self, dest: Path | None = None) -> bytes | int: file content will be returned as bytes. Returns: - If dest is None: The file content as bytes. - If dest is provided: The number of bytes written to the file. + If ``dest`` is None: The file content as bytes. + If ``dest`` is provided: The number of bytes written to the file. Raises: FeatureNotSupportedError: If this node doesn't inherit from CoreFileObject. @@ -1691,11 +1691,11 @@ def download_file(self, dest: Path | None = None) -> bytes | int: AuthenticationError: If authentication fails. Examples: - # Download to memory - content = contract.download_file() + >>> # Download to memory + >>> content = contract.download_file() - # Stream to file (memory-efficient for large files) - bytes_written = contract.download_file(dest=Path("/tmp/contract.pdf")) + >>> # Stream to file (memory-efficient for large files) + >>> bytes_written = contract.download_file(dest=Path("/tmp/contract.pdf")) """ self._validate_file_object_support(message=FILE_DOWNLOAD_FEATURE_NOT_SUPPORTED_MESSAGE) @@ -2332,7 +2332,7 @@ def get_flat_value(self, key: str, separator: str = "__") -> Any: return related_node.peer.get_flat_value(key=remaining, separator=separator) def extract(self, params: dict[str, str]) -> dict[str, Any]: - """Extract some datapoints defined in a flat notation.""" + """Extract some data points defined in a flat notation.""" result: dict[str, Any] = {} for key, value in params.items(): result[key] = self.get_flat_value(key=value) diff --git a/infrahub_sdk/node/parsers.py b/infrahub_sdk/node/parsers.py index 0ae830f1..c5d2fbbd 100644 --- a/infrahub_sdk/node/parsers.py +++ b/infrahub_sdk/node/parsers.py @@ -4,7 +4,7 @@ def parse_human_friendly_id(hfid: str | list[str]) -> tuple[str | None, list[str]]: - """Parse a human friendly ID into a kind and an identifier.""" + """Parse a human-friendly ID into a kind and an identifier.""" if isinstance(hfid, str): hfid_parts = hfid.split(HFID_STR_SEPARATOR) if len(hfid_parts) == 1: diff --git a/infrahub_sdk/pytest_plugin/items/base.py b/infrahub_sdk/pytest_plugin/items/base.py index a1b35a00..ae08f036 100644 --- a/infrahub_sdk/pytest_plugin/items/base.py +++ b/infrahub_sdk/pytest_plugin/items/base.py @@ -75,7 +75,7 @@ def reportinfo(self) -> tuple[Path | str, int | None, str]: def repository_base(self) -> str: """Return the path to the root of the repository - This will be an absolute path if --infrahub-config-path is an absolut path as happens when + This will be an absolute path if --infrahub-config-path is an absolute path as happens when tests are started from within Infrahub server. """ config_path: Path = getattr(self.session, _infrahub_config_path_attribute) diff --git a/pyproject.toml b/pyproject.toml index 2aa9b28e..e07eb255 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ ctl = [ "typer>=0.12.5", "click==8.1.*", "ariadne-codegen==0.15.3", + "mdxify>=0.2.23; python_version>='3.10'", ] all = [ diff --git a/tasks.py b/tasks.py index 24dcb642..32b13ae6 100644 --- a/tasks.py +++ b/tasks.py @@ -1,32 +1,64 @@ -import asyncio +from __future__ import annotations + import json +import operator +import shutil import sys +from functools import reduce from pathlib import Path from shutil import which -from typing import Any +from typing import TYPE_CHECKING + +from invoke import Context, Exit, task + +if TYPE_CHECKING: + from docs.docs_generation.content_gen_methods.command.typer_command import ATyperCommand -from invoke import Context, task +from docs.docs_generation.content_gen_methods.mdx.mdx_priority import PagePriority CURRENT_DIRECTORY = Path(__file__).resolve() DOCUMENTATION_DIRECTORY = CURRENT_DIRECTORY.parent / "docs" MAIN_DIRECTORY_PATH = Path(__file__).parent - -def is_tool_installed(name: str) -> bool: - """Check whether `name` is on PATH and marked as executable.""" - return which(name) is not None - - -def _generate(context: Context) -> None: - """Generate documentation output from code.""" +# Priority ordering for generated API documentation pages. +# Keys match the mdxify output filenames (same keys used in generated_files dict). +PAGE_PRIORITIES: dict[str, PagePriority] = { + "infrahub_sdk-client.mdx": PagePriority( + sections=["Classes"], + classes=["InfrahubClient", "InfrahubClientSync"], + methods={ + "InfrahubClient": ["get", "delete", "create"], + "InfrahubClientSync": ["get", "delete", "create"], + }, + ), + "infrahub_sdk-node-node.mdx": PagePriority( + classes=["InfrahubNode", "InfrahubNodeSync"], + ), +} + + +def require_tool(name: str, install_hint: str) -> None: + """Raise ``Exit`` if *name* is not found on PATH.""" + if which(name) is None: + raise Exit(f" - {name} is not installed. {install_hint}", code=1) + + +@task(name="docs-generate") +def docs_generate(context: Context) -> None: + """Generate all documentation (infrahubctl CLI + Python SDK).""" _generate_infrahubctl_documentation(context=context) - _generate_infrahub_sdk_configuration_documentation() - _generate_infrahub_sdk_template_documentation() + generate_python_sdk(context) def _generate_infrahubctl_documentation(context: Context) -> None: """Generate the documentation for infrahubctl CLI using typer-cli.""" + from docs.docs_generation.content_gen_methods import ( + CommandOutputDocContentGenMethod, + TyperGroupCommand, + TyperSingleCommand, + ) + from docs.docs_generation.pages import DocPage, MDXDocPage from infrahub_sdk.ctl.cli import app output_dir = DOCUMENTATION_DIRECTORY / "docs" / "infrahubctl" @@ -37,113 +69,158 @@ def _generate_infrahubctl_documentation(context: Context) -> None: file.unlink() print(" - Generate infrahubctl CLI documentation") - for cmd in app.registered_commands: - if cmd.hidden: - continue - exec_cmd = ( - f'uv run typer --func {cmd.name} infrahub_sdk.ctl.cli_commands utils docs --name "infrahubctl {cmd.name}"' + commands: list[ATyperCommand] = [ + TyperSingleCommand(name=cmd.name) for cmd in app.registered_commands if not cmd.hidden and cmd.name + ] + commands.extend(TyperGroupCommand(name=cmd.name) for cmd in app.registered_groups if not cmd.hidden and cmd.name) + + for typer_cmd in commands: + # Generating one documentation page for one command + page = DocPage( + content_gen_method=CommandOutputDocContentGenMethod( + context=context, + working_directory=MAIN_DIRECTORY_PATH, + command=typer_cmd, + ), ) - exec_cmd += f" --output docs/docs/infrahubctl/infrahubctl-{cmd.name}.mdx" - with context.cd(MAIN_DIRECTORY_PATH): - context.run(exec_cmd) - - for cmd in app.registered_groups: - if cmd.hidden: - continue - exec_cmd = f"uv run typer infrahub_sdk.ctl.{cmd.name} utils docs" - exec_cmd += f' --name "infrahubctl {cmd.name}" --output docs/docs/infrahubctl/infrahubctl-{cmd.name}.mdx' - with context.cd(MAIN_DIRECTORY_PATH): - context.run(exec_cmd) + output_path = output_dir / f"infrahubctl-{typer_cmd.name}.mdx" + MDXDocPage(page=page, output_path=output_path).to_mdx() def _generate_infrahub_sdk_configuration_documentation() -> None: """Generate documentation for the Infrahub SDK configuration.""" - import jinja2 - - from infrahub_sdk.config import ConfigBase - - schema = ConfigBase.model_json_schema() - env_vars = _get_env_vars() - definitions = schema.get("$defs", {}) - - properties = [] - for name, prop in schema["properties"].items(): - choices: list[dict[str, Any]] = [] - kind = "" - composed_type = "" - - if "allOf" in prop: - choices = definitions.get(prop["allOf"][0]["$ref"].split("/")[-1], {}).get("enum", []) - kind = definitions.get(prop["allOf"][0]["$ref"].split("/")[-1], {}).get("type", "") - - if "anyOf" in prop: - composed_type = ", ".join(i["type"] for i in prop.get("anyOf", []) if "type" in i and i["type"] != "null") - - properties.append( - { - "name": name, - "description": prop.get("description", ""), - "type": prop.get("type", kind) or composed_type or "object", - "choices": choices, - "default": prop.get("default", ""), - "env_vars": env_vars.get(name, []), - } - ) + from docs.docs_generation.content_gen_methods import Jinja2DocContentGenMethod + from docs.docs_generation.helpers import build_config_properties + from docs.docs_generation.pages import DocPage, MDXDocPage + from infrahub_sdk.template import Jinja2Template print(" - Generate Infrahub SDK configuration documentation") - - template_file = DOCUMENTATION_DIRECTORY / "_templates" / "sdk_config.j2" - output_file = DOCUMENTATION_DIRECTORY / "docs" / "python-sdk" / "reference" / "config.mdx" - - if not template_file.exists(): - print(f"Unable to find the template file at {template_file}") - sys.exit(-1) - - template_text = template_file.read_text(encoding="utf-8") - - environment = jinja2.Environment(trim_blocks=True, autoescape=jinja2.select_autoescape(default_for_string=False)) - template = environment.from_string(template_text) - rendered_file = template.render(properties=properties) - - output_file.write_text(rendered_file, encoding="utf-8") - print(f"Docs saved to: {output_file}") + # Generating one documentation page for the ConfigBase.model_json_schema() + page = DocPage( + content_gen_method=Jinja2DocContentGenMethod( + template=Jinja2Template( + template=Path("sdk_config.j2"), + template_directory=DOCUMENTATION_DIRECTORY / "_templates", + ), + template_variables={"properties": build_config_properties()}, + ), + ) + output_path = DOCUMENTATION_DIRECTORY / "docs" / "python-sdk" / "reference" / "config.mdx" + MDXDocPage(page=page, output_path=output_path).to_mdx() def _generate_infrahub_sdk_template_documentation() -> None: """Generate documentation for the Infrahub SDK template reference.""" + from docs.docs_generation.content_gen_methods import Jinja2DocContentGenMethod + from docs.docs_generation.pages import DocPage, MDXDocPage from infrahub_sdk.template import Jinja2Template from infrahub_sdk.template.filters import BUILTIN_FILTERS, NETUTILS_FILTERS - output_file = DOCUMENTATION_DIRECTORY / "docs" / "python-sdk" / "reference" / "templating.mdx" - jinja2_template = Jinja2Template( - template=Path("sdk_template_reference.j2"), - template_directory=DOCUMENTATION_DIRECTORY / "_templates", + print(" - Generate Infrahub SDK template documentation") + # Generating one documentation page for template documentation + page = DocPage( + content_gen_method=Jinja2DocContentGenMethod( + template=Jinja2Template( + template=Path("sdk_template_reference.j2"), + template_directory=DOCUMENTATION_DIRECTORY / "_templates", + ), + template_variables={"builtin": BUILTIN_FILTERS, "netutils": NETUTILS_FILTERS}, + ), ) + output_path = DOCUMENTATION_DIRECTORY / "docs" / "python-sdk" / "reference" / "templating.mdx" + MDXDocPage(page=page, output_path=output_path).to_mdx() + + +def get_modules_to_document() -> list[str]: + """Return the list of Python module paths to document with mdxify. + + Auto-discovers packages under ``infrahub_sdk/`` and validates that every + discovered package is explicitly categorised as either *to document* or + *to ignore*. Individual ``.py`` modules can be added via + ``extra_modules_to_document``. + """ + # Packages (sub-folders of infrahub_sdk/) to document. + # Passed to mdxify as "infrahub_sdk.". + packages_to_document = [ + "node", + ] + + # Packages explicitly ignored for API doc generation. + packages_to_ignore = [ + "ctl", + "graphql", + "protocols_generator", + "pytest_plugin", + "schema", + "spec", + "task", + "template", + "testing", + "transfer", + ] + + # Extra modules (individual .py files, not packages) to document. + extra_modules_to_document = [ + "infrahub_sdk.client", + ] + + # Auto-discover all packages under infrahub_sdk/ + sdk_dir = Path(__file__).parent / "infrahub_sdk" + discovered_packages = {d.name for d in sdk_dir.iterdir() if d.is_dir() and (d / "__init__.py").exists()} + + # Validate that every discovered package is categorized and vice versa + declared = set(packages_to_document) | set(packages_to_ignore) + uncategorized = discovered_packages - declared + unknown = declared - discovered_packages + + if uncategorized: + raise ValueError( + f"Uncategorized packages under infrahub_sdk/: {sorted(uncategorized)}. " + "Add them to packages_to_document or packages_to_ignore in tasks.py" + ) + + if unknown: + raise ValueError(f"Declared packages that no longer exist: {sorted(unknown)}") + + return [f"infrahub_sdk.{pkg}" for pkg in packages_to_document] + extra_modules_to_document + - rendered_file = asyncio.run( - jinja2_template.render(variables={"builtin": BUILTIN_FILTERS, "netutils": NETUTILS_FILTERS}) +@task(name="generate-sdk-api-docs") +def _generate_sdk_api_docs(context: Context) -> None: + """Generate API documentation for the Python SDK.""" + from docs.docs_generation.content_gen_methods import ( + CollapsedOverloadCodeDocumentation, + FilePrintingDocContentGenMethod, + MdxCodeDocumentation, + OrderedMdxCodeDocumentation, ) - output_file.write_text(rendered_file, encoding="utf-8") - print(f"Docs saved to: {output_file}") + from docs.docs_generation.pages import DocPage, MDXDocPage + print(" - Generate Python SDK API documentation") + require_tool("mdxify", "Install it with: uv sync --all-groups --all-extras") -def _get_env_vars() -> dict[str, list[str]]: - """Retrieve environment variables for Infrahub SDK configuration.""" - from collections import defaultdict + modules_to_document = get_modules_to_document() - from pydantic_settings import EnvSettingsSource + output_dir = DOCUMENTATION_DIRECTORY / "docs" / "python-sdk" / "sdk_ref" - from infrahub_sdk.config import ConfigBase + if (output_dir / "infrahub_sdk").exists(): + shutil.rmtree(output_dir / "infrahub_sdk") - env_vars: dict[str, list[str]] = defaultdict(list) - settings = ConfigBase() - env_settings = EnvSettingsSource(settings.__class__, env_prefix=settings.model_config.get("env_prefix", "")) + documentation = CollapsedOverloadCodeDocumentation( + documentation=OrderedMdxCodeDocumentation( + documentation=MdxCodeDocumentation(), + page_priorities=PAGE_PRIORITIES, + ) + ) + generated_files = documentation.generate(context=context, modules_to_document=modules_to_document) - for field_name, field in settings.model_fields.items(): - for field_key, field_env_name, _ in env_settings._extract_field_info(field, field_name): - env_vars[field_key].append(field_env_name.upper()) + for file_key, mdxified_file in generated_files.items(): + page = DocPage(content_gen_method=FilePrintingDocContentGenMethod(file=mdxified_file)) + target_path = output_dir / reduce(operator.truediv, (Path(part) for part in file_key.split("-"))) + MDXDocPage(page=page, output_path=target_path).to_mdx() - return env_vars + with context.cd(DOCUMENTATION_DIRECTORY): + context.run(f"npx --no-install markdownlint-cli2 {output_dir}/ --fix --config .markdownlint.yaml", pty=True) @task @@ -195,24 +272,18 @@ def lint_ruff(context: Context) -> None: @task def lint_markdownlint(context: Context) -> None: """Run markdownlint to check all markdown files.""" - if not is_tool_installed("markdownlint-cli2"): - print(" - markdownlint-cli2 is not installed, skipping documentation linting") - return - print(" - Check documentation with markdownlint-cli2") - exec_cmd = "markdownlint-cli2 **/*.{md,mdx} --config .markdownlint.yaml" - with context.cd(MAIN_DIRECTORY_PATH): + exec_cmd = "npx --no-install markdownlint-cli2 **/*.{md,mdx} !node_modules/** --config .markdownlint.yaml" + with context.cd(DOCUMENTATION_DIRECTORY): context.run(exec_cmd) @task def lint_vale(context: Context) -> None: """Run vale to check all documentation files.""" - if not is_tool_installed("vale"): - print(" - vale is not installed, skipping documentation style linting") - return - print(" - Check documentation style with vale") + require_tool("vale", "Install it from: https://vale.sh/docs/install") + exec_cmd = r'vale $(find ./docs -type f \( -name "*.mdx" -o -name "*.md" \) -not -path "*/node_modules/*")' with context.cd(MAIN_DIRECTORY_PATH): context.run(exec_cmd) @@ -243,11 +314,27 @@ def lint_all(context: Context) -> None: @task(name="docs-validate") def docs_validate(context: Context) -> None: - """Validate that the generated documentation is committed to Git.""" - _generate(context=context) - exec_cmd = "git diff --exit-code docs" - with context.cd(MAIN_DIRECTORY_PATH): - context.run(exec_cmd) + """Validate that the generated documentation matches the committed version. + + Regenerates all documentation and checks for modified, deleted, or new + untracked files under docs/. Exits with a non-zero code and a descriptive + message when the working tree diverges from what is committed. + """ + docs_generate(context) + with context.cd(DOCUMENTATION_DIRECTORY): + diff_result = context.run("git diff --name-only docs", hide=True) + changed_files = diff_result.stdout.strip() if diff_result else "" + untracked_result = context.run("git ls-files --others --exclude-standard docs", hide=True) + untracked_files = untracked_result.stdout.strip() if untracked_result else "" + + if changed_files or untracked_files: + message = "Generated documentation is out of sync with the committed version.\n" + message += "Run 'uv run invoke docs-generate' and commit the result.\n\n" + if changed_files: + message += f"Modified or deleted files:\n{changed_files}\n\n" + if untracked_files: + message += f"New untracked files:\n{untracked_files}\n" + raise Exit(message, code=1) @task(name="docs") @@ -269,10 +356,11 @@ def generate_infrahubctl(context: Context) -> None: @task(name="generate-sdk") -def generate_python_sdk(context: Context) -> None: # noqa: ARG001 +def generate_python_sdk(context: Context) -> None: """Generate documentation for the Python SDK.""" _generate_infrahub_sdk_configuration_documentation() _generate_infrahub_sdk_template_documentation() + _generate_sdk_api_docs(context) @task diff --git a/tests/unit/doc_generation/__init__.py b/tests/unit/doc_generation/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/doc_generation/content_gen_methods/__init__.py b/tests/unit/doc_generation/content_gen_methods/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/doc_generation/content_gen_methods/command/__init__.py b/tests/unit/doc_generation/content_gen_methods/command/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/doc_generation/content_gen_methods/command/test_typer_command.py b/tests/unit/doc_generation/content_gen_methods/command/test_typer_command.py new file mode 100644 index 00000000..4b0d2363 --- /dev/null +++ b/tests/unit/doc_generation/content_gen_methods/command/test_typer_command.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from docs.docs_generation.content_gen_methods import TyperGroupCommand, TyperSingleCommand + + +class TestTyperSingleCommand: + def test_build_exec_cmd(self) -> None: + # Arrange + cmd = TyperSingleCommand(name="dump") + + # Act + result = cmd.build() + + # Assert + assert "uv run typer --func dump" in result + assert "infrahub_sdk.ctl.cli_commands" in result + assert 'utils docs --name "infrahubctl dump"' in result + + +class TestTyperGroupCommand: + def test_build_exec_cmd(self) -> None: + # Arrange + cmd = TyperGroupCommand(name="branch") + + # Act + result = cmd.build() + + # Assert + assert "uv run typer infrahub_sdk.ctl.branch" in result + assert 'utils docs --name "infrahubctl branch"' in result diff --git a/tests/unit/doc_generation/content_gen_methods/mdx/__init__.py b/tests/unit/doc_generation/content_gen_methods/mdx/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/doc_generation/content_gen_methods/mdx/test_mdx_code_doc.py b/tests/unit/doc_generation/content_gen_methods/mdx/test_mdx_code_doc.py new file mode 100644 index 00000000..d08d7e1a --- /dev/null +++ b/tests/unit/doc_generation/content_gen_methods/mdx/test_mdx_code_doc.py @@ -0,0 +1,290 @@ +from __future__ import annotations + +from pathlib import Path +from unittest.mock import create_autospec + +from invoke import Context, Result + +from docs.docs_generation.content_gen_methods import ( + MdxCodeDocumentation, +) + + +def _make_mock_context( + module_files: dict[str, dict[str, str]], + calls: list[str] | None = None, +) -> Context: + """Build a mock ``Context`` whose ``run()`` writes files based on requested modules. + + Args: + module_files: Mapping of module name to its output files + (e.g. ``{"infrahub_sdk.node": {"node.mdx": "# Node"}}``). + Only files belonging to modules present in the ``mdxify`` command + are written to the output directory. + calls: If provided, each executed command string is appended to this + list so the caller can verify how many times ``run()`` was invoked. + """ + ctx = create_autospec(Context, instance=True) + + def fake_run(cmd: str, **kwargs: object) -> Result: + if calls is not None: + calls.append(cmd) + prefix, output_dir_str = cmd.split("--output-dir ") + output_dir = Path(output_dir_str.strip()) + requested_modules = prefix.replace("mdxify ", "").split() + for module in requested_modules: + for filename, content in module_files.get(module, {}).items(): + (output_dir / filename).write_text(content, encoding="utf-8") + return Result() + + ctx.run.side_effect = fake_run + return ctx + + +def _generate_one(raw_content: str) -> str: + """Run a single piece of raw content through ``generate()`` and return the processed result.""" + mock_context = _make_mock_context({"mod": {"mod.mdx": raw_content}}) + doc = MdxCodeDocumentation() + return doc.generate(context=mock_context, modules_to_document=["mod"])["mod.mdx"].content + + +class TestDoctestWrapping: + def test_wraps_bare_doctest_in_code_fence(self) -> None: + """Bare doctest lines (>>>) following prose are wrapped in a ```python fence.""" + # Arrange + raw = "**Examples:**\n\n>>> foo()\n'bar'" + + # Act + result = _generate_one(raw) + + # Assert + assert result == "**Examples:**\n\n```python\n>>> foo()\n'bar'\n```" + + def test_leaves_existing_fenced_code_blocks_untouched(self) -> None: + """Content already inside a fenced code block is not double-wrapped.""" + # Arrange + raw = "```python\ndef foo():\n pass\n```\n" + + # Act + result = _generate_one(raw) + + # Assert + assert result == raw + + def test_wraps_doctest_with_curly_braces(self) -> None: + """Curly braces in doctest are preserved inside the fence, preventing MDX/JSX interpolation.""" + # Arrange + raw = '>>> data = {"key": "value"}\n>>> func(data)' + + # Act + result = _generate_one(raw) + + # Assert + assert result.startswith("```python\n") + assert result.endswith("\n```") + assert '{"key": "value"}' in result + + def test_closes_doctest_fence_on_blank_line(self) -> None: + """A blank line between two doctest blocks produces two separate fenced blocks.""" + # Arrange + raw = ">>> first()\n'a'\n\n>>> second()\n'b'" + + # Act + result = _generate_one(raw) + + # Assert + assert result.count("```python") == 2 + assert result.count("```") == 4 # 2 opening + 2 closing + + def test_content_with_no_doctest_is_unchanged(self) -> None: + """Plain Markdown without any >>> prompt is returned as-is.""" + # Arrange + raw = "# Title\n\nSome text.\n" + + # Act + result = _generate_one(raw) + + # Assert + assert result == raw + + +class TestMdxCodeDocumentation: + def test_generate_default_filter_returns_filtered_files(self) -> None: + """Files matching the default filter (``__init__``) are excluded.""" + # Arrange + mock_context = _make_mock_context( + { + "infrahub_sdk.node": { + "infrahub_sdk-node-node.mdx": "# Node", + "infrahub_sdk-node-__init__.mdx": "# Init (should be filtered)", + }, + "infrahub_sdk.client": { + "infrahub_sdk-client.mdx": "# Client", + }, + } + ) + doc = MdxCodeDocumentation() + + # Act + results = doc.generate( + context=mock_context, + modules_to_document=["infrahub_sdk.node", "infrahub_sdk.client"], + ) + + # Assert + assert "infrahub_sdk-node-node.mdx" in results + assert "infrahub_sdk-client.mdx" in results + assert "infrahub_sdk-node-__init__.mdx" not in results + + def test_generate_runs_mdxify_only_once(self) -> None: + """Second call returns the same result without re-running mdxify.""" + # Arrange + calls: list[str] = [] + mock_context = _make_mock_context( + {"infrahub_sdk.client": {"infrahub_sdk-client.mdx": "# Client"}}, + calls=calls, + ) + doc = MdxCodeDocumentation() + + # Act + result1 = doc.generate( + context=mock_context, + modules_to_document=["infrahub_sdk.client"], + ) + result2 = doc.generate( + context=mock_context, + modules_to_document=["infrahub_sdk.client"], + ) + + # Assert + assert result1 is result2 + assert len(calls) == 1 + + def test_generate_with_custom_filters(self) -> None: + """Custom file_filters exclude files whose names contain the filter substring.""" + # Arrange + mock_context = _make_mock_context( + { + "infrahub_sdk.node": { + "infrahub_sdk-node-_private.mdx": "# Private", + "infrahub_sdk-node-public.mdx": "# Public", + }, + } + ) + doc = MdxCodeDocumentation(file_filters=["_private"]) + + # Act + results = doc.generate( + context=mock_context, + modules_to_document=["infrahub_sdk.node"], + ) + + # Assert + assert "infrahub_sdk-node-public.mdx" in results + assert "infrahub_sdk-node-_private.mdx" not in results + + def test_generate_empty_output(self) -> None: + """When mdxify produces no files for the requested module, an empty dict is returned.""" + # Arrange + mock_context = _make_mock_context({}) + doc = MdxCodeDocumentation() + + # Act + results = doc.generate( + context=mock_context, + modules_to_document=["infrahub_sdk.empty"], + ) + + # Assert + assert results == {} + + def test_generate_only_includes_requested_modules(self) -> None: + """Only files belonging to requested modules are returned.""" + # Arrange + mock_context = _make_mock_context( + { + "infrahub_sdk.node": { + "infrahub_sdk-node-node.mdx": "# Node", + }, + "infrahub_sdk.client": { + "infrahub_sdk-client.mdx": "# Client", + }, + } + ) + doc = MdxCodeDocumentation() + + # Act + results = doc.generate( + context=mock_context, + modules_to_document=["infrahub_sdk.node"], + ) + + # Assert + assert "infrahub_sdk-node-node.mdx" in results + assert "infrahub_sdk-client.mdx" not in results + + def test_generate_reruns_for_different_modules(self) -> None: + """Calling generate with different modules re-runs mdxify.""" + # Arrange + calls: list[str] = [] + mock_context = _make_mock_context( + { + "infrahub_sdk.node": {"infrahub_sdk-node-node.mdx": "# Node"}, + "infrahub_sdk.client": {"infrahub_sdk-client.mdx": "# Client"}, + }, + calls=calls, + ) + doc = MdxCodeDocumentation() + + # Act + result_node = doc.generate(context=mock_context, modules_to_document=["infrahub_sdk.node"]) + result_client = doc.generate(context=mock_context, modules_to_document=["infrahub_sdk.client"]) + + # Assert + assert len(calls) == 2 + assert "infrahub_sdk-node-node.mdx" in result_node + assert "infrahub_sdk-client.mdx" in result_client + + +class TestSourcePathDerivation: + def test_nested_module(self) -> None: + """Deeply nested mdxify filename resolves to the correct Python source path.""" + # Arrange + mock_context = _make_mock_context( + {"infrahub_sdk.node": {"infrahub_sdk-node-node.mdx": "# Node"}}, + ) + doc = MdxCodeDocumentation() + + # Act + results = doc.generate(context=mock_context, modules_to_document=["infrahub_sdk.node"]) + + # Assert + mdx = results["infrahub_sdk-node-node.mdx"] + assert mdx.name == "infrahub_sdk-node-node.mdx" + assert mdx.source_path == Path("infrahub_sdk/node/node.py") + + def test_top_level_module(self) -> None: + """Single-dash mdxify filename resolves to a top-level module source path.""" + # Arrange + mock_context = _make_mock_context( + {"infrahub_sdk.client": {"infrahub_sdk-client.mdx": "# Client"}}, + ) + doc = MdxCodeDocumentation() + + # Act + results = doc.generate(context=mock_context, modules_to_document=["infrahub_sdk.client"]) + + # Assert + assert results["infrahub_sdk-client.mdx"].source_path == Path("infrahub_sdk/client.py") + + def test_single_name(self) -> None: + """Filename without dashes resolves to a single .py file.""" + # Arrange + mock_context = _make_mock_context({"mod": {"mod.mdx": "# Mod"}}) + doc = MdxCodeDocumentation() + + # Act + results = doc.generate(context=mock_context, modules_to_document=["mod"]) + + # Assert + assert results["mod.mdx"].source_path == Path("mod.py") diff --git a/tests/unit/doc_generation/content_gen_methods/test_command_output_method.py b/tests/unit/doc_generation/content_gen_methods/test_command_output_method.py new file mode 100644 index 00000000..9b1befcf --- /dev/null +++ b/tests/unit/doc_generation/content_gen_methods/test_command_output_method.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from pathlib import Path +from unittest.mock import create_autospec + +from invoke import Context, Result + +from docs.docs_generation import ACommand, CommandOutputDocContentGenMethod + + +class StubCommand(ACommand): + def __init__(self, cmd: str) -> None: + self.cmd = cmd + + def build(self) -> str: + return self.cmd + + +class TestCommandOutputDocContentGenMethod: + def test_apply_runs_command_and_reads_output(self, tmp_path: Path) -> None: + """The method executes the command via context.run, then reads + the content from the temp file whose path was appended via --output.""" + output_content = "# Generated docs" + + # Arrange + def fake_run(cmd: str, **kwargs: object) -> Result: + parts = cmd.split("--output ") + output_path = Path(parts[1].strip()) + output_path.write_text(output_content, encoding="utf-8") + return Result() + + mock_context = create_autospec(Context, instance=True) + mock_context.run.side_effect = fake_run + + method = CommandOutputDocContentGenMethod( + context=mock_context, + working_directory=tmp_path, + command=StubCommand("some_command"), + ) + + # Act + result = method.apply() + + # Assert + assert result == output_content + + def test_apply_appends_output_flag(self, tmp_path: Path) -> None: + """Verify that --output is appended to the command.""" + captured_cmd: list[str] = [] + + # Arrange + def fake_run(cmd: str, **kwargs: object) -> Result: + captured_cmd.append(cmd) + parts = cmd.split("--output ") + Path(parts[1].strip()).write_text("", encoding="utf-8") + return Result() + + mock_context = create_autospec(Context, instance=True) + mock_context.run.side_effect = fake_run + + method = CommandOutputDocContentGenMethod( + context=mock_context, + working_directory=tmp_path, + command=StubCommand("base_cmd"), + ) + + # Act + method.apply() + + # Assert + assert captured_cmd[0].startswith("base_cmd --output ") diff --git a/tests/unit/doc_generation/content_gen_methods/test_file_printing_method.py b/tests/unit/doc_generation/content_gen_methods/test_file_printing_method.py new file mode 100644 index 00000000..bda91bd9 --- /dev/null +++ b/tests/unit/doc_generation/content_gen_methods/test_file_printing_method.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from pathlib import Path + +from docs.docs_generation.content_gen_methods.file_printing_method import FilePrintingDocContentGenMethod +from docs.docs_generation.content_gen_methods.mdx import MdxFile + + +class TestFilePrintingDocContentGenMethod: + def test_apply_returns_file_content(self) -> None: + # Arrange + file = MdxFile(name="node.mdx", content="# Node API\n\nSome content", source_path=Path("node.py")) + method = FilePrintingDocContentGenMethod(file=file) + + # Act + result = method.apply() + + # Assert + assert result == "# Node API\n\nSome content" + + def test_apply_returns_empty_string(self) -> None: + # Arrange + file = MdxFile(name="empty.mdx", content="", source_path=Path("empty.py")) + method = FilePrintingDocContentGenMethod(file=file) + + # Act + result = method.apply() + + # Assert + assert not result diff --git a/tests/unit/doc_generation/content_gen_methods/test_jinja2_method.py b/tests/unit/doc_generation/content_gen_methods/test_jinja2_method.py new file mode 100644 index 00000000..db332bac --- /dev/null +++ b/tests/unit/doc_generation/content_gen_methods/test_jinja2_method.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from pathlib import Path + +from docs.docs_generation import Jinja2DocContentGenMethod +from infrahub_sdk.template import Jinja2Template + + +class TestJinja2DocContentGenMethod: + def test_apply_renders_string_template(self) -> None: + """Verify the method renders a string-based Jinja2Template correctly.""" + # Arrange + template = Jinja2Template(template="rendered {{ key }}") + method = Jinja2DocContentGenMethod( + template=template, + template_variables={"key": "content"}, + ) + + # Act + result = method.apply() + + # Assert + assert result == "rendered content" + + def test_apply_renders_template(self, tmp_path: Path) -> None: + # Arrange + template_file = tmp_path / "test.j2" + template_file.write_text("Hello {{ name }}!", encoding="utf-8") + template = Jinja2Template(template=Path("test.j2"), template_directory=tmp_path) + method = Jinja2DocContentGenMethod( + template=template, + template_variables={"name": "World"}, + ) + + # Act + result = method.apply() + + # Assert + assert result == "Hello World!" + + def test_apply_renders_with_multiple_variables(self, tmp_path: Path) -> None: + # Arrange + template_file = tmp_path / "test.j2" + template_file.write_text("{{ greeting }} {{ target }}!", encoding="utf-8") + template = Jinja2Template(template=Path("test.j2"), template_directory=tmp_path) + method = Jinja2DocContentGenMethod( + template=template, + template_variables={"greeting": "Hi", "target": "there"}, + ) + + # Act + result = method.apply() + + # Assert + assert result == "Hi there!" + + def test_auto_escaping_is_disabled(self, tmp_path: Path) -> None: + """HTML content in template variables must not be auto-escaped, + since the SDK Jinja2 environment does not enable autoescape.""" + # Arrange + template_file = tmp_path / "test.j2" + template_file.write_text("{{ html_content }}", encoding="utf-8") + html_input = 'text' + template = Jinja2Template(template=Path("test.j2"), template_directory=tmp_path) + method = Jinja2DocContentGenMethod( + template=template, + template_variables={"html_content": html_input}, + ) + + # Act + result: str = method.apply() + + # Assert + assert result == html_input diff --git a/tests/unit/doc_generation/mdx/__init__.py b/tests/unit/doc_generation/mdx/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/doc_generation/mdx/conftest.py b/tests/unit/doc_generation/mdx/conftest.py new file mode 100644 index 00000000..4e6dbef2 --- /dev/null +++ b/tests/unit/doc_generation/mdx/conftest.py @@ -0,0 +1,215 @@ +"""Shared fixtures and helpers for MDX documentation tests.""" + +from __future__ import annotations + +import re +from pathlib import Path +from typing import TYPE_CHECKING +from unittest.mock import MagicMock + +import pytest + +from docs.docs_generation.content_gen_methods.mdx.mdx_code_doc import ACodeDocumentation, MdxFile +from docs.docs_generation.content_gen_methods.mdx.mdx_ordered_code_doc import OrderedMdxCodeDocumentation +from docs.docs_generation.content_gen_methods.mdx.mdx_section import MdxSection + +if TYPE_CHECKING: + from invoke import Context + + from docs.docs_generation.content_gen_methods.mdx.mdx_priority import PagePriority + +FILE_KEY = "test.mdx" +MOCK_CONTEXT = MagicMock(spec="Context") +MODULES: list[str] = [] + + +# --- Helpers --- + + +class StubDocumentation(ACodeDocumentation): + """Minimal stub returning pre-built MdxFile dicts.""" + + def __init__(self, files: dict[str, MdxFile]) -> None: + self._files = files + + def generate(self, context: Context, modules_to_document: list[str]) -> dict[str, MdxFile]: + return self._files + + +def build_ordered_doc(content: str, priority: PagePriority) -> OrderedMdxCodeDocumentation: + """Build an ``OrderedMdxCodeDocumentation`` with a stub inner documentation.""" + inner = StubDocumentation({FILE_KEY: MdxFile(name=FILE_KEY, content=content, source_path=Path("test.py"))}) + return OrderedMdxCodeDocumentation(documentation=inner, page_priorities={FILE_KEY: priority}) + + +def section_order(content: str) -> list[str]: + """Extract the order of H2 section names.""" + return re.findall(r"^## (\w+)", content, re.MULTILINE) + + +def class_order(content: str) -> list[str]: + """Extract the order of H3 class names under ``## Classes``.""" + match = re.search(r"^## Classes\n(.*?)(?=^## |\Z)", content, re.MULTILINE | re.DOTALL) + if not match: + return [] + return re.findall(r"^### `([^`]+)`", match.group(1), re.MULTILINE) + + +def method_order(content: str, class_name: str) -> list[str]: + """Extract the order of H4 method names under a given H3 class section.""" + pattern = rf"^### `{re.escape(class_name)}`\n(.*?)(?=^### |\Z)" + match = re.search(pattern, content, re.MULTILINE | re.DOTALL) + if not match: + return [] + return re.findall(r"^#### `([^`]+)`", match.group(1), re.MULTILINE) + + +def make_method_section(name: str, signature: str, docstring: str = "") -> MdxSection: + """Create an MdxSection mimicking a method entry in MDX output.""" + lines = [ + f"#### `{name}`", + "", + "```python", + signature, + "```", + ] + if docstring: + lines.extend(("", docstring)) + return MdxSection(name=name, heading_level=4, _lines=lines) + + +# --- Fixtures --- + + +@pytest.fixture +def sample_mdx() -> str: + return """\ +--- +title: client +sidebarTitle: client +--- + +# `infrahub_sdk.client` + +## Functions + +### `handle_relogin` + +```python +handle_relogin(func: Callable) -> Callable +``` + +### `handle_relogin_sync` + +```python +handle_relogin_sync(func: Callable) -> Callable +``` + +## Classes + +### `ProcessRelationsNode` + +Process relations for a node. + +### `BaseClient` + +Base class for InfrahubClient and InfrahubClientSync + +**Methods:** + +#### `start_tracking` + +```python +start_tracking(self) -> Self +``` + +#### `set_context_properties` + +```python +set_context_properties(self, identifier: str) -> None +``` + +### `InfrahubClient` + +GraphQL Client to interact with Infrahub. + +**Methods:** + +#### `get_version` + +```python +get_version(self) -> str +``` + +Return the Infrahub version. + +#### `create` + +```python +create(self, kind: str) -> InfrahubNode +``` + +#### `get` + +```python +get(self, kind: str) -> InfrahubNode +``` + +#### `get` + +```python +get(self, kind: type[SchemaType]) -> SchemaType +``` + +#### `save` + +```python +save(self, node: InfrahubNode) -> None +``` + +#### `delete` + +```python +delete(self, kind: str, id: str) -> None +``` + +### `InfrahubClientSync` + +Synchronous GraphQL Client to interact with Infrahub. + +**Methods:** + +#### `get_version` + +```python +get_version(self) -> str +``` + +#### `create` + +```python +create(self, kind: str) -> InfrahubNodeSync +``` +""" + + +@pytest.fixture +def sample_mdx_no_methods() -> str: + return """\ +--- +title: constants +sidebarTitle: constants +--- + +# `infrahub_sdk.node.constants` + +## Classes + +### `RelatedNodeState` + +State of a related node. + +### `InfrahubNodeMode` + +Mode of an Infrahub node. +""" diff --git a/tests/unit/doc_generation/mdx/test_mdx_collapsed_overload_code_doc.py b/tests/unit/doc_generation/mdx/test_mdx_collapsed_overload_code_doc.py new file mode 100644 index 00000000..a7ae1c4b --- /dev/null +++ b/tests/unit/doc_generation/mdx/test_mdx_collapsed_overload_code_doc.py @@ -0,0 +1,341 @@ +"""Tests for CollapsedOverloadCodeDocumentation pipeline decorator.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from docs.docs_generation.content_gen_methods.mdx.mdx_code_doc import MdxFile +from docs.docs_generation.content_gen_methods.mdx.mdx_collapsed_overload_code_doc import ( + CollapsedOverloadCodeDocumentation, +) + +from .conftest import FILE_KEY, MOCK_CONTEXT, MODULES, StubDocumentation + + +class TestCollapseOverloads: + def test_overloaded_methods_collapsed_to_primary_plus_details(self, sample_mdx_with_overloads: str) -> None: + # Arrange + doc = _build_collapsed_doc(sample_mdx_with_overloads) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + visible_part = result.split("
")[0] + assert visible_part.count("#### `get`") == 1 + assert "
" in result + + def test_non_overloaded_methods_unchanged(self, sample_mdx_no_overloads: str) -> None: + # Arrange + doc = _build_collapsed_doc(sample_mdx_no_overloads) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert result == sample_mdx_no_overloads + + def test_mixed_overloaded_and_non_overloaded(self, sample_mdx_with_overloads: str) -> None: + # Arrange + doc = _build_collapsed_doc(sample_mdx_with_overloads) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert "#### `get`" in result + assert "#### `create`" in result + assert "#### `delete`" in result + assert result.count("
") == 2 + + def test_primary_is_overload_with_most_params(self, sample_mdx_with_overloads: str) -> None: + # Arrange + doc = _build_collapsed_doc(sample_mdx_with_overloads) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert "timeout" in result + + def test_details_label_shows_correct_count(self, sample_mdx_with_overloads: str) -> None: + # Arrange + doc = _build_collapsed_doc(sample_mdx_with_overloads) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert "Show 2 other overloads" in result + + def test_singular_label_for_two_overloads(self, sample_mdx_with_overloads: str) -> None: + # Arrange + doc = _build_collapsed_doc(sample_mdx_with_overloads) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert "Show 1 other overload" in result + + +class TestPropertyPairNotCollapsed: + def test_property_getter_setter_kept_separate(self, sample_mdx_with_property_pair: str) -> None: + # Arrange + doc = _build_collapsed_doc(sample_mdx_with_property_pair) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert "
" not in result + assert result.count("#### `value`") == 2 + + def test_property_getter_setter_deleter_kept_separate(self, sample_mdx_with_property_triplet: str) -> None: + # Arrange + doc = _build_collapsed_doc(sample_mdx_with_property_triplet) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert "
" not in result + assert result.count("#### `value`") == 3 + + def test_property_pair_alongside_real_overloads(self, sample_mdx_property_and_overloads: str) -> None: + # Arrange + doc = _build_collapsed_doc(sample_mdx_property_and_overloads) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert result.count("#### `value`") == 2 + assert "
" in result + assert "#### `get`" in result + + +class TestNoOverloads: + def test_empty_content_passes_through(self) -> None: + # Arrange + content = "# minimal" + doc = _build_collapsed_doc(content) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert result == content + + +def _build_collapsed_doc(content: str) -> CollapsedOverloadCodeDocumentation: + """Build a ``CollapsedOverloadCodeDocumentation`` with a stub inner documentation.""" + inner = StubDocumentation({FILE_KEY: MdxFile(name=FILE_KEY, content=content, source_path=Path("test.py"))}) + return CollapsedOverloadCodeDocumentation(documentation=inner) + + +# --- Fixtures --- + + +@pytest.fixture +def sample_mdx_no_overloads() -> str: + return """\ +--- +title: test +--- + +# `test_module` + +## Classes + +### `MyClass` + +A simple class. + +**Methods:** + +#### `save` + +```python +save(self, data: str) -> None +``` + +#### `delete` + +```python +delete(self, id: str) -> None +```""" + + +@pytest.fixture +def sample_mdx_with_overloads() -> str: + return """\ +--- +title: client +sidebarTitle: client +--- + +# `infrahub_sdk.client` + +## Classes + +### `InfrahubClient` + +GraphQL Client to interact with Infrahub. + +**Methods:** + +#### `get` + +```python +get(self, kind: str) -> InfrahubNode +``` + +#### `get` + +```python +get(self, kind: type[SchemaType]) -> SchemaType +``` + +#### `get` + +```python +get(self, kind: str | type[SchemaType], raise_when_missing: bool = True) -> InfrahubNode | SchemaType | None +``` + +#### `create` + +```python +create(self, kind: str) -> InfrahubNode +``` + +#### `create` + +```python +create(self, kind: str, data: dict | None = None, branch: str | None = None, timeout: int | None = None) -> InfrahubNode +``` + +#### `delete` + +```python +delete(self, kind: str, id: str) -> None +```""" + + +@pytest.fixture +def sample_mdx_with_property_pair() -> str: + return """\ +--- +title: attribute +sidebarTitle: attribute +--- + +# `infrahub_sdk.node.attribute` + +## Classes + +### `Attribute` + +Represents an attribute of a Node. + +**Methods:** + +#### `value` + +```python +value(self) -> Any +``` + +#### `value` + +```python +value(self, value: Any) -> None +```""" + + +@pytest.fixture +def sample_mdx_property_and_overloads() -> str: + return """\ +--- +title: attribute +sidebarTitle: attribute +--- + +# `infrahub_sdk.node.attribute` + +## Classes + +### `Attribute` + +Represents an attribute of a Node. + +**Methods:** + +#### `value` + +```python +value(self) -> Any +``` + +#### `value` + +```python +value(self, value: Any) -> None +``` + +#### `get` + +```python +get(self, kind: str) -> InfrahubNode +``` + +#### `get` + +```python +get(self, kind: str, id: int) -> InfrahubNode +``` + +#### `get` + +```python +get(self, kind: str, id: int, branch: str) -> InfrahubNode +```""" + + +@pytest.fixture +def sample_mdx_with_property_triplet() -> str: + return """\ +--- +title: attribute +sidebarTitle: attribute +--- + +# `infrahub_sdk.node.attribute` + +## Classes + +### `Attribute` + +Represents an attribute of a Node. + +**Methods:** + +#### `value` + +```python +value(self) -> Any +``` + +#### `value` + +```python +value(self, value: Any) -> None +``` + +#### `value` + +```python +value(self) -> None +```""" diff --git a/tests/unit/doc_generation/mdx/test_mdx_collapsed_overload_section.py b/tests/unit/doc_generation/mdx/test_mdx_collapsed_overload_section.py new file mode 100644 index 00000000..8c25f3ec --- /dev/null +++ b/tests/unit/doc_generation/mdx/test_mdx_collapsed_overload_section.py @@ -0,0 +1,148 @@ +"""Tests for CollapsedOverloadSection.""" + +from __future__ import annotations + +import pytest + +from docs.docs_generation.content_gen_methods.mdx.mdx_collapsed_overload_section import ( + CollapsedOverloadSection, +) +from docs.docs_generation.content_gen_methods.mdx.mdx_section import ASection + +from .conftest import make_method_section + + +class TestCollapsedOverloadSection: + def test_heading_delegates_to_primary(self) -> None: + # Arrange + primary = make_method_section("get", "get(self, kind: str)") + section = CollapsedOverloadSection(primary=primary, others=[]) + + # Act + result = section.heading + + # Assert + assert result == primary.heading + + def test_no_others_returns_primary_content_only(self) -> None: + # Arrange + primary = make_method_section("get", "get(self, kind: str)") + section = CollapsedOverloadSection(primary=primary, others=[]) + + # Act + result = section.content + + # Assert + assert result == primary.content + assert "
" not in "\n".join(result) + + def test_others_rendered_in_details_block(self) -> None: + # Arrange + primary = make_method_section("get", "get(self, kind: str, id: int)") + other1 = make_method_section("get", "get(self, kind: str)") + other2 = make_method_section("get", "get(self)") + section = CollapsedOverloadSection(primary=primary, others=[other1, other2]) + + # Act + result = "\n".join(section.content) + + # Assert + assert "
" in result + assert "Show 2 other overloads" in result + assert "
" in result + + def test_singular_label_for_one_other(self) -> None: + # Arrange + primary = make_method_section("get", "get(self, kind: str)") + other = make_method_section("get", "get(self)") + section = CollapsedOverloadSection(primary=primary, others=[other]) + + # Act + result = "\n".join(section.content) + + # Assert + assert "Show 1 other overload" in result + + def test_plural_label_for_multiple_others(self) -> None: + # Arrange + primary = make_method_section("get", "get(self, a: int, b: int, c: int)") + others = [make_method_section("get", f"get(self, x{i}: int)") for i in range(3)] + section = CollapsedOverloadSection(primary=primary, others=others) + + # Act + result = "\n".join(section.content) + + # Assert + assert "Show 3 other overloads" in result + + def test_lines_includes_heading_plus_content(self) -> None: + # Arrange + primary = make_method_section("get", "get(self, kind: str)") + section = CollapsedOverloadSection(primary=primary, others=[]) + + # Act + result = section.lines + + # Assert + assert result[0] == section.heading + assert result[1:] == section.content + + def test_is_asection_subclass(self) -> None: + # Assert + assert issubclass(CollapsedOverloadSection, ASection) + + def test_details_block_contains_other_section_lines(self) -> None: + # Arrange + primary = make_method_section("get", "get(self, kind: str, id: int)") + other = make_method_section("get", "get(self, kind: str)", docstring="Get by kind.") + section = CollapsedOverloadSection(primary=primary, others=[other]) + + # Act + result = "\n".join(section.content) + + # Assert + assert "Get by kind." in result + assert "get(self, kind: str)" in result + + +class TestCollapsedOverloadSectionFromOverloads: + def test_primary_is_overload_with_most_params(self) -> None: + # Arrange + s1 = make_method_section("get", "get(self, a: int, b: int)") + s2 = make_method_section("get", "get(self, a: int, b: int, c: int, d: int, e: int)") + s3 = make_method_section("get", "get(self, a: int, b: int, c: int)") + + # Act + section = CollapsedOverloadSection.from_overloads([s1, s2, s3]) + + # Assert + assert "e: int" in "\n".join(section.primary.lines) + assert len(section.others) == 2 + + def test_tie_breaking_selects_first_in_source_order(self) -> None: + # Arrange + s1 = make_method_section("get", "get(self, a: int, b: str)") + s2 = make_method_section("get", "get(self, x: int, y: str)") + + # Act + section = CollapsedOverloadSection.from_overloads([s1, s2]) + + # Assert + assert "a: int" in "\n".join(section.primary.lines) + assert len(section.others) == 1 + + def test_single_section_returns_no_others(self) -> None: + # Arrange + s1 = make_method_section("get", "get(self, kind: str)") + + # Act + section = CollapsedOverloadSection.from_overloads([s1]) + + # Assert + assert section.primary is s1 + assert section.others == [] + + def test_empty_list_raises(self) -> None: + # Act / Assert + with pytest.raises(ValueError, match="empty"): + CollapsedOverloadSection.from_overloads([]) diff --git a/tests/unit/doc_generation/mdx/test_mdx_method_signature.py b/tests/unit/doc_generation/mdx/test_mdx_method_signature.py new file mode 100644 index 00000000..f9a18606 --- /dev/null +++ b/tests/unit/doc_generation/mdx/test_mdx_method_signature.py @@ -0,0 +1,196 @@ +"""Tests for MethodSignature.""" + +from __future__ import annotations + +from docs.docs_generation.content_gen_methods.mdx.mdx_collapsed_overload_section import ( + MethodSignature, +) +from docs.docs_generation.content_gen_methods.mdx.mdx_section import MdxSection + +from .conftest import make_method_section + + +class TestMethodSignatureParamCount: + def test_simple_signature_returns_correct_count(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self, kind: str, id: int)")) + + # Act + result = sig.param_count() + + # Assert + assert result == 2 + + def test_self_only_returns_zero(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self)")) + + # Act + result = sig.param_count() + + # Assert + assert result == 0 + + def test_kwargs_counts_as_one(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self, **kwargs: Any)")) + + # Act + result = sig.param_count() + + # Assert + assert result == 1 + + def test_args_and_kwargs_count_separately(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self, *args: str, **kwargs: Any)")) + + # Act + result = sig.param_count() + + # Assert + assert result == 2 + + def test_nested_brackets_not_split(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self, kind: dict[str, int], other: list[str])")) + + # Act + result = sig.param_count() + + # Assert + assert result == 2 + + def test_deeply_nested_generics(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self, x: dict[str, list[tuple[int, ...]]])")) + + # Act + result = sig.param_count() + + # Assert + assert result == 1 + + def test_signature_with_return_type(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self, kind: str) -> InfrahubNode")) + + # Act + result = sig.param_count() + + # Assert + assert result == 1 + + def test_default_values_dont_affect_count(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self, kind: str = ..., id: int = None)")) + + # Act + result = sig.param_count() + + # Assert + assert result == 2 + + def test_real_world_get_signature(self) -> None: + # Arrange + signature = ( + "get(self, kind: str | type[SchemaType], raise_when_missing: bool = True, " + "at: Timestamp | None = None, branch: str | None = None, " + "timeout: int | None = None, id: str | None = None, " + "hfid: list[str] | None = None, include: list[str] | None = None, " + "exclude: list[str] | None = None, populate_store: bool = True, " + "fragment: bool = False, prefetch_relationships: bool = False, " + "property: bool = False, include_metadata: bool = False, " + "**kwargs: Any) -> InfrahubNode | SchemaType | None" + ) + sig = MethodSignature(make_method_section("get", signature)) + + # Act + result = sig.param_count() + + # Assert + assert result == 15 + + def test_empty_signature(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get()")) + + # Act + result = sig.param_count() + + # Assert + assert result == 0 + + def test_no_code_fence_returns_zero(self) -> None: + # Arrange + section = MdxSection(name="get", heading_level=4, _lines=["#### `get`", "", "Some description."]) + sig = MethodSignature(section) + + # Act + result = sig.param_count() + + # Assert + assert result == 0 + + +class TestMethodSignatureReturnType: + def test_returns_none_type(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("value", "value(self, value: Any) -> None")) + + # Act + result = sig.return_type() + + # Assert + assert result == "None" + + def test_returns_concrete_type(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("value", "value(self) -> Any")) + + # Act + result = sig.return_type() + + # Assert + assert result == "Any" + + def test_no_return_annotation_returns_empty(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self, kind: str)")) + + # Act + result = sig.return_type() + + # Assert + assert not result + + def test_generic_return_type(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self) -> dict[str, list[int]]")) + + # Act + result = sig.return_type() + + # Assert + assert result == "dict[str, list[int]]" + + def test_union_return_type(self) -> None: + # Arrange + sig = MethodSignature(make_method_section("get", "get(self, kind: str) -> InfrahubNode | None")) + + # Act + result = sig.return_type() + + # Assert + assert result == "InfrahubNode | None" + + def test_no_code_fence_returns_empty(self) -> None: + # Arrange + section = MdxSection(name="get", heading_level=4, _lines=["#### `get`", "", "Some description."]) + sig = MethodSignature(section) + + # Act + result = sig.return_type() + + # Assert + assert not result diff --git a/tests/unit/doc_generation/mdx/test_mdx_ordered_section.py b/tests/unit/doc_generation/mdx/test_mdx_ordered_section.py new file mode 100644 index 00000000..f279ca8e --- /dev/null +++ b/tests/unit/doc_generation/mdx/test_mdx_ordered_section.py @@ -0,0 +1,95 @@ +"""Tests for OrderedMdxSection.""" + +from __future__ import annotations + +import re + +from docs.docs_generation.content_gen_methods.mdx.mdx_ordered_section import OrderedMdxSection +from docs.docs_generation.content_gen_methods.mdx.mdx_priority import SectionPriority +from docs.docs_generation.content_gen_methods.mdx.mdx_section import ASection, MdxSection + + +class TestOrderedMdxSection: + def test_content_returns_reordered_children(self) -> None: + # Arrange + children = [ + "### `Alpha`\n", + "Alpha body\n", + "### `Bravo`\n", + "Bravo body\n", + "### `Charlie`\n", + "Charlie body", + ] + ordered = _make_ordered("Classes", 2, children, priority=SectionPriority(names=["Charlie", "Alpha"])) + + # Act + content = ordered.content + + # Assert + content_str = "\n".join(content) + names = re.findall(r"^### `([^`]+)`", content_str, re.MULTILINE) + assert names == ["Charlie", "Alpha", "Bravo"] + + def test_lines_includes_heading_plus_ordered_content(self) -> None: + # Arrange + children = [ + "### `A`\n", + "### `B`\n", + ] + ordered = _make_ordered("Classes", 2, children, priority=SectionPriority(names=["B"])) + + # Act + lines = ordered.lines + + # Assert + assert lines[0] == "## `Classes`" + names = re.findall(r"^### `([^`]+)`", "\n".join(lines), re.MULTILINE) + assert names == ["B", "A"] + + def test_empty_priority_returns_original_content(self) -> None: + # Arrange + ordered = _make_ordered("Sec", 2, ["### `X`\n", "body"], priority=SectionPriority()) + base = MdxSection(name="Sec", heading_level=2, _lines=["## `Sec`", "### `X`\n", "body"]) + + # Act + content = ordered.content + + # Assert + assert content == base.content + + def test_no_children_returns_original_content(self) -> None: + # Arrange + ordered = _make_ordered("Sec", 2, ["Just some text"], priority=SectionPriority(names=["Anything"])) + base = MdxSection(name="Sec", heading_level=2, _lines=["## `Sec`", "Just some text"]) + + # Act + content = ordered.content + + # Assert + assert content == base.content + + def test_is_asection_subclass(self) -> None: + # Arrange + section = MdxSection(name="MySection", heading_level=2, _lines=["## `MySection`"]) + ordered = OrderedMdxSection(section=section, priority=SectionPriority(), child_heading_level=3) + + # Assert + assert isinstance(ordered, ASection) + assert isinstance(section, ASection) + assert ordered.heading == "## `MySection`" + + +def _make_ordered( + name: str, + heading_level: int, + children_lines: list[str], + priority: SectionPriority, + child_heading_level: int = 3, +) -> OrderedMdxSection: + heading = "#" * heading_level + f" `{name}`" + section = MdxSection(name=name, heading_level=heading_level, _lines=[heading, *children_lines]) + return OrderedMdxSection( + section=section, + priority=priority, + child_heading_level=child_heading_level, + ) diff --git a/tests/unit/doc_generation/mdx/test_reorder_classes.py b/tests/unit/doc_generation/mdx/test_reorder_classes.py new file mode 100644 index 00000000..0c8ebf8f --- /dev/null +++ b/tests/unit/doc_generation/mdx/test_reorder_classes.py @@ -0,0 +1,93 @@ +"""Tests for class reordering in OrderedMdxCodeDocumentation.""" + +from __future__ import annotations + +import pytest + +from docs.docs_generation.content_gen_methods.mdx.mdx_priority import PagePriority + +from .conftest import FILE_KEY, MOCK_CONTEXT, MODULES, build_ordered_doc, class_order + + +class TestReorderClasses: + def test_single_priority_class_moves_to_top(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(classes=["InfrahubClient"]) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + order = class_order(result) + assert order[0] == "InfrahubClient" + + def test_multiple_priority_classes_in_specified_order(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(classes=["InfrahubClientSync", "InfrahubClient"]) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + order = class_order(result) + assert order[0] == "InfrahubClientSync" + assert order[1] == "InfrahubClient" + + def test_non_priority_classes_retain_original_order(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(classes=["InfrahubClient"]) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + order = class_order(result) + remaining = order[1:] + assert remaining == ["ProcessRelationsNode", "BaseClient", "InfrahubClientSync"] + + def test_no_priority_config_returns_unchanged(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority() + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert result == sample_mdx + + def test_empty_classes_list_returns_unchanged(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(classes=[]) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert result == sample_mdx + + def test_nonexistent_class_name_raises(self, sample_mdx: str) -> None: + # Arrange + fake_class_name = "DoesNotExist" + priority = PagePriority(classes=[fake_class_name]) + doc = build_ordered_doc(sample_mdx, priority) + + # Act / Assert + with pytest.raises(ValueError, match=fake_class_name): + doc.generate(MOCK_CONTEXT, MODULES) + + def test_reorder_page_without_methods(self, sample_mdx_no_methods: str) -> None: + # Arrange + priority = PagePriority(classes=["InfrahubNodeMode"]) + doc = build_ordered_doc(sample_mdx_no_methods, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + order = class_order(result) + assert order == ["InfrahubNodeMode", "RelatedNodeState"] diff --git a/tests/unit/doc_generation/mdx/test_reorder_methods.py b/tests/unit/doc_generation/mdx/test_reorder_methods.py new file mode 100644 index 00000000..3e588a54 --- /dev/null +++ b/tests/unit/doc_generation/mdx/test_reorder_methods.py @@ -0,0 +1,108 @@ +"""Tests for method reordering in OrderedMdxCodeDocumentation.""" + +from __future__ import annotations + +import pytest + +from docs.docs_generation.content_gen_methods.mdx.mdx_priority import PagePriority + +from .conftest import FILE_KEY, MOCK_CONTEXT, MODULES, build_ordered_doc, class_order, method_order + + +class TestReorderMethods: + def test_single_priority_method_moves_to_top(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(methods={"InfrahubClient": ["save"]}) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert method_order(result, "InfrahubClient")[0] == "save" + + def test_multiple_priority_methods_in_specified_order(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(methods={"InfrahubClient": ["delete", "save"]}) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + order = method_order(result, "InfrahubClient") + assert order[0] == "delete" + assert order[1] == "save" + + def test_non_priority_methods_retain_original_order(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(methods={"InfrahubClient": ["save"]}) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + order = method_order(result, "InfrahubClient") + assert order[0] == "save" + assert order[1:] == ["get_version", "create", "get", "get", "delete"] + + def test_method_only_priority_no_class_reordering(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(methods={"InfrahubClient": ["save"]}) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert class_order(result) == ["ProcessRelationsNode", "BaseClient", "InfrahubClient", "InfrahubClientSync"] + + def test_combined_class_and_method_reordering(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority( + classes=["InfrahubClient"], + methods={"InfrahubClient": ["save"]}, + ) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert class_order(result)[0] == "InfrahubClient" + assert method_order(result, "InfrahubClient")[0] == "save" + + def test_overloaded_methods_move_together(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(methods={"InfrahubClient": ["get"]}) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + order = method_order(result, "InfrahubClient") + assert order[0] == "get" + assert order[1] == "get" + assert order[2:] == ["get_version", "create", "save", "delete"] + + def test_nonexistent_method_name_raises(self, sample_mdx: str) -> None: + # Arrange + fake_method_name = "nonexistent" + priority = PagePriority(methods={"InfrahubClient": [fake_method_name, "save"]}) + doc = build_ordered_doc(sample_mdx, priority) + + # Act / Assert + with pytest.raises(ValueError, match=("%s" % fake_method_name)): + doc.generate(MOCK_CONTEXT, MODULES) + + def test_method_priority_for_nonexistent_class_raises(self, sample_mdx: str) -> None: + # Arrange + fake_class_name = "DoesNotExist" + priority = PagePriority(methods={("%s" % fake_class_name): ["get"]}) + doc = build_ordered_doc(sample_mdx, priority) + + # Act / Assert + with pytest.raises(ValueError, match=fake_class_name): + doc.generate(MOCK_CONTEXT, MODULES) diff --git a/tests/unit/doc_generation/mdx/test_reorder_sections.py b/tests/unit/doc_generation/mdx/test_reorder_sections.py new file mode 100644 index 00000000..12d1b087 --- /dev/null +++ b/tests/unit/doc_generation/mdx/test_reorder_sections.py @@ -0,0 +1,48 @@ +"""Tests for section reordering in OrderedMdxCodeDocumentation.""" + +from __future__ import annotations + +from docs.docs_generation.content_gen_methods.mdx.mdx_priority import PagePriority + +from .conftest import FILE_KEY, MOCK_CONTEXT, MODULES, build_ordered_doc, class_order, method_order, section_order + + +class TestReorderSections: + def test_section_priority_moves_classes_before_functions(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(sections=["Classes"]) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert section_order(result) == ["Classes", "Functions"] + + def test_combined_section_class_and_method_reordering(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority( + sections=["Classes"], + classes=["InfrahubClient"], + methods={"InfrahubClient": ["save"]}, + ) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert section_order(result) == ["Classes", "Functions"] + assert class_order(result)[0] == "InfrahubClient" + assert method_order(result, "InfrahubClient")[0] == "save" + + def test_no_section_priority_retains_original_order(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(sections=[]) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES)[FILE_KEY].content + + # Assert + assert result == sample_mdx diff --git a/tests/unit/doc_generation/mdx/test_validate_priorities.py b/tests/unit/doc_generation/mdx/test_validate_priorities.py new file mode 100644 index 00000000..be73b765 --- /dev/null +++ b/tests/unit/doc_generation/mdx/test_validate_priorities.py @@ -0,0 +1,91 @@ +"""Tests for priority validation in OrderedMdxCodeDocumentation.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from docs.docs_generation.content_gen_methods.mdx.mdx_code_doc import MdxFile +from docs.docs_generation.content_gen_methods.mdx.mdx_ordered_code_doc import OrderedMdxCodeDocumentation +from docs.docs_generation.content_gen_methods.mdx.mdx_priority import PagePriority + +from .conftest import FILE_KEY, MOCK_CONTEXT, MODULES, StubDocumentation, build_ordered_doc + + +class TestNonexistentPriorities: + def test_nonexistent_file_key_raises(self) -> None: + # Arrange + content = "# some content" + inner = StubDocumentation({"actual.mdx": MdxFile(name="actual.mdx", content=content, source_path=Path("a.py"))}) + fake_file_name = "missing.mdx" + doc = OrderedMdxCodeDocumentation( + documentation=inner, + page_priorities={fake_file_name: PagePriority(classes=["Foo"])}, + ) + + # Act / Assert + with pytest.raises(ValueError, match=fake_file_name): + doc.generate(MOCK_CONTEXT, MODULES) + + def test_nonexistent_class_raises(self, sample_mdx: str) -> None: + # Arrange + fake_class_name = "NoSuchClass" + priority = PagePriority(classes=[fake_class_name]) + doc = build_ordered_doc(sample_mdx, priority) + + # Act / Assert + with pytest.raises(ValueError, match=fake_class_name): + doc.generate(MOCK_CONTEXT, MODULES) + + def test_nonexistent_method_raises(self, sample_mdx: str) -> None: + # Arrange + fake_method_name = "no_such_method" + priority = PagePriority(methods={"InfrahubClient": [fake_method_name]}) + doc = build_ordered_doc(sample_mdx, priority) + + # Act / Assert + with pytest.raises(ValueError, match=fake_method_name): + doc.generate(MOCK_CONTEXT, MODULES) + + def test_nonexistent_section_raises(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority(sections=["NoSuchSection"]) + doc = build_ordered_doc(sample_mdx, priority) + + # Act / Assert + with pytest.raises(ValueError, match="NoSuchSection"): + doc.generate(MOCK_CONTEXT, MODULES) + + +class TestValidPriorities: + def test_valid_config_no_error(self, sample_mdx: str) -> None: + # Arrange + priority = PagePriority( + classes=["InfrahubClient"], + methods={"InfrahubClient": ["save"]}, + ) + doc = build_ordered_doc(sample_mdx, priority) + + # Act + result = doc.generate(MOCK_CONTEXT, MODULES) + + # Assert + assert FILE_KEY in result + + +class TestDuplicatePriorities: + def test_duplicate_class_names_raises(self) -> None: + # Act / Assert + with pytest.raises(ValueError, match="Duplicate class 'InfrahubClient'"): + PagePriority(classes=["InfrahubClient", "InfrahubClient"]) + + def test_duplicate_method_names_raises(self) -> None: + # Act / Assert + with pytest.raises(ValueError, match="Duplicate method 'save'"): + PagePriority(methods={"InfrahubClient": ["save", "save"]}) + + def test_duplicate_section_names_raises(self) -> None: + # Act / Assert + with pytest.raises(ValueError, match="Duplicate section 'Classes'"): + PagePriority(sections=["Classes", "Classes"]) diff --git a/tests/unit/doc_generation/pages/__init__.py b/tests/unit/doc_generation/pages/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/doc_generation/pages/test_doc_page.py b/tests/unit/doc_generation/pages/test_doc_page.py new file mode 100644 index 00000000..51dcf190 --- /dev/null +++ b/tests/unit/doc_generation/pages/test_doc_page.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from pathlib import Path + +from docs.docs_generation.content_gen_methods import ADocContentGenMethod +from docs.docs_generation.pages import DocPage, MDXDocPage + + +class StubContentGenMethod(ADocContentGenMethod): + def __init__(self, content: str) -> None: + self._content = content + + def apply(self) -> str: + return self._content + + +class TestDocPage: + def test_content_delegates_to_method(self) -> None: + # Arrange + page = DocPage(content_gen_method=StubContentGenMethod("test content")) + + # Act + result = page.content() + + # Assert + assert result == "test content" + + +class TestMDXDocPage: + def test_to_mdx_writes_file(self, tmp_path: Path) -> None: + # Arrange + page = DocPage(content_gen_method=StubContentGenMethod("# Hello MDX")) + output_path = tmp_path / "output.mdx" + + # Act + MDXDocPage(page=page, output_path=output_path).to_mdx() + + # Assert + assert output_path.exists() + assert output_path.read_text(encoding="utf-8") == "# Hello MDX" + + def test_to_mdx_creates_parent_directories(self, tmp_path: Path) -> None: + # Arrange + page = DocPage(content_gen_method=StubContentGenMethod("content")) + output_path = tmp_path / "nested" / "dir" / "output.mdx" + + # Act + MDXDocPage(page=page, output_path=output_path).to_mdx() + + # Assert + assert output_path.exists() + assert output_path.read_text(encoding="utf-8") == "content" diff --git a/tests/unit/doc_generation/test_docs_validate.py b/tests/unit/doc_generation/test_docs_validate.py new file mode 100644 index 00000000..b65f5bbf --- /dev/null +++ b/tests/unit/doc_generation/test_docs_validate.py @@ -0,0 +1,93 @@ +from __future__ import annotations + +import os +import subprocess # noqa: S404 +from pathlib import Path + +import pytest +from invoke import Context, Exit + +import tasks + +_GIT_ENV = { + "GIT_AUTHOR_NAME": "test", + "GIT_AUTHOR_EMAIL": "test@test.com", + "GIT_COMMITTER_NAME": "test", + "GIT_COMMITTER_EMAIL": "test@test.com", + "PATH": os.environ.get("PATH", ""), + "HOME": os.environ.get("HOME", ""), +} + + +def _git(repo: Path, *args: str) -> None: + """Run a git command inside *repo* with deterministic author info.""" + subprocess.check_call(["git", *args], cwd=repo, env={**_GIT_ENV, "HOME": str(repo)}) # noqa: S603, S607 + + +@pytest.fixture +def git_repo_with_docs(tmp_path: Path) -> Path: + """Create a temporary git repo with a committed docs/ directory.""" + docs_dir = tmp_path / "docs" + docs_dir.mkdir() + (docs_dir / "generated.mdx").write_text("# Original content\n") + + _git(tmp_path, "init") + _git(tmp_path, "add", ".") + _git(tmp_path, "commit", "-m", "initial") + return tmp_path + + +class TestDocsValidate: + """Ensure docs_validate() detects drift between committed and regenerated documentation.""" + + def test_passes_when_generation_produces_no_changes( + self, git_repo_with_docs: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + # Arrange + monkeypatch.setattr(tasks, "docs_generate", lambda context: None) # noqa: ARG005 + monkeypatch.setattr(tasks, "DOCUMENTATION_DIRECTORY", git_repo_with_docs) + + # Act / Assert — no exception means docs are in sync + tasks.docs_validate(Context()) + + def test_fails_when_generation_modifies_existing_file( + self, git_repo_with_docs: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + # Arrange + def fake_generate(context: Context) -> None: + (git_repo_with_docs / "docs" / "generated.mdx").write_text("# Modified content\n") + + monkeypatch.setattr(tasks, "docs_generate", fake_generate) + monkeypatch.setattr(tasks, "DOCUMENTATION_DIRECTORY", git_repo_with_docs) + + # Act / Assert + with pytest.raises(Exit, match="out of sync"): + tasks.docs_validate(Context()) + + def test_fails_when_generation_deletes_tracked_file( + self, git_repo_with_docs: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + # Arrange + def fake_generate(context: Context) -> None: + (git_repo_with_docs / "docs" / "generated.mdx").unlink() + + monkeypatch.setattr(tasks, "docs_generate", fake_generate) + monkeypatch.setattr(tasks, "DOCUMENTATION_DIRECTORY", git_repo_with_docs) + + # Act / Assert + with pytest.raises(Exit, match="Modified or deleted files"): + tasks.docs_validate(Context()) + + def test_fails_when_generation_creates_new_file( + self, git_repo_with_docs: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + # Arrange + def fake_generate(context: Context) -> None: + (git_repo_with_docs / "docs" / "new_file.mdx").write_text("# New\n") + + monkeypatch.setattr(tasks, "docs_generate", fake_generate) + monkeypatch.setattr(tasks, "DOCUMENTATION_DIRECTORY", git_repo_with_docs) + + # Act / Assert + with pytest.raises(Exit, match="New untracked files"): + tasks.docs_validate(Context()) diff --git a/tests/unit/doc_generation/test_helpers.py b/tests/unit/doc_generation/test_helpers.py new file mode 100644 index 00000000..359654f0 --- /dev/null +++ b/tests/unit/doc_generation/test_helpers.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +from docs.docs_generation.helpers import build_config_properties, get_env_vars + + +class TestGetEnvVars: + def test_returns_dict(self) -> None: + # Act + result = get_env_vars() + + # Assert + assert isinstance(result, dict) + + def test_values_are_lists_of_strings(self) -> None: + # Act + result = get_env_vars() + + # Assert + for key, values in result.items(): + assert isinstance(key, str) + assert isinstance(values, list) + for v in values: + assert isinstance(v, str) + + def test_env_vars_are_upper_case(self) -> None: + # Act + result = get_env_vars() + + # Assert + for values in result.values(): + for v in values: + assert v == v.upper() + + def test_address_field_has_env_var(self) -> None: + # Act + result = get_env_vars() + + # Assert + assert "address" in result + assert len(result["address"]) > 0 + + +class TestBuildConfigProperties: + def test_returns_list(self) -> None: + # Act + result = build_config_properties() + + # Assert + assert isinstance(result, list) + assert len(result) > 0 + + def test_each_property_has_required_keys(self) -> None: + # Arrange + required_keys = {"name", "description", "type", "choices", "default", "env_vars"} + + # Act + result = build_config_properties() + + # Assert + for prop in result: + assert required_keys.issubset(prop.keys()) + + def test_address_property_exists(self) -> None: + # Act + result = build_config_properties() + + # Assert + names = [p["name"] for p in result] + assert "address" in names + + def test_address_has_env_vars(self) -> None: + # Act + result = build_config_properties() + + # Assert + address_prop = next(p for p in result if p["name"] == "address") + assert len(address_prop["env_vars"]) > 0 diff --git a/tests/unit/test_tasks.py b/tests/unit/test_tasks.py new file mode 100644 index 00000000..70a4064c --- /dev/null +++ b/tests/unit/test_tasks.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +import pytest +from invoke import Exit + +import tasks + + +class TestRequireTool: + """Verify that require_tool() raises with a helpful message when an external tool is missing.""" + + def test_raises_when_tool_missing(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Arrange + monkeypatch.setattr(tasks, "which", lambda _name: None) + + # Act / Assert + with pytest.raises(Exit, match="mytool is not installed"): + tasks.require_tool("mytool", "Install it with: pip install mytool") + + def test_passes_when_tool_installed(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Arrange + monkeypatch.setattr(tasks, "which", lambda _name: "/usr/bin/mytool") + + # Act / Assert — no exception means tool is found + tasks.require_tool("mytool", "Install it with: pip install mytool") + + def test_error_message_contains_install_hint(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Arrange + monkeypatch.setattr(tasks, "which", lambda _name: None) + + # Act / Assert + with pytest.raises(Exit, match="Install it with: npm install"): + tasks.require_tool("sometool", "Install it with: npm install") diff --git a/uv.lock b/uv.lock index f2607635..a3945b1b 100644 --- a/uv.lock +++ b/uv.lock @@ -717,6 +717,7 @@ ctl = [ { name = "ariadne-codegen" }, { name = "click" }, { name = "jinja2" }, + { name = "mdxify" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pyarrow" }, @@ -783,6 +784,7 @@ requires-dist = [ { name = "httpx", specifier = ">=0.20" }, { name = "jinja2", marker = "extra == 'all'", specifier = ">=3" }, { name = "jinja2", marker = "extra == 'ctl'", specifier = ">=3" }, + { name = "mdxify", marker = "python_full_version >= '3.10' and extra == 'ctl'", specifier = ">=0.2.23" }, { name = "netutils", specifier = ">=1.0.0" }, { name = "numpy", marker = "python_full_version >= '3.12' and extra == 'all'", specifier = ">=1.26.2" }, { name = "numpy", marker = "python_full_version >= '3.12' and extra == 'ctl'", specifier = ">=1.26.2" }, @@ -1149,6 +1151,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "mdxify" +version = "0.2.36" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "griffe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/8b/eec3cc2f5b9e15a1d5d1a7399cf68b420bbd7ab8c363c789cfb14f783a09/mdxify-0.2.36.tar.gz", hash = "sha256:bd8afc3036b8258b13cd6d44413f1805088a9959b1b2d63eae9160cc037ee8e4", size = 1250127, upload-time = "2026-02-06T17:58:19.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/b4/3ad6aac18dbd5913201cd3bbf19a896a59fd418c7e87a5abf18575fb339a/mdxify-0.2.36-py3-none-any.whl", hash = "sha256:9dbe9b3e608ad1b9d5d95f95fcc66788d0d737a52eadd8bdb1244e628dc6d98c", size = 24552, upload-time = "2026-02-06T17:58:18.542Z" }, +] + [[package]] name = "mypy" version = "1.11.2"