← Բաց կոդի կատալոգ

iOS Testing Tools

Swift package and agent skill for iOS/macOS UI testing: screenshot capture, Page Object helpers, and multi-peer E2E test coordination.

Դիտել GitHub-ում

UITestToolkit

Swift package for iOS/macOS UI testing with screenshot capture and validation.

Products

ProductTypeDescription
ScreenshotKitLibraryScreenshot capture with structured naming
UITestKitLibraryPage Object protocols, XCUIElement extensions, Allure annotations
IOSE2EPeerClientLibraryReusable UI test peer client for generalized E2E coordinator sessions
extract-screenshotsCLIExtract/organize screenshots from xcresult (macOS only)
snapshotsdiffCLICreate visual diffs between snapshot images (macOS only)
ios-device-buildCLIBuild an iOS app across connected physical devices and the Apple Silicon Mac iOS-app destination (macOS only)
ios-e2e-runnerCLICoordinate generalized multi-peer iOS UI E2E sessions (macOS only)
e2e-fake-peerCLILocal sample peer for coordinator smoke tests (macOS only)
e2e-listener-fake-peerCLILocal sample peer for peer-listener transport smoke tests (macOS only)

Note: Add ScreenshotKit and UITestKit for standard UI testing. Add IOSE2EPeerClient only to UI test targets that participate in generalized E2E coordinator sessions. The CLI tools use Foundation.Process/AppKit which are unavailable on iOS, so do NOT add them as target dependencies. Run from terminal only.

Installation

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/ivalx1s/swift-ui-testing-tools", from: "1.0.0")
    // or local: .package(path: "../swift-ui-testing-tools")
]

Add to UI test target:

.testTarget(
    name: "YourAppUITests",
    dependencies: [
        .product(name: "ScreenshotKit", package: "UITestToolkit"),
        .product(name: "UITestKit", package: "UITestToolkit"),
        // Optional for generalized multi-peer E2E tests:
        // .product(name: "IOSE2EPeerClient", package: "UITestToolkit")
    ]
)

Usage

import XCTest
import ScreenshotKit
import UITestKit

final class MyUITests: BaseUITestSuite {

    override class func setUp() {
        super.setUp()
        ScreenshotManager.shared.startSession()
    }

    override func setUpWithError() throws {
        try super.setUpWithError()
        startTestScreenshots()
    }

    @MainActor
    func testLogin() throws {
        let app = XCUIApplication()
        app.launch()
        screenshot(step: 1, "app_launched", app: app)

        app.textFields["username"].tap()
        app.textFields["username"].typeText("test@example.com")
        screenshot(step: 2, "username_entered", app: app)

        app.buttons["Login"].tap()

        XCTAssertTrue(app.staticTexts["Welcome"].waitFor(\.exists, toBe: true, timeout: 5))
        screenshot(step: 3, "login_success", app: app)
    }
}

Test Platform Selection

Choose the execution platform from the target declaration before running tests.

  • iOS app target or app package: run tests and runtime by default on iOS Simulator.
  • App target or app package on another declared platform: run against that declared platform.
  • If the app/package declares no platform, default to macOS.
  • Package module or multi-platform package: run unit and integration tests on macOS when macOS is supported.
  • If macOS is unavailable, prefer iOS when declared.
  • Otherwise use the first declared platform.
  • Use simulator destinations only for iOS-family runs. For macOS, run on macOS directly.
  • If a reusable iOS package is consumed through an app-local graph and standalone package schemes cannot resolve local dependencies, verify it through the app workspace/test host instead of treating the standalone package build as the product build.

Apple Silicon Mac iOS-App Runtime

When an iOS app needs a Mac-side endpoint, use the existing iPhoneOS app on the Apple Silicon Mac Designed for iPad/iPhone destination. Do not add a separate macOS app target unless the product explicitly needs native macOS UI.

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
xcodebuild \
  -workspace App.xcworkspace \
  -scheme App \
  -destination 'platform=macOS,arch=arm64,variant=Designed for iPad' \
  build

Launching the built Debug-iphoneos/App.app with open is not reliable for this runtime, and XCUITest cannot drive My Mac (Designed for iPad). Use Xcode’s run action through AppleScript to launch the Mac-hosted iOS app, then run the actual UI test against a physical iPhone or Simulator endpoint. See agents/skills/ios-testing-tools/SKILL.md for the full workflow.

Connected Device Builds

Use ios-device-build when a project needs one command that builds an iOS app for every currently discovered physical iPhone/iPad. The command discovers devices through xcrun xcdevice list --json; there is no hard-coded expected device list, so a missing device is skipped and the remaining devices still build. If no matching destinations are found, the command fails.

The build contract is intentionally explicit. Pass the project source, scheme, discovery interfaces, target set, and DerivedData/log root every time. The CLI fails fast on missing required parameters instead of guessing.

Discovery is an interface option set:

  • --discovery usb builds only cable-connected physical iOS devices. This is the preferred stable mode.
  • --discovery wifi builds only network-discovered physical iOS devices.
  • --discovery usb,wifi builds every available physical iOS device Xcode currently sees.

Target selection is separate:

  • --targets iphones builds physical iOS devices.
  • --targets macbook builds the Apple Silicon Mac Designed for iPad/iPhone destination when Xcode exposes it.
  • --targets iphones,macbook builds both groups.
./Scripts/ios-device-build.sh \
  --workspace App.xcworkspace \
  --scheme App \
  --discovery usb \
  --targets iphones \
  --derived-data-root .temp/device-builds
./Scripts/ios-device-build.sh \
  --project App.xcodeproj \
  --scheme App \
  --discovery usb,wifi \
  --targets iphones \
  --derived-data-root .temp/device-builds
./Scripts/ios-device-build.sh \
  --workspace App.xcworkspace \
  --scheme App \
  --discovery usb \
  --targets iphones,macbook \
  --mac-destination 'platform=macOS,arch=arm64,variant=Designed for iPad' \
  --derived-data-root .temp/device-builds
./Scripts/ios-device-build.sh \
  --workspace App.xcworkspace \
  --scheme App \
  --targets macbook \
  --mac-destination 'platform=macOS,arch=arm64,variant=Designed for iPad' \
  --derived-data-root .temp/device-builds

Per-destination DerivedData and xcodebuild.log files are written under the required --derived-data-root.

iOS E2E Runner

Use ios-e2e-runner to validate and run generalized multi-peer iOS UI E2E sessions. The runner config is project-neutral: product repositories provide peer mappings, Xcode destinations, selectors, and scenario event names in their own config or test code.

The full config contract, reserved environment keys, and artifact layout are documented in .spec/e2e-coordinator-config-schema.md.

Keep product-specific examples in the consumer project that owns the workflow. Toolkit docs should use neutral peer names, event names, bundle placeholders, and device placeholders.

Dry-run validates the config and prints the exact peer launch plan without building, installing, launching devices, or starting UI tests:

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
swift run ios-e2e-runner \
  --config Samples/IOSE2ECoordinator/dry-run-two-peer.yaml \
  --dry-run \
  --session-id sample-session

To run a real coordinator session, use the same config shape without --dry-run. The config must point at reachable physical devices or simulators, valid Xcode projects, and UI test selectors owned by the consumer project:

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
swift run ios-e2e-runner \
  --config path/to/e2e-physical.yaml \
  --session-id physical-validation-001 \
  --developer-dir /Applications/Xcode.app/Contents/Developer

The runner injects reserved E2E_* environment values into every peer process so UI tests can connect to the coordinator through IOSE2EPeerClient.

UI test targets that participate in a coordinator session should import IOSE2EPeerClient and create the peer client from the injected environment:

import IOSE2EPeerClient
import XCTest

final class PeerScenarioTests: XCTestCase {
    func testScenario() async throws {
        let client = try await UITestE2EClient.fromEnvironment()
        defer { client.close() }

        if client.environment.peerNameValue == "peer-a" {
            try await client.publish("peer-a.ready", delivery: .acked())
            _ = try await client.waitFor("peer-b.ready", timeout: 30)
        } else {
            _ = try await client.waitFor("peer-a.ready", timeout: 30)
            try await client.publish("peer-b.ready", delivery: .acked())
        }
    }
}

Physical devices must be able to open TCP connections to the coordinator host. When all peers share the same LAN route, set coordinator.advertisedHost once. When peers need different routes to the same coordinator, for example separate wired or lab-network interfaces, set coordinatorHost on the individual peer; the runner keeps the global coordinator bind/port/path and injects a peer-specific E2E_COORDINATOR_URL.

peers:
  - name: peer-a
    coordinatorHost: 192.168.50.10
    launch:
      kind: xctest
      startWhen:
        type: immediate

Build the local sample peer before running the process-peer sample:

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
swift build --product e2e-fake-peer

Then run the three-peer local sample:

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
./Scripts/run-e2e-sample-smoke.sh

Peer-Listener Transport

Use coordinator.transport: peer-listener when physical iOS peers cannot reach a Mac-hosted WebSocket coordinator directly. In this mode each UI test peer starts a device-side TCP listener, the Mac runner starts iproxy for each connected device, and the Mac-side coordinator connects to every peer through the forwarded localhost ports.

Consumer UI tests should import IOSE2EPeerClient and use UITestE2EClient.fromEnvironment(). Do not import IOSE2ECoordinatorCore from product tests just to inspect peer names; use client.environment.peerNameValue for project scenario branching.

coordinator:
  transport: peer-listener

peers:
  - name: peer-a
    connection:
      listenPort: 19131
      connectHost: 127.0.0.1
      connectPort: 18131
      proxy:
        kind: iproxy
        udid: 00000000-0000000000000000

Run the toolkit-local smoke:

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
./Scripts/run-e2e-peer-listener-sample-smoke.sh

Physical Device Runtime Logs

When debugging runtime behavior on a physical iPhone, prefer the live device console over guessing from xcodebuild output.

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
xcrun xcdevice list
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
xcrun devicectl device process launch \
  --device <device-udid> \
  --terminate-existing \
  --console \
  <bundle-id>

Use --console for app-scoped logs, --terminate-existing to avoid stale processes, and the device UDID rather than the display name.

Scripts

ScriptDescription
check-tools.shVerify required tools, plus optional iOS Simulator support
run-tests-and-extract.shRun UI tests + extract screenshots
extract-screenshots.shExtract screenshots from xcresult
ios-device-build.shBuild an iOS app across discovered physical devices and optional Mac-compatible iOS destination
run-e2e-sample-smoke.shBuild the fake peer, run the local three-peer coordinator sample, and validate artifacts
run-e2e-peer-listener-sample-smoke.shBuild the listener fake peer, run the local peer-listener coordinator sample, and validate artifacts
ios-e2e-runnerSwiftPM executable for generalized multi-peer E2E dry-run and execution
e2e-fake-peerSwiftPM executable used by local coordinator samples
e2e-listener-fake-peerSwiftPM executable used by local peer-listener samples
setup.shCanonical global skill install into ~/.agents
setup-project-skills.shInstall AI skill to a project-local .agents
setup-global-skills.shCompatibility wrapper around ./setup.sh
# Check prerequisites
./Scripts/check-tools.sh

# Run tests and extract screenshots (run from your project directory!)
cd /path/to/your/project
~/src/swift-ui-testing-tools/Scripts/run-tests-and-extract.sh \
  -workspace App.xcworkspace \
  -scheme App \
  -output .temp/screenshots

Options:

  • -output dir: where to put screenshots (default: .temp/{timestamp}_screenshots, relative to CWD)
  • -destination "...": simulator to use (default: iPhone 16)

The simulator examples above are for iOS app/UI-test flows. Package module unit and integration tests should stay on macOS when macOS is supported by the package.

Project Structure

agents/skills/                        ← source repo content
  ios-testing-tools/                  ← source skill
~/.agents/skills/ios-testing-tools/   ← global installed runtime copy
<project>/.agents/skills/ios-testing-tools/  ← project-local installed runtime copy
.claude/skills → ../.agents/skills    ← symlink for Claude Code
.codex/skills → ../.agents/skills     ← symlink for Codex CLI

Source content lives in agents/skills/. Installed runtime copies live in ~/.agents/skills/ or <project>/.agents/skills/, and those copies are degitized after sync. .claude/ and .codex/ only point at the installed copy.

AI Agent Skill

Includes ios-testing-tools skill for AI-assisted UI test development.

Setup:

./setup.sh                                               # canonical global install
./Scripts/setup-project-skills.sh /path/to/your/project  # project-local install
./Scripts/setup-global-skills.sh                         # compatibility wrapper

Provides:

  • Page Object pattern templates
  • Accessibility ID naming conventions (BEM-like)
  • Shared identifiers setup (app + test targets)
  • Screenshot workflow with verification
  • Snapshot testing with swift-snapshot-testing
  • Allure integration (optional)

Snapshot Testing

For automated UI regression testing, use swift-snapshot-testing.

Add to your snapshot test target:

dependencies: [
    .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.17.0")
]

See the ios-testing-tools skill documentation for detailed patterns and examples.

snapshotsdiff CLI

Create visual diffs between snapshot images:

# Compare two images
swift run snapshotsdiff reference.png failed.png diff.png

# Batch compare all failed snapshots
swift run snapshotsdiff \
  --artifacts ./SnapshotArtifacts \
  --output ./SnapshotDiffs \
  --tests ./AppSnapshotTests

Output for batch mode:

SnapshotDiffs/
  TestName/
    snapshotName/
      ├── reference.png   (expected)
      ├── failed.png      (actual)
      └── diff.png        (visual diff)

CI/CD Integration

GitLab CI

# .gitlab-ci.yml
stages:
  - build_and_test
  - generate_snapshot_diffs

variables:
  SNAPSHOT_ARTIFACTS: "$CI_PROJECT_DIR/SnapshotArtifacts"
  SNAPSHOT_DIFFS: "$CI_PROJECT_DIR/SnapshotDiffs"
  SNAPSHOT_TESTS: "$CI_PROJECT_DIR/AppSnapshotTests"

build_and_test:
  stage: build_and_test
  script:
    - bundle exec fastlane build_and_test
  artifacts:
    when: always
    paths:
      - $SNAPSHOT_ARTIFACTS
    expire_in: 1 day

generate_snapshot_diffs:
  stage: generate_snapshot_diffs
  dependencies:
    - build_and_test
  script:
    - |
      if [ -d "$SNAPSHOT_ARTIFACTS" ] && [ "$(ls -A $SNAPSHOT_ARTIFACTS)" ]; then
        swift run --package-path /path/to/swift-ui-testing-tools snapshotsdiff \
          --artifacts "$SNAPSHOT_ARTIFACTS" \
          --output "$SNAPSHOT_DIFFS" \
          --tests "$SNAPSHOT_TESTS"
      fi
  artifacts:
    when: always
    paths:
      - $SNAPSHOT_DIFFS
    expire_in: 1 week

GitHub Actions

# .github/workflows/snapshot-tests.yml
name: Snapshot Tests
on:
  pull_request:

jobs:
  test:
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: xcodebuild test -scheme AppSnapshotTests -destination "platform=iOS Simulator,name=iPhone 16" || true
      - name: Generate diffs
        run: |
          git clone https://github.com/user/swift-ui-testing-tools.git /tmp/toolkit
          if [ -d "SnapshotArtifacts" ]; then
            swift run --package-path /tmp/toolkit snapshotsdiff \
              --artifacts ./SnapshotArtifacts --output ./SnapshotDiffs --tests ./AppSnapshotTests
          fi
      - uses: actions/upload-artifact@v4
        with:
          name: snapshot-diffs
          path: SnapshotDiffs/

Fastlane

# fastlane/Fastfile
lane :generate_snapshot_diffs do
  artifacts = ENV['SNAPSHOT_ARTIFACTS']
  return unless File.directory?(artifacts) && !Dir.empty?(artifacts)

  sh("swift run --package-path ../swift-ui-testing-tools snapshotsdiff " \
     "--artifacts #{artifacts} --output #{ENV['SNAPSHOT_DIFFS']} --tests #{ENV['SNAPSHOT_TESTS']}")
end

See ios-testing-tools skill for detailed examples.

Requirements

  • iOS 15.0+ / macOS 13.0+
  • Swift 6.2+
  • Xcode 26+

Run ./Scripts/check-tools.sh to verify.

The Relux stack

This package is part of the Relux stack: the Relux unidirectional data-flow architecture for Swift 6, a family of modules around it, and agent-ready testing tools. The stack is how we build MVPs fast on agentic rails and then scale them into enterprise-grade apps: Tuist workspaces, strict modularization, and a UDF architecture proven in production for years. Browse the full picture in the Relux Works open-source catalog.

About Relux Works

This project is part of the open-source ecosystem of Relux Works, an AI-native software development studio. We build fixed-price MVPs, rescue vibe-coded apps, run local AI inference, and train teams to work with coding agents. Much of the infrastructure behind that work is open source.

Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to the Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by the Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding any notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   Copyright 2026 Alexey Grigorev

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.