From 69c5e51588c496699fe3a49b8c61a374f1058a98 Mon Sep 17 00:00:00 2001 From: Talha Date: Fri, 31 Jul 2026 16:08:08 +0500 Subject: [PATCH 01/18] fix(models): harden preparation downloads --- src/vidxp/application.py | 15 +- src/vidxp/application_models.py | 37 ++++ src/vidxp/cli_commands/runtime.py | 5 +- src/vidxp/model_contracts.py | 23 +++ src/vidxp/runtime.py | 305 ++++++++++++++++++++++++------ tests/test_api.py | 30 +++ tests/test_application.py | 54 +++++- tests/test_cli.py | 69 ++++++- tests/test_job_contracts.py | 41 ++++ tests/test_mcp.py | 37 ++++ tests/test_models.py | 230 +++++++++++++++++++++- 11 files changed, 776 insertions(+), 70 deletions(-) diff --git a/src/vidxp/application.py b/src/vidxp/application.py index fcbd1cf..dd25eaf 100644 --- a/src/vidxp/application.py +++ b/src/vidxp/application.py @@ -27,6 +27,7 @@ ImportMediaCommand, IndexSnapshotReference, MediaAsset, + ModelDownloadError, ModelUnavailableError, PrepareModelsCommand, PrepareModelsResult, @@ -59,7 +60,10 @@ from vidxp.ports import IndexBackend, ModelRuntimePort, QueryModelPort from vidxp.query_service import GroundedQueryService from vidxp.search_fusion import fuse_search_results -from vidxp.model_contracts import ModelArtifactUnavailableError +from vidxp.model_contracts import ( + ModelArtifactDownloadError, + ModelArtifactUnavailableError, +) from vidxp.repository_layout import RepositoryLayout from vidxp.settings import VidXPSettings from vidxp.control_plane import ControlPlaneApplication @@ -119,6 +123,15 @@ def _capability_dependencies( capabilities, self.registry.install_hint(capabilities), ) from exc + except ModelArtifactDownloadError as exc: + raise ModelDownloadError( + exc.capability, + exc.model_id, + attempts=exc.attempts, + reason=exc.reason, + resumable=exc.resumable, + retryable=exc.retryable, + ) from exc except ModelArtifactUnavailableError as exc: raise ModelUnavailableError(exc.capability) from exc except (ModuleNotFoundError, CapabilityDependencyError) as exc: diff --git a/src/vidxp/application_models.py b/src/vidxp/application_models.py index 9975e3a..807a0a0 100644 --- a/src/vidxp/application_models.py +++ b/src/vidxp/application_models.py @@ -199,6 +199,43 @@ def __init__(self, capability: str) -> None: ) +class ModelDownloadError(ApplicationError): + def __init__( + self, + capability: str, + model_id: str, + *, + attempts: int, + reason: str, + resumable: bool, + retryable: bool, + ) -> None: + modality = capability.split(".", 1)[0] + remediation = f"vidxp prepare --modalities {modality}" + retry_message = ( + "Partial files were kept and the next preparation attempt will " + "resume them." + if resumable + else "The next preparation attempt will restart this file." + ) + super().__init__( + "model_download_failed", + ErrorCategory.unavailable, + f"Downloading {model_id} failed after {attempts} attempt(s) " + f"({reason}). {retry_message} Run " + f"`{remediation}` again when the connection is available.", + details={ + "capability": capability, + "model": model_id, + "attempts": attempts, + "reason": reason, + "partial_files_preserved": resumable, + "remediation": remediation, + }, + retryable=retryable, + ) + + class InvalidRequestError(ApplicationError): def __init__( self, diff --git a/src/vidxp/cli_commands/runtime.py b/src/vidxp/cli_commands/runtime.py index f5992bc..560827b 100644 --- a/src/vidxp/cli_commands/runtime.py +++ b/src/vidxp/cli_commands/runtime.py @@ -471,9 +471,8 @@ def prepare( typer.confirm("Download these models?", abort=True) show_progress = not state.quiet and output_format == OutputFormat.rich if show_progress: - emit_progress( - "Starting model preparation for " + ", ".join(selected) + "." - ) + action = "Downloading and validating" if missing else "Validating cached" + emit_progress(f"{action} models for " + ", ".join(selected) + ".") job = state.jobs.submit_prepare_models( PrepareModelsCommand( modalities=selected, diff --git a/src/vidxp/model_contracts.py b/src/vidxp/model_contracts.py index 6b6788e..b1ab521 100644 --- a/src/vidxp/model_contracts.py +++ b/src/vidxp/model_contracts.py @@ -24,6 +24,29 @@ def __init__(self, capability: str) -> None: super().__init__(f"Model artifacts for {capability} are unavailable.") +class ModelArtifactDownloadError(RuntimeError): + def __init__( + self, + capability: str, + model_id: str, + *, + attempts: int, + reason: str, + resumable: bool, + retryable: bool, + ) -> None: + self.capability = capability + self.model_id = model_id + self.attempts = attempts + self.reason = reason + self.resumable = resumable + self.retryable = retryable + super().__init__( + f"Downloading {model_id} failed after {attempts} attempt(s): " + f"{reason}." + ) + + @dataclass(frozen=True) class ModelKey: capability: str diff --git a/src/vidxp/runtime.py b/src/vidxp/runtime.py index 4fe70d7..abda32c 100644 --- a/src/vidxp/runtime.py +++ b/src/vidxp/runtime.py @@ -6,13 +6,14 @@ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeout from contextlib import contextmanager from threading import BoundedSemaphore, Lock, RLock -from time import monotonic +from time import monotonic, sleep from typing import Any, Callable, Iterator from pathlib import Path from vidxp.application_models import RuntimeProfile from vidxp.model_contracts import ( ArtifactSpec, + ModelArtifactDownloadError, ModelArtifactUnavailableError, ModelKey, ModelSpec, @@ -24,6 +25,64 @@ class RuntimeBackendUnavailableError(RuntimeError): """Raised when an explicitly requested compute backend cannot be used.""" +class _ModelDownloadVerificationError(RuntimeError): + """Raised internally when a completed transfer is missing pinned weights.""" + + +_MODEL_DOWNLOAD_ATTEMPTS = 3 +_DOWNLOAD_HEARTBEAT_SECONDS = 5.0 +_MINIMUM_PROGRESS_BYTES = 1024 * 1024 + + +def _download_failure_reason(exc: Exception) -> str: + if isinstance(exc, _ModelDownloadVerificationError): + return "artifact verification failed" + response = getattr(exc, "response", None) + status = getattr(response, "status_code", None) + if isinstance(status, int): + return f"HTTP {status} {type(exc).__name__}" + return type(exc).__name__ + + +def _download_failure_retryable( + exc: Exception, + *, + hash_mismatch_is_retryable: bool = False, +) -> bool | None: + if isinstance(exc, _ModelDownloadVerificationError): + return True + response = getattr(exc, "response", None) + status = getattr(response, "status_code", None) + if isinstance(status, int): + return status in {408, 409, 425, 429} or status >= 500 + if isinstance(exc, (ConnectionError, TimeoutError)): + return True + try: + import httpx + except ModuleNotFoundError: + pass + else: + if isinstance(exc, httpx.TransportError): + return True + try: + from requests import exceptions as requests_exceptions + except ModuleNotFoundError: + pass + else: + if isinstance( + exc, + (requests_exceptions.ConnectionError, requests_exceptions.Timeout), + ): + return True + if hash_mismatch_is_retryable and isinstance(exc, ValueError): + return True + if isinstance(exc, OSError): + return False + if type(exc).__module__.startswith(("huggingface_hub", "pooch")): + return False + return None + + def _torch_accelerators() -> tuple[bool, bool]: try: import torch @@ -190,60 +249,121 @@ def update(self, n=1): result = super().update(n) if self.unit == "B": with state_lock: - state.update( - { - "current": int(self.n), - "total": ( - int(self.total) - if self.total - else None - ), - "message": f"Downloading {spec.model_id}.", - } + previous = getattr(self, "_vidxp_reported_bytes", 0) + current = int(self.n) + self._vidxp_reported_bytes = current + state["current"] = min( + spec.download_size_bytes, + int(state["current"]) + max(0, current - previous), ) + state["message"] = f"Downloading {spec.model_id}." return result def download() -> str: - return snapshot_download( - repo_id=spec.model_id, - revision=spec.revision, - cache_dir=str(cache), - local_files_only=False, - tqdm_class=ReportingTqdm, + snapshot = Path( + snapshot_download( + repo_id=spec.model_id, + revision=spec.revision, + cache_dir=str(cache), + local_files_only=False, + tqdm_class=ReportingTqdm, + ) ) + weights = snapshot / spec.weights_file + if ( + not weights.is_file() + or _sha256(weights) != spec.weights_sha256 + ): + raise _ModelDownloadVerificationError + return str(snapshot) reported_at = 0.0 - with ThreadPoolExecutor(max_workers=1) as pool: - future = pool.submit(download) - while True: - try: - snapshot = future.result(timeout=0.5) - if progress is not None: - with state_lock: - event = dict(state) - progress( - { - "state": "preparing", - "stage": "downloading_model", - **event, - } - ) + reported_current = -1 + progress_step = max( + _MINIMUM_PROGRESS_BYTES, + spec.download_size_bytes // 100, + ) + + def report(*, force: bool = False): + nonlocal reported_at, reported_current + if progress is None: + return + now = monotonic() + with state_lock: + event = dict(state) + current = int(event["current"]) + advanced = current - reported_current >= progress_step + heartbeat = now - reported_at >= _DOWNLOAD_HEARTBEAT_SECONDS + if not force and reported_current >= 0 and not advanced and not heartbeat: + return + progress( + { + "state": "preparing", + "stage": "downloading_model", + **event, + } + ) + reported_at = now + reported_current = current + + last_error: Exception | None = None + last_retryable = False + attempts = 0 + for attempt in range(1, _MODEL_DOWNLOAD_ATTEMPTS + 1): + attempts = attempt + with state_lock: + state["message"] = ( + f"Connecting to download {spec.model_id}." + if attempt == 1 + else ( + f"Retrying download of {spec.model_id} " + f"(attempt {attempt} of {_MODEL_DOWNLOAD_ATTEMPTS})." + ) + ) + report(force=True) + try: + with ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit(download) + while True: + try: + snapshot = future.result(timeout=0.5) + break + except FutureTimeout: + report() + except Exception as exc: + last_error = exc + retryable = _download_failure_retryable(exc) + if retryable is None: + raise + last_retryable = retryable + if ( + attempt >= _MODEL_DOWNLOAD_ATTEMPTS + or not retryable + ): break - except FutureTimeout: - now = monotonic() - if progress is None or now - reported_at < 1: - continue - with state_lock: - event = dict(state) - progress( - { - "state": "preparing", - "stage": "downloading_model", - **event, - } + with state_lock: + state["message"] = ( + f"Download interrupted for {spec.model_id}; cached " + "partial files will be resumed." ) - reported_at = now - return Path(snapshot) + report(force=True) + sleep(2 ** (attempt - 1)) + continue + with state_lock: + state["current"] = spec.download_size_bytes + state["message"] = f"Downloaded {spec.model_id}." + report(force=True) + return Path(snapshot) + + assert last_error is not None + raise ModelArtifactDownloadError( + spec.capability, + spec.model_id, + attempts=attempts, + reason=_download_failure_reason(last_error), + resumable=True, + retryable=last_retryable, + ) from last_error def resolve_model( self, @@ -257,8 +377,9 @@ def resolve_model( from huggingface_hub import snapshot_download try: + snapshot: Path | None try: - snapshot = Path( + local_snapshot = Path( snapshot_download( repo_id=spec.model_id, revision=spec.revision, @@ -266,7 +387,16 @@ def resolve_model( local_files_only=True, ) ) + local_weights = local_snapshot / spec.weights_file + snapshot = ( + local_snapshot + if local_weights.is_file() + and _sha256(local_weights) == spec.weights_sha256 + else None + ) except Exception: + snapshot = None + if snapshot is None: if not download or not self.settings.allow_model_downloads: raise ModelArtifactUnavailableError(spec.capability) snapshot = self._download_snapshot( @@ -274,10 +404,7 @@ def resolve_model( cache=self.settings.model_cache, progress=progress, ) - weights = snapshot / spec.weights_file - if not weights.is_file() or _sha256(weights) != spec.weights_sha256: - raise ModelArtifactUnavailableError(spec.capability) - except ModelArtifactUnavailableError: + except (ModelArtifactDownloadError, ModelArtifactUnavailableError): raise except Exception as exc: raise ModelArtifactUnavailableError(spec.capability) from exc @@ -306,24 +433,80 @@ def resolve_artifact( "state": "preparing", "stage": "downloading_model", "message": f"Downloading {spec.model_id}.", + "current": 0, + "total": spec.download_size_bytes, } ) import pooch - resolved = Path( - pooch.retrieve( - url=spec.url, - known_hash=f"sha256:{spec.sha256}", - fname=spec.filename, - path=destination, - progressbar=False, - ) - ) + last_error = None + for attempt in range(1, _MODEL_DOWNLOAD_ATTEMPTS + 1): + try: + resolved = Path( + pooch.retrieve( + url=spec.url, + known_hash=f"sha256:{spec.sha256}", + fname=spec.filename, + path=destination, + progressbar=False, + ) + ) + break + except Exception as exc: + last_error = exc + retryable = _download_failure_retryable( + exc, + hash_mismatch_is_retryable=True, + ) + if retryable is None: + raise + if ( + attempt >= _MODEL_DOWNLOAD_ATTEMPTS + or not retryable + ): + raise ModelArtifactDownloadError( + spec.capability, + spec.model_id, + attempts=attempt, + reason=_download_failure_reason(exc), + resumable=False, + retryable=retryable, + ) from exc + if progress is not None: + progress( + { + "state": "preparing", + "stage": "downloading_model", + "message": ( + f"Download interrupted for " + f"{spec.model_id}; retrying attempt " + f"{attempt + 1} of " + f"{_MODEL_DOWNLOAD_ATTEMPTS}. This " + "file will restart from zero." + ), + "current": 0, + "total": spec.download_size_bytes, + } + ) + sleep(2 ** (attempt - 1)) + else: + assert last_error is not None + raise last_error else: resolved = path if not resolved.is_file() or _sha256(resolved) != spec.sha256: raise ModelArtifactUnavailableError(spec.capability) - except ModelArtifactUnavailableError: + if progress is not None: + progress( + { + "state": "preparing", + "stage": "downloading_model", + "message": f"Verified {spec.model_id}.", + "current": spec.download_size_bytes, + "total": spec.download_size_bytes, + } + ) + except (ModelArtifactDownloadError, ModelArtifactUnavailableError): raise except Exception as exc: raise ModelArtifactUnavailableError(spec.capability) from exc diff --git a/tests/test_api.py b/tests/test_api.py index b862c1e..658c0e1 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -19,6 +19,7 @@ from vidxp.application_models import ( ApplicationError, ErrorCategory, + ErrorDetail, ComponentReadiness, Job, JobKind, @@ -405,6 +406,35 @@ def test_job_submission_is_thin_idempotent_delegation(self): ), ) + def test_failed_model_preparation_job_is_structured_over_http(self): + with TemporaryDirectory() as directory: + context = self.context(Path(directory)) + context.jobs.get.return_value = Job( + job_id=JOB_ID, + kind=JobKind.prepare_models, + state=JobState.failed, + queue=JobQueue.cpu, + error=ErrorDetail( + code="model_download_failed", + category=ErrorCategory.unavailable, + message="The model download failed after three attempts.", + details={ + "model": "publisher/model", + "partial_files_preserved": True, + "remediation": "vidxp prepare --modalities dialogue", + }, + retryable=True, + ), + ) + with TestClient(create_app(context=context)) as client: + response = client.get(f"/api/v1/jobs/{JOB_ID}") + + self.assertEqual(response.status_code, 200) + error = response.json()["error"] + self.assertEqual(error["code"], "model_download_failed") + self.assertTrue(error["retryable"]) + self.assertTrue(error["details"]["partial_files_preserved"]) + def test_missing_models_fail_before_job_submission(self): with TemporaryDirectory() as directory: context = self.context(Path(directory)) diff --git a/tests/test_application.py b/tests/test_application.py index f3bb2d4..ee55144 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -21,6 +21,7 @@ IndexResult, IndexSnapshotReference, ModelUnavailableError, + ModelDownloadError, PrepareModelsCommand, QueryAnswerMode, QueryVideoCommand, @@ -30,7 +31,10 @@ from vidxp.core.media import MediaUnavailableError from vidxp.core.contracts import IndexConfig, IndexSchemaError from vidxp.infrastructure.local_index import LocalIndexBackend -from vidxp.model_contracts import ModelArtifactUnavailableError +from vidxp.model_contracts import ( + ModelArtifactDownloadError, + ModelArtifactUnavailableError, +) from vidxp.capabilities.contracts import ( CapabilityDefinition, CapabilityExecutor, @@ -818,6 +822,54 @@ def test_missing_model_is_not_misclassified_as_a_package_dependency(self): json.dumps(raised.exception.to_dict()), ) + def test_model_download_failure_preserves_retry_details(self): + definition = CapabilityDefinition( + name="prepare-only", + description="Prepare a provider.", + extra="prepare-only", + operations={ + "noop": OperationDefinition( + input_model=SearchInput, + output_model=SearchResult, + requires_index=False, + ) + }, + prepares_models=True, + ) + failure = ModelArtifactDownloadError( + "prepare-only.embedding", + "publisher/model", + attempts=3, + reason="ConnectionError", + resumable=True, + retryable=True, + ) + plugin = CapabilityPlugin( + definition=definition, + executor_factory=lambda: CapabilityExecutor( + operations={"noop": Mock()}, + prepare=Mock(side_effect=failure), + ), + ) + registry = CapabilityRegistry((plugin,)) + registry.dependency_checks = Mock(return_value=()) + application, _ = self.application("unused", registry=registry) + + with self.assertRaises(ModelDownloadError) as raised: + application.prepare_models( + PrepareModelsCommand(modalities=("prepare-only",)) + ) + + payload = raised.exception.to_dict() + self.assertEqual(payload["code"], "model_download_failed") + self.assertTrue(payload["retryable"]) + self.assertEqual(payload["details"]["attempts"], 3) + self.assertTrue(payload["details"]["partial_files_preserved"]) + self.assertEqual( + payload["details"]["remediation"], + "vidxp prepare --modalities prepare-only", + ) + def test_unexpected_handler_error_is_not_misclassified(self): failure = RuntimeError("implementation bug") definition = CapabilityDefinition( diff --git a/tests/test_cli.py b/tests/test_cli.py index b3b4466..b819fe9 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -25,6 +25,8 @@ CreateIndexCommand, DependencyCheckResult, DependencyKind, + ErrorCategory, + ErrorDetail, FusedSearchResult, FusionProvenance, IndexJobResult, @@ -145,6 +147,33 @@ def test_grouped_commands_are_exposed(self): ): self.assertIn(command, result.output) + def test_failed_model_preparation_job_is_structured_in_cli_json(self): + self.jobs.get.return_value = Job( + job_id=JOB_ID, + kind=JobKind.prepare_models, + state=JobState.failed, + queue=JobQueue.cpu, + error=ErrorDetail( + code="model_download_failed", + category=ErrorCategory.unavailable, + message="The model download failed after three attempts.", + details={ + "model": "publisher/model", + "partial_files_preserved": True, + "remediation": "vidxp prepare --modalities dialogue", + }, + retryable=True, + ), + ) + + result = self.invoke(["jobs", "show", JOB_ID]) + + self.assertEqual(result.exit_code, 0, result.output) + error = json.loads(result.output)["error"] + self.assertEqual(error["code"], "model_download_failed") + self.assertTrue(error["retryable"]) + self.assertTrue(error["details"]["partial_files_preserved"]) + def test_mcp_config_is_copy_paste_json_without_opening_repository(self): result = self.invoke(["mcp-config"]) @@ -813,10 +842,48 @@ def test_prepare_announces_start_and_subscribes_to_job_progress(self): self.assertIn("1.43 GiB", result.output) self.assertRegex( result.output, - r"\[\d{2}:\d{2}:\d{2}\] Starting model preparation for scene\.", + r"\[\d{2}:\d{2}:\d{2}\] Downloading and validating models for " + r"scene\.", ) self.assertTrue(callable(self.jobs.wait.call_args.kwargs["progress"])) + def test_prepare_distinguishes_cached_model_verification(self): + self.service.model_readiness.return_value = DependencyCheckResult( + ok=True, + modalities=("scene",), + checks=(), + ) + prepared = PrepareModelsResult( + prepared=("scene-model",), + modalities=("scene",), + runtime={ + "requested": "cpu", + "torch_device": "cpu", + "transcription_device": "cpu", + }, + ) + self.jobs.submit_prepare_models.return_value = Job( + job_id=JOB_ID, + kind=JobKind.prepare_models, + state=JobState.queued, + queue=JobQueue.cpu, + ) + self.jobs.wait.return_value = Job( + job_id=JOB_ID, + kind=JobKind.prepare_models, + state=JobState.succeeded, + queue=JobQueue.cpu, + result=PrepareModelsJobResult(result=prepared), + ) + + result = self.invoke(["prepare", "--modalities", "scene"]) + + self.assertEqual(result.exit_code, 0, result.output) + self.assertRegex( + result.output, + r"\[\d{2}:\d{2}:\d{2}\] Validating cached models for scene\.", + ) + def test_prepare_discloses_size_and_requires_confirmation(self): self.service.model_readiness.return_value = DependencyCheckResult( ok=False, diff --git a/tests/test_job_contracts.py b/tests/test_job_contracts.py index 817f05f..34f67aa 100644 --- a/tests/test_job_contracts.py +++ b/tests/test_job_contracts.py @@ -9,6 +9,8 @@ from vidxp.application_models import ( ActorOverlayJobRequest, ApplicationError, + ErrorCategory, + ErrorDetail, CreateActorOverlayCommand, CreateIndexCommand, Job, @@ -303,6 +305,45 @@ def test_job_backend_errors_are_normalized_for_every_adapter(self): service.get(JOB_ID) self.assertEqual(raised.exception.code, "job_backend_unavailable") + def test_failed_model_preparation_error_round_trips_through_job_service(self): + error = ErrorDetail( + code="model_download_failed", + category=ErrorCategory.unavailable, + message="The model download failed after three attempts.", + details={ + "capability": "dialogue.transcription", + "model": "publisher/model", + "attempts": 3, + "reason": "ConnectionError", + "partial_files_preserved": True, + "remediation": "vidxp prepare --modalities dialogue", + }, + retryable=True, + ) + backend = Mock() + backend.get.return_value = Job( + job_id=JOB_ID, + kind=JobKind.prepare_models, + state=JobState.failed, + queue=JobQueue.cpu, + error=error, + ) + service = JobService( + settings=VidXPSettings( + repository_root=Path("repository"), + runtime_backend="cpu", + ), + backend=backend, + ) + + with self.assertRaises(ApplicationError) as raised: + service.result(JOB_ID) + + self.assertEqual( + raised.exception.to_dict(), + error.model_dump(mode="json"), + ) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_mcp.py b/tests/test_mcp.py index eda1d88..39de1e7 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -24,6 +24,7 @@ ApplicationError, Artifact, ErrorCategory, + ErrorDetail, IndexStatus, Job, JobKind, @@ -401,6 +402,42 @@ async def test_discovery_tools_use_shared_media_and_job_pages(self): ) self.assertEqual(context.jobs.list.call_args.args[0].page_size, 9) + async def test_failed_model_preparation_job_is_structured_over_mcp(self): + with TemporaryDirectory() as directory: + context = self.context(Path(directory)) + context.jobs.get.return_value = Job( + job_id=JOB_ID, + kind=JobKind.prepare_models, + state=JobState.failed, + queue=JobQueue.cpu, + error=ErrorDetail( + code="model_download_failed", + category=ErrorCategory.unavailable, + message="The model download failed after three attempts.", + details={ + "model": "publisher/model", + "partial_files_preserved": True, + "remediation": "vidxp prepare --modalities dialogue", + }, + retryable=True, + ), + ) + server = create_mcp_server( + context, + default_principal=Principal( + subject="agent", + scopes=frozenset({"vidxp.read"}), + ), + ) + async with Client(server) as client: + result = await client.call_tool("get_job", {"job_id": JOB_ID}) + + self.assertFalse(result.is_error) + error = result.structured_content["error"] + self.assertEqual(error["code"], "model_download_failed") + self.assertTrue(error["retryable"]) + self.assertTrue(error["details"]["partial_files_preserved"]) + async def test_retry_job_uses_shared_stable_idempotency(self): with TemporaryDirectory() as directory: context = self.context(Path(directory)) diff --git a/tests/test_models.py b/tests/test_models.py index 74af819..c56923a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -37,6 +37,7 @@ SERVER_INDEX_RUNTIME_CHECKS, ) from vidxp.model_contracts import ( + ModelArtifactDownloadError, ModelArtifactUnavailableError, ModelKey, model_artifact_path, @@ -150,6 +151,39 @@ def test_actor_download_uses_pinned_media_object_not_lfs_pointer(self): "yunet", ) + def test_actor_download_retries_without_claiming_partial_resume(self): + with TemporaryDirectory() as directory: + spec = replace(YUNET_MODEL, filename="missing.onnx") + runtime = self.runtime( + directory, + allowed_specs=(spec,), + allow_model_downloads=True, + ) + retrieve = Mock(side_effect=ConnectionError("interrupted")) + events = [] + with patch.dict( + sys.modules, + {"pooch": types.SimpleNamespace(retrieve=retrieve)}, + ), patch("vidxp.runtime.sleep"), self.assertRaises( + ModelArtifactDownloadError + ) as raised: + runtime.resolve_artifact( + spec, + download=True, + progress=events.append, + ) + + self.assertEqual(retrieve.call_count, 3) + self.assertFalse(raised.exception.resumable) + self.assertTrue(raised.exception.retryable) + self.assertTrue( + any( + event["stage"] == "downloading_model" + and "restart from zero" in event["message"] + for event in events + ) + ) + def test_normal_model_resolution_never_downloads_implicitly(self): with TemporaryDirectory() as directory: spec = replace(YUNET_MODEL, filename="missing.onnx") @@ -203,6 +237,14 @@ def test_model_readiness_checks_the_pinned_cache_without_loading(self): def test_explicit_snapshot_download_reports_bytes(self): with TemporaryDirectory() as directory: snapshot = Path(directory) / "snapshot" + weights = snapshot / FASTER_WHISPER_MODEL.weights_file + weights.parent.mkdir(parents=True) + content = b"verified weights" + weights.write_bytes(content) + spec = replace( + FASTER_WHISPER_MODEL, + weights_sha256=hashlib.sha256(content).hexdigest(), + ) events = [] def download(**options): @@ -226,7 +268,7 @@ def download(**options): False, ): resolved = ModelRuntime._download_snapshot( - FASTER_WHISPER_MODEL, + spec, cache=Path(directory), progress=events.append, ) @@ -235,15 +277,197 @@ def download(**options): ) self.assertEqual(resolved, snapshot) + download_events = [ + event for event in events if event["stage"] == "downloading_model" + ] + self.assertTrue(download_events) + self.assertTrue( + all( + event["total"] == spec.download_size_bytes + for event in download_events + ) + ) self.assertTrue( any( event["stage"] == "downloading_model" - and event["current"] == 1024 - and event["total"] == 1024 + and event["current"] + == spec.download_size_bytes + and event["total"] + == spec.download_size_bytes for event in events ) ) + def test_snapshot_download_retries_and_resumes_partial_cache(self): + with TemporaryDirectory() as directory: + snapshot = Path(directory) / "snapshot" + weights = snapshot / FASTER_WHISPER_MODEL.weights_file + weights.parent.mkdir(parents=True) + content = b"verified weights" + weights.write_bytes(content) + spec = replace( + FASTER_WHISPER_MODEL, + weights_sha256=hashlib.sha256(content).hexdigest(), + ) + download = Mock( + side_effect=(ConnectionError("interrupted"), str(snapshot)) + ) + events = [] + + with patch( + "huggingface_hub.snapshot_download", + download, + ), patch("vidxp.runtime.sleep") as retry_sleep: + resolved = ModelRuntime._download_snapshot( + spec, + cache=Path(directory), + progress=events.append, + ) + + self.assertEqual(resolved, snapshot) + self.assertEqual(download.call_count, 2) + retry_sleep.assert_called_once_with(1) + self.assertTrue( + any( + event["stage"] == "downloading_model" + and "partial files will be resumed" in event["message"] + for event in events + ) + ) + + def test_snapshot_download_retries_an_incomplete_returned_snapshot(self): + with TemporaryDirectory() as directory: + snapshot = Path(directory) / "snapshot" + weights = snapshot / FASTER_WHISPER_MODEL.weights_file + content = b"verified weights" + spec = replace( + FASTER_WHISPER_MODEL, + weights_sha256=hashlib.sha256(content).hexdigest(), + ) + calls = 0 + + def download(**_options): + nonlocal calls + calls += 1 + if calls == 2: + weights.parent.mkdir(parents=True) + weights.write_bytes(content) + return str(snapshot) + + events = [] + with patch( + "huggingface_hub.snapshot_download", + side_effect=download, + ), patch("vidxp.runtime.sleep") as retry_sleep: + resolved = ModelRuntime._download_snapshot( + spec, + cache=Path(directory), + progress=events.append, + ) + + self.assertEqual(resolved, snapshot) + self.assertEqual(calls, 2) + retry_sleep.assert_called_once_with(1) + self.assertTrue( + any( + event["stage"] == "downloading_model" + and "partial files will be resumed" in event["message"] + for event in events + ) + ) + + def test_snapshot_download_reports_actionable_terminal_failure(self): + with TemporaryDirectory() as directory: + download = Mock(side_effect=ConnectionError("private detail")) + + with patch( + "huggingface_hub.snapshot_download", + download, + ), patch("vidxp.runtime.sleep"), self.assertRaises( + ModelArtifactDownloadError + ) as raised: + ModelRuntime._download_snapshot( + FASTER_WHISPER_MODEL, + cache=Path(directory), + progress=None, + ) + + self.assertEqual(download.call_count, 3) + self.assertEqual(raised.exception.attempts, 3) + self.assertEqual(raised.exception.reason, "ConnectionError") + self.assertTrue(raised.exception.resumable) + self.assertTrue(raised.exception.retryable) + self.assertNotIn("private detail", str(raised.exception)) + + def test_snapshot_download_does_not_mask_programming_errors(self): + with TemporaryDirectory() as directory: + download = Mock(side_effect=TypeError("implementation bug")) + + with patch( + "huggingface_hub.snapshot_download", + download, + ), self.assertRaisesRegex(TypeError, "implementation bug"): + ModelRuntime._download_snapshot( + FASTER_WHISPER_MODEL, + cache=Path(directory), + progress=None, + ) + + download.assert_called_once() + + def test_snapshot_download_does_not_retry_terminal_http_errors(self): + with TemporaryDirectory() as directory: + response = Mock(status_code=404) + failure = RuntimeError("not found") + failure.response = response + download = Mock(side_effect=failure) + + with patch( + "huggingface_hub.snapshot_download", + download, + ), self.assertRaises(ModelArtifactDownloadError) as raised: + ModelRuntime._download_snapshot( + FASTER_WHISPER_MODEL, + cache=Path(directory), + progress=None, + ) + + download.assert_called_once() + self.assertEqual(raised.exception.reason, "HTTP 404 RuntimeError") + self.assertFalse(raised.exception.retryable) + + def test_incomplete_cached_snapshot_is_resumed_during_prepare(self): + with TemporaryDirectory() as directory: + cache = Path(directory) / "models" + incomplete = Path(directory) / "incomplete" + complete = Path(directory) / "complete" + content = b"verified weights" + weights = complete / FASTER_WHISPER_MODEL.weights_file + weights.parent.mkdir(parents=True) + weights.write_bytes(content) + spec = replace( + FASTER_WHISPER_MODEL, + weights_sha256=hashlib.sha256(content).hexdigest(), + ) + runtime = self.runtime( + directory, + allowed_specs=(spec,), + allow_model_downloads=True, + ) + + with patch( + "huggingface_hub.snapshot_download", + return_value=str(incomplete), + ), patch.object( + ModelRuntime, + "_download_snapshot", + return_value=complete, + ) as resume: + resolved = runtime.resolve_model(spec, download=True) + + self.assertEqual(resolved, complete) + resume.assert_called_once_with(spec, cache=cache, progress=None) + def test_runtime_rejects_specs_not_declared_by_enabled_capabilities(self): with TemporaryDirectory() as directory: runtime = self.runtime(directory) From 37f3a84f03dcfc9022d723bac91d970352c3c1e3 Mon Sep 17 00:00:00 2001 From: Talha Amjad Date: Fri, 31 Jul 2026 17:53:45 +0500 Subject: [PATCH 02/18] feat(api): add explicit local network sharing (#39) --- Dockerfile | 3 +- INSTALLATION_GUIDE.md | 39 ++++++++++-- README.md | 5 ++ src/vidxp/api.py | 8 +++ src/vidxp/api_cli.py | 94 +++++++++++++++++++++++++++ src/vidxp/api_middleware.py | 2 +- src/vidxp/cli_commands/runtime.py | 29 ++++++++- src/vidxp/network_share.py | 102 ++++++++++++++++++++++++++++++ src/vidxp/settings.py | 5 +- tests/test_api.py | 3 + tests/test_api_cli.py | 82 +++++++++++++++++++++++- tests/test_cli.py | 29 ++++++++- tests/test_network_share.py | 32 ++++++++++ 13 files changed, 418 insertions(+), 15 deletions(-) create mode 100644 src/vidxp/network_share.py create mode 100644 tests/test_network_share.py diff --git a/Dockerfile b/Dockerfile index 75c0d46..b27524f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ENV PATH="/opt/vidxp/bin:${PATH}" \ PYTHONDONTWRITEBYTECODE=1 \ - PYTHONUNBUFFERED=1 + PYTHONUNBUFFERED=1 \ + VIDXP_HTTP_PORT=8000 USER vidxp WORKDIR /var/lib/vidxp diff --git a/INSTALLATION_GUIDE.md b/INSTALLATION_GUIDE.md index d8ddca4..f0f50c7 100644 --- a/INSTALLATION_GUIDE.md +++ b/INSTALLATION_GUIDE.md @@ -179,10 +179,19 @@ vidxp doctor --modalities dialogue,actor ### 5. Start the selected surface - CLI: `vidxp --help` -- Browser UI: `vidxp ui` -- Local HTTP API and remote MCP: `vidxp-api` +- Loopback browser UI: `vidxp ui` +- LAN-shared unauthenticated browser UI: `vidxp ui --share` +- Local HTTP API and MCP: `vidxp-api` +- LAN-shared authenticated HTTP API and MCP: `vidxp-api --share` - Local stdio MCP: `vidxp-mcp` +The browser UI binds to loopback unless `--share` is present. In share mode, +VidXP gives Streamlit an explicit wildcard bind and Streamlit prints both the +Local and Network URLs. The UI has no authentication, so share it only on a +trusted network. +VidXP suppresses Streamlit's first-run email prompt and disables Streamlit +usage-statistics collection for this managed launch. + ## First CLI index ```bash @@ -263,15 +272,31 @@ Install `local-worker,server`, prepare models, then run: vidxp-api ``` +The default is reachable only from the same machine. To deliberately share it +on the machine's detected LAN address, run `vidxp-api --share`. Share mode: + +- generates and then reuses an app-owned bearer token; +- binds Uvicorn to the detected LAN address; +- configures the HTTP and MCP Host-header policies for that address; and +- prints the exact health URL, Streamable HTTP MCP URL, and bearer token. + +The managed token is stored as `api-share-token` in VidXP's platform-native +configuration directory. Share mode uses plain HTTP and is intended for a +trusted local network; use the supported reverse-proxy deployment when TLS is +required. + The unauthenticated local default is deliberately loopback-only: | Endpoint | Purpose | |---|---| -| `http://127.0.0.1:8000/docs` | Interactive OpenAPI | -| `http://127.0.0.1:8000/openapi.json` | Machine-readable contract | -| `http://127.0.0.1:8000/health` | Process liveness | -| `http://127.0.0.1:8000/ready` | Aggregate runtime readiness | -| `http://127.0.0.1:8000/mcp` | Streamable HTTP MCP | +| `http://127.0.0.1:32191/docs` | Interactive OpenAPI | +| `http://127.0.0.1:32191/openapi.json` | Machine-readable contract | +| `http://127.0.0.1:32191/health` | Process liveness | +| `http://127.0.0.1:32191/ready` | Aggregate runtime readiness | +| `http://127.0.0.1:32191/mcp` | Streamable HTTP MCP | + +Native installs default to port `32191` to avoid the heavily reused development +port `8000`. Use `vidxp-api --port ` when a specific port is required. Do not bind an unauthenticated API to a non-loopback address. Public deployments require static bearer or OIDC authentication and should use the diff --git a/README.md b/README.md index fff63e4..401136f 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,11 @@ uv tool install --python 3.14 --torch-backend cpu \ vidxp ui ``` +`vidxp ui` binds to loopback by default. Use `vidxp ui --share` only when you +intend to expose the unauthenticated browser interface on the local network. +Streamlit prints its Local and Network URLs when it starts. VidXP disables +Streamlit's first-run email prompt and usage-statistics collection. + If the `vidxp` command is not found, run `uv tool update-shell` once and reopen the terminal. diff --git a/src/vidxp/api.py b/src/vidxp/api.py index 8431d5c..7f4d86d 100644 --- a/src/vidxp/api.py +++ b/src/vidxp/api.py @@ -91,6 +91,14 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]: def health() -> HealthResponse: return HealthResponse() + @app.get( + "/favicon.ico", + include_in_schema=False, + status_code=204, + ) + def favicon() -> Response: + return Response(status_code=204) + @app.get( "/ready", response_model=ReadinessResponse, diff --git a/src/vidxp/api_cli.py b/src/vidxp/api_cli.py index 4b49c32..e482041 100644 --- a/src/vidxp/api_cli.py +++ b/src/vidxp/api_cli.py @@ -4,6 +4,21 @@ from collections.abc import Sequence from pathlib import Path +from vidxp.network_share import ( + load_or_create_api_share_token, + primary_lan_address, +) + + +def _port(value: str) -> int: + try: + port = int(value) + except ValueError as exc: + raise argparse.ArgumentTypeError("port must be an integer") from exc + if not 1 <= port <= 65535: + raise argparse.ArgumentTypeError("port must be between 1 and 65535") + return port + def _arguments(values: Sequence[str] | None = None) -> argparse.Namespace: parser = argparse.ArgumentParser( @@ -17,9 +32,65 @@ def _arguments(values: Sequence[str] | None = None) -> argparse.Namespace: type=Path, help="Store VidXP models and the default repository here.", ) + parser.add_argument( + "--port", + type=_port, + metavar="PORT", + help="Listen on this port instead of the VidXP default.", + ) + parser.add_argument( + "--share", + action="store_true", + help=( + "Share the API and MCP on this machine's LAN address using an " + "app-managed bearer token." + ), + ) return parser.parse_args(values) +def _shared_settings(settings, *, host: str, token: str): + from vidxp.settings import HttpAuthMode, VidXPSettings + + if settings.http_auth_mode not in {HttpAuthMode.none, HttpAuthMode.static}: + raise ValueError( + "--share supports the app-managed static bearer mode, not OIDC." + ) + active_token = ( + settings.http_static_bearer_token.get_secret_value() + if settings.http_auth_mode == HttpAuthMode.static + and settings.http_static_bearer_token is not None + else token + ) + payload = settings.model_dump(mode="python") + payload.update( + { + "http_bind_host": host, + "http_auth_mode": HttpAuthMode.static, + "http_static_bearer_token": active_token, + "http_trusted_hosts": tuple( + dict.fromkeys((*settings.http_trusted_hosts, host)) + ), + "mcp_allowed_hosts": tuple( + dict.fromkeys((*settings.mcp_allowed_hosts, f"{host}:*")) + ), + } + ) + return VidXPSettings.model_validate(payload), active_token + + +def _print_share_details(settings, token: str) -> None: + origin = f"http://{settings.http_bind_host}:{settings.http_port}" + print("VidXP network sharing is enabled.", flush=True) + print(f"Health: {origin}/health", flush=True) + print(f"MCP: {origin}/mcp", flush=True) + print(f"Bearer token: {token}", flush=True) + print( + "Keep this token private. LAN traffic uses HTTP and is not encrypted.", + flush=True, + ) + + def main(arguments: Sequence[str] | None = None) -> None: options = _arguments(arguments) @@ -33,7 +104,30 @@ def main(arguments: Sequence[str] | None = None) -> None: if options.data_dir is not None else VidXPSettings() ) + if options.port is not None: + payload = settings.model_dump(mode="python") + payload["http_port"] = options.port + settings = VidXPSettings.model_validate(payload) + share_token = None + if options.share: + if settings.http_auth_mode.value not in {"none", "static"}: + raise ValueError( + "--share supports the app-managed static bearer mode, not OIDC." + ) + configured_token = ( + settings.http_static_bearer_token.get_secret_value() + if settings.http_auth_mode.value == "static" + and settings.http_static_bearer_token is not None + else None + ) + settings, share_token = _shared_settings( + settings, + host=primary_lan_address(), + token=configured_token or load_or_create_api_share_token(), + ) settings.validate_http_server() + if share_token is not None: + _print_share_details(settings, share_token) uvicorn.run( create_app(settings), host=settings.http_bind_host, diff --git a/src/vidxp/api_middleware.py b/src/vidxp/api_middleware.py index c13ae4d..9ae5175 100644 --- a/src/vidxp/api_middleware.py +++ b/src/vidxp/api_middleware.py @@ -16,7 +16,7 @@ from vidxp.authentication import Authenticator -PUBLIC_HTTP_PATHS = frozenset({"/health", "/ready"}) +PUBLIC_HTTP_PATHS = frozenset({"/favicon.ico", "/health", "/ready"}) UPLOAD_PATH = "/api/v1/media" diff --git a/src/vidxp/cli_commands/runtime.py b/src/vidxp/cli_commands/runtime.py index 560827b..49503b4 100644 --- a/src/vidxp/cli_commands/runtime.py +++ b/src/vidxp/cli_commands/runtime.py @@ -33,6 +33,7 @@ media_runtime_config_path, save_media_runtime_configuration, ) +from vidxp.network_share import is_loopback_host def _format_bytes(size: int) -> str: @@ -513,6 +514,16 @@ def ui( help="Streamlit server port.", ), ] = None, + share: Annotated[ + bool, + typer.Option( + "--share", + help=( + "Share the unauthenticated browser interface on this " + "machine's LAN address." + ), + ), + ] = False, ) -> None: """Launch Streamlit with the selected repository configuration.""" @@ -537,11 +548,23 @@ def ui( ) from exc raise - streamlit_arguments = [] - if host is not None: - streamlit_arguments.append(f"--server.address={host}") + if share and host is not None: + raise typer.BadParameter("--share cannot be combined with --host.") + active_host = "0.0.0.0" if share else (host or "127.0.0.1") + streamlit_arguments = [ + "--server.showEmailPrompt=false", + "--browser.gatherUsageStats=false", + f"--server.address={active_host}", + ] if port is not None: streamlit_arguments.append(f"--server.port={port}") + if share or not is_loopback_host(active_host): + typer.secho( + "WARNING: The browser interface has no authentication and is " + "reachable from the network.", + fg=typer.colors.YELLOW, + err=True, + ) if show_progress: emit_progress("Starting the browser interface...") try: diff --git a/src/vidxp/network_share.py b/src/vidxp/network_share.py new file mode 100644 index 0000000..0cad6cc --- /dev/null +++ b/src/vidxp/network_share.py @@ -0,0 +1,102 @@ +from __future__ import annotations + +import ipaddress +import os +import socket +from pathlib import Path +from secrets import token_urlsafe + +from vidxp.app_paths import default_config_directory + + +API_SHARE_TOKEN_FILE = "api-share-token" + + +def _usable_ipv4(value: str) -> bool: + try: + address = ipaddress.ip_address(value) + except ValueError: + return False + return ( + address.version == 4 + and not address.is_loopback + and not address.is_unspecified + and not address.is_link_local + ) + + +def primary_lan_address() -> str: + """Resolve the IPv4 address selected by the host's default route.""" + + try: + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe: + # UDP connect selects a route without sending application data. + probe.connect(("192.0.2.1", 9)) + candidate = str(probe.getsockname()[0]) + if _usable_ipv4(candidate): + return candidate + except OSError: + pass + + try: + addresses = socket.getaddrinfo( + socket.gethostname(), + None, + family=socket.AF_INET, + type=socket.SOCK_STREAM, + ) + except OSError as exc: + raise RuntimeError( + "VidXP could not determine a LAN address for sharing." + ) from exc + for item in addresses: + candidate = str(item[4][0]) + if _usable_ipv4(candidate): + return candidate + raise RuntimeError("VidXP could not determine a LAN address for sharing.") + + +def api_share_token_path() -> Path: + return default_config_directory() / API_SHARE_TOKEN_FILE + + +def load_or_create_api_share_token(path: Path | None = None) -> str: + """Return the stable app-owned bearer token used by API share mode.""" + + target = path or api_share_token_path() + target.parent.mkdir(parents=True, exist_ok=True, mode=0o700) + try: + existing = target.read_text(encoding="utf-8").strip() + except FileNotFoundError: + existing = "" + if existing: + if len(existing) < 32: + raise RuntimeError(f"The managed VidXP API token is invalid: {target}") + return existing + + token = token_urlsafe(32) + try: + descriptor = os.open( + target, + os.O_WRONLY | os.O_CREAT | os.O_EXCL, + 0o600, + ) + except FileExistsError: + existing = target.read_text(encoding="utf-8").strip() + if len(existing) < 32: + raise RuntimeError(f"The managed VidXP API token is invalid: {target}") + return existing + with os.fdopen(descriptor, "w", encoding="utf-8") as destination: + destination.write(token + "\n") + destination.flush() + os.fsync(destination.fileno()) + return token + + +def is_loopback_host(host: str) -> bool: + if host.lower() == "localhost": + return True + try: + return ipaddress.ip_address(host).is_loopback + except ValueError: + return False diff --git a/src/vidxp/settings.py b/src/vidxp/settings.py index 18a2f1c..8cc0922 100644 --- a/src/vidxp/settings.py +++ b/src/vidxp/settings.py @@ -26,6 +26,9 @@ from vidxp.repository_layout import RepositoryLayout +DEFAULT_HTTP_PORT = 32191 + + class ApplicationMode(StrEnum): local = "local" remote = "remote" @@ -79,7 +82,7 @@ class VidXPSettings(BaseSettings): le=3600, ) http_bind_host: str = Field(default="127.0.0.1", min_length=1) - http_port: int = Field(default=8000, gt=0, le=65535) + http_port: int = Field(default=DEFAULT_HTTP_PORT, gt=0, le=65535) http_auth_mode: HttpAuthMode = HttpAuthMode.none http_static_bearer_token: SecretStr | None = None http_oidc_issuer: str | None = Field( diff --git a/tests/test_api.py b/tests/test_api.py index 658c0e1..097c9b4 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -181,10 +181,13 @@ def test_health_and_minimal_readiness_are_public(self): ) with TestClient(create_app(context=context)) as client: health = client.get("/health") + favicon = client.get("/favicon.ico") ready = client.get("/ready") self.assertEqual(health.status_code, 200) self.assertEqual(health.json(), {"status": "ok"}) + self.assertEqual(favicon.status_code, 204) + self.assertEqual(favicon.content, b"") self.assertEqual(ready.status_code, 200) self.assertEqual(ready.json(), {"ready": True, "status": "ready"}) diff --git a/tests/test_api_cli.py b/tests/test_api_cli.py index 60682e6..996cfb0 100644 --- a/tests/test_api_cli.py +++ b/tests/test_api_cli.py @@ -1,8 +1,10 @@ import unittest from contextlib import redirect_stdout from io import StringIO +from unittest.mock import patch -from vidxp.api_cli import main +from vidxp.api_cli import _shared_settings, main +from vidxp.settings import HttpAuthMode, VidXPSettings class ApiCliTests(unittest.TestCase): @@ -14,6 +16,84 @@ def test_help_exits_without_starting_the_service(self): self.assertEqual(caught.exception.code, 0) self.assertIn("VIDXP_* environment variables", output.getvalue()) + self.assertIn("--port", output.getvalue()) + self.assertIn("--share", output.getvalue()) + + def test_share_mode_configures_managed_static_server(self): + settings, token = _shared_settings( + VidXPSettings(), + host="192.168.100.131", + token="x" * 43, + ) + + self.assertEqual(settings.http_bind_host, "192.168.100.131") + self.assertEqual(settings.http_auth_mode, HttpAuthMode.static) + self.assertEqual(token, "x" * 43) + self.assertIn("192.168.100.131", settings.http_trusted_hosts) + self.assertIn("192.168.100.131:*", settings.mcp_allowed_hosts) + settings.validate_http_server() + + def test_share_mode_reuses_an_explicit_static_token(self): + settings, token = _shared_settings( + VidXPSettings( + http_auth_mode=HttpAuthMode.static, + http_static_bearer_token="configured-token-1234567890123456", + ), + host="192.168.100.131", + token="managed-token-123456789012345678", + ) + + self.assertEqual(token, "configured-token-1234567890123456") + + def test_main_shares_on_the_detected_address(self): + import uvicorn + from vidxp import api + + output = StringIO() + with ( + patch.object(uvicorn, "run") as run, + patch.object( + api, + "create_app", + ) as create_app, + patch( + "vidxp.api_cli.primary_lan_address", + return_value="192.168.100.131", + ), + patch( + "vidxp.api_cli.load_or_create_api_share_token", + return_value="x" * 43, + ), + redirect_stdout(output), + ): + main(["--share"]) + + run.assert_called_once_with( + create_app.return_value, + host="192.168.100.131", + port=32191, + ) + self.assertIn( + "MCP: http://192.168.100.131:32191/mcp", + output.getvalue(), + ) + self.assertIn("Bearer token:", output.getvalue()) + + def test_main_accepts_an_explicit_port(self): + import uvicorn + from vidxp import api + + with ( + patch.object(uvicorn, "run") as run, + patch.object(api, "create_app") as create_app, + ): + main(["--port", "32192"]) + + run.assert_called_once_with( + create_app.return_value, + host="127.0.0.1", + port=32192, + ) if __name__ == "__main__": diff --git a/tests/test_cli.py b/tests/test_cli.py index b819fe9..81fb3d6 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -334,12 +334,39 @@ def test_ui_shutdown_stops_its_local_worker(self): with patch( "vidxp.frontend.main", side_effect=SystemExit(0), - ): + ) as frontend: result = self.invoke(["ui"]) self.assertEqual(result.exit_code, 0, result.output) + self.assertIn( + "--server.address=127.0.0.1", + frontend.call_args.args[0], + ) + self.assertIn( + "--server.showEmailPrompt=false", + frontend.call_args.args[0], + ) + self.assertIn( + "--browser.gatherUsageStats=false", + frontend.call_args.args[0], + ) self.jobs.stop_worker.assert_called_once_with() + def test_ui_share_uses_streamlit_wildcard_bind_and_warns(self): + with patch( + "vidxp.frontend.main", + side_effect=SystemExit(0), + ) as frontend: + result = self.invoke(["ui", "--share"]) + + self.assertEqual(result.exit_code, 0, result.output) + arguments = frontend.call_args.args[0] + self.assertIn("--server.address=0.0.0.0", arguments) + self.assertIn("--server.showEmailPrompt=false", arguments) + self.assertIn("--browser.gatherUsageStats=false", arguments) + self.assertIn("has no authentication", result.output) + self.assertNotIn("Browser UI:", result.output) + def test_snippet_rejects_an_inverted_time_range_before_submission(self): result = self.invoke( ["artifacts", "snippet", MEDIA_ID, "3", "2"] diff --git a/tests/test_network_share.py b/tests/test_network_share.py new file mode 100644 index 0000000..4248451 --- /dev/null +++ b/tests/test_network_share.py @@ -0,0 +1,32 @@ +import os +import unittest +from pathlib import Path +from tempfile import TemporaryDirectory + +from vidxp.network_share import load_or_create_api_share_token + + +class NetworkShareTests(unittest.TestCase): + def test_managed_token_is_stable_and_private(self): + with TemporaryDirectory() as directory: + path = Path(directory) / "api-share-token" + + first = load_or_create_api_share_token(path) + second = load_or_create_api_share_token(path) + + self.assertEqual(first, second) + self.assertGreaterEqual(len(first), 32) + if os.name != "nt": + self.assertEqual(path.stat().st_mode & 0o777, 0o600) + + def test_invalid_existing_token_is_rejected(self): + with TemporaryDirectory() as directory: + path = Path(directory) / "api-share-token" + path.write_text("short\n", encoding="utf-8") + + with self.assertRaisesRegex(RuntimeError, "token is invalid"): + load_or_create_api_share_token(path) + + +if __name__ == "__main__": + unittest.main() From 87b66195a1c609c64b9eb8bb2c7472775628f226 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 18:02:41 +0500 Subject: [PATCH 03/18] chore(release): prepare main (#38) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ desktop/runtime-manifest.json | 2 +- pyproject.toml | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index da42fbd..b3b5684 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "0.3.0", + ".": "0.4.0-b", "desktop": "0.3.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 273dfd2..885fbe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # CHANGELOG +## [0.4.0-b](https://github.com/grayhatdevelopers/vidxp/compare/v0.3.0...v0.4.0-b) (2026-07-31) + + +### Features + +* **api:** add explicit local network sharing ([#39](https://github.com/grayhatdevelopers/vidxp/issues/39)) ([37f3a84](https://github.com/grayhatdevelopers/vidxp/commit/37f3a84f03dcfc9022d723bac91d970352c3c1e3)) + + +### Bug Fixes + +* **models:** harden preparation downloads ([62cb7b1](https://github.com/grayhatdevelopers/vidxp/commit/62cb7b1b2dbdfbc968435bb76101b3199a88c1f8)) +* **models:** harden preparation downloads ([69c5e51](https://github.com/grayhatdevelopers/vidxp/commit/69c5e51588c496699fe3a49b8c61a374f1058a98)) + ## [0.3.0](https://github.com/grayhatdevelopers/vidxp/compare/v0.2.0...v0.3.0) (2026-07-31) diff --git a/desktop/runtime-manifest.json b/desktop/runtime-manifest.json index 0478be3..9fb13ee 100644 --- a/desktop/runtime-manifest.json +++ b/desktop/runtime-manifest.json @@ -2,7 +2,7 @@ "schema_version": 1, "desktop_version": "0.3.0", "package_name": "vidxp", - "package_version": "0.3.0", + "package_version": "0.4.0-b", "dependency_index": "https://pypi.org/simple", "dependency_constraints_sha256": "d3ed16906841952017f112903356bfe433a8515a37aa3f56e4af4dfa1f985835", "python_version": "3.14.6", diff --git a/pyproject.toml b/pyproject.toml index e2e1f3d..c99385a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "vidxp" -version = "0.3.0" +version = "0.4.0-b" authors = [ { name = "Muhammad Haroon" }, { name = "Talha Momin" }, From 44ed2341d9e3aca071b15fbfecedb2cc08376e4e Mon Sep 17 00:00:00 2001 From: Talha Amjad Date: Fri, 31 Jul 2026 18:26:43 +0500 Subject: [PATCH 04/18] fix(release): restore beta release creation (#40) --- .release-please-manifest.json | 2 +- desktop/VERSION | 2 +- desktop/package-lock.json | 4 ++-- desktop/package.json | 2 +- desktop/runtime-manifest.json | 2 +- desktop/src-tauri/Cargo.lock | 2 +- desktop/src-tauri/Cargo.toml | 2 +- desktop/src-tauri/tauri.conf.json | 2 +- release-please-config.json | 4 +--- release-please-config.stable.json | 4 +--- tests/test_packaging.py | 6 +++++- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b3b5684..f9e5f5f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { ".": "0.4.0-b", - "desktop": "0.3.0" + "desktop": "0.4.0-b" } diff --git a/desktop/VERSION b/desktop/VERSION index 0d91a54..8d1a53c 100644 --- a/desktop/VERSION +++ b/desktop/VERSION @@ -1 +1 @@ -0.3.0 +0.4.0-b diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 962ca19..b039307 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "vidxp-desktop", - "version": "0.3.0", + "version": "0.4.0-b", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vidxp-desktop", - "version": "0.3.0", + "version": "0.4.0-b", "devDependencies": { "@tauri-apps/cli": "2.11.4" } diff --git a/desktop/package.json b/desktop/package.json index ec701f5..d300dab 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,7 +1,7 @@ { "name": "vidxp-desktop", "private": true, - "version": "0.3.0", + "version": "0.4.0-b", "type": "module", "scripts": { "tauri": "tauri", diff --git a/desktop/runtime-manifest.json b/desktop/runtime-manifest.json index 9fb13ee..9951b4c 100644 --- a/desktop/runtime-manifest.json +++ b/desktop/runtime-manifest.json @@ -1,6 +1,6 @@ { "schema_version": 1, - "desktop_version": "0.3.0", + "desktop_version": "0.4.0-b", "package_name": "vidxp", "package_version": "0.4.0-b", "dependency_index": "https://pypi.org/simple", diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index 1ca8142..06cf314 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -4373,7 +4373,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vidxp-desktop" -version = "0.3.0" # x-release-please-version +version = "0.4.0-b" # x-release-please-version dependencies = [ "atomic-write-file", "hex", diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index d6f17a7..0280c49 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vidxp-desktop" -version = "0.3.0" # x-release-please-version +version = "0.4.0-b" # x-release-please-version description = "The VidXP desktop launcher and local runtime supervisor" edition = "2024" rust-version = "1.97" diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index c270063..663588c 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "VidXP", - "version": "0.3.0", + "version": "0.4.0-b", "identifier": "dev.grayhat.vidxp", "build": { "frontendDist": "../web" diff --git a/release-please-config.json b/release-please-config.json index fc92393..e74a9f2 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -5,7 +5,6 @@ "bump-patch-for-minor-pre-major": false, "draft": true, "force-tag-creation": true, - "group-pull-request-title-pattern": "chore(release): prepare ${branch}", "include-v-in-tag": true, "prerelease": true, "separate-pull-requests": false, @@ -15,14 +14,13 @@ "type": "linked-versions", "groupName": "VidXP", "components": [ - "vidxp", + "", "desktop" ] } ], "packages": { ".": { - "component": "vidxp", "exclude-paths": [ "desktop" ], diff --git a/release-please-config.stable.json b/release-please-config.stable.json index dfb9d38..ae6f200 100644 --- a/release-please-config.stable.json +++ b/release-please-config.stable.json @@ -5,7 +5,6 @@ "bump-patch-for-minor-pre-major": false, "draft": true, "force-tag-creation": true, - "group-pull-request-title-pattern": "chore(release): prepare ${branch}", "include-v-in-tag": true, "separate-pull-requests": false, "plugins": [ @@ -13,14 +12,13 @@ "type": "linked-versions", "groupName": "VidXP", "components": [ - "vidxp", + "", "desktop" ] } ], "packages": { ".": { - "component": "vidxp", "exclude-paths": [ "desktop" ], diff --git a/tests/test_packaging.py b/tests/test_packaging.py index bdc4c9f..ba7c0ce 100644 --- a/tests/test_packaging.py +++ b/tests/test_packaging.py @@ -337,15 +337,19 @@ def test_release_please_preserves_desktop_manifests_and_links_versions(self): config = json.loads( (ROOT / filename).read_text(encoding="utf-8") ) + self.assertNotIn("group-pull-request-title-pattern", config) linked_versions = [ plugin for plugin in config["plugins"] if plugin["type"] == "linked-versions" ] self.assertEqual(len(linked_versions), 1, filename) + root_package = config["packages"]["."] + self.assertFalse(root_package["include-component-in-tag"]) + self.assertNotIn("component", root_package) self.assertEqual( set(linked_versions[0]["components"]), - {"vidxp", "desktop"}, + {"", "desktop"}, filename, ) From 62af88e70e6dd1c2ac629fdaa7584d8679740ef0 Mon Sep 17 00:00:00 2001 From: Talha Amjad Date: Sat, 1 Aug 2026 12:02:55 +0500 Subject: [PATCH 05/18] feat: add capability-aware agent preflight (#41) * feat: add capability-aware agent preflight * fix: share index preflight across job callers --- README.md | 6 + src/vidxp/api_routes/jobs.py | 1 - src/vidxp/api_routes/platform.py | 23 ++- src/vidxp/application.py | 3 + src/vidxp/application_boundary.py | 10 +- src/vidxp/application_models.py | 58 +++++++ src/vidxp/capabilities/actor/definition.py | 7 + src/vidxp/capabilities/contracts.py | 51 +++++- src/vidxp/capabilities/dialogue/definition.py | 2 + src/vidxp/capabilities/registry.py | 25 ++- src/vidxp/capabilities/scene/definition.py | 2 + src/vidxp/capability_service.py | 2 + src/vidxp/cli_commands/index.py | 1 - src/vidxp/composition.py | 34 +++- src/vidxp/control_plane.py | 147 ++++++++++++++++- src/vidxp/frontend.py | 7 +- src/vidxp/infrastructure/local_index.py | 10 ++ src/vidxp/job_service.py | 4 + src/vidxp/mcp.py | 34 +++- src/vidxp/read_job_planner.py | 140 +++++++++++++--- tests/test_api.py | 29 +++- tests/test_control_plane.py | 152 ++++++++++++++++++ tests/test_frontend.py | 2 +- tests/test_job_contracts.py | 31 ++++ tests/test_mcp.py | 41 ++++- tests/test_read_job_planner.py | 67 +++++++- 26 files changed, 834 insertions(+), 55 deletions(-) create mode 100644 tests/test_control_plane.py diff --git a/README.md b/README.md index 401136f..f314c36 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,12 @@ scenes, ask questions about a library, and create clips or actor overlays. Local agents can connect over stdio; remote agents can connect to a self-hosted VidXP server. +Agents can call `get_workspace` before acting to inspect registered media, +active-index coverage, model readiness, and the searchable, queryable, +inspectable, or renderable roles available for each video. Invalid capability +or media selections are rejected before a durable job is queued and include an +actionable next step. + - [Python, HTTP, and MCP installation](INSTALLATION_GUIDE.md) - [Optional capability packages](INSTALLATION_GUIDE.md#optional-dependency-extras) - [Coolify server setup](docs/deployment/coolify.md) diff --git a/src/vidxp/api_routes/jobs.py b/src/vidxp/api_routes/jobs.py index 72e85c6..22cc186 100644 --- a/src/vidxp/api_routes/jobs.py +++ b/src/vidxp/api_routes/jobs.py @@ -49,7 +49,6 @@ def submit_index( actor: Annotated[Principal, Depends(write_principal)], idempotency_key: HttpIdempotencyKey, ) -> Job: - service.application.require_models(command.modalities) return accepted( response, service.jobs.submit_index( diff --git a/src/vidxp/api_routes/platform.py b/src/vidxp/api_routes/platform.py index 59fc8fa..1e3be99 100644 --- a/src/vidxp/api_routes/platform.py +++ b/src/vidxp/api_routes/platform.py @@ -1,12 +1,14 @@ from typing import Annotated -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Query from vidxp.api_routes.dependencies import context, read_principal from vidxp.application_models import ( CapabilityInfo, CapabilityList, + ListMediaCommand, RuntimeReadiness, + WorkspaceOverview, ) from vidxp.composition import HttpApplicationContext @@ -17,6 +19,25 @@ ) +@router.get( + "/workspace", + response_model=WorkspaceOverview, + operation_id="getWorkspace", + summary="Inspect media and usable capability roles", +) +def workspace( + service: Annotated[HttpApplicationContext, Depends(context)], + page_size: Annotated[int, Query(gt=0, le=100)] = 50, + cursor: Annotated[ + str | None, + Query(min_length=1, max_length=512), + ] = None, +) -> WorkspaceOverview: + return service.application.workspace( + ListMediaCommand(page_size=page_size, cursor=cursor) + ) + + @router.get( "/runtime/readiness", response_model=RuntimeReadiness, diff --git a/src/vidxp/application.py b/src/vidxp/application.py index dd25eaf..56bbb1e 100644 --- a/src/vidxp/application.py +++ b/src/vidxp/application.py @@ -56,6 +56,7 @@ from vidxp.core.contracts import ( IndexConfig, ) +from vidxp.core.snapshots import IndexSnapshot from vidxp.execution import ExecutionContext, execution_context from vidxp.ports import IndexBackend, ModelRuntimePort, QueryModelPort from vidxp.query_service import GroundedQueryService @@ -89,6 +90,7 @@ def __init__( media: MediaService, artifacts: ArtifactService, index_status: Callable[[], dict[str, Any] | None], + active_snapshot: Callable[[], IndexSnapshot | None] | None = None, completed_upload_importer: Callable[[str], MediaAsset] | None = None, query_model: QueryModelPort | None = None, ) -> None: @@ -104,6 +106,7 @@ def __init__( artifacts=artifacts, index_status=index_status, model_cache=settings.model_cache, + active_snapshot=active_snapshot, ) self.settings = settings diff --git a/src/vidxp/application_boundary.py b/src/vidxp/application_boundary.py index 33caa4a..51c0ea1 100644 --- a/src/vidxp/application_boundary.py +++ b/src/vidxp/application_boundary.py @@ -43,6 +43,8 @@ MediaIdempotencyConflictError, MediaImportNotAllowedError, ) + + def _validation_details( exc: ValidationError, ) -> list[dict[str, JsonValue]]: @@ -113,7 +115,13 @@ def wrapped(*args: Any, **kwargs: Any) -> Any: errors=_validation_details(exc), ) from exc except CapabilityRequestError as exc: - raise InvalidRequestError() from exc + error = { + "type": "capability_request", + "location": [exc.field], + "message": str(exc), + **exc.details(), + } + raise InvalidRequestError(errors=[error]) from exc except InvalidMediaError as exc: raise ApplicationError( "media_invalid", diff --git a/src/vidxp/application_models.py b/src/vidxp/application_models.py index 807a0a0..be6ab3e 100644 --- a/src/vidxp/application_models.py +++ b/src/vidxp/application_models.py @@ -291,12 +291,29 @@ class CapabilityOperationInfo(ApplicationModel): output_schema: dict[str, JsonValue] +class CapabilityRole(StrEnum): + searchable = "searchable" + queryable = "queryable" + inspectable = "inspectable" + renderable = "renderable" + + +class CapabilityIdentityMode(StrEnum): + not_applicable = "not_applicable" + anonymous_clusters = "anonymous_clusters" + registered_entities = "registered_entities" + + class CapabilitySummary(ApplicationModel): name: str = Field(min_length=1) description: str = Field(min_length=1) install_extra: str = Field(min_length=1) supports_indexing: bool prepares_models: bool + roles: tuple[CapabilityRole, ...] = () + identity_mode: CapabilityIdentityMode = ( + CapabilityIdentityMode.not_applicable + ) provenance: CapabilityProvenance | None = None @@ -588,6 +605,47 @@ def _validate_media_id_window(self) -> "IndexStatusSummary": return self +class WorkspaceCapability(CapabilitySummary): + models_ready: bool | None = Field( + default=None, + description=( + "Whether required model artifacts are prepared. Null means the " + "capability does not prepare model artifacts." + ), + ) + + +class WorkspaceMediaCapability(ApplicationModel): + name: Identifier + indexed: bool + record_count: NonNegativeInt | None = None + roles: tuple[CapabilityRole, ...] = Field( + default=(), + description="Capability roles currently usable for this media item.", + ) + identity_mode: CapabilityIdentityMode = ( + CapabilityIdentityMode.not_applicable + ) + + +class WorkspaceMedia(ApplicationModel): + media_id: MediaId + original_filename: str = Field(min_length=1) + duration_seconds: float = Field(gt=0) + state: MediaState + in_active_snapshot: bool + capabilities: tuple[WorkspaceMediaCapability, ...] = () + + +class WorkspaceOverview(ApplicationModel): + capabilities: tuple[WorkspaceCapability, ...] = () + media: tuple[WorkspaceMedia, ...] = () + media_total: NonNegativeInt + next_cursor: str | None = None + index: IndexStatus + next_actions: tuple[str, ...] = () + + class FusionProfile(StrEnum): reciprocal_rank = "rrf_v1" diff --git a/src/vidxp/capabilities/actor/definition.py b/src/vidxp/capabilities/actor/definition.py index f8e1e1c..c9ba378 100644 --- a/src/vidxp/capabilities/actor/definition.py +++ b/src/vidxp/capabilities/actor/definition.py @@ -2,6 +2,7 @@ from typing import Any, Mapping +from vidxp.application_models import CapabilityIdentityMode, CapabilityRole from vidxp.capabilities.actor.config import ActorConfig, actor_config from vidxp.capabilities.actor.indexing import VISUAL_PROCESSOR from vidxp.capabilities.actor.models import ( @@ -77,6 +78,12 @@ def model_manifest( index_stage="visual_indexing", execution_group="visual", prepares_models=True, + roles=( + CapabilityRole.queryable, + CapabilityRole.inspectable, + CapabilityRole.renderable, + ), + identity_mode=CapabilityIdentityMode.anonymous_clusters, model_specs=(YUNET_MODEL, SFACE_MODEL), operations={ "cluster": OperationDefinition( diff --git a/src/vidxp/capabilities/contracts.py b/src/vidxp/capabilities/contracts.py index 9448077..ddb9ef4 100644 --- a/src/vidxp/capabilities/contracts.py +++ b/src/vidxp/capabilities/contracts.py @@ -18,7 +18,11 @@ from vidxp.core.indexing_common import ProgressCallback from vidxp.model_contracts import ArtifactSpec, ModelSpec from vidxp.ports import IndexReader, ModelRuntimePort -from vidxp.application_models import CapabilityProvenance +from vidxp.application_models import ( + CapabilityIdentityMode, + CapabilityProvenance, + CapabilityRole, +) CAPABILITY_CONTRACT_VERSION = 1 @@ -224,6 +228,10 @@ class CapabilityDefinition(_ContractModel): index_stage: str | None = None execution_group: str | None = None operations: Mapping[str, OperationDefinition] = Field(default_factory=dict) + roles: tuple[CapabilityRole, ...] = () + identity_mode: CapabilityIdentityMode = ( + CapabilityIdentityMode.not_applicable + ) model_specs: tuple[ModelSpec | ArtifactSpec, ...] = () prepares_models: bool = False @@ -250,6 +258,16 @@ def _freeze_operations( ) -> Mapping[str, OperationDefinition]: return MappingProxyType(dict(value)) + @field_validator("roles") + @classmethod + def _unique_roles( + cls, + value: tuple[CapabilityRole, ...], + ) -> tuple[CapabilityRole, ...]: + if len(value) != len(set(value)): + raise ValueError("Capability roles must be unique.") + return value + @model_validator(mode="after") def _require_complete_metadata(self) -> CapabilityDefinition: indexing_fields = ( @@ -341,6 +359,37 @@ def capability_install_hint(name: str) -> str: class CapabilityRequestError(ValueError): """Expected invalid capability selection or options.""" + def __init__( + self, + message: str, + *, + field: str = "capabilities", + reason: str = "capability_request_invalid", + requested: tuple[str, ...] = (), + available: tuple[str, ...] = (), + indexed: tuple[str, ...] = (), + next_action: str | None = None, + ) -> None: + self.field = field + self.reason = reason + self.requested = requested + self.available = available + self.indexed = indexed + self.next_action = next_action + super().__init__(message) + + def details(self) -> dict[str, Any]: + result: dict[str, Any] = {"reason": self.reason} + if self.requested: + result["requested"] = list(self.requested) + if self.available: + result["available"] = list(self.available) + if self.indexed: + result["indexed"] = list(self.indexed) + if self.next_action is not None: + result["next_action"] = self.next_action + return result + class CapabilityDependencyError(RuntimeError): def __init__( diff --git a/src/vidxp/capabilities/dialogue/definition.py b/src/vidxp/capabilities/dialogue/definition.py index 2d6b1c7..950bd8c 100644 --- a/src/vidxp/capabilities/dialogue/definition.py +++ b/src/vidxp/capabilities/dialogue/definition.py @@ -5,6 +5,7 @@ from packaging.requirements import Requirement from packaging.utils import canonicalize_name +from vidxp.application_models import CapabilityRole from vidxp.capabilities.contracts import ( CapabilityDefinition, CapabilityExecutor, @@ -96,6 +97,7 @@ def model_manifest( index_stage="dialogue_indexing", execution_group="dialogue", prepares_models=True, + roles=(CapabilityRole.searchable, CapabilityRole.queryable), model_specs=(QWEN3_EMBEDDING_MODEL, FASTER_WHISPER_MODEL), operations={ "search": OperationDefinition( diff --git a/src/vidxp/capabilities/registry.py b/src/vidxp/capabilities/registry.py index b18d9e9..5e82f82 100644 --- a/src/vidxp/capabilities/registry.py +++ b/src/vidxp/capabilities/registry.py @@ -187,7 +187,12 @@ def get(self, name: str) -> CapabilityDefinition: available = ", ".join(self.names()) raise CapabilityRequestError( f"Unknown capability {name!r}. " - f"Available capabilities: {available}." + f"Available capabilities: {available}.", + field="modalities", + reason="capability_unknown", + requested=(name,), + available=self.names(), + next_action="Choose a capability returned by get_workspace.", ) from exc def executor(self, name: str) -> CapabilityExecutor: @@ -222,7 +227,13 @@ def executor(self, name: str) -> CapabilityExecutor: def validate_names(self, names: Iterable[str]) -> tuple[str, ...]: selected = tuple(dict.fromkeys(str(name).strip() for name in names)) if not selected: - raise CapabilityRequestError("At least one capability is required.") + raise CapabilityRequestError( + "At least one capability is required.", + field="modalities", + reason="capability_required", + available=self.names(), + next_action="Choose a capability returned by get_workspace.", + ) for name in selected: self.get(name) return selected @@ -249,7 +260,15 @@ def validate_options( if unknown: raise CapabilityRequestError( "Options were supplied for disabled capabilities: " - + ", ".join(unknown) + + ", ".join(unknown), + field="capability_options", + reason="capability_options_disabled", + requested=tuple(unknown), + available=selected, + next_action=( + "Remove options for disabled capabilities or enable those " + "capabilities in modalities." + ), ) return { name: self.get(name) diff --git a/src/vidxp/capabilities/scene/definition.py b/src/vidxp/capabilities/scene/definition.py index aee444c..366ad4e 100644 --- a/src/vidxp/capabilities/scene/definition.py +++ b/src/vidxp/capabilities/scene/definition.py @@ -2,6 +2,7 @@ from typing import Any, Mapping +from vidxp.application_models import CapabilityRole from vidxp.capabilities.contracts import ( CapabilityDefinition, CapabilityExecutor, @@ -59,6 +60,7 @@ def model_manifest( index_stage="visual_indexing", execution_group="visual", prepares_models=True, + roles=(CapabilityRole.searchable, CapabilityRole.queryable), model_specs=(SIGLIP2_MODEL,), operations={ "search": OperationDefinition( diff --git a/src/vidxp/capability_service.py b/src/vidxp/capability_service.py index 9b3844b..7b0c64c 100644 --- a/src/vidxp/capability_service.py +++ b/src/vidxp/capability_service.py @@ -28,6 +28,8 @@ def _summary(self, name: str) -> CapabilitySummary: install_extra=definition.extra, supports_indexing=definition.collection_name is not None, prepares_models=definition.prepares_models, + roles=definition.roles, + identity_mode=definition.identity_mode, provenance=self.registry.provenance(name), ) diff --git a/src/vidxp/cli_commands/index.py b/src/vidxp/cli_commands/index.py index 8d0036a..14eb192 100644 --- a/src/vidxp/cli_commands/index.py +++ b/src/vidxp/cli_commands/index.py @@ -40,7 +40,6 @@ def create_index( not state.quiet and state.output_format == OutputFormat.rich ) selected = tuple(modalities) - state.service.require_models(selected) with IndexProgress(show_progress) as progress: job = state.jobs.submit_index( CreateIndexCommand( diff --git a/src/vidxp/composition.py b/src/vidxp/composition.py index f0c514b..856c2af 100644 --- a/src/vidxp/composition.py +++ b/src/vidxp/composition.py @@ -3,11 +3,16 @@ from dataclasses import dataclass from functools import cached_property from pathlib import Path +from typing import Callable from pydantic import ValidationError from vidxp.application import VidXPApplication -from vidxp.application_models import ApplicationError, ErrorCategory +from vidxp.application_models import ( + ApplicationError, + CreateIndexCommand, + ErrorCategory, +) from vidxp.artifact_service import ArtifactQueryService, ArtifactService from vidxp.authentication import Authenticator, create_authenticator from vidxp.authorization import AuthorizationPolicy @@ -94,7 +99,10 @@ def settings(self) -> VidXPSettings: @cached_property def jobs(self) -> JobService: assert self._settings is not None - return create_job_service(self._settings) + return create_job_service( + self._settings, + index_preflight=self.application.preflight_index, + ) def close(self) -> None: jobs = self.__dict__.get("jobs") @@ -159,12 +167,9 @@ def _server_chroma_url(settings: VidXPSettings) -> str | None: return BUNDLED_CHROMA_SERVER_URL -def _create_control_plane_components( - settings: VidXPSettings, -) -> _ControlPlaneComponents: - settings.layout.ensure_local_directories() +def _capability_registry(settings: VidXPSettings) -> CapabilityRegistry: server_mode = settings.mode == ApplicationMode.server - registry = create_capability_registry( + return create_capability_registry( external=settings.external_capabilities, allowlist=settings.capability_allowlist, platform_runtime_checks=( @@ -183,6 +188,13 @@ def _create_control_plane_components( else None ), ) + + +def _create_control_plane_components( + settings: VidXPSettings, +) -> _ControlPlaneComponents: + settings.layout.ensure_local_directories() + registry = _capability_registry(settings) catalog = ( SQLCatalog( workflow_database_url(settings), @@ -305,6 +317,7 @@ def create_application( media=components.media, artifacts=artifacts, index_status=backend.repository.status, + active_snapshot=components.snapshots.read_active, completed_upload_importer=( upload_service.import_completed if upload_service is not None @@ -319,6 +332,8 @@ def create_job_service( *, catalog: SQLCatalog | None = None, snapshots: LocalSnapshotRepository | None = None, + registry: CapabilityRegistry | None = None, + index_preflight: Callable[[CreateIndexCommand], None] | None = None, include_read_planner: bool = True, ) -> JobService: settings.layout.ensure_local_directories() @@ -332,6 +347,7 @@ def create_job_service( stop_executor = supervisor.stop return JobService( settings=settings, + index_preflight=index_preflight, backend=DBOSJobBackend( system_database_url=( None @@ -353,6 +369,7 @@ def create_job_service( read_planner=( LocalReadJobPlanner( layout=settings.layout, + registry=registry or _capability_registry(settings), index=LocalIndexReader( settings.layout, chroma_server_url=_server_chroma_url(settings), @@ -380,11 +397,14 @@ def create_control_plane_application( ), index_status=components.snapshots.status, model_cache=active_settings.model_cache, + active_snapshot=components.snapshots.read_active, ) jobs = create_job_service( active_settings, catalog=components.catalog, snapshots=components.snapshots, + registry=components.registry, + index_preflight=application.preflight_index, ) uploads = ( RemoteUploadService( diff --git a/src/vidxp/control_plane.py b/src/vidxp/control_plane.py index 991f48c..1897080 100644 --- a/src/vidxp/control_plane.py +++ b/src/vidxp/control_plane.py @@ -7,8 +7,10 @@ from vidxp.application_models import ( Artifact, CapabilityInfo, + CapabilityRole, CapabilitySummary, ComponentReadiness, + CreateIndexCommand, DependencyCheckResult, IndexStatus, InvalidRequestError, @@ -18,11 +20,16 @@ ModelUnavailableError, ResourceNotFoundError, RuntimeReadiness, + WorkspaceCapability, + WorkspaceMedia, + WorkspaceMediaCapability, + WorkspaceOverview, ) from vidxp.artifact_service import ArtifactQueryService from vidxp.capabilities.contracts import CapabilityRequestError from vidxp.capability_service import CapabilityService from vidxp.core.media import QuarantinedMedia +from vidxp.core.snapshots import IndexSnapshot from vidxp.index_state import INDEX_STATUS_SCHEMA from vidxp.media_service import MediaService from vidxp.ports import LocalFileResource @@ -41,12 +48,14 @@ def __init__( artifacts: ArtifactQueryService, index_status: Callable[[], dict | None], model_cache: Path, + active_snapshot: Callable[[], IndexSnapshot | None] | None = None, ) -> None: self.layout = layout self.capabilities = capabilities self.media = media self.artifacts = artifacts self._read_index_status = index_status + self._read_active_snapshot = active_snapshot or (lambda: None) self.model_cache = model_cache @application_boundary @@ -105,6 +114,139 @@ def list_media(self, command: ListMediaCommand) -> MediaPage: except ValueError as exc: raise InvalidRequestError() from exc + @application_boundary + def workspace(self, command: ListMediaCommand) -> WorkspaceOverview: + page = self.list_media(command) + index = self.index_status() + snapshot = self._read_active_snapshot() + capabilities = self.list_capabilities() + readiness = self.model_readiness() + readiness_by_capability = { + capability.name: all( + check.ok + for check in readiness.checks + if check.capability == capability.name + ) + for capability in capabilities + if capability.prepares_models + } + projected_capabilities = tuple( + WorkspaceCapability( + **capability.model_dump(), + models_ready=readiness_by_capability.get(capability.name), + ) + for capability in capabilities + ) + media = tuple( + self._workspace_media( + asset, + capabilities=capabilities, + snapshot=snapshot, + ) + for asset in page.items + ) + indexed_media = ( + frozenset(snapshot.generations) if snapshot is not None else frozenset() + ) + indexed_capabilities = ( + { + name + for generation in snapshot.generations.values() + for name in generation.modalities + } + if snapshot is not None + else set() + ) + active_roles = { + role + for capability in capabilities + if capability.name in indexed_capabilities + for role in capability.roles + } + next_actions = [] + if page.total == 0: + next_actions.append("register_media") + if page.total > len(indexed_media) or any( + item.media_id not in indexed_media for item in page.items + ): + next_actions.append("index_media") + if CapabilityRole.searchable in active_roles: + next_actions.append("find_moments") + if CapabilityRole.queryable in active_roles: + next_actions.append("answer_video") + return WorkspaceOverview( + capabilities=projected_capabilities, + media=media, + media_total=page.total, + next_cursor=page.next_cursor, + index=index, + next_actions=tuple(next_actions), + ) + + @staticmethod + def _workspace_media( + asset: MediaAsset, + *, + capabilities: tuple[CapabilitySummary, ...], + snapshot: IndexSnapshot | None, + ) -> WorkspaceMedia: + generation = ( + snapshot.generations.get(asset.media_id) if snapshot is not None else None + ) + coverage = tuple( + WorkspaceMediaCapability( + name=capability.name, + indexed=( + generation is not None and capability.name in generation.modalities + ), + record_count=( + generation.record_counts.get(capability.name) + if generation is not None + and capability.name in generation.modalities + else None + ), + roles=( + capability.roles + if generation is not None + and capability.name in generation.modalities + else () + ), + identity_mode=capability.identity_mode, + ) + for capability in capabilities + ) + return WorkspaceMedia( + media_id=asset.media_id, + original_filename=asset.original_filename, + duration_seconds=asset.duration_seconds, + state=asset.state, + in_active_snapshot=generation is not None, + capabilities=coverage, + ) + + @application_boundary + def preflight_index(self, command: CreateIndexCommand) -> None: + selected = self.capabilities.registry.validate_names(command.modalities) + indexable = self.capabilities.registry.index_names() + unsupported = tuple(name for name in selected if name not in indexable) + if unsupported: + raise CapabilityRequestError( + "Indexing does not support these capabilities: " + + ", ".join(unsupported) + + ".", + field="modalities", + reason="capability_not_indexable", + requested=unsupported, + available=indexable, + next_action="Choose capabilities returned by get_workspace.", + ) + self.capabilities.registry.validate_options( + selected, + command.capability_options, + ) + self.get_media(command.media_id) + self.require_models(selected) + @application_boundary def open_media_content(self, media_id: str) -> LocalFileResource: return self.media.content(media_id) @@ -186,10 +328,7 @@ def runtime_readiness(self) -> RuntimeReadiness: components = self.control_plane_readiness() models = self.model_readiness() return RuntimeReadiness( - ready=( - all(component.ready for component in components) - and models.ok - ), + ready=(all(component.ready for component in components) and models.ok), runtime=None, components=components, dependencies=models, diff --git a/src/vidxp/frontend.py b/src/vidxp/frontend.py index ce15d84..dffa81c 100644 --- a/src/vidxp/frontend.py +++ b/src/vidxp/frontend.py @@ -43,7 +43,11 @@ def _configured_service( @lru_cache(maxsize=1) def _configured_jobs(settings: VidXPSettings | None = None) -> JobService: - return create_job_service(settings or _settings_from_arguments()) + active_settings = settings or _settings_from_arguments() + return create_job_service( + active_settings, + index_preflight=_configured_service(active_settings).preflight_index, + ) def _settings_from_arguments( @@ -340,7 +344,6 @@ def _run_indexing( media_id = media_ids[0] if len(media_ids) == 1 else None if media_id is None: raise ValueError("Select or import media before indexing.") - service.require_models(modalities) job = _configured_jobs().submit_index( CreateIndexCommand( media_id=media_id, diff --git a/src/vidxp/infrastructure/local_index.py b/src/vidxp/infrastructure/local_index.py index e333d3e..982e419 100644 --- a/src/vidxp/infrastructure/local_index.py +++ b/src/vidxp/infrastructure/local_index.py @@ -20,6 +20,7 @@ from vidxp.core.indexing_common import ProgressCallback from vidxp.core.manifest import MANIFEST_FILE, ManifestStore from vidxp.core.runner import index_video +from vidxp.core.snapshots import IndexSnapshot from vidxp.core.storage import ( ChromaClientFactory, IndexStorage, @@ -89,6 +90,15 @@ def active_config( config, _snapshot = self.repository.active_config(device=device) return config + def active_snapshot( + self, + index_directory: Path, + *, + device: str, + ) -> tuple[IndexConfig, IndexSnapshot]: + self._require_index_directory(index_directory) + return self.repository.active_config(device=device) + def config_for_snapshot( self, index_directory: Path, diff --git a/src/vidxp/job_service.py b/src/vidxp/job_service.py index dcad2df..0fac51f 100644 --- a/src/vidxp/job_service.py +++ b/src/vidxp/job_service.py @@ -89,10 +89,12 @@ def __init__( settings: VidXPSettings, backend: JobBackend, read_planner: ReadJobPlanner | None = None, + index_preflight: Callable[[CreateIndexCommand], None] | None = None, ) -> None: self.settings = settings self.backend = backend self.read_planner = read_planner + self.index_preflight = index_preflight def _read_job_planner(self) -> ReadJobPlanner: if self.read_planner is None: @@ -114,6 +116,8 @@ def submit_index( *, job_id: str | None = None, ) -> Job: + if self.index_preflight is not None: + self.index_preflight(command) return self.backend.submit( IndexJobRequest(command=command), queue=self._model_queue(), diff --git a/src/vidxp/mcp.py b/src/vidxp/mcp.py index e9a297f..89f9ae4 100644 --- a/src/vidxp/mcp.py +++ b/src/vidxp/mcp.py @@ -44,6 +44,7 @@ QueryVideoCommand, RuntimeReadiness, SearchCommand, + WorkspaceOverview, ) from vidxp.authentication import ( OIDCBearerAuthenticator, @@ -345,9 +346,11 @@ def create_mcp_server( ) ], instructions=( - "Call get_runtime_readiness before indexing. If selected model " + "Call get_workspace before planning index, search, query, or actor " + "work; it reports valid capability roles for each media item. Call " + "get_runtime_readiness before indexing. If selected model " "artifacts are missing, submit prepare_models and poll get_job " - "until it completes. Discover registered media with list_media. " + "until it completes. " "Register and upload new video through the HTTP/tus API, then use " "its media_id with start_indexing. get_index_status identifies the " "media included in the active index snapshot. For search_moments " @@ -418,6 +421,32 @@ async def read_matroska_artifact(artifact_id: ArtifactId) -> bytes: expected_mime_type="video/x-matroska", ) + @server.tool( + description=( + "Inspect registered media, the active index, installed capabilities, " + "and the searchable, queryable, inspectable, or renderable roles " + "currently usable for each media item. Call this before planning " + "index, search, query, or actor work." + ), + annotations=_READ_ONLY, + structured_output=True, + ) + async def get_workspace( + page_size: Annotated[int, Field(gt=0, le=100)] = 50, + cursor: Annotated[ + str | None, + Field(min_length=1, max_length=512), + ] = None, + ) -> WorkspaceOverview: + return await _invoke_async( + context, + default_principal=default_principal, + permission=RepositoryPermission.read, + operation=lambda _actor: context.application.workspace( + ListMediaCommand(page_size=page_size, cursor=cursor) + ), + ) + @server.tool( description="List installed VidXP capabilities.", annotations=_READ_ONLY, @@ -532,7 +561,6 @@ async def start_indexing( idempotency_key: IdempotencyKey, ) -> Job: def submit(actor: Principal) -> Job: - context.application.require_models(command.modalities) return context.jobs.submit_index( command, job_id=scoped_job_id( diff --git a/src/vidxp/read_job_planner.py b/src/vidxp/read_job_planner.py index 19079b8..6e70e95 100644 --- a/src/vidxp/read_job_planner.py +++ b/src/vidxp/read_job_planner.py @@ -9,8 +9,11 @@ QueryVideoCommand, SearchCommand, SearchJobRequest, + CapabilityRole, ) from vidxp.capabilities.contracts import CapabilityRequestError +from vidxp.capabilities.registry import CapabilityRegistry +from vidxp.core.snapshots import IndexSnapshot from vidxp.infrastructure.local_index import LocalIndexReader from vidxp.repository_layout import RepositoryLayout @@ -22,13 +25,15 @@ def __init__( self, *, layout: RepositoryLayout, + registry: CapabilityRegistry, index: LocalIndexReader | None = None, ) -> None: self.layout = layout + self.registry = registry self.index = index or LocalIndexReader(layout) def _active(self): - config = self.index.active_config( + config, snapshot = self.index.active_snapshot( self.layout.indexes, device="cpu", ) @@ -36,39 +41,138 @@ def _active(self): raise RuntimeError( "The active index did not provide an immutable reference." ) - return config, IndexSnapshotReference( - snapshot_id=config.snapshot_id, - snapshot_sha256=config.snapshot_sha256, + return ( + config, + IndexSnapshotReference( + snapshot_id=config.snapshot_id, + snapshot_sha256=config.snapshot_sha256, + ), + snapshot, ) - @staticmethod - def _require_capability(capability: str, config) -> None: - if capability not in config.enabled_modalities: + def _select_capabilities( + self, + requested: tuple[str, ...], + *, + indexed: tuple[str, ...], + role: CapabilityRole, + operation: str, + ) -> tuple[str, ...]: + explicit = bool(requested) + selected = self.registry.validate_names(requested) if explicit else indexed + absent = tuple(name for name in selected if name not in indexed) + if absent: + raise CapabilityRequestError( + f"{operation} capabilities are not present in the active index: " + + ", ".join(absent) + + ".", + field="modalities", + reason="capability_not_indexed", + requested=absent, + available=tuple( + name for name in indexed if role in self.registry.get(name).roles + ), + indexed=indexed, + next_action=( + "Choose an indexed capability returned by get_workspace or " + "index the media with the requested capability." + ), + ) + supported = tuple( + name for name in selected if role in self.registry.get(name).roles + ) + unsupported = tuple(name for name in selected if name not in supported) + if explicit and unsupported: + available = tuple( + name for name in indexed if role in self.registry.get(name).roles + ) raise CapabilityRequestError( - f"The {capability} capability is not present in this index." + f"{operation} does not support these indexed capabilities: " + + ", ".join(unsupported) + + ".", + field="modalities", + reason="capability_role_unsupported", + requested=unsupported, + available=available, + indexed=indexed, + next_action=( + f"Choose a {role.value} capability returned by get_workspace." + ), ) + if not supported: + raise CapabilityRequestError( + f"The active index has no {role.value} capabilities.", + field="modalities", + reason="capability_role_unavailable", + indexed=indexed, + next_action=( + f"Index media with a capability whose role is {role.value}." + ), + ) + return supported + + @staticmethod + def _require_media( + media_id: str | None, + snapshot: IndexSnapshot, + ) -> None: + if media_id is None or media_id in snapshot.generations: + return + raise CapabilityRequestError( + "The selected media item is not present in the active index snapshot.", + field="media_id", + reason="media_not_indexed", + requested=(media_id,), + available=tuple(sorted(snapshot.generations)[:100]), + next_action=( + "Choose indexed media returned by get_workspace or index this " + "media item first." + ), + ) @application_boundary def plan_search(self, command: SearchCommand) -> SearchJobRequest: - config, reference = self._active() - for capability in command.modalities: - self._require_capability(capability, config) - return SearchJobRequest(command=command, snapshot=reference) + config, reference, snapshot = self._active() + selected = self._select_capabilities( + command.modalities, + indexed=config.enabled_modalities, + role=CapabilityRole.searchable, + operation="Search", + ) + self._require_media(command.media_id, snapshot) + return SearchJobRequest( + command=command.model_copy(update={"modalities": selected}), + snapshot=reference, + ) @application_boundary def plan_query(self, command: QueryVideoCommand) -> QueryJobRequest: - config, reference = self._active() - for capability in command.modalities: - self._require_capability(capability, config) - return QueryJobRequest(command=command, snapshot=reference) + config, reference, snapshot = self._active() + selected = self._select_capabilities( + command.modalities, + indexed=config.enabled_modalities, + role=CapabilityRole.queryable, + operation="Query", + ) + self._require_media(command.media_id, snapshot) + return QueryJobRequest( + command=command.model_copy(update={"modalities": selected}), + snapshot=reference, + ) @application_boundary def plan_actor_overlay( self, command: CreateActorOverlayCommand, ) -> ActorOverlayJobRequest: - config, reference = self._active() - self._require_capability("actor", config) + config, reference, _snapshot = self._active() + selected = self._select_capabilities( + ("actor",), + indexed=config.enabled_modalities, + role=CapabilityRole.renderable, + operation="Actor overlay", + ) + assert selected == ("actor",) return ActorOverlayJobRequest( command=command, snapshot=reference, diff --git a/tests/test_api.py b/tests/test_api.py index 097c9b4..dab53f5 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -25,11 +25,13 @@ JobKind, JobQueue, JobState, + IndexStatus, MediaAsset, Principal, SearchCommand, QueryVideoCommand, UploadIntent, + WorkspaceOverview, ) from vidxp.composition import ( HttpApplicationContext, @@ -278,6 +280,28 @@ def test_static_bearer_is_enforced_before_dispatch(self): self.assertEqual(accepted.json(), {"items": []}) context.application.list_capabilities.assert_called_once_with() + def test_workspace_endpoint_returns_actionable_repository_state(self): + with TemporaryDirectory() as directory: + context = self.context(Path(directory)) + context.application.workspace.return_value = WorkspaceOverview( + media_total=0, + index=IndexStatus( + schema_version=2, + state="missing", + stage="status", + message="No index.", + ), + next_actions=("register_media",), + ) + with TestClient(create_app(context=context)) as client: + response = client.get("/api/v1/workspace?page_size=25") + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["media_total"], 0) + self.assertEqual(response.json()["next_actions"], ["register_media"]) + command = context.application.workspace.call_args.args[0] + self.assertEqual(command.page_size, 25) + def test_repository_scopes_are_enforced_per_operation(self): with TemporaryDirectory() as directory: context = self.context( @@ -378,7 +402,6 @@ def test_job_submission_is_thin_idempotent_delegation(self): self.assertEqual(command.media_id, MEDIA_ID) self.assertEqual(command.modalities, ("scene",)) self.assertEqual(command.scene_sample_fps, 0.5) - context.application.require_models.assert_called_once_with(("scene",)) expected_job_id = scoped_job_id( context, context.authenticator.authenticate(None), @@ -441,7 +464,7 @@ def test_failed_model_preparation_job_is_structured_over_http(self): def test_missing_models_fail_before_job_submission(self): with TemporaryDirectory() as directory: context = self.context(Path(directory)) - context.application.require_models.side_effect = ApplicationError( + context.jobs.submit_index.side_effect = ApplicationError( "model_unavailable", ErrorCategory.unavailable, "Run vidxp prepare --modalities scene.", @@ -466,7 +489,7 @@ def test_missing_models_fail_before_job_submission(self): response.json()["error"]["details"]["remediation"], "vidxp prepare --modalities scene", ) - context.jobs.submit_index.assert_not_called() + context.jobs.submit_index.assert_called_once() def test_job_submission_requires_an_idempotency_key(self): with TemporaryDirectory() as directory: diff --git a/tests/test_control_plane.py b/tests/test_control_plane.py new file mode 100644 index 0000000..a6ae69b --- /dev/null +++ b/tests/test_control_plane.py @@ -0,0 +1,152 @@ +import unittest +from datetime import datetime, timezone +from pathlib import Path +from tempfile import TemporaryDirectory +from unittest.mock import Mock + +from vidxp.application_models import ( + ApplicationError, + CapabilityIdentityMode, + CapabilityRole, + CreateIndexCommand, + ListMediaCommand, + MediaAsset, + MediaPage, +) +from vidxp.capabilities.registry import create_capability_registry +from vidxp.capability_service import CapabilityService +from vidxp.control_plane import ControlPlaneApplication +from vidxp.core.media import MediaState, MediaStream +from vidxp.core.snapshots import GenerationReference, IndexSnapshot +from vidxp.repository_layout import RepositoryLayout + + +MEDIA_ID = "123456781234423481234567890abcde" +OTHER_MEDIA_ID = "223456781234423481234567890abcde" +GENERATION_ID = "323456781234423481234567890abcde" +SNAPSHOT_ID = "423456781234423481234567890abcde" + + +def media_asset(media_id: str, filename: str) -> MediaAsset: + return MediaAsset( + media_id=media_id, + video_id=media_id, + original_filename=filename, + sha256="a" * 64, + byte_size=10, + detected_mime_type="video/mp4", + container="mp4", + duration_seconds=2, + streams=( + MediaStream( + index=0, + kind="video", + codec="h264", + width=1, + height=1, + ), + ), + state=MediaState.ready, + created_at=datetime.now(timezone.utc), + ) + + +class ControlPlaneWorkspaceTests(unittest.TestCase): + def test_index_preflight_rejects_unknown_capability_with_next_action(self): + with TemporaryDirectory() as directory: + root = Path(directory) + media = Mock() + application = ControlPlaneApplication( + layout=RepositoryLayout(root=root), + capabilities=CapabilityService(create_capability_registry()), + media=media, + artifacts=Mock(), + index_status=lambda: None, + model_cache=root / "models", + ) + + with self.assertRaises(ApplicationError) as raised: + application.preflight_index( + CreateIndexCommand( + media_id=MEDIA_ID, + modalities=("unknown",), + ) + ) + + error = raised.exception.to_dict()["details"]["errors"][0] + self.assertEqual(error["reason"], "capability_unknown") + self.assertEqual(error["requested"], ["unknown"]) + self.assertIn("get_workspace", error["next_action"]) + media.get.assert_not_called() + + def test_workspace_projects_index_coverage_roles_and_next_actions(self): + indexed = media_asset(MEDIA_ID, "indexed.mp4") + unindexed = media_asset(OTHER_MEDIA_ID, "new.mp4") + snapshot = IndexSnapshot( + snapshot_id=SNAPSHOT_ID, + created_at=datetime.now(timezone.utc), + config_fingerprint="b" * 64, + configuration={}, + generations={ + MEDIA_ID: GenerationReference( + generation_id=GENERATION_ID, + media_id=MEDIA_ID, + manifest_sha256="c" * 64, + input_sha256="d" * 64, + config_fingerprint="e" * 64, + modalities=("scene", "actor"), + record_counts={"scene": 12, "actor": 4}, + store_size_bytes_at_commit=100, + ) + }, + ) + media = Mock() + media.list.return_value = MediaPage( + items=(indexed, unindexed), + total=2, + ) + with TemporaryDirectory() as directory: + root = Path(directory) + application = ControlPlaneApplication( + layout=RepositoryLayout(root=root), + capabilities=CapabilityService(create_capability_registry()), + media=media, + artifacts=Mock(), + index_status=lambda: { + "schema_version": 2, + "state": "ready", + "stage": "status", + "message": "Index ready.", + }, + active_snapshot=lambda: snapshot, + model_cache=root / "models", + ) + + workspace = application.workspace(ListMediaCommand()) + + self.assertEqual(workspace.media_total, 2) + self.assertEqual( + workspace.next_actions, + ("index_media", "find_moments", "answer_video"), + ) + indexed_projection = workspace.media[0] + self.assertTrue(indexed_projection.in_active_snapshot) + by_name = { + capability.name: capability + for capability in indexed_projection.capabilities + } + self.assertEqual(by_name["scene"].record_count, 12) + self.assertEqual( + by_name["scene"].roles, + (CapabilityRole.searchable, CapabilityRole.queryable), + ) + self.assertEqual(by_name["actor"].record_count, 4) + self.assertEqual( + by_name["actor"].identity_mode, + CapabilityIdentityMode.anonymous_clusters, + ) + self.assertEqual(workspace.media[1].capabilities[0].roles, ()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_frontend.py b/tests/test_frontend.py index ee426c4..9991b0a 100644 --- a/tests/test_frontend.py +++ b/tests/test_frontend.py @@ -337,7 +337,7 @@ def test_indexing_submits_selected_scene_sample_rate(self): command = jobs.submit_index.call_args.args[0] self.assertEqual(command.scene_sample_fps, 2.0) - service.require_models.assert_called_once_with(("scene",)) + service.require_models.assert_not_called() def test_indexing_omits_scene_sample_rate_without_scene(self): jobs = Mock() diff --git a/tests/test_job_contracts.py b/tests/test_job_contracts.py index 34f67aa..018e757 100644 --- a/tests/test_job_contracts.py +++ b/tests/test_job_contracts.py @@ -79,6 +79,7 @@ def test_public_job_contract_has_no_path_or_storage_fields(self): def test_job_service_routes_model_work_without_reimplementing_it(self): backend = Mock() + preflight = Mock() backend.submit.return_value = Job( job_id=JOB_ID, kind=JobKind.index, @@ -91,6 +92,7 @@ def test_job_service_routes_model_work_without_reimplementing_it(self): runtime_backend="cpu", ), backend=backend, + index_preflight=preflight, ) job = service.submit_index( @@ -102,6 +104,7 @@ def test_job_service_routes_model_work_without_reimplementing_it(self): self.assertEqual(job.job_id, JOB_ID) request = backend.submit.call_args.args[0] + preflight.assert_called_once_with(request.command) self.assertEqual(request.kind, JobKind.index) self.assertEqual(request.command.media_id, MEDIA_ID) self.assertEqual( @@ -109,6 +112,34 @@ def test_job_service_routes_model_work_without_reimplementing_it(self): JobQueue.cpu, ) + def test_index_preflight_failure_never_reaches_the_job_backend(self): + backend = Mock() + command = CreateIndexCommand( + media_id=MEDIA_ID, + modalities=("scene",), + ) + preflight = Mock( + side_effect=ApplicationError( + "invalid_request", + ErrorCategory.validation, + "The capability is not usable.", + ) + ) + service = JobService( + settings=VidXPSettings( + repository_root=Path("repository"), + runtime_backend="cpu", + ), + backend=backend, + index_preflight=preflight, + ) + + with self.assertRaises(ApplicationError): + service.submit_index(command) + + preflight.assert_called_once_with(command) + backend.submit.assert_not_called() + def test_job_list_cursor_is_bounded(self): with self.assertRaises(ValidationError): ListJobsCommand(cursor="x" * 513) diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 39de1e7..2639628 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -34,6 +34,7 @@ MediaPage, Principal, QueryVideoCommand, + WorkspaceOverview, ) from vidxp.authentication import ( AuthenticatedBearer, @@ -103,6 +104,11 @@ def context( stage="status", message="No index.", ) + application.workspace.return_value = WorkspaceOverview( + media_total=0, + index=application.index_status.return_value, + next_actions=("register_media",), + ) jobs = Mock(spec=JobService) readiness = Mock() readiness.ready.return_value = True @@ -132,6 +138,7 @@ async def test_curated_tools_have_structured_output_schemas(self): self.assertEqual( [tool.name for tool in discovered.tools], [ + "get_workspace", "list_capabilities", "get_capability", "get_runtime_readiness", @@ -166,6 +173,26 @@ async def test_curated_tools_have_structured_output_schemas(self): self.assertEqual(result.structured_content, {"items": []}) self.assertFalse(result.is_error) + async def test_workspace_tool_projects_actionable_repository_state(self): + with TemporaryDirectory() as directory: + context = self.context(Path(directory)) + server = create_mcp_server( + context, + default_principal=Principal( + subject="local", + scopes=frozenset({"*"}), + ), + ) + async with Client(server) as client: + result = await client.call_tool("get_workspace", {}) + + self.assertEqual(result.structured_content["media_total"], 0) + self.assertEqual( + result.structured_content["next_actions"], + ["register_media"], + ) + context.application.workspace.assert_called_once() + def test_stdio_help_and_config_are_ready_to_copy(self): config = stdio_client_config( command=r"C:\VidXP\vidxp-mcp.exe", @@ -217,7 +244,7 @@ def test_stdio_check_performs_handshake_and_tool_probe(self): rendered = output.getvalue() self.assertIn("OK VidXP MCP", rendered) self.assertIn("Index state: missing", rendered) - self.assertIn("Tools: 16", rendered) + self.assertIn("Tools: 17", rendered) self.assertIn("get_index_status", rendered) async def test_server_info_exposes_vidxp_branding(self): @@ -280,15 +307,12 @@ async def test_index_submission_uses_shared_stable_idempotency(self): calls[0].kwargs["job_id"], calls[1].kwargs["job_id"], ) - self.assertEqual( - context.application.require_models.call_args.args[0], - ("scene",), - ) + self.assertEqual(calls[0].args[0].modalities, ("scene",)) async def test_missing_models_fail_before_index_submission(self): with TemporaryDirectory() as directory: context = self.context(Path(directory)) - context.application.require_models.side_effect = ApplicationError( + context.jobs.submit_index.side_effect = ApplicationError( "model_unavailable", ErrorCategory.unavailable, "Run vidxp prepare --modalities scene.", @@ -321,7 +345,7 @@ async def test_missing_models_fail_before_index_submission(self): '"remediation":"vidxp prepare --modalities scene"', result.content[0].text, ) - context.jobs.submit_index.assert_not_called() + context.jobs.submit_index.assert_called_once() async def test_query_video_submits_the_shared_durable_command(self): with TemporaryDirectory() as directory: @@ -628,6 +652,7 @@ async def test_stdio_entrypoint_serves_the_same_curated_surface(self): self.assertEqual( [tool.name for tool in discovered.tools], [ + "get_workspace", "list_capabilities", "get_capability", "get_runtime_readiness", @@ -697,7 +722,7 @@ async def test_streamable_http_works_with_the_official_remote_client(self): server.should_exit = True await serving - self.assertEqual(len(discovered.tools), 16) + self.assertEqual(len(discovered.tools), 17) self.assertEqual(result.structured_content, {"items": []}) async def test_oidc_verifier_projects_the_shared_validated_token(self): diff --git a/tests/test_read_job_planner.py b/tests/test_read_job_planner.py index bb12274..0aba9be 100644 --- a/tests/test_read_job_planner.py +++ b/tests/test_read_job_planner.py @@ -1,12 +1,16 @@ import unittest +from datetime import datetime, timezone from unittest.mock import Mock from vidxp.application_models import ( + ApplicationError, CreateActorOverlayCommand, QueryVideoCommand, SearchCommand, ) +from vidxp.capabilities.registry import create_capability_registry from vidxp.core.contracts import IndexConfig +from vidxp.core.snapshots import GenerationReference, IndexSnapshot from vidxp.read_job_planner import LocalReadJobPlanner from vidxp.repository_layout import RepositoryLayout @@ -27,10 +31,32 @@ def setUp(self): storage_directory=self.layout.index_store, collection_names={"scene": "scene", "actor": "actor"}, ) + self.snapshot = IndexSnapshot( + snapshot_id=SNAPSHOT_ID, + created_at=datetime.now(timezone.utc), + config_fingerprint="b" * 64, + configuration={}, + generations={ + MEDIA_ID: GenerationReference( + generation_id=GENERATION_ID, + media_id=MEDIA_ID, + manifest_sha256="c" * 64, + input_sha256="d" * 64, + config_fingerprint="e" * 64, + modalities=("scene", "actor"), + record_counts={"scene": 2, "actor": 1}, + store_size_bytes_at_commit=100, + ) + }, + ) self.index = Mock() - self.index.active_config.return_value = self.config + self.index.active_snapshot.return_value = ( + self.config, + self.snapshot, + ) self.planner = LocalReadJobPlanner( layout=self.layout, + registry=create_capability_registry(), index=self.index, ) @@ -68,6 +94,45 @@ def test_query_job_carries_the_same_logical_snapshot_reference(self): self.assertEqual(request.snapshot.snapshot_sha256, "a" * 64) self.index.open_store.assert_not_called() + def test_omitted_search_capabilities_exclude_non_searchable_actor(self): + request = self.planner.plan_search(SearchCommand(query="a taxi")) + + self.assertEqual(request.command.modalities, ("scene",)) + + def test_explicit_actor_search_fails_before_a_job_can_be_submitted(self): + with self.assertRaises(ApplicationError) as raised: + self.planner.plan_search( + SearchCommand(modalities=("actor",), query="Harry") + ) + + error = raised.exception.to_dict()["details"] + self.assertEqual(error["errors"][0]["reason"], "capability_role_unsupported") + self.assertEqual(error["errors"][0]["requested"], ["actor"]) + self.assertEqual(error["errors"][0]["available"], ["scene"]) + + def test_actor_remains_available_to_grounded_query(self): + request = self.planner.plan_query( + QueryVideoCommand( + question="When does this actor appear?", + modalities=("actor",), + ) + ) + + self.assertEqual(request.command.modalities, ("actor",)) + + def test_media_outside_active_snapshot_fails_during_planning(self): + with self.assertRaises(ApplicationError) as raised: + self.planner.plan_search( + SearchCommand( + media_id="423456781234423481234567890abcde", + query="a taxi", + ) + ) + + error = raised.exception.to_dict()["details"]["errors"][0] + self.assertEqual(error["reason"], "media_not_indexed") + self.assertEqual(error["available"], [MEDIA_ID]) + if __name__ == "__main__": unittest.main() From c775e6a193bc412ea2cf4e91cfd4fa887c844baf Mon Sep 17 00:00:00 2001 From: "Saad A. Bazaz" Date: Sat, 1 Aug 2026 16:24:44 +0500 Subject: [PATCH 06/18] Update credits section in README.md (#46) Added original researchers and collaborators to credits section. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index f314c36..4a75977 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,12 @@ Contributions are welcome. Read the ## Credits Built by Grayhat Developers PVT Ltd. and maintained by the community. +Originally researched by students: +- Abdullah Mansoor (@abdullahmansoor321) +- Muhammad Haroon (@haroon10725) +- Sarah Jawaid (@sarr266) +- Talha Ahmed (@talhaahmed1234) +Working with [Dr Shahab Tahzeeb](https://scholar.google.com/citations?user=cryeRB0AAAAJ&hl=en) ([NED University of Engineering and Technology](https://www.neduet.edu.pk/)) and [Saad Bazaz](https://scholar.google.com/citations?user=mrJo09oAAAAJ&hl=en) ([Grayhat](https://grayhat.studio). Email: info@grayhat.studio From be7d4ef7a64208e0b01c3916995942593fda367b Mon Sep 17 00:00:00 2001 From: "Saad A. Bazaz" Date: Sat, 1 Aug 2026 16:26:18 +0500 Subject: [PATCH 07/18] Add GitHub links for contributors in README (#47) Updated contributor names to include links to their GitHub profiles. --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4a75977..69daa56 100644 --- a/README.md +++ b/README.md @@ -240,10 +240,11 @@ Contributions are welcome. Read the Built by Grayhat Developers PVT Ltd. and maintained by the community. Originally researched by students: -- Abdullah Mansoor (@abdullahmansoor321) -- Muhammad Haroon (@haroon10725) -- Sarah Jawaid (@sarr266) -- Talha Ahmed (@talhaahmed1234) +- [Abdullah Mansoor](https://github.com/abdullahmansoor321) +- [Muhammad Haroon](https://github.com/haroon10725) +- [Sarah Jawaid](https://github.com/sarr266) +- [Talha Ahmed](https://github.com/talhaahmed1234) + Working with [Dr Shahab Tahzeeb](https://scholar.google.com/citations?user=cryeRB0AAAAJ&hl=en) ([NED University of Engineering and Technology](https://www.neduet.edu.pk/)) and [Saad Bazaz](https://scholar.google.com/citations?user=mrJo09oAAAAJ&hl=en) ([Grayhat](https://grayhat.studio). Email: info@grayhat.studio From 9a96b3ac4dd299fafa6c682a6794b627c16b2bf1 Mon Sep 17 00:00:00 2001 From: Talha Amjad Date: Sun, 2 Aug 2026 17:56:46 +0500 Subject: [PATCH 08/18] feat(desktop): add local target and managed runtime workflows (#50) * feat(desktop): add local target adoption flow * fix(desktop): validate local targets by capability contract * fix(desktop): clarify browser surface ownership * fix(desktop): refine native setup window * fix(desktop): clarify target and model readiness * fix(desktop): correct Windows target validation * fix(desktop): open browser once * fix(desktop): keep control panel accessible * refactor(desktop): centralize target and process lifecycle * fix(desktop): complete managed profile workflows * docs(desktop): align public setup and licensing * fix(desktop): make target transitions atomic * fix(desktop): settle managed setup lifecycle * build(desktop): complete distributable notices * docs(desktop): align installation guidance * fix(desktop): harden managed runtime lifecycle * fix(desktop): align setup and window lifecycle UI * build(desktop): use authoritative license notices * docs(desktop): correct platform setup contracts * fix(desktop): harden supervised operation shutdown * fix(desktop): correct managed and platform UI states * ci(desktop): validate all supported platforms * docs(desktop): clarify media prerequisites * fix(desktop): close lifecycle and release blockers * chore(desktop): expose stale notice diff * fix(desktop): normalize generated notice line endings * fix(desktop): stabilize cross-platform CI checks * fix(desktop): make platform checks deterministic --- .github/workflows/desktop.yml | 25 +- .gitignore | 3 +- INSTALLATION_GUIDE.md | 59 +- README.md | 13 +- desktop/THIRD_PARTY_NOTICES.txt | 11543 ++++++++++++++++ desktop/about.hbs | 12 + desktop/about.toml | 14 + desktop/eslint.config.js | 31 + desktop/index.html | 15 + .../react-remove-scroll-bar-2.3.8-LICENSE.txt | 21 + desktop/licenses/uv-LICENSE-MIT.txt | 19 + desktop/model-cache-catalog.json | 27 + desktop/package-lock.json | 5327 ++++++- desktop/package.json | 37 +- desktop/scripts/generate-notices.mjs | 156 + desktop/scripts/model-catalog.py | 45 + desktop/scripts/sync-branding.mjs | 6 +- desktop/src-tauri/Cargo.lock | 205 +- desktop/src-tauri/Cargo.toml | 7 +- desktop/src-tauri/capabilities/main.json | 12 +- desktop/src-tauri/src/activation.rs | 121 + desktop/src-tauri/src/background_process.rs | 468 + desktop/src-tauri/src/browser_readiness.rs | 269 + desktop/src-tauri/src/lib.rs | 3173 ++++- desktop/src-tauri/src/lifecycle.rs | 209 + desktop/src-tauri/src/media_setup.rs | 88 + desktop/src-tauri/src/target_profiles.rs | 2079 +++ desktop/src-tauri/tauri.conf.json | 19 +- desktop/src-tauri/tauri.windows.conf.json | 20 + desktop/src/App.test.tsx | 381 + desktop/src/App.tsx | 237 + desktop/src/components/LocalSetup.tsx | 261 + desktop/src/components/ManagedSetup.tsx | 384 + desktop/src/components/TargetChoice.tsx | 83 + desktop/src/components/TargetSummary.tsx | 75 + desktop/src/components/TitleBar.test.tsx | 50 + desktop/src/components/TitleBar.tsx | 115 + desktop/src/desktopPackagingContracts.test.ts | 76 + desktop/src/main.tsx | 29 + desktop/src/styles.css | 146 + desktop/src/tauri.test.ts | 73 + desktop/src/tauri.ts | 322 + desktop/src/test/setup.ts | 27 + desktop/src/useAsyncAction.test.tsx | 34 + desktop/src/useAsyncAction.ts | 81 + desktop/src/vite-env.d.ts | 1 + desktop/tsconfig.app.json | 22 + desktop/tsconfig.json | 7 + desktop/tsconfig.node.json | 17 + desktop/vite.config.ts | 29 + desktop/web/app.js | 188 - desktop/web/index.html | 73 - desktop/web/styles.css | 211 - docs/CONTRIBUTING.md | 29 +- docs/desktop.md | 125 +- src/vidxp/cli.py | 4 +- src/vidxp/cli_commands/probe.py | 52 + src/vidxp/frontend.py | 32 + src/vidxp/local_probe.py | 171 + tests/test_cli.py | 1 + tests/test_local_probe.py | 240 + tests/test_packaging.py | 51 +- 62 files changed, 26479 insertions(+), 1171 deletions(-) create mode 100644 desktop/THIRD_PARTY_NOTICES.txt create mode 100644 desktop/about.hbs create mode 100644 desktop/about.toml create mode 100644 desktop/eslint.config.js create mode 100644 desktop/index.html create mode 100644 desktop/licenses/npm/react-remove-scroll-bar-2.3.8-LICENSE.txt create mode 100644 desktop/licenses/uv-LICENSE-MIT.txt create mode 100644 desktop/model-cache-catalog.json create mode 100644 desktop/scripts/generate-notices.mjs create mode 100644 desktop/scripts/model-catalog.py create mode 100644 desktop/src-tauri/src/activation.rs create mode 100644 desktop/src-tauri/src/background_process.rs create mode 100644 desktop/src-tauri/src/browser_readiness.rs create mode 100644 desktop/src-tauri/src/lifecycle.rs create mode 100644 desktop/src-tauri/src/media_setup.rs create mode 100644 desktop/src-tauri/src/target_profiles.rs create mode 100644 desktop/src-tauri/tauri.windows.conf.json create mode 100644 desktop/src/App.test.tsx create mode 100644 desktop/src/App.tsx create mode 100644 desktop/src/components/LocalSetup.tsx create mode 100644 desktop/src/components/ManagedSetup.tsx create mode 100644 desktop/src/components/TargetChoice.tsx create mode 100644 desktop/src/components/TargetSummary.tsx create mode 100644 desktop/src/components/TitleBar.test.tsx create mode 100644 desktop/src/components/TitleBar.tsx create mode 100644 desktop/src/desktopPackagingContracts.test.ts create mode 100644 desktop/src/main.tsx create mode 100644 desktop/src/styles.css create mode 100644 desktop/src/tauri.test.ts create mode 100644 desktop/src/tauri.ts create mode 100644 desktop/src/test/setup.ts create mode 100644 desktop/src/useAsyncAction.test.tsx create mode 100644 desktop/src/useAsyncAction.ts create mode 100644 desktop/src/vite-env.d.ts create mode 100644 desktop/tsconfig.app.json create mode 100644 desktop/tsconfig.json create mode 100644 desktop/tsconfig.node.json create mode 100644 desktop/vite.config.ts delete mode 100644 desktop/web/app.js delete mode 100644 desktop/web/index.html delete mode 100644 desktop/web/styles.css create mode 100644 src/vidxp/cli_commands/probe.py create mode 100644 src/vidxp/local_probe.py create mode 100644 tests/test_local_probe.py diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index e2c27d0..a170b69 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -22,6 +22,15 @@ on: - "desktop/**" - "src/vidxp/requirements/**" - "src/vidxp/capabilities/*/requirements.txt" + - "src/vidxp/local_probe.py" + - "src/vidxp/frontend.py" + - "src/vidxp/cli.py" + - "src/vidxp/cli_commands/probe.py" + - "tests/test_local_probe.py" + - "tests/test_cli.py" + - "tests/test_frontend.py" + - "tests/test_frontend_app.py" + - "tests/test_packaging.py" - "pyproject.toml" - "uv.lock" - ".github/workflows/desktop.yml" @@ -44,7 +53,7 @@ jobs: strategy: fail-fast: false matrix: - target: ${{ fromJSON(github.event_name == 'pull_request' && !startsWith(github.head_ref, 'release-please--branches--') && '["windows"]' || '["windows","macos","linux"]') }} + target: [windows, macos, linux] steps: - uses: actions/checkout@v7 @@ -77,6 +86,9 @@ jobs: - uses: dtolnay/rust-toolchain@1.97.1 + - name: Install locked license tooling + run: cargo install cargo-about --version 0.9.1 --locked --features cli + - name: Restore Rust build cache uses: actions/cache@v5 with: @@ -92,6 +104,17 @@ jobs: run: npm ci working-directory: desktop + - name: Check the desktop frontend + run: npm run check + working-directory: desktop + + - name: Verify generated desktop contracts and notices + run: | + cargo fetch --manifest-path src-tauri/Cargo.toml --locked + npm run model-catalog:check + npm run notices:check + working-directory: desktop + - name: Verify the desktop runtime constraints shell: bash run: | diff --git a/.gitignore b/.gitignore index 49668d6..1e94432 100644 --- a/.gitignore +++ b/.gitignore @@ -38,10 +38,11 @@ pnpm-debug.log* # compiled output /target /desktop/node_modules/ +/desktop/dist/ /desktop/src-tauri/target/ /desktop/src-tauri/gen/ /desktop/src-tauri/binaries/uv-* -/desktop/web/icon.png +/desktop/public/icon.png /desktop/src-tauri/icons/android/ /desktop/src-tauri/icons/ios/ /desktop/src-tauri/icons/64x64.png diff --git a/INSTALLATION_GUIDE.md b/INSTALLATION_GUIDE.md index f0f50c7..427f03a 100644 --- a/INSTALLATION_GUIDE.md +++ b/INSTALLATION_GUIDE.md @@ -11,7 +11,7 @@ shape needs. | Local agent integration | `vidxp[local-worker,mcp]` | Local worker and stdio MCP | | Native browser UI | `vidxp[local-worker,frontend]` | CLI, local worker, Streamlit | | Local application server | `vidxp[local-worker,server]` | Loopback HTTP API, remote MCP, local worker | -| Desktop app | Install the native package | Guided app-owned Python and worker runtime with an optional browser interface | +| Desktop app | Install the native package | Adopt a compatible local installation or create a private Desktop-managed runtime | | Browser UI in Docker | Published `vidxp` image | One CPU worker/UI container | | Public/self-hosted service | `compose.coolify.yaml` | API/MCP control plane, CPU worker, PostgreSQL, Chroma, tusd | | Embed one capability | `dialogue`, `scene`, or `actor` extra | Python indexing/retrieval code | @@ -25,14 +25,17 @@ Local model work requires a capability or worker extra. | Installation | Install first | Managed by VidXP | |---|---|---| | CLI or MCP | [uv 0.12+](https://docs.astral.sh/uv/getting-started/installation/) | Python and the isolated VidXP environment | -| Desktop | A supported OS and internet access for first setup | uv, Python, VidXP, and selected model files | +| Desktop-managed target | A supported OS, internet access for first setup, FFmpeg, ffprobe, `libx264`, and `aac` | uv, Python, VidXP, and selected model files | +| Desktop with existing target | A compatible local `vidxp` executable and that installation's own media-runtime setup | Target discovery and launch coordination only | | Docker | Docker Engine or Docker Desktop | Python, VidXP, and FFmpeg inside the image | Native CLI and desktop processing require FFmpeg, ffprobe, `libx264`, and `aac`. `vidxp init` checks them and offers the supported operating-system -package-manager command when something is missing. The desktop app performs -the same check through native confirmation dialogs. Docker already includes -FFmpeg. +package-manager command when something is missing. On Windows, Desktop can +show and run the WinGet command after consent when WinGet is available. On +macOS it can do the same with Homebrew; without Homebrew it provides Homebrew +or manual FFmpeg remediation. Linux displays the applicable APT, DNF, or +manual command without automating elevation. Docker already includes FFmpeg. Supported native systems are Windows x86-64, Linux x86-64, and Apple Silicon macOS 14 or newer. CPU is the supported runtime; GPU installation remains @@ -307,18 +310,40 @@ supported server Compose topology. Download the Windows, Apple Silicon macOS, or Linux package from [GitHub Releases](https://github.com/grayhatdevelopers/vidxp/releases). -On first launch, the application checks FFmpeg, provisions its own Python and -VidXP runtime, lets you choose capabilities and model storage, and optionally -downloads the selected models. Python and uv do not need to be installed -separately. - -Capability code is selected independently from the optional browser interface. -After configuration, the Tauri supervisor stays in the system tray. Closing -the window hides it; **Quit VidXP** from the tray shuts down the interface and -worker. - -The NSIS, DMG, and AppImage packages do not bundle FFmpeg. The application uses -a native confirmation dialog before running a supported package manager. +Desktop opens its control panel first and asks which local target to use. It +does not install anything before that choice: + +- **Use an existing installation** discovers compatible `vidxp` executables or + lets you browse to one. Desktop validates the versioned probe and launch + contracts, but the installation stays externally owned. Desktop never + installs, repairs, updates, removes, or broadly stops it. If its browser + surface is missing, enable the `frontend` extra with that installation's own + package-management workflow before Desktop can open it. +- **Set up VidXP for me** creates a private Python and VidXP runtime owned by + Desktop. Python and uv do not need to be installed separately. Capability + code, the optional browser interface, model storage, and initial model + preparation are selected before applying the draft. + +A managed setup or update remains a draft until its candidate runtime passes +the Desktop probe and launch contracts. Activation then replaces the previous +managed target atomically; failed or cancelled work leaves the previous target +authoritative. For an unchanged ready runtime, **Prepare / verify models** +checks cached files and downloads only missing selected model material without +requiring a configuration change. + +Starting Desktop, or starting it a second time, shows and focuses the control +panel without opening a browser. **Open VidXP** explicitly starts or reuses the +loopback browser service and opens one tab. Closing a configured window hides +it to the tray. Tray actions are **Manage VidXP**, **Open VidXP**, and **Quit +VidXP**. Quit stops the exact browser service Desktop launched; broad worker +shutdown is limited to a Desktop-owned runtime. + +The NSIS, DMG, and AppImage packages do not bundle FFmpeg. Managed setup can +run WinGet on Windows or Homebrew on macOS only after native confirmation and +only when that package manager is available. Without Homebrew, macOS shows +installation remediation instead. Linux displays an APT, DNF, or manual +command and does not automate elevation. An adopted installation keeps +responsibility for its own FFmpeg setup. Windows SmartScreen and macOS Gatekeeper may require explicit confirmation until signing is added. See [Desktop application](docs/desktop.md) for runtime, storage, and build details. diff --git a/README.md b/README.md index 69daa56..68515fe 100644 --- a/README.md +++ b/README.md @@ -99,13 +99,16 @@ the terminal. Download the installer for Windows, Apple Silicon macOS, or Linux from [GitHub Releases](https://github.com/grayhatdevelopers/vidxp/releases). -The desktop installer manages Python, VidXP, and the local worker for you. On -first launch, choose the search capabilities you want, where model files should -live, and whether to download them immediately. Python and uv do not need to be +On first launch, choose whether to adopt an existing compatible VidXP +installation without downloading another runtime, or create a private runtime +managed by VidXP Desktop. For a managed runtime, choose the search capabilities, +model location, and optional browser interface; Python and uv do not need to be installed separately. -After setup, VidXP can stay available from the system tray. The browser -interface is optional and can be left out of the installed VidXP runtime. +Desktop opens its control panel when started. Browser launch is a separate, +explicit **Open VidXP** action. After configuration, closing the control panel +keeps VidXP available through **Manage VidXP**, **Open VidXP**, and **Quit +VidXP** in the system tray. ### 3. Docker for a server diff --git a/desktop/THIRD_PARTY_NOTICES.txt b/desktop/THIRD_PARTY_NOTICES.txt new file mode 100644 index 0000000..d08bb5d --- /dev/null +++ b/desktop/THIRD_PARTY_NOTICES.txt @@ -0,0 +1,11543 @@ +VidXP Desktop Legal Notices +=========================== + +This artifact is generated from locked production dependency graphs and bundled sidecar metadata. + +VIDXP PROJECT LICENSE +===================== +VidXP Desktop and VidXP | MIT | https://github.com/grayhatdevelopers/vidxp + +MIT License + +Copyright (c) 2026 Grayhat Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +BUNDLED EXECUTABLES +=================== +uv 0.12.0 | MIT OR Apache-2.0 | https://github.com/astral-sh/uv/tree/0.12.0 +The complete MIT terms from the pinned uv release follow. The complete Apache-2.0 terms are included in the Rust dependency section below. + +MIT License + +Copyright (c) 2025 Astral Software Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +RUST DEPENDENCIES +================= +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- num_threads 0.1.7 | https://github.com/jhpratt/num_threads | registry+https://github.com/rust-lang/crates.io-index + + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Jacob Pratt + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- powerfmt 0.2.0 | https://github.com/jhpratt/powerfmt | registry+https://github.com/rust-lang/crates.io-index + + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 Jacob Pratt et al. + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- deranged 0.5.8 | https://github.com/jhpratt/deranged | registry+https://github.com/rust-lang/crates.io-index + + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Jacob Pratt et al. + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- embed_plist 1.2.2 | https://github.com/nvzqz/embed-plist-rs | registry+https://github.com/rust-lang/crates.io-index +- encoding_rs 0.8.35 | https://github.com/hsivonen/encoding_rs | registry+https://github.com/rust-lang/crates.io-index +- utf8_iter 1.0.4 | https://github.com/hsivonen/utf8_iter | registry+https://github.com/rust-lang/crates.io-index + + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- windows-collections 0.2.0 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-collections 0.3.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-core 0.61.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-core 0.62.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-future 0.2.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-future 0.3.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-implement 0.60.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-interface 0.59.3 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-link 0.1.3 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-link 0.2.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-numerics 0.2.0 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-numerics 0.3.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-result 0.3.4 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-result 0.4.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-strings 0.4.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-strings 0.5.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-sys 0.45.0 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-sys 0.59.0 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-sys 0.60.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-sys 0.61.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-targets 0.42.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-targets 0.52.6 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-targets 0.53.5 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-threading 0.1.0 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-threading 0.2.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows-version 0.1.7 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows 0.61.3 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows 0.62.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_aarch64_gnullvm 0.42.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_aarch64_gnullvm 0.52.6 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_aarch64_gnullvm 0.53.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_aarch64_msvc 0.42.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_aarch64_msvc 0.52.6 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_aarch64_msvc 0.53.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_i686_gnu 0.42.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_i686_gnu 0.52.6 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_i686_gnu 0.53.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_i686_gnullvm 0.52.6 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_i686_gnullvm 0.53.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_i686_msvc 0.42.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_i686_msvc 0.52.6 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_i686_msvc 0.53.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_x86_64_gnu 0.42.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_x86_64_gnu 0.52.6 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_x86_64_gnu 0.53.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_x86_64_gnullvm 0.42.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_x86_64_gnullvm 0.52.6 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_x86_64_gnullvm 0.53.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_x86_64_msvc 0.42.2 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_x86_64_msvc 0.52.6 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index +- windows_x86_64_msvc 0.53.1 | https://github.com/microsoft/windows-rs | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- zerocopy 0.8.55 | https://github.com/google/zerocopy | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 The Fuchsia Authors + + 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. + + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- serialize-to-javascript-impl 0.1.2 | https://github.com/chippers/serialize-to-javascript | registry+https://github.com/rust-lang/crates.io-index +- serialize-to-javascript 0.1.2 | https://github.com/chippers/serialize-to-javascript | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- ipnet 2.12.0 | https://github.com/krisprice/ipnet | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 Juniper Networks, Inc. + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- swift-rs 1.0.7 | https://github.com/Brendonovich/swift-rs | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 The swift-rs developers + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- json-patch 3.0.1 | https://github.com/idubrov/json-patch | registry+https://github.com/rust-lang/crates.io-index +- winapi 0.3.9 | https://github.com/retep998/winapi-rs | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- crc32fast 1.5.0 | https://github.com/srijs/rust-crc32fast | registry+https://github.com/rust-lang/crates.io-index +- env_filter 0.1.4 | https://github.com/rust-cli/env_logger | registry+https://github.com/rust-lang/crates.io-index +- foreign-types-macros 0.2.4 | https://github.com/sfackler/foreign-types | registry+https://github.com/rust-lang/crates.io-index +- foreign-types-shared 0.3.1 | https://github.com/sfackler/foreign-types | registry+https://github.com/rust-lang/crates.io-index +- foreign-types 0.5.0 | https://github.com/sfackler/foreign-types | registry+https://github.com/rust-lang/crates.io-index +- hex 0.4.3 | https://github.com/KokaKiwi/rust-hex | registry+https://github.com/rust-lang/crates.io-index +- jni-sys 0.3.1 | https://github.com/jni-rs/jni-sys | registry+https://github.com/rust-lang/crates.io-index +- jni-sys 0.4.1 | https://github.com/jni-rs/jni-sys | registry+https://github.com/rust-lang/crates.io-index +- serde_spanned 1.1.1 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index +- toml 1.1.4+spec-1.1.0 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index +- toml_datetime 1.1.1+spec-1.1.0 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index +- toml_edit 0.19.15 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index +- toml_edit 0.20.2 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index +- toml_edit 0.25.13+spec-1.1.0 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index +- toml_parser 1.1.3+spec-1.1.0 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index +- toml_writer 1.1.2+spec-1.1.0 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- async-broadcast 0.7.2 | https://github.com/smol-rs/async-broadcast | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 2020 Yoshua Wuyts + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- jsonptr 0.6.3 | https://github.com/chanced/jsonptr | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2024 Chance Dinkins + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- futures-channel 0.3.33 | https://github.com/rust-lang/futures-rs | registry+https://github.com/rust-lang/crates.io-index +- futures-core 0.3.33 | https://github.com/rust-lang/futures-rs | registry+https://github.com/rust-lang/crates.io-index +- futures-executor 0.3.33 | https://github.com/rust-lang/futures-rs | registry+https://github.com/rust-lang/crates.io-index +- futures-io 0.3.33 | https://github.com/rust-lang/futures-rs | registry+https://github.com/rust-lang/crates.io-index +- futures-macro 0.3.33 | https://github.com/rust-lang/futures-rs | registry+https://github.com/rust-lang/crates.io-index +- futures-sink 0.3.33 | https://github.com/rust-lang/futures-rs | registry+https://github.com/rust-lang/crates.io-index +- futures-task 0.3.33 | https://github.com/rust-lang/futures-rs | registry+https://github.com/rust-lang/crates.io-index +- futures-util 0.3.33 | https://github.com/rust-lang/futures-rs | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- typenum 1.20.1 | https://github.com/paholg/typenum | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2014 Paho Lurie-Gregg + +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. +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- reqwest 0.13.4 | https://github.com/seanmonstar/reqwest | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2016 Sean McArthur + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- cookie 0.18.1 | https://github.com/SergioBenitez/cookie-rs | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2017 Sergio Benitez +Copyright 2014 Alex Chricton + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- http 1.4.2 | https://github.com/hyperium/http | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2017 http-rs authors + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- ppv-lite86 0.2.21 | https://github.com/cryptocorrosion/cryptocorrosion | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2019 The CryptoCorrosion Contributors + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- proc-macro-error-attr 1.0.4 | https://gitlab.com/CreepySkeleton/proc-macro-error | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2019-2020 CreepySkeleton + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- async-recursion 1.1.1 | https://github.com/dcchut/async-recursion | registry+https://github.com/rust-lang/crates.io-index +- keyboard-types 0.7.0 | https://github.com/pyfisch/keyboard-types | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- async-channel 2.5.0 | https://github.com/smol-rs/async-channel | registry+https://github.com/rust-lang/crates.io-index +- async-executor 1.14.0 | https://github.com/smol-rs/async-executor | registry+https://github.com/rust-lang/crates.io-index +- async-io 2.6.0 | https://github.com/smol-rs/async-io | registry+https://github.com/rust-lang/crates.io-index +- async-lock 3.4.2 | https://github.com/smol-rs/async-lock | registry+https://github.com/rust-lang/crates.io-index +- async-process 2.5.0 | https://github.com/smol-rs/async-process | registry+https://github.com/rust-lang/crates.io-index +- async-signal 0.2.14 | https://github.com/smol-rs/async-signal | registry+https://github.com/rust-lang/crates.io-index +- async-task 4.7.1 | https://github.com/smol-rs/async-task | registry+https://github.com/rust-lang/crates.io-index +- atomic-waker 1.1.2 | https://github.com/smol-rs/atomic-waker | registry+https://github.com/rust-lang/crates.io-index +- base64 0.21.7 | https://github.com/marshallpierce/rust-base64 | registry+https://github.com/rust-lang/crates.io-index +- base64 0.22.1 | https://github.com/marshallpierce/rust-base64 | registry+https://github.com/rust-lang/crates.io-index +- bitflags 1.3.2 | https://github.com/bitflags/bitflags | registry+https://github.com/rust-lang/crates.io-index +- bitflags 2.13.1 | https://github.com/bitflags/bitflags | registry+https://github.com/rust-lang/crates.io-index +- blocking 1.6.2 | https://github.com/smol-rs/blocking | registry+https://github.com/rust-lang/crates.io-index +- bumpalo 3.20.3 | https://github.com/fitzgen/bumpalo | registry+https://github.com/rust-lang/crates.io-index +- camino 1.2.5 | https://github.com/camino-rs/camino | registry+https://github.com/rust-lang/crates.io-index +- cfg-if 1.0.4 | https://github.com/rust-lang/cfg-if | registry+https://github.com/rust-lang/crates.io-index +- concurrent-queue 2.5.0 | https://github.com/smol-rs/concurrent-queue | registry+https://github.com/rust-lang/crates.io-index +- core-foundation-sys 0.8.7 | https://github.com/servo/core-foundation-rs | registry+https://github.com/rust-lang/crates.io-index +- core-foundation 0.10.1 | https://github.com/servo/core-foundation-rs | registry+https://github.com/rust-lang/crates.io-index +- core-graphics-types 0.2.0 | https://github.com/servo/core-foundation-rs | registry+https://github.com/rust-lang/crates.io-index +- core-graphics 0.25.0 | https://github.com/servo/core-foundation-rs | registry+https://github.com/rust-lang/crates.io-index +- crossbeam-channel 0.5.16 | https://github.com/crossbeam-rs/crossbeam | registry+https://github.com/rust-lang/crates.io-index +- crossbeam-utils 0.8.22 | https://github.com/crossbeam-rs/crossbeam | registry+https://github.com/rust-lang/crates.io-index +- displaydoc 0.2.7 | https://github.com/yaahc/displaydoc | registry+https://github.com/rust-lang/crates.io-index +- equivalent 1.0.2 | https://github.com/indexmap-rs/equivalent | registry+https://github.com/rust-lang/crates.io-index +- errno 0.3.14 | https://github.com/lambda-fairy/rust-errno | registry+https://github.com/rust-lang/crates.io-index +- event-listener-strategy 0.5.4 | https://github.com/smol-rs/event-listener-strategy | registry+https://github.com/rust-lang/crates.io-index +- event-listener 5.4.2 | https://github.com/smol-rs/event-listener | registry+https://github.com/rust-lang/crates.io-index +- fastrand 2.5.0 | https://github.com/smol-rs/fastrand | registry+https://github.com/rust-lang/crates.io-index +- flate2 1.1.9 | https://github.com/rust-lang/flate2-rs | registry+https://github.com/rust-lang/crates.io-index +- fnv 1.0.7 | https://github.com/servo/rust-fnv | registry+https://github.com/rust-lang/crates.io-index +- form_urlencoded 1.2.2 | https://github.com/servo/rust-url | registry+https://github.com/rust-lang/crates.io-index +- futures-lite 2.6.1 | https://github.com/smol-rs/futures-lite | registry+https://github.com/rust-lang/crates.io-index +- glob 0.3.4 | https://github.com/rust-lang/glob | registry+https://github.com/rust-lang/crates.io-index +- hashbrown 0.17.1 | https://github.com/rust-lang/hashbrown | registry+https://github.com/rust-lang/crates.io-index +- heck 0.4.1 | https://github.com/withoutboats/heck | registry+https://github.com/rust-lang/crates.io-index +- heck 0.5.0 | https://github.com/withoutboats/heck | registry+https://github.com/rust-lang/crates.io-index +- hermit-abi 0.5.2 | https://github.com/hermit-os/hermit-rs | registry+https://github.com/rust-lang/crates.io-index +- html5ever 0.38.0 | https://github.com/servo/html5ever | registry+https://github.com/rust-lang/crates.io-index +- httparse 1.10.1 | https://github.com/seanmonstar/httparse | registry+https://github.com/rust-lang/crates.io-index +- idna 1.1.0 | https://github.com/servo/rust-url/ | registry+https://github.com/rust-lang/crates.io-index +- idna_adapter 1.2.1 | https://github.com/hsivonen/idna_adapter | registry+https://github.com/rust-lang/crates.io-index +- indexmap 2.14.0 | https://github.com/indexmap-rs/indexmap | registry+https://github.com/rust-lang/crates.io-index +- jni 0.21.1 | https://github.com/jni-rs/jni-rs | registry+https://github.com/rust-lang/crates.io-index +- js-sys 0.3.103 | https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/js-sys | registry+https://github.com/rust-lang/crates.io-index +- libappindicator 0.9.0 | | registry+https://github.com/rust-lang/crates.io-index +- linux-raw-sys 0.12.1 | https://github.com/sunfishcode/linux-raw-sys | registry+https://github.com/rust-lang/crates.io-index +- lock_api 0.4.14 | https://github.com/Amanieu/parking_lot | registry+https://github.com/rust-lang/crates.io-index +- log 0.4.33 | https://github.com/rust-lang/log | registry+https://github.com/rust-lang/crates.io-index +- markup5ever 0.38.0 | https://github.com/servo/html5ever | registry+https://github.com/rust-lang/crates.io-index +- mime 0.3.17 | https://github.com/hyperium/mime | registry+https://github.com/rust-lang/crates.io-index +- muda 0.19.3 | https://github.com/tauri-apps/muda | registry+https://github.com/rust-lang/crates.io-index +- once_cell 1.21.4 | https://github.com/matklad/once_cell | registry+https://github.com/rust-lang/crates.io-index +- ordered-stream 0.2.0 | https://github.com/danieldg/ordered-stream | registry+https://github.com/rust-lang/crates.io-index +- parking 2.2.1 | https://github.com/smol-rs/parking | registry+https://github.com/rust-lang/crates.io-index +- parking_lot 0.12.5 | https://github.com/Amanieu/parking_lot | registry+https://github.com/rust-lang/crates.io-index +- parking_lot_core 0.9.12 | https://github.com/Amanieu/parking_lot | registry+https://github.com/rust-lang/crates.io-index +- percent-encoding 2.3.2 | https://github.com/servo/rust-url/ | registry+https://github.com/rust-lang/crates.io-index +- piper 0.2.5 | https://github.com/smol-rs/piper | registry+https://github.com/rust-lang/crates.io-index +- png 0.17.16 | https://github.com/image-rs/image-png | registry+https://github.com/rust-lang/crates.io-index +- png 0.18.1 | https://github.com/image-rs/image-png | registry+https://github.com/rust-lang/crates.io-index +- polling 3.11.0 | https://github.com/smol-rs/polling | registry+https://github.com/rust-lang/crates.io-index +- regex-automata 0.4.16 | https://github.com/rust-lang/regex | registry+https://github.com/rust-lang/crates.io-index +- regex-syntax 0.8.11 | https://github.com/rust-lang/regex | registry+https://github.com/rust-lang/crates.io-index +- regex 1.13.1 | https://github.com/rust-lang/regex | registry+https://github.com/rust-lang/crates.io-index +- rustix 1.1.4 | https://github.com/bytecodealliance/rustix | registry+https://github.com/rust-lang/crates.io-index +- scopeguard 1.2.0 | https://github.com/bluss/scopeguard | registry+https://github.com/rust-lang/crates.io-index +- serde_with 3.21.0 | https://github.com/jonasbb/serde_with/ | registry+https://github.com/rust-lang/crates.io-index +- serde_with_macros 3.21.0 | https://github.com/jonasbb/serde_with/ | registry+https://github.com/rust-lang/crates.io-index +- servo_arc 0.4.3 | https://github.com/servo/stylo | registry+https://github.com/rust-lang/crates.io-index +- signal-hook-registry 1.4.8 | https://github.com/vorner/signal-hook | registry+https://github.com/rust-lang/crates.io-index +- signal-hook 0.3.18 | https://github.com/vorner/signal-hook | registry+https://github.com/rust-lang/crates.io-index +- smallvec 1.15.2 | https://github.com/servo/rust-smallvec | registry+https://github.com/rust-lang/crates.io-index +- socket2 0.6.5 | https://github.com/rust-lang/socket2 | registry+https://github.com/rust-lang/crates.io-index +- stable_deref_trait 1.2.1 | https://github.com/storyyeller/stable_deref_trait | registry+https://github.com/rust-lang/crates.io-index +- string_cache 0.9.0 | https://github.com/servo/string-cache | registry+https://github.com/rust-lang/crates.io-index +- syn 1.0.109 | https://github.com/dtolnay/syn | registry+https://github.com/rust-lang/crates.io-index +- tao-macros 0.1.4 | https://github.com/tauri-apps/tao | registry+https://github.com/rust-lang/crates.io-index +- tempfile 3.27.0 | https://github.com/Stebalien/tempfile | registry+https://github.com/rust-lang/crates.io-index +- tendril 0.5.1 | https://github.com/servo/html5ever | registry+https://github.com/rust-lang/crates.io-index +- toml_datetime 0.6.3 | https://github.com/toml-rs/toml | registry+https://github.com/rust-lang/crates.io-index +- tray-icon 0.24.2 | https://github.com/tauri-apps/tray-icon | registry+https://github.com/rust-lang/crates.io-index +- unicode-segmentation 1.13.3 | https://github.com/unicode-rs/unicode-segmentation | registry+https://github.com/rust-lang/crates.io-index +- url 2.5.8 | https://github.com/servo/rust-url | registry+https://github.com/rust-lang/crates.io-index +- uuid 1.24.0 | https://github.com/uuid-rs/uuid | registry+https://github.com/rust-lang/crates.io-index +- wasi 0.11.1+wasi-snapshot-preview1 | https://github.com/bytecodealliance/wasi | registry+https://github.com/rust-lang/crates.io-index +- wasm-bindgen-futures 0.4.76 | https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/futures | registry+https://github.com/rust-lang/crates.io-index +- wasm-bindgen-macro-support 0.2.126 | https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro-support | registry+https://github.com/rust-lang/crates.io-index +- wasm-bindgen-macro 0.2.126 | https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro | registry+https://github.com/rust-lang/crates.io-index +- wasm-bindgen-shared 0.2.126 | https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/shared | registry+https://github.com/rust-lang/crates.io-index +- wasm-bindgen 0.2.126 | https://github.com/wasm-bindgen/wasm-bindgen | registry+https://github.com/rust-lang/crates.io-index +- web-sys 0.3.103 | https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/web-sys | registry+https://github.com/rust-lang/crates.io-index +- web_atoms 0.2.5 | https://github.com/servo/html5ever | registry+https://github.com/rust-lang/crates.io-index +- window-vibrancy 0.6.0 | https://github.com/tauri-apps/tauri-plugin-vibrancy | registry+https://github.com/rust-lang/crates.io-index +- wit-bindgen 0.46.0 | https://github.com/bytecodealliance/wit-bindgen | registry+https://github.com/rust-lang/crates.io-index +- wry 0.55.1 | https://github.com/tauri-apps/wry | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- bit-set 0.8.0 | https://github.com/contain-rs/bit-set | registry+https://github.com/rust-lang/crates.io-index +- bit-vec 0.8.0 | https://github.com/contain-rs/bit-vec | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- block-buffer 0.10.4 | https://github.com/RustCrypto/utils | registry+https://github.com/rust-lang/crates.io-index +- block-buffer 0.12.1 | https://github.com/RustCrypto/utils | registry+https://github.com/rust-lang/crates.io-index +- const-oid 0.10.2 | https://github.com/RustCrypto/formats | registry+https://github.com/rust-lang/crates.io-index +- cpufeatures 0.2.17 | https://github.com/RustCrypto/utils | registry+https://github.com/rust-lang/crates.io-index +- cpufeatures 0.3.0 | https://github.com/RustCrypto/utils | registry+https://github.com/rust-lang/crates.io-index +- crypto-common 0.1.7 | https://github.com/RustCrypto/traits | registry+https://github.com/rust-lang/crates.io-index +- crypto-common 0.2.2 | https://github.com/RustCrypto/traits | registry+https://github.com/rust-lang/crates.io-index +- digest 0.10.7 | https://github.com/RustCrypto/traits | registry+https://github.com/rust-lang/crates.io-index +- digest 0.11.3 | https://github.com/RustCrypto/traits | registry+https://github.com/rust-lang/crates.io-index +- hybrid-array 0.4.13 | https://github.com/RustCrypto/hybrid-array | registry+https://github.com/rust-lang/crates.io-index +- sha2 0.10.9 | https://github.com/RustCrypto/hashes | registry+https://github.com/rust-lang/crates.io-index +- sha2 0.11.0 | https://github.com/RustCrypto/hashes | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- rand_core 0.9.5 | https://github.com/rust-random/rand | registry+https://github.com/rust-lang/crates.io-index + + Apache License + Version 2.0, January 2004 + https://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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- getrandom 0.2.17 | https://github.com/rust-random/getrandom | registry+https://github.com/rust-lang/crates.io-index +- getrandom 0.3.4 | https://github.com/rust-random/getrandom | registry+https://github.com/rust-lang/crates.io-index +- getrandom 0.4.3 | https://github.com/rust-random/getrandom | registry+https://github.com/rust-lang/crates.io-index + + Apache License + Version 2.0, January 2004 + https://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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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 + + https://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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- adler2 2.0.1 | https://github.com/oyvindln/adler2 | registry+https://github.com/rust-lang/crates.io-index +- cargo-platform 0.1.9 | https://github.com/rust-lang/cargo | registry+https://github.com/rust-lang/crates.io-index +- proc-macro-crate 1.3.1 | https://github.com/bkchr/proc-macro-crate | registry+https://github.com/rust-lang/crates.io-index +- proc-macro-crate 2.0.2 | https://github.com/bkchr/proc-macro-crate | registry+https://github.com/rust-lang/crates.io-index +- proc-macro-crate 3.5.0 | https://github.com/bkchr/proc-macro-crate | registry+https://github.com/rust-lang/crates.io-index + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/LICENSE-2.0 + +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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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 + + https://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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- proc-macro-error 1.0.4 | https://gitlab.com/CreepySkeleton/proc-macro-error | registry+https://github.com/rust-lang/crates.io-index + + 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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2019-2020 CreepySkeleton + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- enumflags2 0.7.12 | https://github.com/meithecatte/enumflags2 | registry+https://github.com/rust-lang/crates.io-index + +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 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 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: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +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 +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 those 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 2017-2023 Maik Klein, Maja Kądziołka + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- enumflags2_derive 0.7.12 | https://github.com/meithecatte/enumflags2 | registry+https://github.com/rust-lang/crates.io-index + +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 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 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: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +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 +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 those 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 [2017] [Maik Klein] + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- dbus 0.9.12 | https://github.com/diwic/dbus-rs | registry+https://github.com/rust-lang/crates.io-index +- libdbus-sys 0.2.7 | https://github.com/diwic/dbus-rs | registry+https://github.com/rust-lang/crates.io-index + +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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014-2018 David Henningsson and other contributors + + 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. + + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- softbuffer 0.4.8 | https://github.com/rust-windowing/softbuffer | registry+https://github.com/rust-lang/crates.io-index + +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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Kirill Chibisov + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- dpi 0.1.2 | https://github.com/rust-windowing/winit | registry+https://github.com/rust-lang/crates.io-index +- tao 0.35.3 | https://github.com/tauri-apps/tao | registry+https://github.com/rust-lang/crates.io-index + +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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- ctor-proc-macro 0.0.7 | https://github.com/mmastrac/rust-ctor | registry+https://github.com/rust-lang/crates.io-index +- ctor 0.8.0 | https://github.com/mmastrac/rust-ctor | registry+https://github.com/rust-lang/crates.io-index + +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 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 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 those 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- bytemuck 1.25.2 | https://github.com/Lokathor/bytemuck | registry+https://github.com/rust-lang/crates.io-index + +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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- android_log-sys 0.3.2 | https://github.com/rust-mobile/android_log-sys-rs | registry+https://github.com/rust-lang/crates.io-index + +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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "{}" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright 2016 The android_log_sys Developers + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- android_logger 0.15.1 | https://github.com/rust-mobile/android_logger-rs | registry+https://github.com/rust-lang/crates.io-index + +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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "{}" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright 2016 The android_logger Developers + +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. + +------------------------------------------------------------------------------- +License: Apache License 2.0 (Apache-2.0) +Used by: +- anyhow 1.0.104 | https://github.com/dtolnay/anyhow | registry+https://github.com/rust-lang/crates.io-index +- async-trait 0.1.91 | https://github.com/dtolnay/async-trait | registry+https://github.com/rust-lang/crates.io-index +- cesu8 1.1.0 | https://github.com/emk/cesu8-rs | registry+https://github.com/rust-lang/crates.io-index +- dirs-sys 0.5.0 | https://github.com/dirs-dev/dirs-sys-rs | registry+https://github.com/rust-lang/crates.io-index +- dirs 6.0.0 | https://github.com/soc/dirs-rs | registry+https://github.com/rust-lang/crates.io-index +- dispatch2 0.3.1 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- dtoa 1.0.11 | https://github.com/dtolnay/dtoa | registry+https://github.com/rust-lang/crates.io-index +- dunce 1.0.5 | https://gitlab.com/kornelski/dunce | registry+https://github.com/rust-lang/crates.io-index +- dyn-clone 1.0.20 | https://github.com/dtolnay/dyn-clone | registry+https://github.com/rust-lang/crates.io-index +- erased-serde 0.4.10 | https://github.com/dtolnay/erased-serde | registry+https://github.com/rust-lang/crates.io-index +- fdeflate 0.3.7 | https://github.com/image-rs/fdeflate | registry+https://github.com/rust-lang/crates.io-index +- field-offset 0.3.6 | https://github.com/Diggsey/rust-field-offset | registry+https://github.com/rust-lang/crates.io-index +- ident_case 1.0.1 | https://github.com/TedDriggs/ident_case | registry+https://github.com/rust-lang/crates.io-index +- itoa 1.0.18 | https://github.com/dtolnay/itoa | registry+https://github.com/rust-lang/crates.io-index +- jni-sys-macros 0.4.1 | https://github.com/jni-rs/jni-sys | registry+https://github.com/rust-lang/crates.io-index +- libappindicator-sys 0.9.0 | | registry+https://github.com/rust-lang/crates.io-index +- libc 0.2.189 | https://github.com/rust-lang/libc | registry+https://github.com/rust-lang/crates.io-index +- miniz_oxide 0.8.9 | https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide | registry+https://github.com/rust-lang/crates.io-index +- ndk-sys 0.6.0+11769913 | https://github.com/rust-mobile/ndk | registry+https://github.com/rust-lang/crates.io-index +- ndk 0.9.0 | https://github.com/rust-mobile/ndk | registry+https://github.com/rust-lang/crates.io-index +- num-conv 0.2.2 | https://github.com/jhpratt/num-conv | registry+https://github.com/rust-lang/crates.io-index +- num_enum 0.7.6 | https://github.com/illicitonion/num_enum | registry+https://github.com/rust-lang/crates.io-index +- num_enum_derive 0.7.6 | https://github.com/illicitonion/num_enum | registry+https://github.com/rust-lang/crates.io-index +- objc2-app-kit 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-cloud-kit 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-core-data 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-core-foundation 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-core-graphics 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-core-image 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-core-location 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-core-text 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-exception-helper 0.1.1 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-quartz-core 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-ui-kit 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-user-notifications 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-web-kit 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- pin-project-lite 0.2.17 | https://github.com/taiki-e/pin-project-lite | registry+https://github.com/rust-lang/crates.io-index +- proc-macro2 1.0.107 | https://github.com/dtolnay/proc-macro2 | registry+https://github.com/rust-lang/crates.io-index +- process-wrap 9.1.0 | https://github.com/watchexec/process-wrap | registry+https://github.com/rust-lang/crates.io-index +- quote 1.0.47 | https://github.com/dtolnay/quote | registry+https://github.com/rust-lang/crates.io-index +- r-efi 5.3.0 | https://github.com/r-efi/r-efi | registry+https://github.com/rust-lang/crates.io-index +- r-efi 6.0.0 | https://github.com/r-efi/r-efi | registry+https://github.com/rust-lang/crates.io-index +- rand 0.9.5 | https://github.com/rust-random/rand | registry+https://github.com/rust-lang/crates.io-index +- rand_chacha 0.9.0 | https://github.com/rust-random/rand | registry+https://github.com/rust-lang/crates.io-index +- raw-window-handle 0.6.2 | https://github.com/rust-windowing/raw-window-handle | registry+https://github.com/rust-lang/crates.io-index +- rustc-hash 2.1.3 | https://github.com/rust-lang/rustc-hash | registry+https://github.com/rust-lang/crates.io-index +- rustversion 1.0.23 | https://github.com/dtolnay/rustversion | registry+https://github.com/rust-lang/crates.io-index +- semver 1.0.28 | https://github.com/dtolnay/semver | registry+https://github.com/rust-lang/crates.io-index +- serde-untagged 0.1.9 | https://github.com/dtolnay/serde-untagged | registry+https://github.com/rust-lang/crates.io-index +- serde 1.0.229 | https://github.com/serde-rs/serde | registry+https://github.com/rust-lang/crates.io-index +- serde_core 1.0.229 | https://github.com/serde-rs/serde | registry+https://github.com/rust-lang/crates.io-index +- serde_derive 1.0.229 | https://github.com/serde-rs/serde | registry+https://github.com/rust-lang/crates.io-index +- serde_derive_internals 0.29.1 | https://github.com/serde-rs/serde | registry+https://github.com/rust-lang/crates.io-index +- serde_json 1.0.151 | https://github.com/serde-rs/json | registry+https://github.com/rust-lang/crates.io-index +- serde_repr 0.1.21 | https://github.com/dtolnay/serde-repr | registry+https://github.com/rust-lang/crates.io-index +- siphasher 1.0.3 | https://github.com/jedisct1/rust-siphash | registry+https://github.com/rust-lang/crates.io-index +- syn 2.0.119 | https://github.com/dtolnay/syn | registry+https://github.com/rust-lang/crates.io-index +- syn 3.0.3 | https://github.com/dtolnay/syn | registry+https://github.com/rust-lang/crates.io-index +- sync_wrapper 1.0.2 | https://github.com/Actyx/sync_wrapper | registry+https://github.com/rust-lang/crates.io-index +- tauri-codegen 2.6.3 | https://github.com/tauri-apps/tauri | registry+https://github.com/rust-lang/crates.io-index +- tauri-macros 2.6.3 | https://github.com/tauri-apps/tauri | registry+https://github.com/rust-lang/crates.io-index +- tauri-plugin-dialog 2.7.2 | https://github.com/tauri-apps/plugins-workspace | registry+https://github.com/rust-lang/crates.io-index +- tauri-plugin-fs 2.5.1 | https://github.com/tauri-apps/plugins-workspace | registry+https://github.com/rust-lang/crates.io-index +- tauri-plugin-log 2.9.0 | https://github.com/tauri-apps/plugins-workspace | registry+https://github.com/rust-lang/crates.io-index +- tauri-plugin-opener 2.5.4 | https://github.com/tauri-apps/plugins-workspace | registry+https://github.com/rust-lang/crates.io-index +- tauri-plugin-shell 2.3.5 | https://github.com/tauri-apps/plugins-workspace | registry+https://github.com/rust-lang/crates.io-index +- tauri-plugin-single-instance 2.4.3 | https://github.com/tauri-apps/plugins-workspace | registry+https://github.com/rust-lang/crates.io-index +- tauri-plugin-store 2.4.4 | https://github.com/tauri-apps/plugins-workspace | registry+https://github.com/rust-lang/crates.io-index +- tauri-runtime-wry 2.11.4 | https://github.com/tauri-apps/tauri | registry+https://github.com/rust-lang/crates.io-index +- tauri-runtime 2.11.3 | https://github.com/tauri-apps/tauri | registry+https://github.com/rust-lang/crates.io-index +- tauri-utils 2.9.3 | https://github.com/tauri-apps/tauri | registry+https://github.com/rust-lang/crates.io-index +- tauri 2.11.5 | https://github.com/tauri-apps/tauri | registry+https://github.com/rust-lang/crates.io-index +- thiserror-impl 1.0.69 | https://github.com/dtolnay/thiserror | registry+https://github.com/rust-lang/crates.io-index +- thiserror-impl 2.0.19 | https://github.com/dtolnay/thiserror | registry+https://github.com/rust-lang/crates.io-index +- thiserror 1.0.69 | https://github.com/dtolnay/thiserror | registry+https://github.com/rust-lang/crates.io-index +- thiserror 2.0.19 | https://github.com/dtolnay/thiserror | registry+https://github.com/rust-lang/crates.io-index +- time-core 0.1.8 | https://github.com/time-rs/time | registry+https://github.com/rust-lang/crates.io-index +- time-macros 0.2.27 | https://github.com/time-rs/time | registry+https://github.com/rust-lang/crates.io-index +- time 0.3.47 | https://github.com/time-rs/time | registry+https://github.com/rust-lang/crates.io-index +- typeid 1.0.3 | https://github.com/dtolnay/typeid | registry+https://github.com/rust-lang/crates.io-index +- unic-char-property 0.9.0 | https://github.com/open-i18n/rust-unic/ | registry+https://github.com/rust-lang/crates.io-index +- unic-char-range 0.9.0 | https://github.com/open-i18n/rust-unic/ | registry+https://github.com/rust-lang/crates.io-index +- unic-common 0.9.0 | https://github.com/open-i18n/rust-unic/ | registry+https://github.com/rust-lang/crates.io-index +- unic-ucd-ident 0.9.0 | https://github.com/open-i18n/rust-unic/ | registry+https://github.com/rust-lang/crates.io-index +- unic-ucd-version 0.9.0 | https://github.com/open-i18n/rust-unic/ | registry+https://github.com/rust-lang/crates.io-index +- unicode-ident 1.0.24 | https://github.com/dtolnay/unicode-ident | registry+https://github.com/rust-lang/crates.io-index +- wasip2 1.0.1+wasi-0.2.4 | https://github.com/bytecodealliance/wasi-rs | registry+https://github.com/rust-lang/crates.io-index +- wasm-streams 0.5.0 | https://github.com/MattiasBuelens/wasm-streams/ | registry+https://github.com/rust-lang/crates.io-index +- winapi-i686-pc-windows-gnu 0.4.0 | https://github.com/retep998/winapi-rs | registry+https://github.com/rust-lang/crates.io-index +- winapi-x86_64-pc-windows-gnu 0.4.0 | https://github.com/retep998/winapi-rs | registry+https://github.com/rust-lang/crates.io-index + +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 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 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 those 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 + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. + +------------------------------------------------------------------------------- +License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause) +Used by: +- alloc-no-stdlib 2.0.4 | https://github.com/dropbox/rust-alloc-no-stdlib | registry+https://github.com/rust-lang/crates.io-index +- brotli 8.0.4 | https://github.com/dropbox/rust-brotli | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2016 Dropbox, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause) +Used by: +- alloc-stdlib 0.2.4 | https://github.com/dropbox/rust-alloc-no-stdlib | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) . + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause) +Used by: +- encoding_rs 0.8.35 | https://github.com/hsivonen/encoding_rs | registry+https://github.com/rust-lang/crates.io-index + +Copyright © WHATWG (Apple, Google, Mozilla, Microsoft). + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause) +Used by: +- atomic-write-file 0.3.0 | https://github.com/andreacorbellini/rust-atomic-write-file | registry+https://github.com/rust-lang/crates.io-index + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +License: ISC License (ISC) +Used by: +- libloading 0.7.4 | https://github.com/nagisa/rust_libloading/ | registry+https://github.com/rust-lang/crates.io-index + +Copyright © 2015, Simonas Kazlauskas + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without +fee is hereby granted, provided that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- uds_windows 1.2.1 | https://github.com/haraldh/rust_uds_windows | registry+https://github.com/rust-lang/crates.io-index + + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- brotli 8.0.4 | https://github.com/dropbox/rust-brotli | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- mio 1.2.2 | https://github.com/tokio-rs/mio | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2014 Carl Lerche and other MIO contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- fern 0.7.1 | https://github.com/daboross/fern | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2014-2017 David Ross + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- hyper 1.11.0 | https://github.com/hyperium/hyper | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2014-2026 Sean McArthur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- plist 1.8.0 | https://github.com/ebarnard/rust-plist/ | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2015 Edward Barnard + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- new_debug_unreachable 1.0.6 | https://github.com/mbrubeck/rust-debug-unreachable | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2015 Jonathan Reem + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- which 8.0.5 | https://github.com/harryfei/which-rs.git | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2015 fangyuanziti + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- webkit2gtk-sys 2.0.2 | https://github.com/tauri-apps/webkit2gtk-rs | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2016 Boucher, Antoni + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- webkit2gtk 2.0.2 | https://github.com/tauri-apps/webkit2gtk-rs | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2016 Boucher, Antoni +Copyright (c) 2017-2021, The Gtk-rs Project Developers. +Copyright (c) 2021, Tauri Programme within The Commons Conservancy + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- memoffset 0.9.1 | https://github.com/Gilnaa/memoffset | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2017 Gilad Naaman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- redox_syscall 0.5.18 | https://gitlab.redox-os.org/redox-os/syscall | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2017 Redox OS Developers + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- bytes 1.12.1 | https://github.com/tokio-rs/bytes | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2018 Carl Lerche + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- want 0.3.1 | https://github.com/seanmonstar/want | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2018-2019 Sean McArthur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- try-lock 0.2.5 | https://github.com/seanmonstar/try-lock | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2018-2023 Sean McArthur +Copyright (c) 2016 Alex Crichton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- slab 0.4.12 | https://github.com/tokio-rs/slab | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2019 Carl Lerche + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- tracing-attributes 0.1.31 | https://github.com/tokio-rs/tracing | registry+https://github.com/rust-lang/crates.io-index +- tracing-core 0.1.36 | https://github.com/tokio-rs/tracing | registry+https://github.com/rust-lang/crates.io-index +- tracing 0.1.44 | https://github.com/tokio-rs/tracing | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2019 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- tower-layer 0.3.3 | https://github.com/tower-rs/tower | registry+https://github.com/rust-lang/crates.io-index +- tower-service 0.3.3 | https://github.com/tower-rs/tower | registry+https://github.com/rust-lang/crates.io-index +- tower 0.5.3 | https://github.com/tower-rs/tower | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2019 Tower Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- tower-http 0.6.11 | https://github.com/tower-rs/tower-http | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2019-2021 Tower Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- http-body-util 0.1.4 | https://github.com/hyperium/http-body | registry+https://github.com/rust-lang/crates.io-index +- http-body 1.1.0 | https://github.com/hyperium/http-body | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2019-2026 Sean McArthur & Hyper Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- hyper-util 0.1.20 | https://github.com/hyperium/hyper-util | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2023-2025 Sean McArthur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- zbus 5.13.2 | https://github.com/z-galaxy/zbus/ | registry+https://github.com/rust-lang/crates.io-index +- zbus_macros 5.13.2 | https://github.com/z-galaxy/zbus/ | registry+https://github.com/rust-lang/crates.io-index +- zbus_names 4.3.1 | https://github.com/z-galaxy/zbus/ | registry+https://github.com/rust-lang/crates.io-index +- zvariant 5.9.2 | https://github.com/z-galaxy/zbus/ | registry+https://github.com/rust-lang/crates.io-index +- zvariant_derive 5.9.2 | https://github.com/z-galaxy/zbus/ | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2024 Zeeshan Ali Khan & zbus contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- synstructure 0.13.2 | https://github.com/mystor/synstructure | registry+https://github.com/rust-lang/crates.io-index + +Copyright 2016 Nika Layzell + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- precomputed-hash 0.1.1 | https://github.com/emilio/precomputed-hash | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2017 Emilio Cobos Álvarez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- cfb 0.7.3 | https://github.com/mdsteele/rust-cfb | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2017 Matthew D. Steele + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- darling 0.23.0 | https://github.com/TedDriggs/darling | registry+https://github.com/rust-lang/crates.io-index +- darling_core 0.23.0 | https://github.com/TedDriggs/darling | registry+https://github.com/rust-lang/crates.io-index +- darling_macro 0.23.0 | https://github.com/TedDriggs/darling | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2017 Ted Driggs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- ico 0.5.0 | https://github.com/mdsteele/rust-ico | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2018 Matthew D. Steele + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- infer 0.19.0 | https://github.com/bojand/infer | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2019 Bojan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- schemars 0.8.22 | https://github.com/GREsau/schemars | registry+https://github.com/rust-lang/crates.io-index +- schemars_derive 0.8.22 | https://github.com/GREsau/schemars | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2019 Graham Esau + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- tokio-macros 2.7.2 | https://github.com/tokio-rs/tokio | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2019 Yoshua Wuyts +Copyright (c) Tokio Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- urlpattern 0.3.0 | https://github.com/denoland/rust-urlpattern | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2021 the Deno authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- rfd 0.16.0 | https://github.com/PolyMeilex/rfd | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2022 Bartłomiej Maryńczak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- libredox 0.1.18 | https://gitlab.redox-os.org/redox-os/libredox.git | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2023 4lDO2 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- dom_query 0.27.0 | https://github.com/niklak/dom_query | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2023 Mykola Humanov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +This project contains portions of code and architectural concepts originally +derived from the "nipper" project (https://github.com/importcjj/nipper), +developed by Chen Jiaju, licensed under the MIT License and the Apache License 2.0 (dual licensed). +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- is-docker 0.2.0 | https://github.com/TheLarkInn/is-docker | registry+https://github.com/rust-lang/crates.io-index +- is-wsl 0.4.0 | https://github.com/TheLarkInn/is-wsl | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) 2023 Sean Larkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- vidxp-desktop 0.4.0-b | | +- block2 0.6.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- brotli-decompressor 5.0.3 | https://github.com/dropbox/rust-brotli-decompressor | registry+https://github.com/rust-lang/crates.io-index +- dlopen2 0.8.2 | https://github.com/OpenByteDev/dlopen2 | registry+https://github.com/rust-lang/crates.io-index +- dlopen2_derive 0.4.3 | https://github.com/OpenByteDev/dlopen2 | registry+https://github.com/rust-lang/crates.io-index +- dpi 0.1.2 | https://github.com/rust-windowing/winit | registry+https://github.com/rust-lang/crates.io-index +- objc2-encode 4.1.0 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2-foundation 0.3.2 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- objc2 0.6.4 | https://github.com/madsmtm/objc2 | registry+https://github.com/rust-lang/crates.io-index +- sigchld 0.2.4 | https://github.com/oconnor663/sigchld.rs | registry+https://github.com/rust-lang/crates.io-index +- webview2-com-macros 0.8.1 | https://github.com/wravery/webview2-rs | registry+https://github.com/rust-lang/crates.io-index +- webview2-com-sys 0.38.2 | https://github.com/wravery/webview2-rs | registry+https://github.com/rust-lang/crates.io-index +- webview2-com 0.38.2 | https://github.com/wravery/webview2-rs | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- tokio-util 0.7.19 | https://github.com/tokio-rs/tokio | registry+https://github.com/rust-lang/crates.io-index +- tokio 1.53.1 | https://github.com/tokio-rs/tokio | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) Tokio Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- simd-adler32 0.3.10 | https://github.com/mcountryman/simd-adler32 | registry+https://github.com/rust-lang/crates.io-index + +MIT License + +Copyright (c) [2021] [Marvin Countryman] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- cargo_metadata 0.19.2 | https://github.com/oli-obk/cargo_metadata | registry+https://github.com/rust-lang/crates.io-index +- endi 1.1.1 | https://github.com/zeenix/endi | registry+https://github.com/rust-lang/crates.io-index +- x11-dl 2.21.0 | https://github.com/AltF02/x11-rs.git | registry+https://github.com/rust-lang/crates.io-index +- x11 2.21.0 | https://github.com/AltF02/x11-rs.git | registry+https://github.com/rust-lang/crates.io-index +- zmij 1.0.23 | https://github.com/dtolnay/zmij | registry+https://github.com/rust-lang/crates.io-index +- zvariant_utils 3.3.0 | https://github.com/z-galaxy/zbus/ | registry+https://github.com/rust-lang/crates.io-index + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- winnow 0.5.40 | https://github.com/winnow-rs/winnow | registry+https://github.com/rust-lang/crates.io-index +- winnow 0.7.15 | https://github.com/winnow-rs/winnow | registry+https://github.com/rust-lang/crates.io-index +- winnow 1.0.4 | https://github.com/winnow-rs/winnow | registry+https://github.com/rust-lang/crates.io-index + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- atk-sys 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- atk 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- cairo-rs 0.18.5 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- cairo-sys-rs 0.18.2 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- gdk-pixbuf-sys 0.18.0 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- gdk-pixbuf 0.18.5 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- gdk-sys 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- gdk 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- gdkwayland-sys 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- gdkx11-sys 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- gdkx11 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- gio-sys 0.18.1 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- gio 0.18.4 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- glib-macros 0.18.5 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- glib-sys 0.18.1 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- glib 0.18.5 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- gobject-sys 0.18.0 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- gtk-sys 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- gtk3-macros 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- gtk 0.18.2 | https://github.com/gtk-rs/gtk3-rs | registry+https://github.com/rust-lang/crates.io-index +- pango-sys 0.18.0 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index +- pango 0.18.3 | https://github.com/gtk-rs/gtk-rs-core | registry+https://github.com/rust-lang/crates.io-index + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- javascriptcore-rs-sys 1.1.1 | https://github.com/tauri-apps/javascriptcore-rs | registry+https://github.com/rust-lang/crates.io-index +- soup3-sys 0.5.0 | https://gitlab.gnome.org/World/Rust/soup3-rs | registry+https://github.com/rust-lang/crates.io-index +- soup3 0.5.0 | https://gitlab.gnome.org/World/Rust/soup3-rs | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2013-2017, The Gtk-rs Project Developers. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- javascriptcore-rs 1.1.2 | https://github.com/tauri-apps/javascriptcore-rs | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2013-2021, The Gtk-rs Project Developers. +Copyright (c) 2021, Tauri Programme within The Commons Conservancy. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- phf 0.13.1 | https://github.com/rust-phf/rust-phf | registry+https://github.com/rust-lang/crates.io-index +- phf_generator 0.13.1 | https://github.com/rust-phf/rust-phf | registry+https://github.com/rust-lang/crates.io-index +- phf_macros 0.13.1 | https://github.com/rust-phf/rust-phf | registry+https://github.com/rust-lang/crates.io-index +- phf_shared 0.13.1 | https://github.com/rust-phf/rust-phf | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2014-2022 Steven Fackler, Yuki Okushi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- aho-corasick 1.1.4 | https://github.com/BurntSushi/aho-corasick | registry+https://github.com/rust-lang/crates.io-index +- byteorder 1.5.0 | https://github.com/BurntSushi/byteorder | registry+https://github.com/rust-lang/crates.io-index +- memchr 2.8.3 | https://github.com/BurntSushi/memchr | registry+https://github.com/rust-lang/crates.io-index +- walkdir 2.5.0 | https://github.com/BurntSushi/walkdir | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- nix 0.30.1 | https://github.com/nix-rust/nix | registry+https://github.com/rust-lang/crates.io-index +- nix 0.31.3 | https://github.com/nix-rust/nix | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2015 Carl Lerche + nix-rust Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- strsim 0.11.1 | https://github.com/rapidfuzz/strsim-rs | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2015 Danny Guo +Copyright (c) 2016 Titus Wormer +Copyright (c) 2018 Akash Kurdekar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- combine 4.6.7 | https://github.com/Marwes/combine | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2015 Markus Westerlind + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- derive_more-impl 2.1.1 | https://github.com/JelteF/derive_more | registry+https://github.com/rust-lang/crates.io-index +- derive_more 2.1.1 | https://github.com/JelteF/derive_more | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2016 Jelte Fennema + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- same-file 1.0.6 | https://github.com/BurntSushi/same-file | registry+https://github.com/rust-lang/crates.io-index +- winapi-util 0.1.11 | https://github.com/BurntSushi/winapi-util | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2017 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- redox_users 0.5.2 | https://gitlab.redox-os.org/redox-os/users | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2017 Jose Narvaez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- os_pipe 1.2.3 | https://github.com/oconnor663/os_pipe.rs | registry+https://github.com/rust-lang/crates.io-index +- shared_child 1.1.1 | https://github.com/oconnor663/shared_child.rs | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- open 5.4.0 | https://github.com/Byron/open-rs | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) +===================== + +Copyright © `2015` `Sebastian Thiel` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- generic-array 0.14.7 | https://github.com/fizyk20/generic-array.git | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2015 Bartłomiej Kamiński + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------- +License: MIT License (MIT) +Used by: +- quick-xml 0.38.4 | https://github.com/tafia/quick-xml | registry+https://github.com/rust-lang/crates.io-index + +The MIT License (MIT) + +Copyright (c) 2016 Johann Tuffe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- +License: Mozilla Public License 2.0 (MPL-2.0) +Used by: +- dtoa-short 0.3.5 | https://github.com/upsuper/dtoa-short | registry+https://github.com/rust-lang/crates.io-index + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +------------------------------------------------------------------------------- +License: Mozilla Public License 2.0 (MPL-2.0) +Used by: +- cssparser-macros 0.6.1 | https://github.com/servo/rust-cssparser | registry+https://github.com/rust-lang/crates.io-index +- cssparser 0.36.0 | https://github.com/servo/rust-cssparser | registry+https://github.com/rust-lang/crates.io-index + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +------------------------------------------------------------------------------- +License: Mozilla Public License 2.0 (MPL-2.0) +Used by: +- option-ext 0.2.0 | https://github.com/soc/option-ext.git | registry+https://github.com/rust-lang/crates.io-index +- selectors 0.36.1 | https://github.com/servo/stylo | registry+https://github.com/rust-lang/crates.io-index + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +------------------------------------------------------------------------------- +License: Unicode License v3 (Unicode-3.0) +Used by: +- unicode-ident 1.0.24 | https://github.com/dtolnay/unicode-ident | registry+https://github.com/rust-lang/crates.io-index + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2023 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +------------------------------------------------------------------------------- +License: Unicode License v3 (Unicode-3.0) +Used by: +- icu_collections 2.1.1 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- icu_locale_core 2.1.1 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- icu_normalizer 2.1.1 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- icu_normalizer_data 2.1.1 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- icu_properties 2.1.2 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- icu_properties_data 2.1.2 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- icu_provider 2.1.1 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- litemap 0.8.2 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- potential_utf 0.1.5 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- tinystr 0.8.3 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- writeable 0.6.3 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- yoke-derive 0.8.2 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- yoke 0.8.3 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- zerofrom-derive 0.1.7 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- zerofrom 0.1.8 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- zerotrie 0.2.4 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- zerovec-derive 0.11.3 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index +- zerovec 0.11.6 | https://github.com/unicode-org/icu4x | registry+https://github.com/rust-lang/crates.io-index + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. + +------------------------------------------------------------------------------- +License: zlib License (Zlib) +Used by: +- foldhash 0.2.0 | https://github.com/orlp/foldhash | registry+https://github.com/rust-lang/crates.io-index + +Copyright (c) 2024 Orson Peters + +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use of +this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject to +the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. + +FRONTEND DEPENDENCIES +===================== + +------------------------------------------------------------------------------- +@floating-ui/core@1.8.0 | MIT | https://github.com/floating-ui/floating-ui + +MIT License + +Copyright (c) 2021-present Floating UI contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +@floating-ui/dom@1.8.0 | MIT | https://github.com/floating-ui/floating-ui + +MIT License + +Copyright (c) 2021-present Floating UI contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +@floating-ui/react-dom@2.1.9 | MIT | https://github.com/floating-ui/floating-ui + +MIT License + +Copyright (c) 2021-present Floating UI contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +@floating-ui/react@0.27.20 | MIT | https://github.com/floating-ui/floating-ui + +MIT License + +Copyright (c) 2021-present Floating UI contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +@floating-ui/utils@0.2.12 | MIT | https://github.com/floating-ui/floating-ui + +MIT License + +Copyright (c) 2021-present Floating UI contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +@mantine/core@9.5.0 | MIT | https://github.com/mantinedev/mantine + +MIT License + +Copyright (c) 2021 Vitaly Rtishchev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +@mantine/hooks@9.5.0 | MIT | https://github.com/mantinedev/mantine + +MIT License + +Copyright (c) 2021 Vitaly Rtishchev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +@tabler/icons-react@3.46.0 | MIT | https://github.com/tabler/tabler-icons + +MIT License + +Copyright (c) 2020-2026 Paweł Kuna + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +@tabler/icons@3.46.0 | MIT | https://github.com/tabler/tabler-icons + +MIT License + +Copyright (c) 2020-2026 Paweł Kuna + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +@tauri-apps/api@2.11.1 | Apache-2.0 OR MIT | https://github.com/tauri-apps/tauri + +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 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 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 those 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 + +MIT License + +Copyright (c) 2017 - Present Tauri Apps Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +@types/react@19.2.18 | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE + +------------------------------------------------------------------------------- +clsx@2.1.1 | MIT | https://github.com/lukeed/clsx + +MIT License + +Copyright (c) Luke Edwards (lukeed.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +csstype@3.2.3 | MIT | https://github.com/frenic/csstype + +Copyright (c) 2017-2018 Fredrik Nicol + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +detect-node-es@1.1.0 | MIT | https://github.com/thekashey/detect-node + +MIT License + +Copyright (c) 2017 Ilya Kantor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +get-nonce@1.0.1 | MIT | https://github.com/theKashey/get-nonce + +MIT License + +Copyright (c) 2020 Anton Korzunov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +react-dom@19.2.8 | MIT | https://github.com/react/react + +MIT License + +Copyright (c) Meta Platforms, Inc. and affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +react-number-format@5.4.5 | MIT | https://github.com/s-yadav/react-number-format + +MIT License + +Copyright (c) 2020-present Sudhanshu Yadav + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +react-remove-scroll-bar@2.3.8 | MIT | https://github.com/theKashey/react-remove-scroll-bar + +Vendored verbatim upstream license for react-remove-scroll-bar@2.3.8 +Source: https://github.com/theKashey/react-remove-scroll-bar/blob/8ca9ba5ea52de03308fe8ced94f7b159a44d28ff/LICENSE +SHA-256: a79aae0c0f21990d9d963bb3c5a79cdcea9a46f8523ba55c58d7fe776b6ebc84 + +MIT License + +Copyright (c) 2025 Anton Korzunov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +react-remove-scroll@2.7.2 | MIT | https://github.com/theKashey/react-remove-scroll + +MIT License + +Copyright (c) 2017 Anton Korzunov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +react-style-singleton@2.2.3 | MIT | https://github.com/theKashey/react-style-singleton + +MIT License + +Copyright (c) 2017 Anton Korzunov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +react@19.2.8 | MIT | https://github.com/react/react + +MIT License + +Copyright (c) Meta Platforms, Inc. and affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +scheduler@0.27.0 | MIT | https://github.com/facebook/react + +MIT License + +Copyright (c) Meta Platforms, Inc. and affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +tabbable@6.5.0 | MIT | https://github.com/focus-trap/tabbable + +The MIT License (MIT) + +Copyright (c) 2015 David Clark + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +tagged-tag@1.0.0 | MIT | https://github.com/sindresorhus/tagged-tag + +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +tslib@2.8.1 | 0BSD | https://github.com/Microsoft/tslib + +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +------------------------------------------------------------------------------- +type-fest@5.8.0 | (MIT OR CC0-1.0) | https://github.com/sindresorhus/type-fest + +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------- +use-callback-ref@1.3.3 | MIT | https://github.com/theKashey/use-callback-ref + +MIT License + +Copyright (c) 2017 Anton Korzunov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- +use-sidecar@1.1.3 | MIT | https://github.com/theKashey/use-sidecar + +MIT License + +Copyright (c) 2017 Anton Korzunov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/desktop/about.hbs b/desktop/about.hbs new file mode 100644 index 0000000..fecbfca --- /dev/null +++ b/desktop/about.hbs @@ -0,0 +1,12 @@ +RUST DEPENDENCIES +================= +{{#each licenses}} +------------------------------------------------------------------------------- +License: {{name}} ({{id}}) +Used by: +{{#each used_by}} +- {{crate.name}} {{crate.version}} | {{crate.repository}} | {{crate.source}} +{{/each}} + +{{{text}}} +{{/each}} diff --git a/desktop/about.toml b/desktop/about.toml new file mode 100644 index 0000000..5753ea5 --- /dev/null +++ b/desktop/about.toml @@ -0,0 +1,14 @@ +accepted = [ + "Apache-2.0", + "MIT", + "BSD-2-Clause", + "BSD-3-Clause", + "ISC", + "MPL-2.0", + "Unicode-3.0", + "Zlib", + "OpenSSL", +] +ignore-build-dependencies = true +ignore-dev-dependencies = true +workarounds = ["ring", "rustls", "rustix"] diff --git a/desktop/eslint.config.js b/desktop/eslint.config.js new file mode 100644 index 0000000..766fffc --- /dev/null +++ b/desktop/eslint.config.js @@ -0,0 +1,31 @@ +import eslint from '@eslint/js'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { ignores: ['dist', 'src-tauri', 'web', 'scripts', 'vite.config.ts'] }, + eslint.configs.recommended, + ...tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + { + files: ['src/**/*.{ts,tsx}'], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + 'react-hooks/set-state-in-effect': 'off', + }, + }, + { + files: ['src/**/*.test.{ts,tsx}', 'src/test/**/*.{ts,tsx}'], + rules: { + '@typescript-eslint/no-explicit-any': 'off', + 'react-refresh/only-export-components': 'off', + }, + }, +); diff --git a/desktop/index.html b/desktop/index.html new file mode 100644 index 0000000..8753cd2 --- /dev/null +++ b/desktop/index.html @@ -0,0 +1,15 @@ + + + + + + + + VidXP + + + +
+ + + diff --git a/desktop/licenses/npm/react-remove-scroll-bar-2.3.8-LICENSE.txt b/desktop/licenses/npm/react-remove-scroll-bar-2.3.8-LICENSE.txt new file mode 100644 index 0000000..7c08c39 --- /dev/null +++ b/desktop/licenses/npm/react-remove-scroll-bar-2.3.8-LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Anton Korzunov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/desktop/licenses/uv-LICENSE-MIT.txt b/desktop/licenses/uv-LICENSE-MIT.txt new file mode 100644 index 0000000..986c98b --- /dev/null +++ b/desktop/licenses/uv-LICENSE-MIT.txt @@ -0,0 +1,19 @@ +MIT License + +Copyright (c) 2025 Astral Software Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/desktop/model-cache-catalog.json b/desktop/model-cache-catalog.json new file mode 100644 index 0000000..9bdf1f6 --- /dev/null +++ b/desktop/model-cache-catalog.json @@ -0,0 +1,27 @@ +[ + { + "id": "dropbox-dash/faster-whisper-large-v3-turbo", + "label": "dropbox-dash/faster-whisper-large-v3-turbo", + "relative_artifact": "models--dropbox-dash--faster-whisper-large-v3-turbo/snapshots/0a363e9161cbc7ed1431c9597a8ceaf0c4f78fcf/model.bin" + }, + { + "id": "google/siglip2-base-patch16-224", + "label": "google/siglip2-base-patch16-224", + "relative_artifact": "models--google--siglip2-base-patch16-224/snapshots/75de2d55ec2d0b4efc50b3e9ad70dba96a7b2fa2/model.safetensors" + }, + { + "id": "Qwen/Qwen3-Embedding-0.6B", + "label": "Qwen/Qwen3-Embedding-0.6B", + "relative_artifact": "models--Qwen--Qwen3-Embedding-0.6B/snapshots/97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3/model.safetensors" + }, + { + "id": "sface", + "label": "sface", + "relative_artifact": "opencv-zoo/face_recognition_sface_2021dec.onnx" + }, + { + "id": "yunet", + "label": "yunet", + "relative_artifact": "opencv-zoo/face_detection_yunet_2026may.onnx" + } +] diff --git a/desktop/package-lock.json b/desktop/package-lock.json index b039307..ab03e8e 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -7,226 +7,5239 @@ "": { "name": "vidxp-desktop", "version": "0.4.0-b", + "dependencies": { + "@mantine/core": "9.5.0", + "@tabler/icons-react": "3.46.0", + "@tauri-apps/api": "2.11.1", + "react": "19.2.8", + "react-dom": "19.2.8" + }, "devDependencies": { - "@tauri-apps/cli": "2.11.4" + "@eslint/js": "10.0.1", + "@tauri-apps/cli": "2.11.4", + "@testing-library/jest-dom": "7.0.0", + "@testing-library/react": "16.3.2", + "@testing-library/user-event": "14.6.1", + "@types/node": "26.0.0", + "@types/react": "19.2.18", + "@types/react-dom": "19.2.4", + "@vitejs/plugin-react": "6.0.5", + "eslint": "10.8.0", + "eslint-plugin-react-hooks": "7.1.1", + "eslint-plugin-react-refresh": "0.5.3", + "jsdom": "29.1.1", + "license-checker-rseidelsohn": "4.4.2", + "typescript": "6.0.3", + "typescript-eslint": "8.65.0", + "vite": "8.2.0", + "vitest": "4.1.10" } }, - "node_modules/@tauri-apps/cli": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.4.tgz", - "integrity": "sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ==", + "node_modules/@adobe/css-tools": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", "dev": true, - "license": "Apache-2.0 OR MIT", - "bin": { - "tauri": "tauri.js" + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/tauri" + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, - "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.11.4", - "@tauri-apps/cli-darwin-x64": "2.11.4", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.4", - "@tauri-apps/cli-linux-arm64-gnu": "2.11.4", - "@tauri-apps/cli-linux-arm64-musl": "2.11.4", - "@tauri-apps/cli-linux-riscv64-gnu": "2.11.4", - "@tauri-apps/cli-linux-x64-gnu": "2.11.4", - "@tauri-apps/cli-linux-x64-musl": "2.11.4", - "@tauri-apps/cli-win32-arm64-msvc": "2.11.4", - "@tauri-apps/cli-win32-ia32-msvc": "2.11.4", - "@tauri-apps/cli-win32-x64-msvc": "2.11.4" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.4.tgz", - "integrity": "sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.4.tgz", - "integrity": "sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A==", - "cpu": [ - "x64" - ], + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.4.tgz", - "integrity": "sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ==", - "cpu": [ - "arm" - ], + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.4.tgz", - "integrity": "sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.4.tgz", - "integrity": "sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw==", - "cpu": [ - "arm64" - ], + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.4.tgz", - "integrity": "sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ==", - "cpu": [ - "riscv64" - ], + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.4.tgz", - "integrity": "sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ==", - "cpu": [ - "x64" - ], + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=6.9.0" } }, - "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.4.tgz", - "integrity": "sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A==", - "cpu": [ - "x64" + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz", + "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "linux" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.4.tgz", - "integrity": "sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ==", - "cpu": [ - "arm64" + "node_modules/@csstools/css-color-parser": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz", + "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.0", + "@csstools/css-calc": "^3.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "win32" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" } }, - "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.4.tgz", - "integrity": "sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA==", - "cpu": [ - "ia32" + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", + "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", "dev": true, - "license": "Apache-2.0 OR MIT", - "optional": true, - "os": [ - "win32" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } ], + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=20.19.0" } }, - "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.4.tgz", - "integrity": "sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw==", - "cpu": [ - "x64" - ], + "node_modules/@emnapi/core": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-alpha.3.tgz", + "integrity": "sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==", "dev": true, - "license": "Apache-2.0 OR MIT", + "license": "MIT", "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@emnapi/wasi-threads": "2.0.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-alpha.3.tgz", + "integrity": "sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz", + "integrity": "sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.20", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.20.tgz", + "integrity": "sha512-CMqMy7OaXl9W0eq1Uy7L7i2Y/anPvHmFmESd2CEw0t5YvZhcVCeo4MBevAmswRllX7Y2dEidA4ozGPunLSTQpw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.9", + "@floating-ui/utils": "^0.2.12", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mantine/core": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-9.5.0.tgz", + "integrity": "sha512-lUZtPfW+ZIXthofPyw+SVgWNiV/OkJ498r3OZeuekPgM23p2AspqHzHjEGhe5DMSTyhma6BhIHF2H10dAp0ibw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.27.19", + "clsx": "^2.1.1", + "react-number-format": "^5.4.5", + "react-remove-scroll": "^2.7.2", + "type-fest": "^5.8.0" + }, + "peerDependencies": { + "@mantine/hooks": "9.5.0", + "react": "^19.2.0", + "react-dom": "^19.2.0" + } + }, + "node_modules/@mantine/hooks": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-9.5.0.tgz", + "integrity": "sha512-d67+7dQW0ZJFiWXqZgwcPrZK0KxKMzAX+Sv663g40xhISseiUoK0f8k/Ss2amOA09IhFayeeaqprVRc4Fp5yEw==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "^19.2.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.1.tgz", + "integrity": "sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.1.tgz", + "integrity": "sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.1.tgz", + "integrity": "sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.1.tgz", + "integrity": "sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.1.tgz", + "integrity": "sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.1.tgz", + "integrity": "sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.1.tgz", + "integrity": "sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.1.tgz", + "integrity": "sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.1.tgz", + "integrity": "sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.1.tgz", + "integrity": "sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.1.tgz", + "integrity": "sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.1.tgz", + "integrity": "sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.1.tgz", + "integrity": "sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "2.0.0-alpha.3", + "@emnapi/runtime": "2.0.0-alpha.3", + "@napi-rs/wasm-runtime": "^1.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.1.tgz", + "integrity": "sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.1.tgz", + "integrity": "sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tabler/icons": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.46.0.tgz", + "integrity": "sha512-f2RYFl3fzPwj5WO82x6en0dmkjefxEfOm16D1ByM6cj/McNiwOkL4VaPUoP9VVIrXAD9WnTSVFr70px703b//A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + } + }, + "node_modules/@tabler/icons-react": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.46.0.tgz", + "integrity": "sha512-CCm7xJWhDT2PH4ZIFkP6AgYKtVhq0gpYkjUN+GVh1AzmIQaa77OW0bQPBPQiTE0PsXMR9oSxFqA3qBglzPyrVQ==", + "license": "MIT", + "dependencies": { + "@tabler/icons": "3.46.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + }, + "peerDependencies": { + "react": ">= 16" + } + }, + "node_modules/@tauri-apps/api": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz", + "integrity": "sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/cli": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.4.tgz", + "integrity": "sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.11.4", + "@tauri-apps/cli-darwin-x64": "2.11.4", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.4", + "@tauri-apps/cli-linux-arm64-gnu": "2.11.4", + "@tauri-apps/cli-linux-arm64-musl": "2.11.4", + "@tauri-apps/cli-linux-riscv64-gnu": "2.11.4", + "@tauri-apps/cli-linux-x64-gnu": "2.11.4", + "@tauri-apps/cli-linux-x64-musl": "2.11.4", + "@tauri-apps/cli-win32-arm64-msvc": "2.11.4", + "@tauri-apps/cli-win32-ia32-msvc": "2.11.4", + "@tauri-apps/cli-win32-x64-msvc": "2.11.4" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.4.tgz", + "integrity": "sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.4.tgz", + "integrity": "sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.4.tgz", + "integrity": "sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.4.tgz", + "integrity": "sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.4.tgz", + "integrity": "sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.4.tgz", + "integrity": "sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.4.tgz", + "integrity": "sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.4.tgz", + "integrity": "sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.4.tgz", + "integrity": "sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.4.tgz", + "integrity": "sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.4.tgz", + "integrity": "sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">= 10" } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-7.0.0.tgz", + "integrity": "sha512-HKAH9C6mBo5yBG6yRO5i43L2iisencAo5z+o5P/saHUoY+miC5ivXRxHBJcFyB5ypPNxHJdK3BoF/3O4DIptMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=22", + "npm": ">=6", + "yarn": ">=1" + }, + "peerDependencies": { + "@testing-library/dom": ">=10 <11" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "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/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/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz", + "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.65.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz", + "integrity": "sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "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 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.9.tgz", + "integrity": "sha512-cp447VUsGS07+n1Dqf7YSQ8maeJrjEhaDxTm1ZefbqDtypHBC5GzGMQbklR6IPR13Y8OAJRHZWEMtZipJLCttg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.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", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.399", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.399.tgz", + "integrity": "sha512-lEcqhErbHjXRvd41rnWLpzbyU/IXfIYo7QwaFWmxGeLiLyY2TBCdHnWY88vB+p3ubnihRypDm66panXl7TylLA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", + "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.3.tgz", + "integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "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/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/hosted-git-info": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.3.tgz", + "integrity": "sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/license-checker-rseidelsohn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/license-checker-rseidelsohn/-/license-checker-rseidelsohn-4.4.2.tgz", + "integrity": "sha512-Sf8WaJhd2vELvCne+frS9AXqnY/vv591s2/nZcJDwTnoNgltG4mAmoenffVb8L2YPRYbxARLyrHJBC38AVfpuA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "chalk": "4.1.2", + "debug": "^4.3.4", + "lodash.clonedeep": "^4.5.0", + "mkdirp": "^1.0.4", + "nopt": "^7.2.0", + "read-installed-packages": "^2.0.1", + "semver": "^7.3.5", + "spdx-correct": "^3.1.1", + "spdx-expression-parse": "^3.0.1", + "spdx-satisfies": "^5.0.1", + "treeify": "^1.1.0" + }, + "bin": { + "license-checker-rseidelsohn": "bin/license-checker-rseidelsohn.js" + }, + "engines": { + "node": ">=18", + "npm": ">=8" + } + }, + "node_modules/license-checker-rseidelsohn/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "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/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", + "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "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", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/react-number-format": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.5.tgz", + "integrity": "sha512-y8O2yHHj3w0aE9XO8d2BCcUOOdQTRSVq+WIuMlLVucAm5XNjJAy+BoOJiuQMldVYVOKTMyvVNfnbl2Oqp+YxGw==", + "license": "MIT", + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/read-installed-packages": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/read-installed-packages/-/read-installed-packages-2.0.1.tgz", + "integrity": "sha512-t+fJOFOYaZIjBpTVxiV8Mkt7yQyy4E6MSrrnt5FmPd4enYvpU/9DYGirDmN1XQwkfeuWIhM/iu0t2rm6iSr0CA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "debug": "^4.3.4", + "read-package-json": "^6.0.0", + "semver": "2 || 3 || 4 || 5 || 6 || 7", + "slide": "~1.1.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.2" + } + }, + "node_modules/read-package-json": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", + "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.1.tgz", + "integrity": "sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.142.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.1", + "@rolldown/binding-darwin-arm64": "1.2.1", + "@rolldown/binding-darwin-x64": "1.2.1", + "@rolldown/binding-freebsd-x64": "1.2.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.1", + "@rolldown/binding-linux-arm64-gnu": "1.2.1", + "@rolldown/binding-linux-arm64-musl": "1.2.1", + "@rolldown/binding-linux-ppc64-gnu": "1.2.1", + "@rolldown/binding-linux-s390x-gnu": "1.2.1", + "@rolldown/binding-linux-x64-gnu": "1.2.1", + "@rolldown/binding-linux-x64-musl": "1.2.1", + "@rolldown/binding-openharmony-arm64": "1.2.1", + "@rolldown/binding-wasm32-wasi": "1.2.1", + "@rolldown/binding-win32-arm64-msvc": "1.2.1", + "@rolldown/binding-win32-x64-msvc": "1.2.1" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "*" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", + "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-find-index": "^1.0.2", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdx-ranges": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", + "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", + "dev": true, + "license": "(MIT AND CC-BY-3.0)" + }, + "node_modules/spdx-satisfies": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-5.0.1.tgz", + "integrity": "sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-compare": "^1.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "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/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tabbable": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz", + "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==", + "license": "MIT" + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.10.tgz", + "integrity": "sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.10" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.10.tgz", + "integrity": "sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.8.0.tgz", + "integrity": "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "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==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vite": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz", + "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.23", + "rolldown": "~1.2.0", + "tinyglobby": "^0.2.17" + }, + "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", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.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 + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "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/vitest": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.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.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } } } } diff --git a/desktop/package.json b/desktop/package.json index d300dab..847dde2 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -4,6 +4,17 @@ "version": "0.4.0-b", "type": "module", "scripts": { + "dev": "vite", + "prebuild": "npm run sync:branding", + "build": "tsc -b && vite build", + "typecheck": "tsc -b --pretty false", + "lint": "eslint . --max-warnings 0", + "test": "vitest run", + "check": "npm run typecheck && npm run lint && npm run test && npm run build", + "notices:write": "node scripts/generate-notices.mjs --write", + "notices:check": "node scripts/generate-notices.mjs", + "model-catalog:write": "uv run --frozen python scripts/model-catalog.py --write", + "model-catalog:check": "uv run --frozen python scripts/model-catalog.py --check", "tauri": "tauri", "sync:branding": "node scripts/sync-branding.mjs", "icons": "npm run sync:branding && tauri icon ../docs/images/logo.png --output src-tauri/icons", @@ -14,7 +25,31 @@ "sidecar:windows": "powershell -ExecutionPolicy Bypass -File scripts/fetch-uv.ps1", "sidecar:unix": "bash scripts/fetch-uv.sh" }, + "dependencies": { + "@mantine/core": "9.5.0", + "@tabler/icons-react": "3.46.0", + "@tauri-apps/api": "2.11.1", + "react": "19.2.8", + "react-dom": "19.2.8" + }, "devDependencies": { - "@tauri-apps/cli": "2.11.4" + "@eslint/js": "10.0.1", + "@tauri-apps/cli": "2.11.4", + "@testing-library/jest-dom": "7.0.0", + "@testing-library/react": "16.3.2", + "@testing-library/user-event": "14.6.1", + "@types/node": "26.0.0", + "@types/react": "19.2.18", + "@types/react-dom": "19.2.4", + "@vitejs/plugin-react": "6.0.5", + "eslint": "10.8.0", + "eslint-plugin-react-hooks": "7.1.1", + "eslint-plugin-react-refresh": "0.5.3", + "jsdom": "29.1.1", + "license-checker-rseidelsohn": "4.4.2", + "typescript": "6.0.3", + "typescript-eslint": "8.65.0", + "vite": "8.2.0", + "vitest": "4.1.10" } } diff --git a/desktop/scripts/generate-notices.mjs b/desktop/scripts/generate-notices.mjs new file mode 100644 index 0000000..57d75ab --- /dev/null +++ b/desktop/scripts/generate-notices.mjs @@ -0,0 +1,156 @@ +import { execFileSync } from 'node:child_process'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + mkdtempSync, + readFileSync, + readdirSync, + rmSync, + writeFileSync, +} from 'node:fs'; +import { createHash } from 'node:crypto'; +import { tmpdir } from 'node:os'; + +const desktop = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const repository = resolve(desktop, '..'); +const temporary = mkdtempSync(join(tmpdir(), 'vidxp-notices-')); +const rustNotices = join(temporary, 'rust.txt'); +const destination = join(desktop, 'THIRD_PARTY_NOTICES.txt'); +const cargoLock = join(desktop, 'src-tauri', 'Cargo.lock'); +const preferredArtifact = /^(licen[cs]e|copying|notice)(?:[._-].*)?$/i; +const documentationArtifact = /^(readme|changelog)(?:[._-].*)?$/i; + +const vendoredArtifacts = new Map([ + ['react-remove-scroll-bar@2.3.8', { + path: join(desktop, 'licenses', 'npm', 'react-remove-scroll-bar-2.3.8-LICENSE.txt'), + source: 'https://github.com/theKashey/react-remove-scroll-bar/blob/8ca9ba5ea52de03308fe8ced94f7b159a44d28ff/LICENSE', + sha256: 'a79aae0c0f21990d9d963bb3c5a79cdcea9a46f8523ba55c58d7fe776b6ebc84', + }], +]); + +function packageRoot(metadata) { + if (!metadata.licenseFile) return null; + return dirname(metadata.licenseFile); +} + +function publishedLicenseArtifacts(metadata) { + const root = packageRoot(metadata); + if (!root) return []; + const preferred = readdirSync(root, { withFileTypes: true }) + .filter((entry) => entry.isFile() && preferredArtifact.test(entry.name)) + .map((entry) => join(root, entry.name)) + .sort((left, right) => left.localeCompare(right)); + if (preferred.length > 0) return preferred; + const selected = metadata.licenseFile; + return selected && !documentationArtifact.test(selected.split(/[\\/]/).at(-1) ?? '') + ? [selected] + : []; +} + +function frontendLicenseText(identity, metadata) { + const artifacts = publishedLicenseArtifacts(metadata); + if (artifacts.length > 0) { + return artifacts.map((path) => readFileSync(path, 'utf8').trim()).join('\n\n'); + } + const vendored = vendoredArtifacts.get(identity); + if (vendored) { + const contents = readFileSync(vendored.path, 'utf8').replace(/\r\n?/g, '\n').trim(); + const actual = createHash('sha256').update(`${contents}\n`, 'utf8').digest('hex'); + if (actual !== vendored.sha256) { + throw new Error(`${identity} vendored license digest ${actual} does not match ${vendored.sha256}.`); + } + return `Vendored verbatim upstream license for ${identity}\nSource: ${vendored.source}\nSHA-256: ${vendored.sha256}\n\n${contents}`; + } + throw new Error(`${identity} has no published license artifact and no exact, provenance-pinned vendored exception for ${metadata.licenses}.`); +} + +try { + const lockBefore = readFileSync(cargoLock); + execFileSync( + 'cargo', + [ + 'about', 'generate', '--locked', '--frozen', + '--manifest-path', 'src-tauri/Cargo.toml', + '-c', 'about.toml', '-o', rustNotices, 'about.hbs', + ], + { cwd: desktop, stdio: 'inherit' }, + ); + const lockAfter = readFileSync(cargoLock); + if (!lockBefore.equals(lockAfter)) { + throw new Error('cargo-about modified desktop/src-tauri/Cargo.lock.'); + } + + const checker = join( + desktop, + 'node_modules', + 'license-checker-rseidelsohn', + 'bin', + 'license-checker-rseidelsohn.js', + ); + const npmInventory = JSON.parse(execFileSync( + process.execPath, + [checker, '--production', '--json'], + { cwd: desktop, encoding: 'utf8' }, + )); + const frontend = ['FRONTEND DEPENDENCIES', '=====================', '']; + for (const [identity, metadata] of Object.entries(npmInventory).sort(([left], [right]) => left.localeCompare(right))) { + if (identity.startsWith('vidxp-desktop@')) continue; + const licenses = String(metadata.licenses ?? ''); + const source = String(metadata.repository ?? ''); + if (!licenses || /unknown|unlicensed/i.test(licenses)) { + throw new Error(`${identity} has an unresolved shipped license.`); + } + if (!source) throw new Error(`${identity} has no published source repository.`); + frontend.push('-------------------------------------------------------------------------------'); + frontend.push(`${identity} | ${licenses} | ${source}`); + frontend.push(''); + frontend.push(frontendLicenseText(identity, metadata)); + frontend.push(''); + } + + const sidecars = JSON.parse(readFileSync(join(desktop, 'sidecars.json'), 'utf8')); + const projectLicense = readFileSync(join(repository, 'LICENSE'), 'utf8').trim(); + const uvMitLicense = readFileSync(join(desktop, 'licenses', 'uv-LICENSE-MIT.txt'), 'utf8').trim(); + const artifact = [ + 'VidXP Desktop Legal Notices', + '===========================', + '', + 'This artifact is generated from locked production dependency graphs and bundled sidecar metadata.', + '', + 'VIDXP PROJECT LICENSE', + '=====================', + 'VidXP Desktop and VidXP | MIT | https://github.com/grayhatdevelopers/vidxp', + '', + projectLicense, + '', + 'BUNDLED EXECUTABLES', + '===================', + `uv ${sidecars.uv_version} | MIT OR Apache-2.0 | https://github.com/astral-sh/uv/tree/${sidecars.uv_version}`, + 'The complete MIT terms from the pinned uv release follow. The complete Apache-2.0 terms are included in the Rust dependency section below.', + '', + uvMitLicense, + '', + readFileSync(rustNotices, 'utf8').trim(), + '', + frontend.join('\n').trim(), + '', + ].join('\n').replace(/\r\n?/g, '\n').replace(/[ \t]+$/gm, ''); + if (process.argv.includes('--write')) { + writeFileSync(destination, artifact, 'utf8'); + } else if (readFileSync(destination, 'utf8').replace(/\r\n?/g, '\n') !== artifact) { + const generated = join(temporary, 'THIRD_PARTY_NOTICES.txt'); + writeFileSync(generated, artifact, 'utf8'); + try { + execFileSync( + 'git', + ['diff', '--no-index', '--', destination, generated], + { cwd: desktop, stdio: 'inherit' }, + ); + } catch (error) { + if (error?.status !== 1) throw error; + } + throw new Error('THIRD_PARTY_NOTICES.txt is stale; run npm run notices:write.'); + } +} finally { + rmSync(temporary, { recursive: true, force: true }); +} diff --git a/desktop/scripts/model-catalog.py b/desktop/scripts/model-catalog.py new file mode 100644 index 0000000..be30acc --- /dev/null +++ b/desktop/scripts/model-catalog.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT / "src")) + +from vidxp.local_probe import desktop_model_cache_catalog # noqa: E402 + + +CATALOG_PATH = ROOT / "desktop" / "model-cache-catalog.json" + + +def rendered_catalog() -> str: + return json.dumps( + desktop_model_cache_catalog(), + indent=2, + ensure_ascii=False, + ) + "\n" + + +def main() -> int: + parser = argparse.ArgumentParser() + mode = parser.add_mutually_exclusive_group(required=True) + mode.add_argument("--write", action="store_true") + mode.add_argument("--check", action="store_true") + arguments = parser.parse_args() + derived = rendered_catalog() + if arguments.write: + CATALOG_PATH.write_text(derived, encoding="utf-8", newline="\n") + return 0 + if CATALOG_PATH.read_text(encoding="utf-8") != derived: + raise SystemExit( + "desktop/model-cache-catalog.json is stale; run npm run " + "model-catalog:write from desktop/." + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/desktop/scripts/sync-branding.mjs b/desktop/scripts/sync-branding.mjs index 218f170..98968d2 100644 --- a/desktop/scripts/sync-branding.mjs +++ b/desktop/scripts/sync-branding.mjs @@ -1,10 +1,12 @@ -import { copyFileSync } from "node:fs"; +import { copyFileSync, mkdirSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; const desktopRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const source = resolve(desktopRoot, "../docs/images/logo.png"); -const favicon = resolve(desktopRoot, "web/icon.png"); +const publicDirectory = resolve(desktopRoot, "public"); +const favicon = resolve(publicDirectory, "icon.png"); +mkdirSync(publicDirectory, { recursive: true }); copyFileSync(source, favicon); console.log("Synced the VidXP desktop favicon from the shared icon."); diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index 06cf314..c88fea6 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -230,7 +230,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84790c55b5704b0d35130bf16a4ce22a8e70eb0ea773522557524d9a4852663d" dependencies = [ - "nix", + "nix 0.30.1", "rand", ] @@ -1647,7 +1647,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -2152,6 +2152,18 @@ dependencies = [ "libc", ] +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.1", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "num-conv" version = "0.2.2" @@ -2718,6 +2730,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "process-wrap" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55" +dependencies = [ + "indexmap 2.14.0", + "nix 0.31.3", + "tracing", + "windows 0.62.2", +] + [[package]] name = "quick-xml" version = "0.38.4" @@ -3495,8 +3519,8 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows", - "windows-core", + "windows 0.61.3", + "windows-core 0.61.2", "windows-version", "x11-dl", ] @@ -3566,7 +3590,7 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows", + "windows 0.61.3", ] [[package]] @@ -3728,7 +3752,7 @@ dependencies = [ "tauri-plugin", "thiserror 2.0.19", "url", - "windows", + "windows 0.61.3", "zbus", ] @@ -3769,6 +3793,22 @@ dependencies = [ "zbus", ] +[[package]] +name = "tauri-plugin-store" +version = "2.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6708afbe549f176b712066e71648ba8fafba20789453718260c7ca356733cb0c" +dependencies = [ + "dunce", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.19", + "tokio", + "tracing", +] + [[package]] name = "tauri-runtime" version = "2.11.3" @@ -3791,7 +3831,7 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.3", ] [[package]] @@ -3816,7 +3856,7 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.3", "wry", ] @@ -4000,9 +4040,21 @@ dependencies = [ "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "tokio-util" version = "0.7.19" @@ -4378,6 +4430,7 @@ dependencies = [ "atomic-write-file", "hex", "log", + "process-wrap", "serde", "serde_json", "sha2 0.11.0", @@ -4388,7 +4441,9 @@ dependencies = [ "tauri-plugin-opener", "tauri-plugin-shell", "tauri-plugin-single-instance", - "wait-timeout", + "tauri-plugin-store", + "which", + "windows 0.62.2", ] [[package]] @@ -4411,15 +4466,6 @@ dependencies = [ "libc", ] -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - [[package]] name = "walkdir" version = "2.5.0" @@ -4596,8 +4642,8 @@ checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", - "windows-core", + "windows 0.61.3", + "windows-core 0.61.2", "windows-implement", "windows-interface", ] @@ -4620,8 +4666,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ "thiserror 2.0.19", - "windows", - "windows-core", + "windows 0.61.3", + "windows-core 0.61.2", +] + +[[package]] +name = "which" +version = "8.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3ef584124b911bcc3875c2f1472e80f24361ceb789bd1c62b3e9a3df9ff43c" +dependencies = [ + "libc", ] [[package]] @@ -4676,11 +4731,23 @@ version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections", - "windows-core", - "windows-future", + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", "windows-link 0.1.3", - "windows-numerics", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] @@ -4689,7 +4756,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core", + "windows-core 0.61.2", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", ] [[package]] @@ -4701,8 +4777,21 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link 0.1.3", - "windows-result", - "windows-strings", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -4711,9 +4800,20 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -4756,10 +4856,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core", + "windows-core 0.61.2", "windows-link 0.1.3", ] +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -4769,6 +4879,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -4778,6 +4897,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -4871,6 +4999,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-version" version = "0.1.7" @@ -5105,8 +5242,8 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", - "windows-core", + "windows 0.61.3", + "windows-core 0.61.2", "windows-version", "x11-dl", ] diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 0280c49..c1f957d 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -18,6 +18,7 @@ tauri-build = { version = "2.6.3", features = [] } atomic-write-file = "0.3.0" hex = "0.4.3" log = "0.4.29" +process-wrap = { version = "9.1.0", features = ["std"] } serde = { version = "1.0.229", features = ["derive"] } serde_json = "1.0.151" sha2 = "0.11.0" @@ -27,4 +28,8 @@ tauri-plugin-dialog = "2.7.2" tauri-plugin-opener = "2.5.4" tauri-plugin-shell = "2.3.5" tauri-plugin-single-instance = "2.4.3" -wait-timeout = "0.2.1" +tauri-plugin-store = "2.4.4" +which = "8.0.0" + +[target.'cfg(windows)'.dependencies] +windows = { version = "0.62.2", features = ["Win32_System_Threading"] } diff --git a/desktop/src-tauri/capabilities/main.json b/desktop/src-tauri/capabilities/main.json index f3f4ca4..4d4fe77 100644 --- a/desktop/src-tauri/capabilities/main.json +++ b/desktop/src-tauri/capabilities/main.json @@ -1,9 +1,17 @@ { "$schema": "../gen/schemas/desktop-schema.json", "identifier": "main-window", - "description": "The bundled setup interface; no shell or filesystem API is exposed.", + "description": "The bundled setup interface with narrow controls for its undecorated main window.", "windows": [ "main" ], - "permissions": [] + "permissions": [ + "core:window:allow-close", + "core:event:allow-listen", + "core:event:allow-unlisten", + "core:window:allow-is-maximized", + "core:window:allow-minimize", + "core:window:allow-start-dragging", + "core:window:allow-toggle-maximize" + ] } diff --git a/desktop/src-tauri/src/activation.rs b/desktop/src-tauri/src/activation.rs new file mode 100644 index 0000000..84eb199 --- /dev/null +++ b/desktop/src-tauri/src/activation.rs @@ -0,0 +1,121 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub(crate) enum ActivationStage { + Prepared, + ProfileWritten, + Committed, + RollingBack, + RolledBack, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum ActivationRecovery { + RollBack, + Complete, +} + +pub(crate) fn activation_recovery( + stage: &ActivationStage, + candidate_authorities_match: bool, +) -> ActivationRecovery { + match stage { + ActivationStage::RollingBack | ActivationStage::RolledBack => ActivationRecovery::RollBack, + ActivationStage::Committed => ActivationRecovery::Complete, + ActivationStage::Prepared | ActivationStage::ProfileWritten + if candidate_authorities_match => + { + ActivationRecovery::Complete + } + ActivationStage::Prepared | ActivationStage::ProfileWritten => ActivationRecovery::RollBack, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + enum StartupResult { + Previous, + Candidate, + } + + #[derive(Clone, Copy, Debug)] + enum FailureBoundary { + FinalizeRename, + PreparedJournalWrite, + ProfileStoreSave, + ProfileWrittenJournalWrite, + ActivePointerWrite, + CommittedJournalWrite, + CommittedJournalRemoval, + RollbackMarkerWrite, + RollbackPointerWrite, + RollbackStoreSave, + RolledBackMarkerWrite, + RolledBackJournalRemoval, + } + + fn startup_after(boundary: FailureBoundary) -> StartupResult { + use FailureBoundary::*; + let (journal, authorities_match) = match boundary { + FinalizeRename | PreparedJournalWrite => return StartupResult::Previous, + ProfileStoreSave => (ActivationStage::Prepared, false), + ProfileWrittenJournalWrite => (ActivationStage::Prepared, false), + ActivePointerWrite => (ActivationStage::ProfileWritten, false), + CommittedJournalWrite => (ActivationStage::ProfileWritten, true), + CommittedJournalRemoval => (ActivationStage::Committed, true), + RollbackMarkerWrite => (ActivationStage::ProfileWritten, false), + RollbackPointerWrite | RollbackStoreSave | RolledBackMarkerWrite => { + (ActivationStage::RollingBack, true) + } + RolledBackJournalRemoval => (ActivationStage::RolledBack, true), + }; + match activation_recovery(&journal, authorities_match) { + ActivationRecovery::RollBack => StartupResult::Previous, + ActivationRecovery::Complete => StartupResult::Candidate, + } + } + + #[test] + fn every_activation_failure_boundary_has_an_unambiguous_startup_result() { + use FailureBoundary::*; + for boundary in [ + FinalizeRename, + PreparedJournalWrite, + ProfileStoreSave, + ProfileWrittenJournalWrite, + ActivePointerWrite, + RollbackMarkerWrite, + RollbackPointerWrite, + RollbackStoreSave, + RolledBackMarkerWrite, + RolledBackJournalRemoval, + ] { + assert_eq!( + startup_after(boundary), + StartupResult::Previous, + "{boundary:?}" + ); + } + for boundary in [CommittedJournalWrite, CommittedJournalRemoval] { + assert_eq!( + startup_after(boundary), + StartupResult::Candidate, + "{boundary:?}" + ); + } + } + + #[test] + fn rollback_markers_permanently_prevent_candidate_recommit() { + for stage in [ActivationStage::RollingBack, ActivationStage::RolledBack] { + assert_eq!( + activation_recovery(&stage, true), + ActivationRecovery::RollBack + ); + } + } +} diff --git a/desktop/src-tauri/src/background_process.rs b/desktop/src-tauri/src/background_process.rs new file mode 100644 index 0000000..5d87d5c --- /dev/null +++ b/desktop/src-tauri/src/background_process.rs @@ -0,0 +1,468 @@ +use std::{ + io::Read, + process::{Command, ExitStatus, Stdio}, + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + mpsc::{self, Receiver, TryRecvError}, + }, + thread, + time::{Duration, Instant}, +}; + +use process_wrap::std::{ChildWrapper, CommandWrap}; + +#[cfg(unix)] +use process_wrap::std::ProcessGroup; +#[cfg(windows)] +use process_wrap::std::{CreationFlags, JobObject}; + +#[derive(Clone, Copy)] +pub struct BackgroundPolicy { + pub timeout: Duration, + pub max_output_bytes: usize, +} + +#[derive(Clone, Default)] +pub struct CancellationToken(Arc); + +impl CancellationToken { + pub fn cancel(&self) { + self.0.store(true, Ordering::Release); + } + + pub fn is_cancelled(&self) -> bool { + self.0.load(Ordering::Acquire) + } + + pub fn same(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} + +#[derive(Debug)] +pub struct BackgroundOutput { + pub status: ExitStatus, + pub stdout: Vec, + pub stderr: Vec, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BackgroundErrorKind { + Start, + Monitor, + Timeout, + Cancelled, + Output, + OutputTooLarge, +} + +#[derive(Debug)] +pub struct BackgroundError { + pub kind: BackgroundErrorKind, + pub detail: String, +} + +struct OutputReader { + receiver: Receiver>>, + handle: Option>, + result: Option>>, +} + +impl OutputReader { + fn poll(&mut self) -> Result { + if self.result.is_some() { + return Ok(true); + } + match self.receiver.try_recv() { + Ok(result) => { + self.result = Some(result); + Ok(true) + } + Err(TryRecvError::Empty) => Ok(false), + Err(TryRecvError::Disconnected) => Err(BackgroundError { + kind: BackgroundErrorKind::Output, + detail: "an output reader stopped without returning its result".into(), + }), + } + } + + fn finish(mut self) -> Result, BackgroundError> { + let result = self.result.take().ok_or_else(|| BackgroundError { + kind: BackgroundErrorKind::Output, + detail: "an output reader was collected before it finished".into(), + })?; + self.handle + .take() + .expect("reader handle is retained until collection") + .join() + .map_err(|_| BackgroundError { + kind: BackgroundErrorKind::Output, + detail: "an output reader panicked".into(), + })?; + result.map_err(|error| BackgroundError { + kind: BackgroundErrorKind::Output, + detail: error.to_string(), + }) + } +} + +fn read_bounded(stream: impl Read + Send + 'static, limit: usize) -> OutputReader { + let (sender, receiver) = mpsc::sync_channel(1); + let handle = thread::spawn(move || { + let mut bytes = Vec::new(); + let result = stream + .take(limit as u64 + 1) + .read_to_end(&mut bytes) + .map(|_| bytes); + let _ = sender.send(result); + }); + OutputReader { + receiver, + handle: Some(handle), + result: None, + } +} + +fn wrapped(command: Command) -> CommandWrap { + let mut wrapped = CommandWrap::from(command); + #[cfg(windows)] + { + use windows::Win32::System::Threading::CREATE_NO_WINDOW; + wrapped.wrap(CreationFlags(CREATE_NO_WINDOW)); + wrapped.wrap(JobObject); + } + #[cfg(unix)] + wrapped.wrap(ProcessGroup::leader()); + wrapped +} + +pub struct OwnedChild(Box); + +impl OwnedChild { + #[cfg(test)] + pub fn id(&self) -> u32 { + self.0.id() + } + + pub fn try_wait(&mut self) -> std::io::Result> { + self.0.try_wait() + } + + pub fn terminate_and_reap(&mut self) { + let _ = self.0.kill(); + let _ = self.0.wait(); + } +} + +impl Drop for OwnedChild { + fn drop(&mut self) { + self.terminate_and_reap(); + } +} + +pub fn spawn_service(mut command: Command) -> Result { + command + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()); + wrapped(command) + .spawn() + .map(OwnedChild) + .map_err(|error| BackgroundError { + kind: BackgroundErrorKind::Start, + detail: error.to_string(), + }) +} + +fn finish_error( + child: &mut OwnedChild, + mut stdout: OutputReader, + mut stderr: OutputReader, + kind: BackgroundErrorKind, + detail: String, +) -> BackgroundError { + child.terminate_and_reap(); + let cleanup_deadline = Instant::now() + Duration::from_secs(2); + while Instant::now() < cleanup_deadline { + let stdout_done = stdout.poll().unwrap_or(true); + let stderr_done = stderr.poll().unwrap_or(true); + if stdout_done && stderr_done { + let _ = stdout.finish(); + let _ = stderr.finish(); + return BackgroundError { kind, detail }; + } + thread::sleep(Duration::from_millis(10)); + } + BackgroundError { + kind: BackgroundErrorKind::Output, + detail: format!( + "{detail}; owned process output pipes did not close after whole-tree termination" + ), + } +} + +fn run_with_monitor( + mut command: Command, + policy: BackgroundPolicy, + cancellation: Option<&CancellationToken>, + mut monitor: F, +) -> Result +where + F: FnMut(&mut OwnedChild) -> std::io::Result>, +{ + command + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + let mut child = wrapped(command) + .spawn() + .map(OwnedChild) + .map_err(|error| BackgroundError { + kind: BackgroundErrorKind::Start, + detail: error.to_string(), + })?; + let mut stdout = read_bounded( + child.0.stdout().take().expect("background stdout is piped"), + policy.max_output_bytes, + ); + let mut stderr = read_bounded( + child.0.stderr().take().expect("background stderr is piped"), + policy.max_output_bytes, + ); + let deadline = Instant::now() + policy.timeout; + let status = loop { + if cancellation.is_some_and(CancellationToken::is_cancelled) { + return Err(finish_error( + &mut child, + stdout, + stderr, + BackgroundErrorKind::Cancelled, + "the operation was cancelled".into(), + )); + } + if stdout.result.as_ref().is_some_and(|result| { + result + .as_ref() + .is_ok_and(|bytes| bytes.len() > policy.max_output_bytes) + }) || stderr.result.as_ref().is_some_and(|result| { + result + .as_ref() + .is_ok_and(|bytes| bytes.len() > policy.max_output_bytes) + }) { + return Err(finish_error( + &mut child, + stdout, + stderr, + BackgroundErrorKind::OutputTooLarge, + format!( + "the operation returned more than {} bytes per output stream", + policy.max_output_bytes + ), + )); + } + if let Err(error) = stdout.poll().and_then(|_| stderr.poll()) { + return Err(finish_error( + &mut child, + stdout, + stderr, + error.kind, + error.detail, + )); + } + if Instant::now() >= deadline { + return Err(finish_error( + &mut child, + stdout, + stderr, + BackgroundErrorKind::Timeout, + format!( + "the operation exceeded {} seconds", + policy.timeout.as_secs() + ), + )); + } + match monitor(&mut child) { + Ok(Some(status)) => break status, + Ok(None) => thread::sleep(Duration::from_millis(50)), + Err(error) => { + return Err(finish_error( + &mut child, + stdout, + stderr, + BackgroundErrorKind::Monitor, + error.to_string(), + )); + } + } + }; + while !(stdout.poll()? && stderr.poll()?) { + if cancellation.is_some_and(CancellationToken::is_cancelled) { + return Err(finish_error( + &mut child, + stdout, + stderr, + BackgroundErrorKind::Cancelled, + "the operation was cancelled while collecting process output".into(), + )); + } + if Instant::now() >= deadline { + return Err(finish_error( + &mut child, + stdout, + stderr, + BackgroundErrorKind::Timeout, + format!( + "the operation exceeded {} seconds while descendants retained its output pipes", + policy.timeout.as_secs() + ), + )); + } + thread::sleep(Duration::from_millis(10)); + } + let stdout = stdout.finish()?; + let stderr = stderr.finish()?; + if stdout.len() > policy.max_output_bytes || stderr.len() > policy.max_output_bytes { + return Err(BackgroundError { + kind: BackgroundErrorKind::OutputTooLarge, + detail: format!( + "the operation returned more than {} bytes per output stream", + policy.max_output_bytes + ), + }); + } + Ok(BackgroundOutput { + status, + stdout, + stderr, + }) +} + +pub fn run( + command: Command, + policy: BackgroundPolicy, + cancellation: Option<&CancellationToken>, +) -> Result { + run_with_monitor(command, policy, cancellation, |child| child.try_wait()) +} + +pub async fn run_async( + command: Command, + policy: BackgroundPolicy, + cancellation: CancellationToken, +) -> Result { + tauri::async_runtime::spawn_blocking(move || run(command, policy, Some(&cancellation))) + .await + .map_err(|error| BackgroundError { + kind: BackgroundErrorKind::Monitor, + detail: format!("the background monitor stopped unexpectedly: {error}"), + })? +} + +#[cfg(test)] +mod tests { + use super::*; + + fn shell_command(script: &str) -> Command { + if cfg!(windows) { + let mut command = Command::new("cmd"); + command.args(["/c", script]); + command + } else { + let mut command = Command::new("sh"); + command.args(["-c", script]); + command + } + } + + #[test] + fn bounded_runner_captures_output() { + let result = run( + shell_command("echo runner"), + BackgroundPolicy { + timeout: Duration::from_secs(2), + max_output_bytes: 1024, + }, + None, + ) + .expect("background command"); + assert!(result.status.success()); + assert!(String::from_utf8_lossy(&result.stdout).contains("runner")); + } + + #[test] + fn timeout_terminates_the_owned_process_tree() { + let script = if cfg!(windows) { + "ping 127.0.0.1 -n 10 > nul" + } else { + "sleep 10" + }; + let error = run( + shell_command(script), + BackgroundPolicy { + timeout: Duration::from_millis(100), + max_output_bytes: 1024, + }, + None, + ) + .expect_err("timeout"); + assert_eq!(error.kind, BackgroundErrorKind::Timeout); + } + + #[test] + fn cancellation_terminates_the_owned_process_tree() { + let cancellation = CancellationToken::default(); + cancellation.cancel(); + let error = run( + shell_command("echo never"), + BackgroundPolicy { + timeout: Duration::from_secs(2), + max_output_bytes: 1024, + }, + Some(&cancellation), + ) + .expect_err("cancelled"); + assert_eq!(error.kind, BackgroundErrorKind::Cancelled); + } + + #[test] + fn monitor_failure_terminates_and_reaps_the_owned_process_tree() { + let script = if cfg!(windows) { + "ping 127.0.0.1 -n 10 > nul" + } else { + "sleep 10" + }; + let error = run_with_monitor( + shell_command(script), + BackgroundPolicy { + timeout: Duration::from_secs(2), + max_output_bytes: 1024, + }, + None, + |_| Err(std::io::Error::other("injected monitor failure")), + ) + .expect_err("monitor failure"); + assert_eq!(error.kind, BackgroundErrorKind::Monitor); + } + + #[test] + fn inherited_output_pipe_cannot_outlive_the_operation_deadline() { + let script = if cfg!(windows) { + "start \"\" /b cmd /c \"ping 127.0.0.1 -n 10 ^> nul\" & echo root-finished" + } else { + "(sleep 10) & echo root-finished" + }; + let started = Instant::now(); + let error = run( + shell_command(script), + BackgroundPolicy { + timeout: Duration::from_millis(150), + max_output_bytes: 1024, + }, + None, + ) + .expect_err("inherited pipe timeout"); + assert_eq!(error.kind, BackgroundErrorKind::Timeout); + assert!(started.elapsed() < Duration::from_secs(3)); + } +} diff --git a/desktop/src-tauri/src/browser_readiness.rs b/desktop/src-tauri/src/browser_readiness.rs new file mode 100644 index 0000000..17225df --- /dev/null +++ b/desktop/src-tauri/src/browser_readiness.rs @@ -0,0 +1,269 @@ +use std::{ + fs, + io::{Read, Write}, + net::{SocketAddr, TcpStream}, + path::Path, + thread, + time::{Duration, Instant}, +}; + +use serde::Deserialize; + +use crate::background_process::{CancellationToken, OwnedChild}; + +const READINESS_PRODUCT: &str = "dev.grayhat.vidxp"; +const READINESS_PROTOCOL_VERSION: u32 = 1; + +#[derive(Debug, Deserialize)] +struct ReadinessMarker { + product: String, + protocol_version: u32, + nonce: String, + port: u16, + #[serde(rename = "pid")] + _pid: u32, +} + +fn marker_matches(contents: &[u8], nonce: &str, port: u16) -> bool { + serde_json::from_slice::(contents).is_ok_and(|marker| { + marker.product == READINESS_PRODUCT + && marker.protocol_version == READINESS_PROTOCOL_VERSION + && marker.nonce == nonce + && marker.port == port + }) +} + +fn streamlit_health_is_ready(address: SocketAddr) -> bool { + let Ok(mut stream) = TcpStream::connect_timeout(&address, Duration::from_millis(150)) else { + return false; + }; + let timeout = Some(Duration::from_millis(250)); + if stream.set_read_timeout(timeout).is_err() || stream.set_write_timeout(timeout).is_err() { + return false; + } + if stream + .write_all(b"GET /_stcore/health HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n") + .is_err() + { + return false; + } + let mut response = Vec::new(); + if stream.take(8192).read_to_end(&mut response).is_err() { + return false; + } + let response = String::from_utf8_lossy(&response); + let Some((headers, body)) = response.split_once("\r\n\r\n") else { + return false; + }; + (headers.starts_with("HTTP/1.1 200") || headers.starts_with("HTTP/1.0 200")) + && body.trim() == "ok" +} + +pub fn wait_for_browser_readiness( + process: &mut OwnedChild, + marker_path: &Path, + nonce: &str, + port: u16, + deadline: Instant, + cancellation: &CancellationToken, +) -> Result<(), String> { + let address = SocketAddr::from(([127, 0, 0, 1], port)); + while Instant::now() < deadline { + if cancellation.is_cancelled() { + process.terminate_and_reap(); + let _ = fs::remove_file(marker_path); + return Err("VidXP interface startup was cancelled.".into()); + } + if let Some(status) = process + .try_wait() + .map_err(|error| format!("Could not inspect the interface process: {error}"))? + { + let _ = fs::remove_file(marker_path); + return Err(format!( + "The VidXP interface exited during startup ({status})." + )); + } + if fs::read(marker_path).is_ok_and(|contents| { + marker_matches(&contents, nonce, port) && streamlit_health_is_ready(address) + }) { + let _ = fs::remove_file(marker_path); + return Ok(()); + } + thread::sleep(Duration::from_millis(50)); + } + process.terminate_and_reap(); + let _ = fs::remove_file(marker_path); + Err("The VidXP interface did not publish its launch identity and become ready in 30 seconds. Another process may have captured the reserved port.".into()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{net::TcpListener, process::Command}; + + fn sleeping_command() -> Command { + if cfg!(windows) { + let mut command = Command::new("cmd"); + command.args(["/c", "ping 127.0.0.1 -n 20 > nul"]); + command + } else { + let mut command = Command::new("sh"); + command.args(["-c", "sleep 20"]); + command + } + } + + fn short_command() -> Command { + if cfg!(windows) { + let mut command = Command::new("cmd"); + command.args(["/c", "exit 0"]); + command + } else { + let mut command = Command::new("sh"); + command.args(["-c", "exit 0"]); + command + } + } + + fn temporary_marker(name: &str) -> std::path::PathBuf { + std::env::temp_dir().join(format!( + "vidxp-readiness-{name}-{}-{}.json", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("clock") + .as_nanos() + )) + } + + fn health_server(listener: TcpListener) -> thread::JoinHandle<()> { + thread::spawn(move || { + if let Ok((mut stream, _)) = listener.accept() { + let mut request = [0_u8; 1024]; + let _ = stream.read(&mut request); + let _ = stream.write_all( + b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok", + ); + } + }) + } + + #[test] + fn readiness_marker_rejects_stale_service_and_wrong_nonce() { + let correct = br#"{"product":"dev.grayhat.vidxp","protocol_version":1,"nonce":"new","port":43123,"pid":99}"#; + assert!(marker_matches(correct, "new", 43123)); + assert!(!marker_matches(correct, "old", 43123)); + assert!(!marker_matches(correct, "new", 43124)); + assert!(!marker_matches(b"not-json", "new", 43123)); + } + + #[test] + fn readiness_requires_the_expected_marker_even_when_a_captured_port_is_healthy() { + let listener = TcpListener::bind(("127.0.0.1", 0)).expect("listener"); + let port = listener.local_addr().expect("address").port(); + listener + .set_nonblocking(true) + .expect("nonblocking listener"); + let server = thread::spawn(move || { + let deadline = Instant::now() + Duration::from_secs(1); + while Instant::now() < deadline { + match listener.accept() { + Ok((mut stream, _)) => { + let _ = stream.write_all( + b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok", + ); + return; + } + Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => { + thread::sleep(Duration::from_millis(10)); + } + Err(_) => return, + } + } + }); + let mut process = + crate::background_process::spawn_service(sleeping_command()).expect("sleeping process"); + let marker = temporary_marker("captured"); + let result = wait_for_browser_readiness( + &mut process, + &marker, + "launch", + port, + Instant::now() + Duration::from_millis(300), + &CancellationToken::default(), + ); + assert!(result.is_err()); + server.join().expect("server"); + } + + #[test] + fn readiness_fails_when_the_supervised_child_exits() { + let mut process = + crate::background_process::spawn_service(short_command()).expect("short process"); + let result = wait_for_browser_readiness( + &mut process, + &temporary_marker("exit"), + "launch", + 9, + Instant::now() + Duration::from_secs(2), + &CancellationToken::default(), + ); + assert!( + result + .expect_err("child exit") + .contains("exited during startup") + ); + } + + #[test] + fn readiness_accepts_a_marker_pid_different_from_the_supervised_launcher() { + let listener = TcpListener::bind(("127.0.0.1", 0)).expect("listener"); + let port = listener.local_addr().expect("address").port(); + let server = health_server(listener); + let mut process = + crate::background_process::spawn_service(sleeping_command()).expect("sleeping process"); + let marker = temporary_marker("success"); + fs::write( + &marker, + serde_json::json!({ + "product": READINESS_PRODUCT, + "protocol_version": READINESS_PROTOCOL_VERSION, + "nonce": "launch", + "port": port, + "pid": process.id() + 1, + }) + .to_string(), + ) + .expect("marker"); + wait_for_browser_readiness( + &mut process, + &marker, + "launch", + port, + Instant::now() + Duration::from_secs(2), + &CancellationToken::default(), + ) + .expect("ready"); + assert!(!marker.exists()); + server.join().expect("server"); + } + + #[test] + fn readiness_cancellation_is_bounded() { + let cancelled = CancellationToken::default(); + cancelled.cancel(); + let mut process = + crate::background_process::spawn_service(sleeping_command()).expect("sleeping process"); + let started = Instant::now(); + let result = wait_for_browser_readiness( + &mut process, + &temporary_marker("cancel"), + "launch", + 9, + Instant::now() + Duration::from_secs(20), + &cancelled, + ); + assert!(result.expect_err("cancelled").contains("cancelled")); + assert!(started.elapsed() < Duration::from_secs(2)); + } +} diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index c2bb1e2..b587b20 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -3,14 +3,13 @@ use std::{ collections::{BTreeMap, BTreeSet}, env, fs, io::{self, Write}, - net::{SocketAddr, TcpListener, TcpStream}, + net::TcpListener, path::{Path, PathBuf}, - process::{Child, Command, Output, Stdio}, + process::Command, sync::{ - Mutex, - atomic::{AtomicBool, Ordering}, + Arc, Mutex, OnceLock, + atomic::{AtomicBool, AtomicU64, Ordering}, }, - thread, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; @@ -24,15 +23,28 @@ use tauri::{ }; use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind}; use tauri_plugin_opener::OpenerExt; -use tauri_plugin_shell::{ - ShellExt, - process::{Command as ShellCommand, CommandChild, CommandEvent}, +use tauri_plugin_shell::ShellExt; + +mod activation; +mod background_process; +mod browser_readiness; +mod lifecycle; +mod media_setup; +mod target_profiles; + +use activation::{ActivationRecovery, ActivationStage, activation_recovery}; +use lifecycle::{ + ActiveOperationGuard, ActiveOperations, DesktopAction, DesktopActivation, DesktopCloseAction, + UiProcessAction, action_for_activation, close_action, ui_process_action, }; -use wait_timeout::ChildExt; +use media_setup::{SystemInstallPlan, display_command, required_encoder_missing}; const RUNTIME_MANIFEST_BYTES: &[u8] = include_bytes!("../../runtime-manifest.json"); const RUNTIME_CONSTRAINTS_BYTES: &[u8] = include_bytes!("../../runtime-constraints.txt"); +const MODEL_CACHE_CATALOG_BYTES: &[u8] = include_bytes!("../../model-cache-catalog.json"); const PRODUCT_DATA_DIRECTORY_NAME: &str = "VidXP"; +const MAX_SETUP_OUTPUT_BYTES: usize = 4 * 1024 * 1024; +static READINESS_SEQUENCE: AtomicU64 = AtomicU64::new(0); #[derive(Clone, Deserialize, Serialize)] struct CapabilitySpec { @@ -77,6 +89,13 @@ struct InstallRequest { surfaces: Vec, prepare_models: bool, model_directory: Option, + draft_id: String, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +struct ManagedSetupDraft { + id: String, + previous_profile_id: Option, } #[derive(Serialize)] @@ -88,9 +107,17 @@ struct InstallResult { prepared: bool, } +#[derive(Serialize)] +struct InstallTransitionResult { + install: InstallResult, + setup: target_profiles::TargetState, +} + #[derive(Serialize)] struct RuntimeStatus { + state: RuntimeState, ready: bool, + runtime_profile: Option, package_version: String, capabilities: Vec, surfaces: Vec, @@ -98,6 +125,41 @@ struct RuntimeStatus { detail: String, } +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +enum RuntimeState { + NeverConfigured, + Ready, + Broken, +} + +#[derive(Clone, Deserialize, Serialize)] +struct CachedModelEntry { + id: String, + label: String, +} + +#[derive(Deserialize)] +struct ModelCacheCatalogEntry { + id: String, + label: String, + relative_artifact: String, +} + +#[derive(Serialize)] +struct ModelDirectoryInventory { + directory: String, + exists: bool, + readable: bool, + total_bytes: u64, + file_count: u64, + recognized_models: Vec, + empty: bool, + verification_required: bool, + truncated: bool, + detail: String, +} + #[derive(Clone, Serialize)] struct MediaRuntimeStatus { ready: bool, @@ -115,13 +177,7 @@ struct VerifiedMediaRuntime { ffprobe: PathBuf, } -struct SystemInstallPlan { - manager: String, - command: Vec, - automatic: bool, -} - -#[derive(Clone, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] struct ActiveRuntime { schema_version: u32, manifest_sha256: String, @@ -134,6 +190,7 @@ struct ActiveRuntime { model_directory: PathBuf, } +#[derive(Clone)] struct DesktopPaths { private_data: PathBuf, data: PathBuf, @@ -143,40 +200,506 @@ struct DesktopPaths { python: PathBuf, models: PathBuf, active_runtime: PathBuf, + activation_journal: PathBuf, +} + +type WorkerStopper = dyn Fn(&Path, &DesktopPaths, Instant) + Send + Sync; + +struct ActiveWorkerOperation { + id: u64, + runtime: PathBuf, + paths: DesktopPaths, + stop_claimed: bool, +} + +#[derive(Default)] +struct WorkerStopState { + next_id: u64, + active: Option, + last_stopped_runtime: Option, +} + +struct WorkerStopSupervisor { + state: Mutex, + stopper: Arc, +} + +impl Default for WorkerStopSupervisor { + fn default() -> Self { + Self { + state: Mutex::new(WorkerStopState::default()), + stopper: Arc::new(stop_worker_before), + } + } +} + +impl WorkerStopSupervisor { + #[cfg(test)] + fn with_stopper(stopper: Arc) -> Self { + Self { + state: Mutex::new(WorkerStopState::default()), + stopper, + } + } + + fn register( + self: &Arc, + runtime: PathBuf, + paths: DesktopPaths, + ) -> Result { + let mut state = self + .state + .lock() + .map_err(|_| "The preparation worker supervisor is unavailable.".to_string())?; + if state.active.is_some() { + return Err("Another worker-backed managed operation is already active.".into()); + } + state.next_id += 1; + let id = state.next_id; + state.last_stopped_runtime = None; + state.active = Some(ActiveWorkerOperation { + id, + runtime, + paths, + stop_claimed: false, + }); + Ok(WorkerOperationGuard { + supervisor: self.clone(), + id, + settled: false, + }) + } + + fn claim(&self, id: u64) -> Option<(PathBuf, DesktopPaths)> { + let mut state = self.state.lock().ok()?; + let active = state.active.as_mut()?; + if active.id != id || active.stop_claimed { + return None; + } + active.stop_claimed = true; + Some((active.runtime.clone(), active.paths.clone())) + } + + fn finish(&self, id: u64, runtime: PathBuf) { + if let Ok(mut state) = self.state.lock() + && state.active.as_ref().is_some_and(|active| active.id == id) + { + state.active = None; + state.last_stopped_runtime = Some(runtime); + } + } + + fn stop_active_before(&self, deadline: Instant) -> Option { + let claim = { + let mut state = self.state.lock().ok()?; + if let Some(active) = state.active.as_mut() { + if active.stop_claimed { + return Some(active.runtime.clone()); + } + active.stop_claimed = true; + Some((active.id, active.runtime.clone(), active.paths.clone())) + } else { + return state.last_stopped_runtime.clone(); + } + }; + let (id, runtime, paths) = claim?; + (self.stopper)(&runtime, &paths, deadline); + self.finish(id, runtime.clone()); + Some(runtime) + } +} + +struct WorkerOperationGuard { + supervisor: Arc, + id: u64, + settled: bool, +} + +impl WorkerOperationGuard { + fn stop_before(&mut self, deadline: Instant) { + if self.settled { + return; + } + if let Some((runtime, paths)) = self.supervisor.claim(self.id) { + (self.supervisor.stopper)(&runtime, &paths, deadline); + self.supervisor.finish(self.id, runtime); + } + self.settled = true; + } +} + +impl Drop for WorkerOperationGuard { + fn drop(&mut self) { + self.stop_before(Instant::now() + Duration::from_secs(5)); + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +struct ActivationJournal { + schema_version: u32, + stage: ActivationStage, + previous_active_bytes: Option>, + previous_targets: target_profiles::TargetState, + candidate_active: ActiveRuntime, + candidate_targets: target_profiles::TargetState, +} + +#[derive(Debug, Default, Eq, PartialEq)] +struct RuntimeReconciliation { + removed_directories: usize, + reclaimed_bytes: u64, + failures: Vec, } struct ManagedUi { - process: Child, + process: background_process::OwnedChild, url: String, + profile_id: String, } struct DesktopState { ui_process: Mutex>, - operation_process: Mutex>, - operation_worker_runtime: Mutex>, - operation_active: AtomicBool, + worker_stop: Arc, + operation_cancellation: Arc>>, + transition: Arc>, + browser_open_active: AtomicBool, + shutdown: background_process::CancellationToken, shutdown_started: AtomicBool, + active_operations: Arc, } impl Default for DesktopState { fn default() -> Self { Self { ui_process: Mutex::new(None), - operation_process: Mutex::new(None), - operation_worker_runtime: Mutex::new(None), - operation_active: AtomicBool::new(false), + worker_stop: Arc::new(WorkerStopSupervisor::default()), + operation_cancellation: Arc::new(Mutex::new(None)), + transition: Arc::new(Mutex::new(TransitionState::default())), + browser_open_active: AtomicBool::new(false), + shutdown: background_process::CancellationToken::default(), shutdown_started: AtomicBool::new(false), + active_operations: Arc::new(ActiveOperations::default()), + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum DraftPhase { + Draft, + Applying, + Committed, + Cancelled, +} + +#[derive(Clone, Debug)] +struct DraftRecord { + draft: ManagedSetupDraft, + phase: DraftPhase, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum TransitionKind { + Revalidate, + Adopt, + Select, + Delete, + InstallMedia, + InstallRuntime, + PrepareModels, + RecoverActivation, + OpenBrowser, +} + +#[derive(Clone, Copy, Debug)] +struct ActiveTransition { + id: u64, + kind: TransitionKind, +} + +#[derive(Default)] +struct TransitionState { + next_id: u64, + active: Option, + draft: Option, +} + +fn transition_error( + code: target_profiles::TargetErrorCode, + message: impl Into, +) -> target_profiles::TargetError { + target_profiles::TargetError { + code, + message: message.into(), + } +} + +fn track_target_operation( + state: &DesktopState, +) -> Result { + state.active_operations.register().map_err(|message| { + transition_error(target_profiles::TargetErrorCode::OperationConflict, message) + }) +} + +struct TransitionGuard { + shared: Arc>, + id: u64, + applying_draft: Option, +} + +impl TransitionGuard { + fn commit_draft(&mut self) { + let Some(draft_id) = self.applying_draft.take() else { + return; + }; + if let Ok(mut transition) = self.shared.lock() + && let Some(record) = transition.draft.as_mut() + && record.draft.id == draft_id + && record.phase == DraftPhase::Applying + { + record.phase = DraftPhase::Committed; + } + } +} + +impl Drop for TransitionGuard { + fn drop(&mut self) { + let Ok(mut transition) = self.shared.lock() else { + return; + }; + if transition.active.is_some_and(|active| active.id == self.id) { + transition.active = None; + } + if let Some(draft_id) = self.applying_draft.take() + && let Some(record) = transition.draft.as_mut() + && record.draft.id == draft_id + && record.phase == DraftPhase::Applying + { + record.phase = DraftPhase::Draft; + } + } +} + +struct TargetTransitionCoordinator; + +impl TargetTransitionCoordinator { + fn begin( + state: &DesktopState, + kind: TransitionKind, + ) -> Result { + let mut transition = state.transition.lock().map_err(|_| { + transition_error( + target_profiles::TargetErrorCode::StoreUnavailable, + "The target transition coordinator is unavailable.", + ) + })?; + if let Some(active) = transition.active { + return Err(transition_error( + target_profiles::TargetErrorCode::OperationConflict, + format!( + "Another target transition ({:?}) is already active.", + active.kind + ), + )); + } + transition.next_id = transition.next_id.wrapping_add(1).max(1); + let id = transition.next_id; + transition.active = Some(ActiveTransition { id, kind }); + Ok(TransitionGuard { + shared: state.transition.clone(), + id, + applying_draft: None, + }) + } + + fn begin_apply( + state: &DesktopState, + draft_id: &str, + kind: TransitionKind, + ) -> Result { + let mut guard = Self::begin(state, kind)?; + let mut transition = state.transition.lock().map_err(|_| { + transition_error( + target_profiles::TargetErrorCode::StoreUnavailable, + "The managed setup draft is unavailable.", + ) + })?; + let record = transition.draft.as_mut().ok_or_else(|| { + transition_error( + target_profiles::TargetErrorCode::DraftMismatch, + "This managed setup draft has expired.", + ) + })?; + if record.draft.id != draft_id { + return Err(transition_error( + target_profiles::TargetErrorCode::DraftMismatch, + "A stale managed setup screen cannot modify the current draft.", + )); + } + if record.phase != DraftPhase::Draft { + return Err(transition_error( + target_profiles::TargetErrorCode::DraftApplying, + "This managed setup draft is already applying or has finished.", + )); + } + record.phase = DraftPhase::Applying; + guard.applying_draft = Some(draft_id.to_owned()); + Ok(guard) + } + + fn begin_managed_draft( + app: &AppHandle, + state: &DesktopState, + ) -> Result { + let mut transition = state.transition.lock().map_err(|_| { + transition_error( + target_profiles::TargetErrorCode::StoreUnavailable, + "The managed setup draft could not be created.", + ) + })?; + if transition.active.is_some() { + return Err(transition_error( + target_profiles::TargetErrorCode::OperationConflict, + "Another target transition is already active.", + )); + } + if let Some(record) = &transition.draft + && record.phase == DraftPhase::Draft + { + return Ok(record.draft.clone()); + } + let current = target_profiles::current_state(app)?; + let seed = format!( + "{}:{}:{}", + std::process::id(), + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|error| { + transition_error( + target_profiles::TargetErrorCode::ValidationRequired, + format!("The system clock is invalid: {error}"), + ) + })? + .as_nanos(), + current.selected_profile_id.as_deref().unwrap_or_default() + ); + let draft = ManagedSetupDraft { + id: hex::encode(Sha256::digest(seed.as_bytes())), + previous_profile_id: current.selected_profile_id, + }; + transition.draft = Some(DraftRecord { + draft: draft.clone(), + phase: DraftPhase::Draft, + }); + Ok(draft) + } + + fn cancel_managed_draft( + app: &AppHandle, + state: &DesktopState, + draft_id: &str, + ) -> Result { + Self::cancel_draft(state, draft_id)?; + target_profiles::current_state(app) + } + + fn cancel_draft( + state: &DesktopState, + draft_id: &str, + ) -> Result<(), target_profiles::TargetError> { + let mut transition = state.transition.lock().map_err(|_| { + transition_error( + target_profiles::TargetErrorCode::StoreUnavailable, + "The managed setup draft could not be cancelled.", + ) + })?; + if transition.active.is_some() { + return Err(transition_error( + target_profiles::TargetErrorCode::DraftApplying, + "Managed setup is applying and cannot be cancelled until it settles.", + )); + } + let record = transition.draft.as_mut().ok_or_else(|| { + transition_error( + target_profiles::TargetErrorCode::DraftMismatch, + "This managed setup draft has expired.", + ) + })?; + if record.draft.id != draft_id { + return Err(transition_error( + target_profiles::TargetErrorCode::DraftMismatch, + "A stale managed setup screen cannot cancel the current draft.", + )); } + match record.phase { + DraftPhase::Draft => {} + DraftPhase::Applying => { + return Err(transition_error( + target_profiles::TargetErrorCode::DraftApplying, + "Managed setup is applying and cannot be cancelled until it settles.", + )); + } + DraftPhase::Committed | DraftPhase::Cancelled => { + return Err(transition_error( + target_profiles::TargetErrorCode::DraftMismatch, + "This managed setup draft has already finished.", + )); + } + } + record.phase = DraftPhase::Cancelled; + Ok(()) } } -struct OperationGuard<'a> { - active: &'a AtomicBool, +struct OperationCancellationGuard { + slot: Arc>>, + token: background_process::CancellationToken, + _active: ActiveOperationGuard, +} + +impl OperationCancellationGuard { + fn register(state: &DesktopState) -> Result { + let token = background_process::CancellationToken::default(); + let active = state.active_operations.register()?; + let mut slot = state + .operation_cancellation + .lock() + .map_err(|_| "The setup cancellation supervisor is unavailable.".to_string())?; + if state.shutdown.is_cancelled() { + return Err("VidXP Desktop is shutting down.".into()); + } + if slot.is_some() { + return Err("Another cancellable Desktop operation is already active.".into()); + } + *slot = Some(token.clone()); + drop(slot); + Ok(Self { + slot: state.operation_cancellation.clone(), + token, + _active: active, + }) + } + + fn token(&self) -> background_process::CancellationToken { + self.token.clone() + } } -impl Drop for OperationGuard<'_> { +impl Drop for OperationCancellationGuard { fn drop(&mut self) { - self.active.store(false, Ordering::Release); + if let Ok(mut active) = self.slot.lock() + && active.as_ref().is_some_and(|token| token.same(&self.token)) + { + active.take(); + } + } +} + +fn cancel_active_operation(state: &DesktopState) { + if let Ok(active) = state.operation_cancellation.lock() + && let Some(cancellation) = active.as_ref() + { + cancellation.cancel(); } } @@ -241,6 +764,7 @@ fn desktop_paths_from_roots(private_data: &Path, cache: &Path, local_data: &Path runtimes: private_data.join("runtimes"), python: private_data.join("python"), active_runtime: private_data.join("active-runtime.json"), + activation_journal: private_data.join("activation-journal.json"), models: data.join("models"), private_data: private_data.to_path_buf(), data, @@ -314,6 +838,119 @@ fn model_directory(paths: &DesktopPaths, requested: Option<&str>) -> Result Option { + static CATALOG: OnceLock> = OnceLock::new(); + let catalog = CATALOG.get_or_init(|| { + serde_json::from_slice(MODEL_CACHE_CATALOG_BYTES) + .expect("the generated model cache catalog must be valid") + }); + let path = relative + .to_string_lossy() + .replace('\\', "/") + .to_ascii_lowercase(); + catalog + .iter() + .find(|entry| path.ends_with(&entry.relative_artifact.to_ascii_lowercase())) + .map(|entry| CachedModelEntry { + id: entry.id.clone(), + label: entry.label.clone(), + }) +} + +fn inventory_model_directory(directory: &Path) -> ModelDirectoryInventory { + let resolved = fs::canonicalize(directory).unwrap_or_else(|_| directory.to_path_buf()); + let mut inventory = ModelDirectoryInventory { + directory: resolved.to_string_lossy().into_owned(), + exists: directory.exists(), + readable: true, + total_bytes: 0, + file_count: 0, + recognized_models: Vec::new(), + empty: true, + verification_required: false, + truncated: false, + detail: String::new(), + }; + if !inventory.exists { + inventory.detail = "No model directory exists yet; no cached models were found.".into(); + return inventory; + } + if !directory.is_dir() { + inventory.readable = false; + inventory.detail = "The selected model location is not a readable directory.".into(); + return inventory; + } + let root = match fs::read_dir(directory) { + Ok(entries) => entries, + Err(error) => { + inventory.readable = false; + inventory.detail = format!("The selected model directory could not be read: {error}"); + return inventory; + } + }; + let mut pending = vec![(directory.to_path_buf(), root)]; + let mut recognized = BTreeMap::::new(); + let mut visited = 0_u64; + while let Some((_parent, entries)) = pending.pop() { + for entry in entries { + visited += 1; + if visited > MAX_MODEL_INVENTORY_ENTRIES { + inventory.truncated = true; + break; + } + let Ok(entry) = entry else { + inventory.truncated = true; + continue; + }; + let Ok(file_type) = entry.file_type() else { + inventory.truncated = true; + continue; + }; + if file_type.is_symlink() { + continue; + } + let path = entry.path(); + if file_type.is_dir() { + match fs::read_dir(&path) { + Ok(children) => pending.push((path, children)), + Err(_) => inventory.truncated = true, + } + } else if file_type.is_file() { + inventory.file_count += 1; + if let Ok(metadata) = entry.metadata() { + inventory.total_bytes = inventory.total_bytes.saturating_add(metadata.len()); + } else { + inventory.truncated = true; + } + if let Ok(relative) = path.strip_prefix(directory) + && let Some(model) = recognize_cached_model(relative) + { + recognized.insert(model.id, model.label); + } + } + } + if inventory.truncated && visited > MAX_MODEL_INVENTORY_ENTRIES { + break; + } + } + inventory.recognized_models = recognized + .into_iter() + .map(|(id, label)| CachedModelEntry { id, label }) + .collect(); + inventory.empty = inventory.file_count == 0; + inventory.verification_required = inventory.file_count > 0; + inventory.detail = if inventory.empty { + "No cached models were found.".into() + } else if inventory.truncated { + "Cached files were found. The bounded inventory is partial; preparation must verify required artifacts.".into() + } else { + "Cached files detected; verification required. VidXP will reuse valid cached files and download only missing material.".into() + }; + inventory +} + fn selected_capabilities( manifest: &RuntimeManifest, requested: &[String], @@ -459,15 +1096,15 @@ fn executable_candidates(name: &str) -> Vec { let mut directories = env::var_os("PATH") .map(|value| env::split_paths(&value).collect::>()) .unwrap_or_default(); - if cfg!(windows) { - if let Some(local) = env::var_os("LOCALAPPDATA") { - directories.push( - PathBuf::from(local) - .join("Microsoft") - .join("WinGet") - .join("Links"), - ); - } + if cfg!(windows) + && let Some(local) = env::var_os("LOCALAPPDATA") + { + directories.push( + PathBuf::from(local) + .join("Microsoft") + .join("WinGet") + .join("Links"), + ); } if cfg!(target_os = "macos") { directories.extend([ @@ -501,7 +1138,7 @@ fn resolve_system_executable(name: &str) -> Option { .and_then(|candidate| fs::canonicalize(&candidate).ok().or(Some(candidate))) } -fn combined_output(output: &Output) -> String { +fn combined_output(output: &background_process::BackgroundOutput) -> String { format!( "{}\n{}", String::from_utf8_lossy(&output.stdout), @@ -509,83 +1146,8 @@ fn combined_output(output: &Output) -> String { ) } -fn required_encoder_missing(output: &str, encoder: &str) -> bool { - !output - .lines() - .flat_map(|line| line.split_whitespace()) - .any(|token| token == encoder) -} - fn system_install_plan() -> Option { - if cfg!(windows) { - resolve_system_executable("winget")?; - return Some(SystemInstallPlan { - manager: "Windows Package Manager".into(), - command: vec![ - "winget".into(), - "install".into(), - "--id".into(), - "Gyan.FFmpeg".into(), - "--exact".into(), - "--source".into(), - "winget".into(), - "--accept-package-agreements".into(), - "--accept-source-agreements".into(), - ], - automatic: true, - }); - } - if cfg!(target_os = "macos") { - let brew = resolve_system_executable("brew")?; - return Some(SystemInstallPlan { - manager: "Homebrew".into(), - command: vec![ - brew.to_string_lossy().into_owned(), - "install".into(), - "ffmpeg".into(), - ], - automatic: true, - }); - } - if resolve_system_executable("apt-get").is_some() { - return Some(SystemInstallPlan { - manager: "APT".into(), - command: vec![ - "sudo".into(), - "apt-get".into(), - "install".into(), - "ffmpeg".into(), - ], - automatic: false, - }); - } - if resolve_system_executable("dnf").is_some() { - return Some(SystemInstallPlan { - manager: "DNF".into(), - command: vec![ - "sudo".into(), - "dnf".into(), - "install".into(), - "ffmpeg".into(), - ], - automatic: false, - }); - } - None -} - -fn display_command(arguments: &[String]) -> String { - arguments - .iter() - .map(|argument| { - if argument.contains(char::is_whitespace) { - format!("\"{}\"", argument.replace('"', "\\\"")) - } else { - argument.clone() - } - }) - .collect::>() - .join(" ") + media_setup::system_install_plan(resolve_system_executable) } fn inspect_media_runtime() -> MediaRuntimeStatus { @@ -667,7 +1229,15 @@ fn verified_media_runtime() -> Result { } fn clean_environment(paths: &DesktopPaths) -> Vec<(String, String)> { - let mut environment: Vec<_> = std::env::vars() + clean_environment_from(paths, std::env::vars()) +} + +fn clean_environment_from( + paths: &DesktopPaths, + source: impl IntoIterator, +) -> Vec<(String, String)> { + let mut environment: Vec<_> = source + .into_iter() .filter(|(key, _)| { let upper = key.to_ascii_uppercase(); !upper.starts_with("VIDXP_") @@ -704,28 +1274,24 @@ fn clean_environment(paths: &DesktopPaths) -> Vec<(String, String)> { fn configured_command(executable_path: &Path, paths: &DesktopPaths) -> Command { let mut command = Command::new(executable_path); - hide_child_console(&mut command); command.env_clear(); command.envs(clean_environment(paths)); command } -#[cfg(windows)] -fn hide_child_console(command: &mut Command) { - use std::os::windows::process::CommandExt; - - const CREATE_NO_WINDOW: u32 = 0x0800_0000; - command.creation_flags(CREATE_NO_WINDOW); -} - -#[cfg(not(windows))] -fn hide_child_console(_command: &mut Command) {} - -fn checked_output(mut command: Command, operation: &str) -> Result { - hide_child_console(&mut command); - let output = command - .output() - .map_err(|error| format!("{operation} could not start: {error}"))?; +fn checked_output( + command: Command, + operation: &str, +) -> Result { + let output = background_process::run( + command, + background_process::BackgroundPolicy { + timeout: Duration::from_secs(30), + max_output_bytes: 1024 * 1024, + }, + None, + ) + .map_err(|error| format!("{operation} failed: {}", error.detail))?; if output.status.success() { return Ok(output); } @@ -740,6 +1306,11 @@ fn active_runtime(paths: &DesktopPaths) -> Result { .map_err(|_| "Local video processing has not been configured yet.".to_string())?; let active: ActiveRuntime = serde_json::from_slice(&contents) .map_err(|error| format!("The active runtime pointer is invalid: {error}"))?; + validate_active_runtime_pointer(&active)?; + Ok(active) +} + +fn validate_active_runtime_pointer(active: &ActiveRuntime) -> Result<(), String> { if active.schema_version != 2 || active.manifest_sha256 != manifest_digest() { return Err("The desktop runtime needs to be installed for this app version.".into()); } @@ -750,7 +1321,7 @@ fn active_runtime(paths: &DesktopPaths) -> Result { { return Err("The active runtime profile identity is invalid.".into()); } - Ok(active) + Ok(()) } fn runtime_directory(paths: &DesktopPaths, active: &ActiveRuntime) -> PathBuf { @@ -769,65 +1340,467 @@ fn write_active_runtime(paths: &DesktopPaths, active: &ActiveRuntime) -> Result< .map_err(|error| format!("Could not activate the validated runtime: {error}")) } -async fn supervised_output( - state: &DesktopState, - command: ShellCommand, - operation: &str, -) -> Result<(Vec, Vec), String> { - if state.shutdown_started.load(Ordering::Acquire) { - return Err(format!( - "{operation} was cancelled because VidXP is closing." - )); - } - let (mut events, child) = command - .spawn() - .map_err(|error| format!("{operation} could not start: {error}"))?; - { - let mut active_child = state - .operation_process - .lock() - .map_err(|_| "The setup process supervisor is unavailable.".to_string())?; - if active_child.is_some() { - drop(active_child); - let _ = child.kill(); - return Err("Another desktop setup process is already running.".into()); - } - *active_child = Some(child); - } - - let mut stdout = Vec::new(); - let mut stderr = Vec::new(); - let mut exit_code = None; - while let Some(event) = events.recv().await { - match event { - CommandEvent::Stdout(bytes) => stdout.extend(bytes), - CommandEvent::Stderr(bytes) => stderr.extend(bytes), - CommandEvent::Error(error) => stderr.extend(error.as_bytes()), - CommandEvent::Terminated(status) => { - exit_code = status.code; - break; - } - _ => {} +fn write_activation_journal( + paths: &DesktopPaths, + journal: &ActivationJournal, +) -> Result<(), String> { + fs::create_dir_all(&paths.private_data) + .map_err(|error| format!("Could not create the activation journal directory: {error}"))?; + let mut destination = AtomicWriteFile::options() + .open(&paths.activation_journal) + .map_err(|error| format!("Could not stage the activation journal: {error}"))?; + serde_json::to_writer(&mut destination, journal) + .map_err(|error| format!("Could not serialize the activation journal: {error}"))?; + destination + .flush() + .and_then(|_| destination.commit()) + .map_err(|error| format!("Could not persist the activation journal: {error}")) +} + +fn read_active_runtime_snapshot(paths: &DesktopPaths) -> Result>, String> { + match fs::read(&paths.active_runtime) { + Ok(bytes) => Ok(Some(bytes)), + Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(None), + Err(error) => Err(format!( + "Could not snapshot the previous active runtime pointer: {error}" + )), + } +} + +fn restore_active_runtime(paths: &DesktopPaths, previous: Option<&[u8]>) -> Result<(), String> { + match previous { + Some(bytes) => { + let mut destination = AtomicWriteFile::options() + .open(&paths.active_runtime) + .map_err(|error| { + format!("Could not stage the previous runtime pointer: {error}") + })?; + destination.write_all(bytes).map_err(|error| { + format!("Could not restore the previous runtime pointer: {error}") + })?; + destination + .flush() + .and_then(|_| destination.commit()) + .map_err(|error| format!("Could not restore the previous runtime pointer: {error}")) } + None if paths.active_runtime.exists() => fs::remove_file(&paths.active_runtime) + .map_err(|error| format!("Could not restore the empty runtime selection: {error}")), + None => Ok(()), } - if let Ok(mut active_child) = state.operation_process.lock() { - active_child.take(); +} + +fn clear_activation_journal(paths: &DesktopPaths) -> Result<(), String> { + if paths.activation_journal.exists() { + fs::remove_file(&paths.activation_journal) + .map_err(|error| format!("Could not clear the activation journal: {error}"))?; + } + Ok(()) +} + +fn managed_runtime_projection_for( + paths: &DesktopPaths, + active: &ActiveRuntime, +) -> target_profiles::ManagedRuntimeProjection { + let runtime = runtime_directory(paths, active); + let requested_executable = executable(&runtime, "vidxp"); + let executable = fs::canonicalize(&requested_executable).unwrap_or(requested_executable); + target_profiles::ManagedRuntimeProjection { + runtime_profile: active.profile.clone(), + executable, + data_root: paths.data.clone(), + repository_root: paths.repository.clone(), + model_directory: active.model_directory.clone(), + package_version: active.package_version.clone(), + capabilities: active.capabilities.clone(), + surfaces: active.surfaces.clone(), + } +} + +fn resolved_path(path: &Path) -> PathBuf { + fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf()) +} + +fn same_path(left: &Path, right: &Path) -> bool { + let left = resolved_path(left); + let right = resolved_path(right); + #[cfg(windows)] + { + left.to_string_lossy() + .eq_ignore_ascii_case(&right.to_string_lossy()) + } + #[cfg(not(windows))] + { + left == right + } +} + +fn path_is_confined(path: &Path, root: &Path) -> bool { + let path = resolved_path(path); + let root = resolved_path(root); + #[cfg(windows)] + { + let path = path.to_string_lossy().to_ascii_lowercase(); + let root = root.to_string_lossy().to_ascii_lowercase(); + path == root || path.starts_with(&format!("{root}\\")) + } + #[cfg(not(windows))] + { + path.starts_with(root) + } +} + +fn managed_probe_error(message: impl Into) -> target_profiles::TargetError { + target_profiles::TargetError { + code: target_profiles::TargetErrorCode::InvalidDataRoot, + message: message.into(), + } +} + +fn validate_managed_projection( + paths: &DesktopPaths, + projection: &target_profiles::ManagedRuntimeProjection, + desktop_version: &str, + cancellation: Option<&background_process::CancellationToken>, +) -> Result { + let runtime = paths.runtimes.join(&projection.runtime_profile); + let validated = target_profiles::validate_executable_using( + &projection.executable, + desktop_version, + cancellation, + |executable| configured_command(executable, paths), + )?; + for (label, reported, authoritative) in [ + ("data", &validated.data_root, &projection.data_root), + ( + "repository", + &validated.repository_root, + &projection.repository_root, + ), + ("model", &validated.model_root, &projection.model_directory), + ] { + if !same_path(reported, authoritative) { + return Err(managed_probe_error(format!( + "The managed probe reported {label} root {}, but VidXP Desktop owns {}.", + reported.display(), + authoritative.display() + ))); + } + } + if !path_is_confined(&validated.executable, &runtime) + || !path_is_confined(&validated.runtime.python_executable, &runtime) + || !same_path(&validated.runtime.prefix, &runtime) + { + return Err(managed_probe_error( + "The managed probe reported a launcher or Python runtime outside the active Desktop-owned environment.", + )); + } + Ok(validated) +} + +fn managed_runtime_projection( + paths: &DesktopPaths, +) -> Option { + let contents = fs::read(&paths.active_runtime).ok()?; + let active: ActiveRuntime = serde_json::from_slice(&contents).ok()?; + if !active + .profile + .chars() + .all(|character| character.is_ascii_hexdigit() || character == '-') + { + log::warn!("Ignoring an active managed runtime with an invalid profile identity"); + return None; + } + Some(managed_runtime_projection_for(paths, &active)) +} + +fn candidate_authorities_match( + app: &AppHandle, + paths: &DesktopPaths, + journal: &ActivationJournal, +) -> bool { + let active_matches = fs::read(&paths.active_runtime) + .ok() + .and_then(|bytes| serde_json::from_slice::(&bytes).ok()) + .is_some_and(|active| active == journal.candidate_active); + let targets_match = target_profiles::current_state(app) + .is_ok_and(|targets| targets == journal.candidate_targets); + active_matches && targets_match +} + +fn mark_journal_stage( + paths: &DesktopPaths, + journal: &mut ActivationJournal, + stage: ActivationStage, +) -> Result<(), String> { + journal.stage = stage; + write_activation_journal(paths, journal) +} + +fn finish_journal_cleanup(paths: &DesktopPaths, context: &str) { + if let Err(error) = clear_activation_journal(paths) { + log::warn!("{context}; activation journal cleanup will be retried: {error}"); + } +} + +fn owned_runtime_directory_name(name: &str) -> bool { + let mut parts = name.split('-'); + let Some(digest) = parts.next() else { + return false; + }; + let Some(timestamp) = parts.next() else { + return false; + }; + parts.next().is_none() + && digest.len() == 64 + && digest + .chars() + .all(|character| character.is_ascii_hexdigit()) + && !timestamp.is_empty() + && timestamp + .chars() + .all(|character| character.is_ascii_digit()) +} + +fn owned_staging_directory_name(name: &str) -> bool { + let Some(remainder) = name.strip_prefix(".staging-") else { + return false; + }; + let mut parts = remainder.split('-'); + let (Some(digest), Some(timestamp), Some(pid)) = (parts.next(), parts.next(), parts.next()) + else { + return false; + }; + parts.next().is_none() + && digest.len() == 64 + && digest + .chars() + .all(|character| character.is_ascii_hexdigit()) + && !timestamp.is_empty() + && timestamp + .chars() + .all(|character| character.is_ascii_digit()) + && !pid.is_empty() + && pid.chars().all(|character| character.is_ascii_digit()) +} + +fn directory_size(path: &Path) -> io::Result { + let mut total = 0_u64; + for entry in fs::read_dir(path)? { + let entry = entry?; + let metadata = entry.path().symlink_metadata()?; + if metadata.file_type().is_symlink() { + continue; + } + if metadata.is_dir() { + total = total.saturating_add(directory_size(&entry.path())?); + } else if metadata.is_file() { + total = total.saturating_add(metadata.len()); + } + } + Ok(total) +} + +fn reconcile_managed_runtime_storage(paths: &DesktopPaths) -> RuntimeReconciliation { + let mut report = RuntimeReconciliation::default(); + let mut retained = BTreeSet::new(); + let preserve_unidentified_finalized = match fs::read(&paths.active_runtime) { + Ok(contents) => match serde_json::from_slice::(&contents) { + Ok(active) => { + retained.insert(active.profile); + false + } + Err(_) => true, + }, + Err(error) => error.kind() != io::ErrorKind::NotFound, + }; + if let Ok(contents) = fs::read(&paths.activation_journal) + && let Ok(journal) = serde_json::from_slice::(&contents) + { + retained.insert(journal.candidate_active.profile); + if let Some(previous) = journal.previous_active_bytes + && let Ok(active) = serde_json::from_slice::(&previous) + { + retained.insert(active.profile); + } + } + let Ok(entries) = fs::read_dir(&paths.runtimes) else { + return report; + }; + for entry in entries.flatten() { + let path = entry.path(); + let name = entry.file_name().to_string_lossy().into_owned(); + let Ok(file_type) = entry.file_type() else { + continue; + }; + if !file_type.is_dir() || file_type.is_symlink() || retained.contains(&name) { + continue; + } + let finalized = owned_runtime_directory_name(&name); + if (!finalized && !owned_staging_directory_name(&name)) + || (finalized && preserve_unidentified_finalized) + { + continue; + } + if !path_is_confined(&path, &paths.runtimes) { + report.failures.push(format!( + "Refused to reconcile a runtime path outside {}: {}", + paths.runtimes.display(), + path.display() + )); + continue; + } + let bytes = directory_size(&path).unwrap_or_default(); + match fs::remove_dir_all(&path) { + Ok(()) => { + report.removed_directories += 1; + report.reclaimed_bytes = report.reclaimed_bytes.saturating_add(bytes); + } + Err(error) => report.failures.push(format!( + "Could not remove untracked Desktop runtime {}: {error}", + path.display() + )), + } + } + report +} + +fn log_runtime_reconciliation(paths: &DesktopPaths) { + let report = reconcile_managed_runtime_storage(paths); + if report.removed_directories > 0 { + log::info!( + "Reconciled {} obsolete managed runtime directories and reclaimed {} bytes", + report.removed_directories, + report.reclaimed_bytes + ); + } + for failure in report.failures { + log::warn!("Managed runtime storage cleanup failed: {failure}"); + } +} + +fn rollback_activation( + app: &AppHandle, + paths: &DesktopPaths, + journal: &mut ActivationJournal, + runtime: &Path, + activation_error: &str, +) -> String { + if let Err(error) = mark_journal_stage(paths, journal, ActivationStage::RollingBack) { + return format!( + "{activation_error}. Rollback could not be durably started: {error}. VidXP Desktop will recover the existing activation journal on its next start. The candidate runtime remains at {}.", + runtime.display() + ); } - if exit_code == Some(0) { - return Ok((stdout, stderr)); + let active_restore = restore_active_runtime(paths, journal.previous_active_bytes.as_deref()); + let target_restore = target_profiles::replace_state(app, journal.previous_targets.clone()) + .map_err(|error| error.to_string()); + if let (Err(active_error), Err(target_error)) = (&active_restore, &target_restore) { + return format!( + "{activation_error}. Rollback remains incomplete (runtime pointer: {active_error}; target profile: {target_error}); startup will retry it. The candidate runtime remains at {}.", + runtime.display() + ); + } + if let Err(error) = active_restore { + return format!( + "{activation_error}. Target profiles were restored, but the runtime pointer could not be restored: {error}; startup will retry rollback. The candidate runtime remains at {}.", + runtime.display() + ); + } + if let Err(error) = target_restore { + return format!( + "{activation_error}. The runtime pointer was restored, but target profiles could not be restored: {error}; startup will retry rollback. The candidate runtime remains at {}.", + runtime.display() + ); + } + if let Err(error) = mark_journal_stage(paths, journal, ActivationStage::RolledBack) { + log::warn!( + "Managed activation rolled back, but its completion marker could not be written: {error}" + ); + } + finish_journal_cleanup(paths, "Rolled back a failed managed activation"); + format!( + "{activation_error}. The candidate runtime was retained at {}, while the previous active runtime and target remain selected.", + runtime.display() + ) +} + +fn recover_interrupted_activation(app: &AppHandle, state: &DesktopState) -> Result<(), String> { + let _transition = TargetTransitionCoordinator::begin(state, TransitionKind::RecoverActivation) + .map_err(|error| error.to_string())?; + let paths = desktop_paths(app)?; + if !paths.activation_journal.exists() { + return Ok(()); + } + let contents = fs::read(&paths.activation_journal) + .map_err(|error| format!("Could not read the activation journal: {error}"))?; + let mut journal: ActivationJournal = serde_json::from_slice(&contents) + .map_err(|error| format!("The activation journal is invalid: {error}"))?; + if journal.schema_version != 2 { + return Err(format!( + "The activation journal uses unsupported schema version {}.", + journal.schema_version + )); + } + let authorities_match = candidate_authorities_match(app, &paths, &journal); + match activation_recovery(&journal.stage, authorities_match) { + ActivationRecovery::Complete => { + write_active_runtime(&paths, &journal.candidate_active)?; + target_profiles::replace_state(app, journal.candidate_targets.clone()) + .map_err(|error| error.to_string())?; + mark_journal_stage(&paths, &mut journal, ActivationStage::Committed)?; + } + ActivationRecovery::RollBack => { + mark_journal_stage(&paths, &mut journal, ActivationStage::RollingBack)?; + restore_active_runtime(&paths, journal.previous_active_bytes.as_deref())?; + target_profiles::replace_state(app, journal.previous_targets.clone()) + .map_err(|error| error.to_string())?; + mark_journal_stage(&paths, &mut journal, ActivationStage::RolledBack)?; + } + } + finish_journal_cleanup(&paths, "Recovered an interrupted managed activation"); + Ok(()) +} + +fn initialize_target_profiles(app: &AppHandle) -> Result { + let manifest = manifest()?; + let paths = desktop_paths(app)?; + target_profiles::initialize( + app, + managed_runtime_projection(&paths), + &manifest.desktop_version, + ) + .map_err(|error| error.to_string()) +} + +async fn supervised_output( + command: Command, + cancellation: background_process::CancellationToken, + operation: &str, +) -> Result { + let output = background_process::run_async( + command, + background_process::BackgroundPolicy { + timeout: Duration::from_secs(30 * 60), + max_output_bytes: MAX_SETUP_OUTPUT_BYTES, + }, + cancellation, + ) + .await + .map_err(|error| format!("{operation} failed: {}", error.detail))?; + if output.status.success() { + return Ok(output); } - let detail = String::from_utf8_lossy(&stderr).trim().to_owned(); - Err(format!( - "{operation} failed{}: {detail}", - exit_code.map_or_else(String::new, |code| format!(" with exit code {code}")) - )) + let stderr = String::from_utf8_lossy(&output.stderr).trim().to_owned(); + let stdout = String::from_utf8_lossy(&output.stdout).trim().to_owned(); + let detail = if stderr.is_empty() { stdout } else { stderr }; + Err(format!("{operation} failed ({}): {detail}", output.status)) } async fn uv_output( app: &AppHandle, - state: &DesktopState, paths: &DesktopPaths, arguments: Vec, + cancellation: background_process::CancellationToken, operation: &str, ) -> Result<(), String> { let mut command = app @@ -844,7 +1817,8 @@ async fn uv_output( .env("UV_PYTHON_INSTALL_DIR", &paths.python) .env("UV_NO_CONFIG", "1") .env("UV_MANAGED_PYTHON", "1"); - supervised_output(state, command, operation).await?; + let command: Command = command.into(); + supervised_output(command, cancellation, operation).await?; Ok(()) } @@ -853,7 +1827,7 @@ fn run_vidxp( paths: &DesktopPaths, arguments: &[String], operation: &str, -) -> Result { +) -> Result { let mut command = configured_command(&executable(runtime, "vidxp"), paths); command .arg("--index-dir") @@ -863,40 +1837,363 @@ fn run_vidxp( } async fn run_vidxp_supervised( - app: &AppHandle, - state: &DesktopState, runtime: &Path, paths: &DesktopPaths, arguments: &[String], + cancellation: background_process::CancellationToken, operation: &str, ) -> Result<(), String> { - let mut command = app - .shell() - .command(executable(runtime, "vidxp")) - .env_clear(); - for (key, value) in clean_environment(paths) { - command = command.env(key, value); - } - command = command + let mut command = configured_command(&executable(runtime, "vidxp"), paths); + command .arg("--index-dir") .arg(&paths.repository) .args(arguments); - supervised_output(state, command, operation).await?; + supervised_output(command, cancellation, operation).await?; Ok(()) } #[tauri::command] -fn runtime_manifest() -> Result { +fn runtime_manifest(state: tauri::State<'_, DesktopState>) -> Result { + let _active = state.active_operations.register()?; manifest() } #[tauri::command] -fn media_runtime_status() -> MediaRuntimeStatus { - inspect_media_runtime() +fn target_state( + app: AppHandle, + state: tauri::State<'_, DesktopState>, +) -> Result { + let _active = track_target_operation(&state)?; + target_profiles::current_state(&app) +} + +#[tauri::command] +async fn refresh_target_state( + app: AppHandle, + state: tauri::State<'_, DesktopState>, +) -> Result { + let _active = track_target_operation(&state)?; + let manifest = manifest().map_err(|error| target_profiles::TargetError { + code: target_profiles::TargetErrorCode::ValidationRequired, + message: error, + })?; + let desktop_version = manifest.desktop_version; + let transition = TargetTransitionCoordinator::begin(&state, TransitionKind::Revalidate)?; + let cancellation = state.shutdown.clone(); + tauri::async_runtime::spawn_blocking(move || { + let _transition = transition; + match target_profiles::selected_profile(&app) { + Ok(profile) => { + if profile.kind == target_profiles::TargetKind::Managed { + let validation = (|| { + let paths = desktop_paths(&app).map_err(|message| { + transition_error( + target_profiles::TargetErrorCode::ManagedRuntimeUnavailable, + message, + ) + })?; + let active = active_runtime(&paths).map_err(|message| { + transition_error( + target_profiles::TargetErrorCode::ManagedRuntimeUnavailable, + message, + ) + })?; + if profile.managed_runtime_profile.as_deref() + != Some(active.profile.as_str()) + { + return Err(transition_error( + target_profiles::TargetErrorCode::ManagedRuntimeUnavailable, + "The selected managed target does not match the active Desktop runtime.", + )); + } + let projection = managed_runtime_projection_for(&paths, &active); + validate_managed_projection( + &paths, + &projection, + &desktop_version, + Some(&cancellation), + ) + })(); + let _ = target_profiles::persist_selected_validation(&app, validation); + } else { + let _ = target_profiles::validated_selected_profile_with_cancellation( + &app, + &desktop_version, + Some(&cancellation), + ); + } + } + Err(error) + if error.code == target_profiles::TargetErrorCode::SelectedProfileMissing => {} + Err(error) => return Err(error), + } + target_profiles::current_state(&app) + }) + .await + .map_err(|error| target_profiles::TargetError { + code: target_profiles::TargetErrorCode::ValidationRequired, + message: format!("Target revalidation stopped unexpectedly: {error}"), + })? +} + +#[tauri::command] +async fn discover_local_targets( + state: tauri::State<'_, DesktopState>, +) -> Result, String> { + let _active = state.active_operations.register()?; + tauri::async_runtime::spawn_blocking(target_profiles::discover_local_targets) + .await + .map_err(|error| format!("Target discovery stopped unexpectedly: {error}")) +} + +#[tauri::command] +fn choose_local_executable( + app: AppHandle, + state: tauri::State<'_, DesktopState>, +) -> Result, String> { + let _active = state.active_operations.register()?; + app.dialog() + .file() + .set_title("Choose an existing VidXP executable") + .blocking_pick_file() + .map(|path| { + path.into_path() + .map(|path| path.to_string_lossy().into_owned()) + .map_err(|error| format!("The selected executable path is invalid: {error}")) + }) + .transpose() } #[tauri::command] -fn choose_model_directory(app: AppHandle) -> Result, String> { +async fn inspect_local_target( + state: tauri::State<'_, DesktopState>, + executable: String, +) -> Result { + let _active = track_target_operation(&state)?; + let manifest = manifest().map_err(|error| target_profiles::TargetError { + code: target_profiles::TargetErrorCode::ValidationRequired, + message: error, + })?; + let desktop_version = manifest.desktop_version; + let cancellation = state.shutdown.clone(); + tauri::async_runtime::spawn_blocking(move || { + target_profiles::inspect_executable_with_cancellation( + Path::new(&executable), + &desktop_version, + &cancellation, + ) + }) + .await + .map_err(|error| { + transition_error( + target_profiles::TargetErrorCode::ValidationRequired, + format!("Target inspection stopped unexpectedly: {error}"), + ) + })? +} + +#[tauri::command] +async fn adopt_local_target( + app: AppHandle, + state: tauri::State<'_, DesktopState>, + executable: String, + display_name: Option, +) -> Result { + let _active = track_target_operation(&state)?; + let manifest = manifest().map_err(|error| target_profiles::TargetError { + code: target_profiles::TargetErrorCode::ValidationRequired, + message: error, + })?; + let transition = TargetTransitionCoordinator::begin(&state, TransitionKind::Adopt)?; + let desktop_version = manifest.desktop_version; + let cancellation = state.shutdown.clone(); + tauri::async_runtime::spawn_blocking(move || { + let _transition = transition; + let canonical = + fs::canonicalize(Path::new(&executable)).unwrap_or_else(|_| PathBuf::from(&executable)); + let validated = target_profiles::validate_executable_using( + &canonical, + &desktop_version, + Some(&cancellation), + |path| Command::new(path), + )?; + let setup = target_profiles::adopt_validated(&app, validated, display_name)?; + stop_ui_process(&app.state::()); + Ok(setup) + }) + .await + .map_err(|error| { + transition_error( + target_profiles::TargetErrorCode::ValidationRequired, + format!("Target adoption stopped unexpectedly: {error}"), + ) + })? +} + +#[tauri::command] +async fn select_target_profile( + app: AppHandle, + state: tauri::State<'_, DesktopState>, + profile_id: String, +) -> Result { + let _active = track_target_operation(&state)?; + let manifest = manifest().map_err(|error| target_profiles::TargetError { + code: target_profiles::TargetErrorCode::ValidationRequired, + message: error, + })?; + let transition = TargetTransitionCoordinator::begin(&state, TransitionKind::Select)?; + let desktop_version = manifest.desktop_version; + let cancellation = state.shutdown.clone(); + tauri::async_runtime::spawn_blocking(move || { + let _transition = transition; + let candidate = target_profiles::current_state(&app)? + .profiles + .into_iter() + .find(|profile| profile.id == profile_id) + .ok_or_else(|| { + transition_error( + target_profiles::TargetErrorCode::ProfileNotFound, + "The selected VidXP target no longer exists.", + ) + })?; + let setup = if candidate.kind == target_profiles::TargetKind::Managed { + let paths = desktop_paths(&app).map_err(|message| { + transition_error( + target_profiles::TargetErrorCode::ManagedRuntimeUnavailable, + message, + ) + })?; + let active = active_runtime(&paths).map_err(|message| { + transition_error( + target_profiles::TargetErrorCode::ManagedRuntimeUnavailable, + message, + ) + })?; + if candidate.managed_runtime_profile.as_deref() != Some(active.profile.as_str()) { + return Err(transition_error( + target_profiles::TargetErrorCode::ManagedRuntimeUnavailable, + "The selected managed target does not match the active Desktop runtime.", + )); + } + let projection = managed_runtime_projection_for(&paths, &active); + let validated = validate_managed_projection( + &paths, + &projection, + &desktop_version, + Some(&cancellation), + )?; + target_profiles::select_validated_profile(&app, &profile_id, validated)? + } else { + target_profiles::select_profile(&app, &profile_id, &desktop_version)? + }; + stop_ui_process(&app.state::()); + Ok(setup) + }) + .await + .map_err(|error| { + transition_error( + target_profiles::TargetErrorCode::ValidationRequired, + format!("Target selection stopped unexpectedly: {error}"), + ) + })? +} + +#[tauri::command] +async fn delete_target_profile( + app: AppHandle, + state: tauri::State<'_, DesktopState>, + profile_id: String, +) -> Result { + let _active = track_target_operation(&state)?; + let transition = TargetTransitionCoordinator::begin(&state, TransitionKind::Delete)?; + tauri::async_runtime::spawn_blocking(move || { + let _transition = transition; + let selected = target_profiles::current_state(&app)?.selected_profile_id; + let result = target_profiles::delete_profile(&app, &profile_id)?; + if selected.as_deref() == Some(&profile_id) { + stop_ui_process(&app.state::()); + } + Ok(result) + }) + .await + .map_err(|error| { + transition_error( + target_profiles::TargetErrorCode::ValidationRequired, + format!("Target deletion stopped unexpectedly: {error}"), + ) + })? +} + +#[tauri::command] +async fn confirm_forget_target( + app: AppHandle, + state: tauri::State<'_, DesktopState>, + display_name: String, +) -> Result { + let _active = state.active_operations.register()?; + tauri::async_runtime::spawn_blocking(move || { + app.dialog() + .message(format!( + "Forget “{display_name}” from VidXP Desktop? The installation itself will not be changed." + )) + .title("Forget saved target?") + .kind(MessageDialogKind::Warning) + .buttons(MessageDialogButtons::OkCancel) + .blocking_show() + }) + .await + .map_err(|error| format!("The confirmation dialog stopped unexpectedly: {error}")) +} + +#[tauri::command] +async fn begin_managed_setup( + app: AppHandle, + state: tauri::State<'_, DesktopState>, +) -> Result { + let _active = track_target_operation(&state)?; + let transition = state.transition.clone(); + tauri::async_runtime::spawn_blocking(move || { + let state = app.state::(); + debug_assert!(Arc::ptr_eq(&transition, &state.transition)); + TargetTransitionCoordinator::begin_managed_draft(&app, &state) + }) + .await + .map_err(|error| { + transition_error( + target_profiles::TargetErrorCode::ValidationRequired, + format!("Managed setup could not start: {error}"), + ) + })? +} + +#[tauri::command] +async fn cancel_managed_setup( + app: AppHandle, + state: tauri::State<'_, DesktopState>, + draft_id: String, +) -> Result { + let _active = track_target_operation(&state)?; + let transition = state.transition.clone(); + tauri::async_runtime::spawn_blocking(move || { + let state = app.state::(); + debug_assert!(Arc::ptr_eq(&transition, &state.transition)); + TargetTransitionCoordinator::cancel_managed_draft(&app, &state, &draft_id) + }) + .await + .map_err(|error| { + transition_error( + target_profiles::TargetErrorCode::ValidationRequired, + format!("Managed setup cancellation stopped unexpectedly: {error}"), + ) + })? +} + +#[tauri::command] +fn choose_model_directory( + app: AppHandle, + state: tauri::State<'_, DesktopState>, +) -> Result, String> { + let _active = state.active_operations.register()?; let selection = app .dialog() .file() @@ -915,22 +2212,25 @@ fn choose_model_directory(app: AppHandle) -> Result, String> { async fn install_media_runtime( app: AppHandle, state: tauri::State<'_, DesktopState>, + draft_id: String, ) -> Result { - let current = inspect_media_runtime(); + let cancellation = OperationCancellationGuard::register(&state)?; + let _transition = + TargetTransitionCoordinator::begin_apply(&state, &draft_id, TransitionKind::InstallMedia) + .map_err(|error| error.to_string())?; + let current = tauri::async_runtime::spawn_blocking(inspect_media_runtime) + .await + .map_err(|error| format!("Media runtime inspection stopped unexpectedly: {error}"))?; if current.ready { return Ok(current); } - if state - .operation_active - .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) - .is_err() - { - return Err("Another install or model-preparation operation is active.".into()); - } - let _operation_guard = OperationGuard { - active: &state.operation_active, - }; - let plan = system_install_plan().ok_or("No supported system package manager was found.")?; + let plan = system_install_plan().ok_or_else(|| { + if cfg!(target_os = "macos") { + "FFmpeg is required. Install Homebrew from https://brew.sh and run `brew install ffmpeg`, or install FFmpeg and ffprobe manually on PATH, then retry.".to_string() + } else { + "No supported system package manager was found. Install FFmpeg and ffprobe on PATH, then retry.".to_string() + } + })?; if !plan.automatic { let instruction = format!( "VidXP needs FFmpeg and ffprobe.\n\nRun this command in a terminal, then return to VidXP:\n\n{}", @@ -964,13 +2264,16 @@ async fn install_media_runtime( .shell() .command(plan.command[0].clone()) .args(&plan.command[1..]); + let command: Command = command.into(); supervised_output( - &state, command, + cancellation.token(), &format!("{} FFmpeg installation", plan.manager), ) .await?; - let status = inspect_media_runtime(); + let status = tauri::async_runtime::spawn_blocking(inspect_media_runtime) + .await + .map_err(|error| format!("Media runtime verification stopped unexpectedly: {error}"))?; if !status.ready { return Err(format!( "FFmpeg installation finished but verification failed: {}", @@ -981,35 +2284,58 @@ async fn install_media_runtime( } #[tauri::command] -fn runtime_status(app: AppHandle) -> Result { +async fn runtime_status( + app: AppHandle, + state: tauri::State<'_, DesktopState>, +) -> Result { + let _active = state.active_operations.register()?; + tauri::async_runtime::spawn_blocking(move || runtime_status_sync(&app)) + .await + .map_err(|error| format!("Managed runtime inspection stopped unexpectedly: {error}"))? +} + +fn runtime_status_sync(app: &AppHandle) -> Result { let manifest = manifest()?; - let mut paths = desktop_paths(&app)?; + let mut paths = desktop_paths(app)?; let default_model_directory = paths.models.to_string_lossy().into_owned(); - if let Err(detail) = verified_media_runtime() { + if !paths.active_runtime.exists() { return Ok(RuntimeStatus { + state: RuntimeState::NeverConfigured, ready: false, + runtime_profile: None, package_version: manifest.package_version, capabilities: Vec::new(), surfaces: Vec::new(), model_directory: default_model_directory, - detail, + detail: "No Desktop-managed runtime has been created yet.".into(), }); } - let active = match active_runtime(&paths) { + let contents = fs::read(&paths.active_runtime) + .map_err(|error| format!("Could not read the active runtime pointer: {error}"))?; + let active: ActiveRuntime = match serde_json::from_slice(&contents) { Ok(active) => active, - Err(detail) => { + Err(error) => { return Ok(RuntimeStatus { + state: RuntimeState::Broken, ready: false, + runtime_profile: None, package_version: manifest.package_version, capabilities: Vec::new(), surfaces: Vec::new(), model_directory: default_model_directory, - detail, + detail: format!("The active runtime pointer is invalid: {error}"), }); } }; paths.models = active.model_directory.clone(); let runtime = runtime_directory(&paths, &active); + let mut problems = Vec::new(); + if let Err(error) = validate_active_runtime_pointer(&active) { + problems.push(error); + } + if let Err(error) = verified_media_runtime() { + problems.push(error); + } let version = run_vidxp( &runtime, &paths, @@ -1027,16 +2353,89 @@ fn runtime_status(app: AppHandle) -> Result { )) } }); - Ok(RuntimeStatus { - ready: version.is_ok(), + if let Err(error) = version { + problems.push(error); + } + Ok(configured_runtime_status(active, problems)) +} + +fn configured_runtime_status(active: ActiveRuntime, problems: Vec) -> RuntimeStatus { + let ready = problems.is_empty(); + RuntimeStatus { + state: if ready { + RuntimeState::Ready + } else { + RuntimeState::Broken + }, + ready, + runtime_profile: Some(active.profile.clone()), package_version: active.package_version, capabilities: active.capabilities, surfaces: active.surfaces, model_directory: active.model_directory.to_string_lossy().into_owned(), - detail: version - .err() - .unwrap_or_else(|| "Local video processing is ready.".into()), + detail: if ready { + "Local video processing is ready.".into() + } else { + problems.join(" ") + }, + } +} + +#[tauri::command] +async fn model_directory_inventory( + app: AppHandle, + state: tauri::State<'_, DesktopState>, + directory: Option, +) -> Result { + let _active = state.active_operations.register()?; + tauri::async_runtime::spawn_blocking(move || { + let paths = desktop_paths(&app)?; + let selected = model_directory(&paths, directory.as_deref())?; + Ok(inventory_model_directory(&selected)) }) + .await + .map_err(|error| format!("Model inventory stopped unexpectedly: {error}"))? +} + +#[tauri::command] +async fn prepare_managed_models( + app: AppHandle, + state: tauri::State<'_, DesktopState>, + draft_id: String, +) -> Result { + let cancellation = OperationCancellationGuard::register(&state)?; + let _transition = + TargetTransitionCoordinator::begin_apply(&state, &draft_id, TransitionKind::PrepareModels) + .map_err(|error| error.to_string())?; + let preparation_app = app.clone(); + let (runtime, paths, capabilities) = tauri::async_runtime::spawn_blocking(move || { + let profile = target_profiles::selected_profile(&preparation_app) + .map_err(|error| error.to_string())?; + let mut paths = desktop_paths(&preparation_app)?; + let active = active_runtime(&paths)?; + target_profiles::authorize_managed_runtime_action(&profile, &active.profile) + .map_err(|error| error.to_string())?; + paths.models = active.model_directory.clone(); + let runtime = runtime_directory(&paths, &active); + Ok::<_, String>((runtime, paths, active.capabilities)) + }) + .await + .map_err(|error| format!("Model preparation setup stopped unexpectedly: {error}"))??; + + let manifest = manifest()?; + let arguments = capability_command_arguments(&manifest, "prepare", &capabilities); + let mut worker = state.worker_stop.register(runtime.clone(), paths.clone())?; + let preparation = run_vidxp_supervised( + &runtime, + &paths, + &arguments, + cancellation.token(), + "VidXP model preparation", + ) + .await; + worker.stop_before(Instant::now() + Duration::from_secs(5)); + preparation?; + target_profiles::current_state(&app).map_err(|error| error.to_string()) } #[tauri::command] @@ -1044,34 +2443,38 @@ async fn install_runtime( app: AppHandle, state: tauri::State<'_, DesktopState>, request: InstallRequest, -) -> Result { - if state - .operation_active - .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) - .is_err() - { - return Err("Another install or model-preparation operation is active.".into()); - } - let _operation_guard = OperationGuard { - active: &state.operation_active, - }; +) -> Result { + let cancellation = OperationCancellationGuard::register(&state)?; + let mut transition = TargetTransitionCoordinator::begin_apply( + &state, + &request.draft_id, + TransitionKind::InstallRuntime, + ) + .map_err(|error| error.to_string())?; let manifest = manifest()?; let capabilities = selected_capabilities(&manifest, &request.capabilities)?; let surfaces = selected_surfaces(&manifest, &request.surfaces)?; - let media_runtime = verified_media_runtime()?; - let mut paths = desktop_paths(&app)?; - paths.models = model_directory(&paths, request.model_directory.as_deref())?; - for directory in [ - &paths.data, - &paths.cache, - &paths.repository, - &paths.runtimes, - &paths.python, - &paths.models, - ] { - fs::create_dir_all(directory) - .map_err(|error| format!("Could not create {}: {error}", directory.display()))?; - } + let requested_model_directory = request.model_directory.clone(); + let preparation_app = app.clone(); + let (media_runtime, paths) = tauri::async_runtime::spawn_blocking(move || { + let media_runtime = verified_media_runtime()?; + let mut paths = desktop_paths(&preparation_app)?; + paths.models = model_directory(&paths, requested_model_directory.as_deref())?; + for directory in [ + &paths.data, + &paths.cache, + &paths.repository, + &paths.runtimes, + &paths.python, + &paths.models, + ] { + fs::create_dir_all(directory) + .map_err(|error| format!("Could not create {}: {error}", directory.display()))?; + } + Ok::<_, String>((media_runtime, paths)) + }) + .await + .map_err(|error| format!("Managed runtime preparation stopped unexpectedly: {error}"))??; let profile_seed = format!( "{}:{}:{}:{}:{}", @@ -1093,7 +2496,6 @@ async fn install_runtime( let install_result = async { uv_output( &app, - &state, &paths, vec![ "venv".into(), @@ -1103,25 +2505,30 @@ async fn install_runtime( "--managed-python".into(), "--no-config".into(), ], + cancellation.token(), "Managed Python setup", ) .await?; - fs::write(&constraints, normalized_runtime_constraints().as_ref()) - .map_err(|error| format!("Could not write runtime constraints: {error}"))?; + let constraints_path = constraints.clone(); + tauri::async_runtime::spawn_blocking(move || { + fs::write(&constraints_path, normalized_runtime_constraints().as_ref()) + .map_err(|error| format!("Could not write runtime constraints: {error}")) + }) + .await + .map_err(|error| format!("Runtime constraint staging stopped unexpectedly: {error}"))??; uv_output( &app, - &state, &paths, package_acquisition_arguments(&manifest, &executable(&staging, "python")), + cancellation.token(), "VidXP package acquisition", ) .await?; uv_output( &app, - &state, &paths, dependency_installation_arguments( &manifest, @@ -1131,13 +2538,12 @@ async fn install_runtime( &constraints, !cfg!(target_os = "macos"), ), + cancellation.token(), "VidXP package installation", ) .await?; run_vidxp_supervised( - &app, - &state, &staging, &paths, &[ @@ -1148,17 +2554,17 @@ async fn install_runtime( "--ffprobe".into(), media_runtime.ffprobe.to_string_lossy().into_owned(), ], + cancellation.token(), "FFmpeg configuration", ) .await?; let doctor_arguments = capability_command_arguments(&manifest, "doctor", &capabilities); run_vidxp_supervised( - &app, - &state, &staging, &paths, &doctor_arguments, + cancellation.token(), "VidXP dependency validation", ) .await?; @@ -1166,29 +2572,16 @@ async fn install_runtime( if request.prepare_models { let prepare_arguments = capability_command_arguments(&manifest, "prepare", &capabilities); - *state - .operation_worker_runtime - .lock() - .map_err(|_| "The preparation worker supervisor is unavailable.")? = - Some(staging.clone()); + let mut worker = state.worker_stop.register(staging.clone(), paths.clone())?; let preparation = run_vidxp_supervised( - &app, - &state, &staging, &paths, &prepare_arguments, + cancellation.token(), "VidXP model preparation", ) .await; - let _ = run_vidxp( - &staging, - &paths, - &["jobs".into(), "stop-worker".into()], - "VidXP preparation worker shutdown", - ); - if let Ok(mut worker_runtime) = state.operation_worker_runtime.lock() { - worker_runtime.take(); - } + worker.stop_before(Instant::now() + Duration::from_secs(5)); preparation?; } @@ -1196,11 +2589,16 @@ async fn install_runtime( } .await; if let Err(error) = install_result { - let cleanup_error = if staging.exists() { - fs::remove_dir_all(&staging).err() - } else { - None - }; + let failed_staging = staging.clone(); + let cleanup_error = tauri::async_runtime::spawn_blocking(move || { + if failed_staging.exists() { + fs::remove_dir_all(&failed_staging).err() + } else { + None + } + }) + .await + .map_err(|join| format!("{error}. Staged-runtime cleanup stopped unexpectedly: {join}"))?; return Err(match cleanup_error { Some(cleanup_error) => format!( "{error}. The previous active runtime was not changed. VidXP could not remove the failed staged runtime at {}: {cleanup_error}", @@ -1214,8 +2612,6 @@ async fn install_runtime( let profile = format!("{profile_hash}-{timestamp}"); let runtime = paths.runtimes.join(&profile); - fs::rename(&staging, &runtime) - .map_err(|error| format!("Could not finalize the validated runtime: {error}"))?; let active = ActiveRuntime { schema_version: 2, manifest_sha256: manifest_digest(), @@ -1225,40 +2621,197 @@ async fn install_runtime( surfaces: surfaces.clone(), model_directory: paths.models.clone(), }; - write_active_runtime(&paths, &active)?; - - Ok(InstallResult { - package_version: manifest.package_version, - capabilities, - surfaces, - model_directory: paths.models.to_string_lossy().into_owned(), - prepared: request.prepare_models, + let activation_app = app.clone(); + let activation_paths = paths; + let activation_manifest_version = manifest.desktop_version.clone(); + let activation = tauri::async_runtime::spawn_blocking(move || { + let previous_active_bytes = read_active_runtime_snapshot(&activation_paths)?; + let previous_targets = + target_profiles::current_state(&activation_app).map_err(|error| error.to_string())?; + if let Err(error) = fs::rename(&staging, &runtime) { + let cleanup = fs::remove_dir_all(&staging); + return Err(match cleanup { + Ok(()) => format!("Could not finalize the validated runtime: {error}"), + Err(cleanup) => format!( + "Could not finalize the validated runtime: {error}. The staging directory at {} could not be removed: {cleanup}", + staging.display() + ), + }); + } + + let projection = managed_runtime_projection_for(&activation_paths, &active); + let validated = validate_managed_projection( + &activation_paths, + &projection, + &activation_manifest_version, + Some(&cancellation.token()), + ); + let candidate_targets = match validated.and_then(|validated| { + target_profiles::prepare_managed_activation( + &activation_app, + projection, + validated, + ) + }) { + Ok(candidate) => candidate, + Err(error) => { + let cleanup = fs::remove_dir_all(&runtime); + return Err(match cleanup { + Ok(()) => format!( + "The installed runtime failed the Desktop compatibility contract and was not activated: {error}" + ), + Err(cleanup) => format!( + "The installed runtime failed the Desktop compatibility contract and was not activated: {error}. Cleanup also failed for {}: {cleanup}", + runtime.display() + ), + }); + } + }; + let mut journal = ActivationJournal { + schema_version: 2, + stage: ActivationStage::Prepared, + previous_active_bytes, + previous_targets, + candidate_active: active.clone(), + candidate_targets: candidate_targets.clone(), + }; + if let Err(error) = write_activation_journal(&activation_paths, &journal) { + let cleanup = fs::remove_dir_all(&runtime); + return Err(match cleanup { + Ok(()) => error, + Err(cleanup) => format!( + "{error}. The finalized but untracked runtime could not be removed from {}: {cleanup}", + runtime.display() + ), + }); + } + + if let Err(error) = target_profiles::replace_state(&activation_app, candidate_targets.clone()) + .map_err(|error| error.to_string()) + { + return Err(rollback_activation( + &activation_app, + &activation_paths, + &mut journal, + &runtime, + &error, + )); + } + if let Err(error) = mark_journal_stage( + &activation_paths, + &mut journal, + ActivationStage::ProfileWritten, + ) { + return Err(rollback_activation( + &activation_app, + &activation_paths, + &mut journal, + &runtime, + &error, + )); + } + if let Err(error) = write_active_runtime(&activation_paths, &active) { + return Err(rollback_activation( + &activation_app, + &activation_paths, + &mut journal, + &runtime, + &error, + )); + } + + // Both authoritative files are durable at this point. Journal marking and + // removal are retryable cleanup and must never turn a committed activation + // into a reported rollback. + if let Err(error) = mark_journal_stage( + &activation_paths, + &mut journal, + ActivationStage::Committed, + ) { + log::warn!( + "Managed activation committed, but its journal could not be marked committed: {error}" + ); + } + finish_journal_cleanup(&activation_paths, "Committed a managed activation"); + log_runtime_reconciliation(&activation_paths); + Ok::<_, String>(candidate_targets) + }) + .await + .map_err(|error| format!("Managed activation stopped unexpectedly: {error}"))??; + stop_ui_process(&state); + transition.commit_draft(); + + Ok(InstallTransitionResult { + install: InstallResult { + package_version: manifest.package_version, + capabilities, + surfaces, + model_directory: activation + .selected_profile() + .and_then(|profile| profile.model_directory.as_ref()) + .map_or_else(String::new, |path| path.to_string_lossy().into_owned()), + prepared: request.prepare_models, + }, + setup: activation, }) } fn start_ui(app: &AppHandle, state: &DesktopState) -> Result { - let mut paths = desktop_paths(&app)?; - let active = active_runtime(&paths)?; - if !active.surfaces.iter().any(|surface| surface == "browser") { + let manifest = manifest()?; + let selected = target_profiles::selected_profile(app).map_err(|error| error.to_string())?; + let mut paths = desktop_paths(app)?; + let profile = if selected.kind == target_profiles::TargetKind::Managed { + let active = active_runtime(&paths)?; + if selected.managed_runtime_profile.as_deref() != Some(active.profile.as_str()) { + return Err( + "The selected managed target no longer matches the active Desktop runtime.".into(), + ); + } + paths.models = active.model_directory.clone(); + let projection = managed_runtime_projection_for(&paths, &active); + let validation = validate_managed_projection( + &paths, + &projection, + &manifest.desktop_version, + Some(&state.shutdown), + ); + target_profiles::persist_selected_validation(app, validation) + .map_err(|error| error.to_string())? + } else { + target_profiles::validated_selected_profile_with_cancellation( + app, + &manifest.desktop_version, + Some(&state.shutdown), + ) + .map_err(|error| error.to_string())? + }; + target_profiles::authorize_lifecycle(&profile, target_profiles::LifecycleAction::Launch) + .map_err(|error| error.to_string())?; + if !profile.frontend.launchable { return Err( - "The browser interface is not installed. Reconfigure VidXP and select Browser interface." - .into(), + "The selected VidXP installation cannot launch the supported browser interface.".into(), ); } - paths.models = active.model_directory.clone(); - let runtime = runtime_directory(&paths, &active); + paths.repository = profile.repository_root.clone(); + if let Some(model_directory) = &profile.model_directory { + paths.models = model_directory.clone(); + } let mut active_process = state .ui_process .lock() .map_err(|_| "The desktop process supervisor is unavailable.".to_string())?; if let Some(ui) = active_process.as_mut() { - if ui + let running = ui .process .try_wait() .map_err(|error| format!("Could not inspect the interface process: {error}"))? - .is_none() - { - return Ok(ui.url.clone()); + .is_none(); + match ui_process_action(running, &ui.profile_id, &profile.id) { + UiProcessAction::Reuse => return Ok(ui.url.clone()), + UiProcessAction::Replace => { + ui.process.terminate_and_reap(); + } + UiProcessAction::Start => {} } *active_process = None; } @@ -1270,43 +2823,96 @@ fn start_ui(app: &AppHandle, state: &DesktopState) -> Result { .map_err(|error| format!("Could not identify the local interface port: {error}"))? .port(); drop(listener); + let nonce = browser_readiness_nonce(); + let readiness_file = paths + .private_data + .join(format!("browser-readiness-{nonce}.json")); + if let Err(error) = fs::remove_file(&readiness_file) + && error.kind() != io::ErrorKind::NotFound + { + return Err(format!( + "Could not clear the stale browser readiness marker: {error}" + )); + } + + let mut command = match profile.kind { + target_profiles::TargetKind::Managed => { + let active = active_runtime(&paths)?; + if profile.managed_runtime_profile.as_deref() != Some(active.profile.as_str()) { + return Err( + "The selected managed target no longer matches the active desktop runtime." + .into(), + ); + } + configured_command(&profile.executable, &paths) + } + target_profiles::TargetKind::ExistingLocal => Command::new(&profile.executable), + }; + configure_ui_service_command( + &mut command, + &profile.repository_root, + port, + &readiness_file, + &nonce, + ); + let mut process = background_process::spawn_service(command) + .map_err(|error| format!("Could not start the VidXP interface: {}", error.detail))?; + browser_readiness::wait_for_browser_readiness( + &mut process, + &readiness_file, + &nonce, + port, + Instant::now() + Duration::from_secs(30), + &state.shutdown, + )?; + let url = format!("http://127.0.0.1:{port}"); + *active_process = Some(ManagedUi { + process, + url: url.clone(), + profile_id: profile.id.clone(), + }); + Ok(url) +} + +fn stop_ui_process(state: &DesktopState) { + let Ok(mut active) = state.ui_process.lock() else { + return; + }; + if let Some(mut ui) = active.take() { + ui.process.terminate_and_reap(); + } +} - let mut command = configured_command(&executable(&runtime, "vidxp"), &paths); +fn browser_readiness_nonce() -> String { + let sequence = READINESS_SEQUENCE.fetch_add(1, Ordering::Relaxed); + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_nanos()) + .unwrap_or_default(); + hex::encode(Sha256::digest(format!( + "{}:{timestamp}:{sequence}", + std::process::id() + ))) +} + +fn configure_ui_service_command( + command: &mut Command, + repository_root: &Path, + port: u16, + readiness_file: &Path, + nonce: &str, +) { command + // The desktop owns the one intentional browser open after readiness. Without + // headless mode Streamlit also opens the URL, producing duplicate tabs and + // potentially visible launcher consoles on Windows. + .env("STREAMLIT_SERVER_HEADLESS", "true") + .env("VIDXP_DESKTOP_READINESS_FILE", readiness_file) + .env("VIDXP_DESKTOP_READINESS_NONCE", nonce) + .env("VIDXP_DESKTOP_UI_PORT", port.to_string()) .arg("--index-dir") - .arg(&paths.repository) - .args(["ui", "--host", "127.0.0.1", "--port", &port.to_string()]) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()); - let mut process = command - .spawn() - .map_err(|error| format!("Could not start the VidXP interface: {error}"))?; - - let address = SocketAddr::from(([127, 0, 0, 1], port)); - let deadline = Instant::now() + Duration::from_secs(30); - while Instant::now() < deadline { - if let Some(status) = process - .try_wait() - .map_err(|error| format!("Could not inspect the interface process: {error}"))? - { - return Err(format!( - "The VidXP interface exited during startup ({status})." - )); - } - if TcpStream::connect_timeout(&address, Duration::from_millis(100)).is_ok() { - let url = format!("http://127.0.0.1:{port}"); - *active_process = Some(ManagedUi { - process, - url: url.clone(), - }); - return Ok(url); - } - thread::sleep(Duration::from_millis(100)); - } - let _ = process.kill(); - let _ = process.wait(); - Err("The VidXP interface did not become ready in 30 seconds.".into()) + .arg(repository_root) + .args(["ui", "--host", "127.0.0.1", "--port", &port.to_string()]); } fn hide_main_window(app: &AppHandle) -> Result<(), String> { @@ -1327,34 +2933,70 @@ fn show_main_window(app: &AppHandle) { } fn configured_runtime(app: &AppHandle) -> bool { - desktop_paths(app) - .and_then(|paths| active_runtime(&paths)) - .is_ok() + target_profiles::current_state(app) + .ok() + .is_some_and(|state| state.selected_profile().is_some()) } fn browser_surface_configured(app: &AppHandle) -> bool { - desktop_paths(app) - .and_then(|paths| active_runtime(&paths)) - .is_ok_and(|active| active.surfaces.iter().any(|surface| surface == "browser")) + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_secs()) + .unwrap_or(u64::MAX); + target_profiles::current_state(app) + .ok() + .and_then(|state| state.selected_profile().cloned()) + .is_some_and(|profile| profile.is_ready(now) && profile.frontend.launchable) +} + +struct BrowserOpenGuard(AppHandle); + +fn claim_browser_open(active: &AtomicBool) -> bool { + active + .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) + .is_ok() +} + +impl Drop for BrowserOpenGuard { + fn drop(&mut self) { + self.0 + .state::() + .browser_open_active + .store(false, Ordering::Release); + } } -fn open_ui_in_browser(app: &AppHandle, state: &DesktopState) -> Result<(), String> { - let url = start_ui(app, state)?; +async fn open_ui_in_browser(app: AppHandle) -> Result<(), String> { + let state = app.state::(); + if !claim_browser_open(&state.browser_open_active) { + return Ok(()); + } + let _browser_guard = BrowserOpenGuard(app.clone()); + let _active = state.active_operations.register()?; + let transition = TargetTransitionCoordinator::begin(&state, TransitionKind::OpenBrowser) + .map_err(|error| error.to_string())?; + let worker_app = app.clone(); + let url = tauri::async_runtime::spawn_blocking(move || { + let _transition = transition; + let state = worker_app.state::(); + start_ui(&worker_app, &state) + }) + .await + .map_err(|error| format!("VidXP interface startup stopped unexpectedly: {error}"))??; app.opener() .open_url(&url, None::<&str>) .map_err(|error| format!("Could not open VidXP in the default browser: {error}"))?; - hide_main_window(app) + hide_main_window(&app) } -fn open_or_show(app: &AppHandle) { +fn open_browser_or_show_manager(app: &AppHandle) { if !browser_surface_configured(app) { show_main_window(app); return; } let app = app.clone(); - thread::spawn(move || { - let state = app.state::(); - if let Err(error) = open_ui_in_browser(&app, &state) { + tauri::async_runtime::spawn(async move { + if let Err(error) = open_ui_in_browser(app.clone()).await { show_main_window(&app); app.dialog() .message(error) @@ -1365,46 +3007,68 @@ fn open_or_show(app: &AppHandle) { }); } +fn perform_desktop_action(app: &AppHandle, action: DesktopAction) { + match action { + DesktopAction::Manage => show_main_window(app), + DesktopAction::OpenBrowser => open_browser_or_show_manager(app), + DesktopAction::Quit => begin_shutdown(app), + } +} + fn begin_shutdown(app: &AppHandle) { - if app - .state::() - .shutdown_started - .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) - .is_err() - { + let state = app.state::(); + if let Err(error) = state.active_operations.close() { + log::error!("Could not close operation registration during shutdown: {error}"); + } + if state.shutdown_started.swap(true, Ordering::AcqRel) { return; } + state.shutdown.cancel(); + cancel_active_operation(&state); log::info!("VidXP supervised shutdown requested"); - shutdown(app); - log::info!("VidXP supervised shutdown completed"); - std::process::exit(0); -} - -#[tauri::command] -fn launch_ui(app: AppHandle, state: tauri::State<'_, DesktopState>) -> Result<(), String> { - open_ui_in_browser(&app, &state) + stop_ui_process(&state); + let app = app.clone(); + let operations = state.active_operations.clone(); + tauri::async_runtime::spawn(async move { + let shutdown_app = app.clone(); + let result = tauri::async_runtime::spawn_blocking(move || { + let deadline = Instant::now() + Duration::from_secs(20); + if !operations.wait_until_idle(Instant::now() + Duration::from_secs(10)) { + log::error!( + "Timed out waiting for supervised operations to acknowledge cancellation; forcing final owned-process cleanup" + ); + } + shutdown(&shutdown_app, deadline); + }) + .await; + if let Err(error) = result { + log::error!("The shutdown coordinator stopped unexpectedly: {error}"); + } + log::info!("VidXP supervised shutdown completed"); + app.exit(0); + }); } #[tauri::command] -fn hide_to_tray(app: AppHandle) -> Result<(), String> { - if !configured_runtime(&app) { - return Err("Local video processing has not been configured yet.".into()); - } - hide_main_window(&app) +async fn launch_ui(app: AppHandle) -> Result<(), String> { + open_ui_in_browser(app).await } fn create_tray(app: &tauri::App) -> tauri::Result<()> { let open = MenuItem::with_id(app, "open", "Open VidXP", true, None::<&str>)?; + let manage = MenuItem::with_id(app, "manage", "Manage VidXP", true, None::<&str>)?; let quit = MenuItem::with_id(app, "quit", "Quit VidXP", true, None::<&str>)?; - let menu = Menu::with_items(app, &[&open, &quit])?; + let menu = Menu::with_items(app, &[&open, &manage, &quit])?; let mut tray = TrayIconBuilder::with_id("vidxp") .tooltip("VidXP") .menu(&menu) .show_menu_on_left_click(true) - .on_menu_event(|app, event| match event.id().as_ref() { - "open" => open_or_show(app), - "quit" => begin_shutdown(app), - _ => {} + .on_menu_event(|app, event| { + if let Some(action) = + action_for_activation(DesktopActivation::Tray(event.id().as_ref())) + { + perform_desktop_action(app, action); + } }); if let Some(icon) = app.default_window_icon() { tray = tray.icon(icon.clone()); @@ -1413,93 +3077,118 @@ fn create_tray(app: &tauri::App) -> tauri::Result<()> { Ok(()) } -fn stop_worker(runtime: &Path, paths: &DesktopPaths) { +fn stop_worker_before(runtime: &Path, paths: &DesktopPaths, deadline: Instant) { + let remaining = deadline.saturating_duration_since(Instant::now()); + if remaining.is_zero() { + log::error!("Shutdown deadline elapsed before a managed worker could be stopped"); + return; + } let mut command = configured_command(&executable(runtime, "vidxp"), paths); command .arg("--index-dir") .arg(&paths.repository) - .args(["jobs", "stop-worker"]) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()); - let Ok(mut process) = command.spawn() else { - return; - }; - match process.wait_timeout(Duration::from_secs(5)) { - Ok(Some(_)) => {} - _ => { - let _ = process.kill(); - let _ = process.wait_timeout(Duration::from_secs(1)); - } - } + .args(["jobs", "stop-worker"]); + let _ = background_process::run( + command, + background_process::BackgroundPolicy { + timeout: remaining.min(Duration::from_secs(5)), + max_output_bytes: 64 * 1024, + }, + None, + ); } -fn shutdown(app: &AppHandle) { +fn shutdown(app: &AppHandle, deadline: Instant) { log::info!("Stopping active VidXP processes"); let state = app.state::(); - if let Ok(mut active_operation) = state.operation_process.lock() { - if let Some(process) = active_operation.take() { - let _ = process.kill(); - } - } - if let Ok(mut active_process) = state.ui_process.lock() { - if let Some(mut ui) = active_process.take() { - let _ = ui.process.kill(); - match ui.process.wait_timeout(Duration::from_secs(5)) { - Ok(Some(_)) => {} - _ => { - let _ = ui.process.kill(); - let _ = ui.process.wait_timeout(Duration::from_secs(1)); - } - } - } - } + cancel_active_operation(&state); + stop_ui_process(&state); let Ok(mut paths) = desktop_paths(app) else { log::warn!("Could not resolve desktop paths during shutdown"); return; }; - if let Ok(mut operation_worker) = state.operation_worker_runtime.lock() { - if let Some(runtime) = operation_worker.take() { - stop_worker(&runtime, &paths); - } + let operation_worker = state.worker_stop.stop_active_before(deadline); + let Ok(profile) = target_profiles::selected_profile(app) else { + log::info!("No selected VidXP target needs worker shutdown"); + return; + }; + if target_profiles::authorize_lifecycle( + &profile, + target_profiles::LifecycleAction::BroadProcessStop, + ) + .is_err() + { + log::info!("Skipping broad worker shutdown for an externally owned VidXP target"); + return; } let Ok(active) = active_runtime(&paths) else { - log::info!("No active VidXP runtime needs worker shutdown"); + log::info!("No active desktop-managed VidXP runtime needs worker shutdown"); return; }; + if profile.managed_runtime_profile.as_deref() != Some(active.profile.as_str()) { + log::warn!("Skipping worker shutdown because the selected managed target is not active"); + return; + } paths.models = active.model_directory.clone(); let runtime = runtime_directory(&paths, &active); - stop_worker(&runtime, &paths); + if operation_worker + .as_ref() + .is_none_or(|stopped| !same_path(stopped, &runtime)) + { + stop_worker_before(&runtime, &paths, deadline); + } log::info!("Active VidXP worker shutdown finished"); } pub fn run() { let builder = tauri::Builder::default() .plugin(tauri_plugin_single_instance::init(|app, _, _| { - open_or_show(app); + if let Some(action) = action_for_activation(DesktopActivation::SingleInstance) { + perform_desktop_action(app, action); + } })) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_log::Builder::new().build()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_store::Builder::default().build()) .manage(DesktopState::default()) .setup(|app| { migrate_legacy_shared_data(app.handle()).map_err(io::Error::other)?; + recover_interrupted_activation(app.handle(), &app.state::()) + .map_err(io::Error::other)?; + if let Ok(paths) = desktop_paths(app.handle()) { + log_runtime_reconciliation(&paths); + } + if let Err(error) = initialize_target_profiles(app.handle()) { + log::error!("Target profile initialization failed: {error}"); + } create_tray(app)?; - if !configured_runtime(app.handle()) { - show_main_window(app.handle()); + if let Some(action) = action_for_activation(DesktopActivation::Startup) { + perform_desktop_action(app.handle(), action); } Ok(()) }) .invoke_handler(tauri::generate_handler![ runtime_manifest, - media_runtime_status, + target_state, + refresh_target_state, + discover_local_targets, + choose_local_executable, + inspect_local_target, + adopt_local_target, + select_target_profile, + delete_target_profile, + confirm_forget_target, + begin_managed_setup, + cancel_managed_setup, choose_model_directory, install_media_runtime, runtime_status, + model_directory_inventory, + prepare_managed_models, install_runtime, - launch_ui, - hide_to_tray + launch_ui ]); let app = builder .build(tauri::generate_context!()) @@ -1510,25 +3199,19 @@ pub fn run() { event: WindowEvent::CloseRequested { api, .. }, .. } if label == "main" => { - if app_handle - .state::() - .shutdown_started - .load(Ordering::Acquire) - { + if app_handle.state::().shutdown.is_cancelled() { return; } api.prevent_close(); - if configured_runtime(app_handle) { - let _ = hide_main_window(app_handle); - } else { - begin_shutdown(app_handle); + match close_action(configured_runtime(app_handle)) { + DesktopCloseAction::HideToTray => { + let _ = hide_main_window(app_handle); + } + DesktopCloseAction::Quit => begin_shutdown(app_handle), } } RunEvent::ExitRequested { api, .. } - if !app_handle - .state::() - .shutdown_started - .load(Ordering::Acquire) => + if !app_handle.state::().shutdown.is_cancelled() => { api.prevent_exit(); begin_shutdown(app_handle); @@ -1540,13 +3223,30 @@ pub fn run() { #[cfg(test)] mod tests { use super::{ - base_package_specification, capability_command_arguments, - dependency_installation_arguments, desktop_paths_from_roots, display_command, manifest, + ActivationJournal, ActivationRecovery, ActivationStage, ActiveRuntime, DesktopAction, + DesktopActivation, DesktopCloseAction, DesktopState, DraftPhase, DraftRecord, + ManagedSetupDraft, TargetTransitionCoordinator, TransitionKind, UiProcessAction, + WorkerStopSupervisor, action_for_activation, activation_recovery, + base_package_specification, capability_command_arguments, claim_browser_open, + clean_environment_from, close_action, configure_ui_service_command, + configured_runtime_status, dependency_installation_arguments, desktop_paths_from_roots, + display_command, inventory_model_directory, manifest, manifest_digest, normalize_line_endings, normalized_runtime_constraints, package_acquisition_arguments, - package_index, package_specification, required_encoder_missing, selected_capabilities, - selected_surfaces, + package_index, package_specification, read_active_runtime_snapshot, + reconcile_managed_runtime_storage, required_encoder_missing, restore_active_runtime, + selected_capabilities, selected_surfaces, ui_process_action, write_activation_journal, + write_active_runtime, + }; + use std::{ + ffi::OsStr, + fs, + path::{Path, PathBuf}, + process::Command, + sync::{ + Arc, + atomic::{AtomicUsize, Ordering}, + }, }; - use std::path::{Path, PathBuf}; #[test] fn desktop_runtime_is_private_while_product_data_is_shared() { @@ -1571,6 +3271,463 @@ mod tests { paths.active_runtime, PathBuf::from("private").join("active-runtime.json") ); + assert_eq!( + paths.activation_journal, + PathBuf::from("private").join("activation-journal.json") + ); + } + + #[test] + fn managed_commands_discard_hostile_inherited_environment_and_restore_owned_roots() { + let paths = + desktop_paths_from_roots(Path::new("private"), Path::new("cache"), Path::new("local")); + let environment = clean_environment_from( + &paths, + [ + ("PATH".into(), "safe-path".into()), + ("VIDXP_DATA_DIR".into(), "attacker-data".into()), + ("VIDXP_MODEL_CACHE".into(), "attacker-models".into()), + ("PYTHONPATH".into(), "attacker-python".into()), + ("VIRTUAL_ENV".into(), "attacker-venv".into()), + ("PIP_INDEX_URL".into(), "attacker-index".into()), + ("CONDA_PREFIX".into(), "attacker-conda".into()), + ("UV_INDEX".into(), "attacker-uv".into()), + ], + ) + .into_iter() + .collect::>(); + + assert_eq!( + environment.get("PATH").map(String::as_str), + Some("safe-path") + ); + assert_eq!( + environment.get("VIDXP_DATA_DIR").map(String::as_str), + Some(paths.data.to_string_lossy().as_ref()) + ); + assert_eq!( + environment.get("VIDXP_MODEL_CACHE").map(String::as_str), + Some(paths.models.to_string_lossy().as_ref()) + ); + for rejected in [ + "PYTHONPATH", + "VIRTUAL_ENV", + "PIP_INDEX_URL", + "CONDA_PREFIX", + "UV_INDEX", + ] { + assert!(!environment.contains_key(rejected)); + } + } + + #[test] + fn interrupted_activation_never_recommits_after_rollback_begins() { + assert_eq!( + activation_recovery(&ActivationStage::Prepared, false), + ActivationRecovery::RollBack + ); + assert_eq!( + activation_recovery(&ActivationStage::ProfileWritten, false), + ActivationRecovery::RollBack + ); + assert_eq!( + activation_recovery(&ActivationStage::ProfileWritten, true), + ActivationRecovery::Complete + ); + assert_eq!( + activation_recovery(&ActivationStage::Committed, false), + ActivationRecovery::Complete + ); + for stage in [ActivationStage::RollingBack, ActivationStage::RolledBack] { + assert_eq!( + activation_recovery(&stage, true), + ActivationRecovery::RollBack + ); + } + } + + #[test] + fn concurrent_target_transitions_return_a_stable_conflict() { + let state = DesktopState::default(); + let first = TargetTransitionCoordinator::begin(&state, TransitionKind::Adopt) + .expect("first transition"); + let conflict = TargetTransitionCoordinator::begin(&state, TransitionKind::Select) + .err() + .expect("conflict"); + + assert_eq!( + conflict.code, + crate::target_profiles::TargetErrorCode::OperationConflict + ); + drop(first); + assert!(TargetTransitionCoordinator::begin(&state, TransitionKind::Select).is_ok()); + } + + #[test] + fn shutdown_tracking_waits_for_probe_install_model_and_browser_operations() { + let state = DesktopState::default(); + let probe = state.active_operations.register().expect("probe"); + let install = state.active_operations.register().expect("package install"); + let models = state + .active_operations + .register() + .expect("model preparation"); + let browser = state.active_operations.register().expect("browser startup"); + assert!( + !state + .active_operations + .wait_until_idle(std::time::Instant::now() + std::time::Duration::from_millis(20)) + ); + drop((probe, install, models, browser)); + assert!( + state + .active_operations + .wait_until_idle(std::time::Instant::now() + std::time::Duration::from_secs(1)) + ); + } + + #[test] + fn concurrent_cancellation_registration_preserves_the_shutdown_owner() { + let state = DesktopState::default(); + let operation_a = super::OperationCancellationGuard::register(&state).expect("operation A"); + let token_a = operation_a.token(); + assert_eq!( + super::OperationCancellationGuard::register(&state) + .err() + .as_deref(), + Some("Another cancellable Desktop operation is already active.") + ); + assert!( + state + .operation_cancellation + .lock() + .expect("cancellation slot") + .as_ref() + .is_some_and(|token| token.same(&token_a)) + ); + state.shutdown.cancel(); + super::cancel_active_operation(&state); + assert!(state.shutdown.is_cancelled()); + assert!(token_a.is_cancelled()); + drop(operation_a); + assert!( + state + .operation_cancellation + .lock() + .expect("cancellation slot") + .is_none() + ); + assert!( + state + .active_operations + .wait_until_idle(std::time::Instant::now() + std::time::Duration::from_secs(1)) + ); + } + + fn worker_supervisor_fixture() -> ( + Arc, + Arc, + super::DesktopPaths, + ) { + let stops = Arc::new(AtomicUsize::new(0)); + let observed = stops.clone(); + let supervisor = Arc::new(WorkerStopSupervisor::with_stopper(Arc::new( + move |_runtime, _paths, _deadline| { + observed.fetch_add(1, Ordering::SeqCst); + }, + ))); + let paths = + desktop_paths_from_roots(Path::new("private"), Path::new("cache"), Path::new("local")); + (supervisor, stops, paths) + } + + #[test] + fn worker_owner_stops_once_on_normal_completion() { + let (supervisor, stops, paths) = worker_supervisor_fixture(); + let mut worker = supervisor + .register(PathBuf::from("runtime"), paths) + .expect("worker registration"); + worker.stop_before(std::time::Instant::now() + std::time::Duration::from_secs(1)); + drop(worker); + assert_eq!(stops.load(Ordering::SeqCst), 1); + } + + #[test] + fn worker_owner_stops_once_when_cancelled_or_failed() { + for _outcome in ["cancelled", "failed"] { + let (supervisor, stops, paths) = worker_supervisor_fixture(); + let worker = supervisor + .register(PathBuf::from("runtime"), paths) + .expect("worker registration"); + drop(worker); + assert_eq!(stops.load(Ordering::SeqCst), 1); + } + } + + #[test] + fn shutdown_claims_an_active_worker_and_prevents_duplicate_stop() { + let (supervisor, stops, paths) = worker_supervisor_fixture(); + let worker = supervisor + .register(PathBuf::from("runtime"), paths) + .expect("worker registration"); + assert_eq!( + supervisor + .stop_active_before(std::time::Instant::now() + std::time::Duration::from_secs(1)), + Some(PathBuf::from("runtime")) + ); + drop(worker); + assert_eq!(stops.load(Ordering::SeqCst), 1); + assert_eq!( + supervisor + .stop_active_before(std::time::Instant::now() + std::time::Duration::from_secs(1)), + Some(PathBuf::from("runtime")) + ); + assert_eq!(stops.load(Ordering::SeqCst), 1); + } + + #[test] + fn managed_draft_cancellation_is_scoped_and_rejected_while_applying() { + let state = DesktopState::default(); + state.transition.lock().expect("transition").draft = Some(DraftRecord { + draft: ManagedSetupDraft { + id: "draft-current".into(), + previous_profile_id: Some("local-1".into()), + }, + phase: DraftPhase::Draft, + }); + + let stale = TargetTransitionCoordinator::cancel_draft(&state, "draft-stale") + .expect_err("stale draft"); + assert_eq!( + stale.code, + crate::target_profiles::TargetErrorCode::DraftMismatch + ); + + let applying = TargetTransitionCoordinator::begin_apply( + &state, + "draft-current", + TransitionKind::InstallRuntime, + ) + .expect("apply"); + let conflict = TargetTransitionCoordinator::cancel_draft(&state, "draft-current") + .expect_err("applying draft"); + assert_eq!( + conflict.code, + crate::target_profiles::TargetErrorCode::DraftApplying + ); + drop(applying); + TargetTransitionCoordinator::cancel_draft(&state, "draft-current") + .expect("cancel settled draft"); + assert_eq!( + state + .transition + .lock() + .expect("transition") + .draft + .as_ref() + .expect("draft") + .phase, + DraftPhase::Cancelled + ); + let finished = TargetTransitionCoordinator::cancel_draft(&state, "draft-current") + .expect_err("finished draft"); + assert_eq!( + finished.code, + crate::target_profiles::TargetErrorCode::DraftMismatch + ); + } + + #[test] + fn previous_runtime_snapshot_accepts_current_old_malformed_and_missing_pointers() { + let root = std::env::temp_dir().join(format!( + "vidxp-active-snapshot-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("clock") + .as_nanos() + )); + let _ = fs::remove_dir_all(&root); + fs::create_dir_all(root.join("private")).expect("private directory"); + let paths = desktop_paths_from_roots( + &root.join("private"), + &root.join("cache"), + &root.join("local"), + ); + assert_eq!(read_active_runtime_snapshot(&paths).expect("missing"), None); + + for bytes in [ + br#"{"schema_version":2,"manifest_sha256":"current"}"#.as_slice(), + br#"{"schema_version":2,"manifest_sha256":"old"}"#.as_slice(), + br#"not-json"#.as_slice(), + ] { + fs::write(&paths.active_runtime, bytes).expect("pointer"); + assert_eq!( + read_active_runtime_snapshot(&paths).expect("snapshot"), + Some(bytes.to_vec()) + ); + restore_active_runtime(&paths, Some(bytes)).expect("restore"); + assert_eq!(fs::read(&paths.active_runtime).expect("restored"), bytes); + } + restore_active_runtime(&paths, None).expect("remove pointer"); + assert!(!paths.active_runtime.exists()); + let _ = fs::remove_dir_all(root); + } + + #[test] + fn runtime_reconciliation_preserves_authorities_and_bounds_repeated_updates() { + let root = std::env::temp_dir().join(format!( + "vidxp-runtime-reconcile-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("clock") + .as_nanos() + )); + let paths = desktop_paths_from_roots( + &root.join("private"), + &root.join("cache"), + &root.join("local"), + ); + fs::create_dir_all(&paths.runtimes).expect("runtimes"); + let profile = |digest: char, generation: u8| { + format!("{}-{generation}", digest.to_string().repeat(64)) + }; + let active_profile = profile('a', 3); + let candidate_profile = profile('b', 4); + let previous_profile = profile('c', 2); + let obsolete_profile = profile('d', 1); + let staging_profile = format!(".staging-{}-5-42", "e".repeat(64)); + for name in [ + &active_profile, + &candidate_profile, + &previous_profile, + &obsolete_profile, + &staging_profile, + "external-runtime", + ] { + fs::create_dir_all(paths.runtimes.join(name)).expect("runtime directory"); + fs::write(paths.runtimes.join(name).join("payload"), b"runtime").expect("payload"); + } + let runtime = |profile: String| ActiveRuntime { + schema_version: 2, + manifest_sha256: manifest_digest(), + profile, + package_version: "0.4.0-b".into(), + capabilities: vec!["scene".into()], + surfaces: vec!["browser".into()], + model_directory: paths.models.clone(), + }; + let active = runtime(active_profile.clone()); + write_active_runtime(&paths, &active).expect("active pointer"); + let journal = ActivationJournal { + schema_version: 2, + stage: ActivationStage::Prepared, + previous_active_bytes: Some( + serde_json::to_vec(&runtime(previous_profile.clone())).expect("previous"), + ), + previous_targets: crate::target_profiles::TargetState::default(), + candidate_active: runtime(candidate_profile.clone()), + candidate_targets: crate::target_profiles::TargetState::default(), + }; + write_activation_journal(&paths, &journal).expect("journal"); + + let report = reconcile_managed_runtime_storage(&paths); + assert_eq!(report.removed_directories, 2); + assert!(report.reclaimed_bytes > 0); + for retained in [ + &active_profile, + &candidate_profile, + &previous_profile, + "external-runtime", + ] { + assert!( + paths.runtimes.join(retained).exists(), + "retained {retained}" + ); + } + assert!(!paths.runtimes.join(obsolete_profile).exists()); + assert!(!paths.runtimes.join(staging_profile).exists()); + + fs::remove_file(&paths.activation_journal).expect("clear journal"); + let report = reconcile_managed_runtime_storage(&paths); + assert_eq!(report.removed_directories, 2); + assert!(paths.runtimes.join(active_profile).exists()); + assert!(paths.runtimes.join("external-runtime").exists()); + let _ = fs::remove_dir_all(root); + } + + #[test] + fn corrupt_active_pointer_preserves_unidentified_finalized_runtimes() { + let root = std::env::temp_dir().join(format!( + "vidxp-corrupt-pointer-reconcile-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("clock") + .as_nanos() + )); + let paths = desktop_paths_from_roots( + &root.join("private"), + &root.join("cache"), + &root.join("local"), + ); + fs::create_dir_all(&paths.runtimes).expect("runtimes"); + let finalized = format!("{}-1", "a".repeat(64)); + fs::create_dir_all(paths.runtimes.join(&finalized)).expect("finalized runtime"); + fs::write(&paths.active_runtime, b"not-json").expect("corrupt pointer"); + + let report = reconcile_managed_runtime_storage(&paths); + assert_eq!(report.removed_directories, 0); + assert!(paths.runtimes.join(finalized).exists()); + let _ = fs::remove_dir_all(root); + } + + fn status_fixture(model_directory: &str) -> ActiveRuntime { + ActiveRuntime { + schema_version: 2, + manifest_sha256: manifest_digest(), + profile: "a".repeat(64) + "-1", + package_version: "0.4.0-b".into(), + capabilities: vec!["scene".into()], + surfaces: vec!["browser".into()], + model_directory: PathBuf::from(model_directory), + } + } + + #[test] + fn missing_ffmpeg_preserves_the_managed_runtime_configuration() { + let status = configured_runtime_status( + status_fixture("custom-models"), + vec!["FFmpeg was not found.".into()], + ); + assert_eq!(status.state, super::RuntimeState::Broken); + assert_eq!(status.capabilities, ["scene"]); + assert_eq!(status.surfaces, ["browser"]); + assert_eq!(status.model_directory, "custom-models"); + assert!(status.runtime_profile.is_some()); + } + + #[test] + fn missing_encoder_and_damaged_runtime_preserve_custom_model_storage() { + for problem in [ + "FFmpeg does not provide required encoder libx264.", + "The active runtime executable is damaged.", + ] { + let status = + configured_runtime_status(status_fixture("D:\\VidXP models"), vec![problem.into()]); + assert_eq!(status.state, super::RuntimeState::Broken); + assert_eq!(status.capabilities, ["scene"]); + assert_eq!(status.surfaces, ["browser"]); + assert_eq!(status.model_directory, "D:\\VidXP models"); + } + } + + #[test] + fn repeated_browser_open_requests_are_coalesced() { + let active = std::sync::atomic::AtomicBool::new(false); + assert!(claim_browser_open(&active)); + assert!(!claim_browser_open(&active)); } #[test] @@ -1694,6 +3851,99 @@ mod tests { assert!(required_encoder_missing(encoders, "libx265")); } + #[test] + fn desktop_ui_service_is_headless_so_only_the_desktop_opens_the_browser() { + let mut command = Command::new("vidxp"); + + configure_ui_service_command( + &mut command, + Path::new("repository"), + 43123, + Path::new("readiness.json"), + "nonce", + ); + + assert!(command.get_envs().any(|(key, value)| { + key == OsStr::new("STREAMLIT_SERVER_HEADLESS") && value == Some(OsStr::new("true")) + })); + assert!(command.get_envs().any(|(key, value)| { + key == OsStr::new("VIDXP_DESKTOP_READINESS_NONCE") && value == Some(OsStr::new("nonce")) + })); + assert_eq!( + command + .get_args() + .map(|argument| argument.to_string_lossy().into_owned()) + .collect::>(), + [ + "--index-dir", + "repository", + "ui", + "--host", + "127.0.0.1", + "--port", + "43123", + ] + ); + } + + #[test] + fn startup_and_single_instance_activation_manage_without_opening_the_browser() { + assert_eq!( + action_for_activation(DesktopActivation::Startup), + Some(DesktopAction::Manage) + ); + assert_eq!( + action_for_activation(DesktopActivation::SingleInstance), + Some(DesktopAction::Manage) + ); + } + + #[test] + fn tray_manage_browser_and_quit_actions_are_unambiguous() { + assert_eq!( + action_for_activation(DesktopActivation::Tray("manage")), + Some(DesktopAction::Manage) + ); + assert_eq!( + action_for_activation(DesktopActivation::Tray("open")), + Some(DesktopAction::OpenBrowser) + ); + assert_eq!( + action_for_activation(DesktopActivation::Tray("quit")), + Some(DesktopAction::Quit) + ); + assert_eq!( + action_for_activation(DesktopActivation::Tray("other")), + None + ); + } + + #[test] + fn repeated_browser_actions_reuse_one_service_and_target_changes_replace_it() { + assert_eq!( + ui_process_action(true, "selected", "selected"), + UiProcessAction::Reuse + ); + assert_eq!( + ui_process_action(true, "previous", "selected"), + UiProcessAction::Replace + ); + assert_eq!( + ui_process_action(false, "selected", "selected"), + UiProcessAction::Start + ); + } + + #[test] + fn closing_a_configured_desktop_hides_it_and_manage_can_restore_it() { + assert_eq!(close_action(true), DesktopCloseAction::HideToTray); + assert_eq!( + action_for_activation(DesktopActivation::Tray("manage")), + Some(DesktopAction::Manage) + ); + assert_eq!(close_action(false), DesktopCloseAction::Quit); + } + #[test] fn package_manager_command_is_presented_as_copyable_text() { assert_eq!( @@ -1706,4 +3956,59 @@ mod tests { "winget install --id Gyan.FFmpeg" ); } + + #[test] + fn populated_model_inventory_reports_totals_and_known_cache_conventions() { + let root = + std::env::temp_dir().join(format!("vidxp-model-inventory-{}", std::process::id())); + let siglip = root + .join("models--google--siglip2-base-patch16-224") + .join("snapshots") + .join("75de2d55ec2d0b4efc50b3e9ad70dba96a7b2fa2"); + let opencv = root.join("opencv-zoo"); + fs::create_dir_all(&siglip).expect("siglip directory"); + fs::create_dir_all(&opencv).expect("opencv directory"); + fs::write(siglip.join("model.safetensors"), [0_u8; 7]).expect("model file"); + fs::write(opencv.join("face_detection_yunet_2026may.onnx"), [0_u8; 5]) + .expect("artifact file"); + + let inventory = inventory_model_directory(&root); + + assert!(inventory.exists); + assert!(inventory.readable); + assert_eq!(inventory.file_count, 2); + assert_eq!(inventory.total_bytes, 12); + assert_eq!( + inventory + .recognized_models + .iter() + .map(|model| model.label.as_str()) + .collect::>(), + ["google/siglip2-base-patch16-224", "yunet"] + ); + assert!(inventory.verification_required); + assert!(inventory.detail.contains("verification required")); + fs::remove_dir_all(root).expect("remove test inventory"); + } + + #[test] + fn empty_and_unreadable_model_locations_are_typed_states() { + let root = std::env::temp_dir().join(format!( + "vidxp-empty-model-inventory-{}", + std::process::id() + )); + fs::create_dir_all(&root).expect("empty directory"); + let empty = inventory_model_directory(&root); + assert!(empty.empty); + assert!(empty.readable); + assert!(!empty.verification_required); + + let file = root.join("not-a-directory"); + fs::write(&file, b"x").expect("file location"); + let unreadable = inventory_model_directory(&file); + assert!(unreadable.exists); + assert!(!unreadable.readable); + assert!(unreadable.detail.contains("not a readable directory")); + fs::remove_dir_all(root).expect("remove test inventory"); + } } diff --git a/desktop/src-tauri/src/lifecycle.rs b/desktop/src-tauri/src/lifecycle.rs new file mode 100644 index 0000000..7d7ece0 --- /dev/null +++ b/desktop/src-tauri/src/lifecycle.rs @@ -0,0 +1,209 @@ +use std::{ + sync::{Arc, Condvar, Mutex}, + time::Instant, +}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum UiProcessAction { + Reuse, + Replace, + Start, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum DesktopAction { + Manage, + OpenBrowser, + Quit, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum DesktopCloseAction { + HideToTray, + Quit, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum DesktopActivation<'a> { + Startup, + SingleInstance, + Tray(&'a str), +} + +pub(crate) fn action_for_activation(activation: DesktopActivation<'_>) -> Option { + match activation { + DesktopActivation::Startup | DesktopActivation::SingleInstance => { + Some(DesktopAction::Manage) + } + DesktopActivation::Tray("manage") => Some(DesktopAction::Manage), + DesktopActivation::Tray("open") => Some(DesktopAction::OpenBrowser), + DesktopActivation::Tray("quit") => Some(DesktopAction::Quit), + DesktopActivation::Tray(_) => None, + } +} + +pub(crate) fn close_action(configured: bool) -> DesktopCloseAction { + if configured { + DesktopCloseAction::HideToTray + } else { + DesktopCloseAction::Quit + } +} + +pub(crate) fn ui_process_action( + running: bool, + active_profile_id: &str, + requested_profile_id: &str, +) -> UiProcessAction { + if !running { + UiProcessAction::Start + } else if active_profile_id == requested_profile_id { + UiProcessAction::Reuse + } else { + UiProcessAction::Replace + } +} + +struct OperationState { + count: usize, + accepting: bool, +} + +impl Default for OperationState { + fn default() -> Self { + Self { + count: 0, + accepting: true, + } + } +} + +#[derive(Default)] +pub(crate) struct ActiveOperations { + state: Mutex, + idle: Condvar, +} + +impl ActiveOperations { + pub(crate) fn register(self: &Arc) -> Result { + let mut state = self + .state + .lock() + .map_err(|_| "The background operation tracker is unavailable.".to_string())?; + if !state.accepting { + return Err("VidXP Desktop is shutting down.".into()); + } + state.count += 1; + drop(state); + Ok(ActiveOperationGuard { + operations: self.clone(), + }) + } + + pub(crate) fn close(&self) -> Result<(), String> { + let mut state = self + .state + .lock() + .map_err(|_| "The background operation tracker is unavailable.".to_string())?; + state.accepting = false; + if state.count == 0 { + self.idle.notify_all(); + } + Ok(()) + } + + pub(crate) fn wait_until_idle(&self, deadline: Instant) -> bool { + let Ok(mut state) = self.state.lock() else { + return false; + }; + while state.count > 0 { + let now = Instant::now(); + if now >= deadline { + return false; + } + let Ok((next, timeout)) = self.idle.wait_timeout(state, deadline - now) else { + return false; + }; + state = next; + if timeout.timed_out() && state.count > 0 { + return false; + } + } + true + } +} + +pub(crate) struct ActiveOperationGuard { + operations: Arc, +} + +impl Drop for ActiveOperationGuard { + fn drop(&mut self) { + if let Ok(mut state) = self.operations.state.lock() { + state.count = state.count.saturating_sub(1); + if state.count == 0 { + self.operations.idle.notify_all(); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{sync::Barrier, thread, time::Duration}; + + #[test] + fn registration_succeeds_before_close_and_is_rejected_afterward() { + let operations = Arc::new(ActiveOperations::default()); + let active = operations.register().expect("registration before close"); + operations.close().expect("close"); + assert_eq!( + operations.register().err().as_deref(), + Some("VidXP Desktop is shutting down.") + ); + drop(active); + assert!(operations.wait_until_idle(Instant::now() + Duration::from_secs(1))); + } + + #[test] + fn idle_observation_after_close_cannot_race_with_a_new_registration() { + let operations = Arc::new(ActiveOperations::default()); + operations.close().expect("close"); + assert!(operations.wait_until_idle(Instant::now() + Duration::from_secs(1))); + + let contender = operations.clone(); + let result = thread::spawn(move || contender.register().map(drop)) + .join() + .expect("registration thread"); + assert_eq!(result.unwrap_err(), "VidXP Desktop is shutting down."); + assert!(operations.wait_until_idle(Instant::now() + Duration::from_secs(1))); + } + + #[test] + fn close_waits_for_an_already_registered_operation() { + let operations = Arc::new(ActiveOperations::default()); + let active = operations.register().expect("registration"); + operations.close().expect("close"); + let started = Arc::new(Barrier::new(2)); + let waiter_operations = operations.clone(); + let waiter_started = started.clone(); + let waiter = thread::spawn(move || { + waiter_started.wait(); + waiter_operations.wait_until_idle(Instant::now() + Duration::from_secs(2)) + }); + started.wait(); + thread::sleep(Duration::from_millis(20)); + assert!(!waiter.is_finished()); + drop(active); + assert!(waiter.join().expect("waiter")); + } + + #[test] + fn repeated_close_is_idempotent() { + let operations = ActiveOperations::default(); + operations.close().expect("first close"); + operations.close().expect("second close"); + assert!(operations.wait_until_idle(Instant::now() + Duration::from_secs(1))); + } +} diff --git a/desktop/src-tauri/src/media_setup.rs b/desktop/src-tauri/src/media_setup.rs new file mode 100644 index 0000000..192ed68 --- /dev/null +++ b/desktop/src-tauri/src/media_setup.rs @@ -0,0 +1,88 @@ +use std::path::PathBuf; + +pub(crate) struct SystemInstallPlan { + pub(crate) manager: String, + pub(crate) command: Vec, + pub(crate) automatic: bool, +} + +pub(crate) fn system_install_plan( + mut resolve: impl FnMut(&str) -> Option, +) -> Option { + if cfg!(windows) { + resolve("winget")?; + return Some(SystemInstallPlan { + manager: "Windows Package Manager".into(), + command: vec![ + "winget".into(), + "install".into(), + "--id".into(), + "Gyan.FFmpeg".into(), + "--exact".into(), + "--source".into(), + "winget".into(), + "--accept-package-agreements".into(), + "--accept-source-agreements".into(), + ], + automatic: true, + }); + } + if cfg!(target_os = "macos") { + let brew = resolve("brew")?; + return Some(SystemInstallPlan { + manager: "Homebrew".into(), + command: vec![ + brew.to_string_lossy().into_owned(), + "install".into(), + "ffmpeg".into(), + ], + automatic: true, + }); + } + if resolve("apt-get").is_some() { + return Some(SystemInstallPlan { + manager: "APT".into(), + command: vec![ + "sudo".into(), + "apt-get".into(), + "install".into(), + "ffmpeg".into(), + ], + automatic: false, + }); + } + if resolve("dnf").is_some() { + return Some(SystemInstallPlan { + manager: "DNF".into(), + command: vec![ + "sudo".into(), + "dnf".into(), + "install".into(), + "ffmpeg".into(), + ], + automatic: false, + }); + } + None +} + +pub(crate) fn display_command(arguments: &[String]) -> String { + arguments + .iter() + .map(|argument| { + if argument.contains(char::is_whitespace) { + format!("\"{}\"", argument.replace('"', "\\\"")) + } else { + argument.clone() + } + }) + .collect::>() + .join(" ") +} + +pub(crate) fn required_encoder_missing(output: &str, encoder: &str) -> bool { + !output + .lines() + .flat_map(|line| line.split_whitespace()) + .any(|token| token == encoder) +} diff --git a/desktop/src-tauri/src/target_profiles.rs b/desktop/src-tauri/src/target_profiles.rs new file mode 100644 index 0000000..7eb11bf --- /dev/null +++ b/desktop/src-tauri/src/target_profiles.rs @@ -0,0 +1,2079 @@ +use std::{ + collections::{BTreeSet, HashSet}, + fs, + path::{Path, PathBuf}, + process::Command, + sync::Arc, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use crate::background_process::{ + self, BackgroundErrorKind, BackgroundOutput, BackgroundPolicy, CancellationToken, +}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use sha2::{Digest, Sha256}; +use tauri::{AppHandle, Wry}; +use tauri_plugin_store::{Store, StoreExt}; + +const STORE_FILE: &str = "target-profiles.json"; +const STORE_SCHEMA_KEY: &str = "schema_version"; +const PROFILES_KEY: &str = "profiles"; +const SELECTED_PROFILE_KEY: &str = "selected_profile_id"; +const CURRENT_STORE_SCHEMA_VERSION: u32 = 1; +pub const CURRENT_PROFILE_SCHEMA_VERSION: u32 = 1; +const SUPPORTED_PROBE_SCHEMA_VERSION: u32 = 1; +const SUPPORTED_PROBE_PROTOCOL_VERSION: u32 = 1; +const SUPPORTED_LAUNCH_PROTOCOL_VERSION: u32 = 2; +const PRODUCT_ID: &str = "dev.grayhat.vidxp"; +const PROBE_TIMEOUT: Duration = Duration::from_secs(10); +const VALIDATION_MAX_AGE: Duration = Duration::from_secs(24 * 60 * 60); +const MAX_PROBE_STREAM_BYTES: usize = 256 * 1024; + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum TargetKind { + ExistingLocal, + Managed, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum LifecycleOwnership { + External, + Desktop, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum LifecycleAction { + Validate, + Launch, + Install, + BroadProcessStop, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum TargetErrorCode { + ExecutableMissing, + ExecutableInvalid, + ProbeCouldNotStart, + ProbeFailed, + ProbeTimeout, + ProbeOutputTooLarge, + MalformedProbe, + NotVidxp, + ProbeChallengeMismatch, + LauncherIdentityMismatch, + UnsupportedProbeSchema, + UnsupportedProbeProtocol, + UnsupportedLaunchProtocol, + UnsupportedLaunchContract, + InvalidDataRoot, + StoreUnavailable, + StoreCorrupt, + UnsupportedStoreSchema, + UnsupportedProfileSchema, + ProfileMalformed, + ProfileNotFound, + SelectedProfileMissing, + ValidationRequired, + ValidationStale, + LifecycleForbidden, + ManagedRuntimeUnavailable, + OperationConflict, + DraftMismatch, + DraftApplying, + ManagedProfileOwned, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct TargetError { + pub code: TargetErrorCode, + pub message: String, +} + +impl TargetError { + fn new(code: TargetErrorCode, message: impl Into) -> Self { + Self { + code, + message: message.into(), + } + } +} + +impl std::fmt::Display for TargetError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "{}", self.message) + } +} + +impl std::error::Error for TargetError {} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct RuntimeIdentity { + pub python_executable: PathBuf, + pub python_version: String, + pub implementation: String, + pub prefix: PathBuf, + pub base_prefix: PathBuf, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct FrontendCapability { + pub available: bool, + pub launchable: bool, + pub optional: bool, + pub code: String, + pub message: String, + pub remediation: String, +} + +impl Default for FrontendCapability { + fn default() -> Self { + Self { + available: false, + launchable: false, + optional: true, + code: "frontend_unavailable".into(), + message: "The optional browser interface is not installed.".into(), + remediation: "Use this installation's own package-management workflow to install the VidXP frontend extra, then revalidate.".into(), + } + } +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct TargetProfile { + pub id: String, + pub display_name: String, + pub schema_version: u32, + pub kind: TargetKind, + pub lifecycle_ownership: LifecycleOwnership, + pub executable: PathBuf, + pub data_root: PathBuf, + pub repository_root: PathBuf, + pub observed_vidxp_version: String, + pub probe_schema_version: u32, + pub probe_protocol_version: u32, + pub launch_protocol_version: u32, + pub runtime: Option, + pub frontend: FrontendCapability, + pub last_successful_validation_at: Option, + pub validation_error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub managed_runtime_profile: Option, + #[serde(default)] + pub capabilities: Vec, + #[serde(default)] + pub surfaces: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub model_directory: Option, +} + +impl TargetProfile { + pub fn is_ready(&self, now: u64) -> bool { + self.validation_error.is_none() + && self.last_successful_validation_at.is_some_and(|validated| { + now.saturating_sub(validated) <= VALIDATION_MAX_AGE.as_secs() + }) + } +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct DiscoveredTarget { + pub executable: PathBuf, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ValidatedTarget { + pub executable: PathBuf, + pub product_version: String, + pub probe_schema_version: u32, + pub probe_protocol_version: u32, + pub launch_protocol_version: u32, + pub runtime: RuntimeIdentity, + pub data_root: PathBuf, + pub repository_root: PathBuf, + pub model_root: PathBuf, + pub frontend: FrontendCapability, + pub validated_at: u64, +} + +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct TargetState { + pub profiles: Vec, + pub selected_profile_id: Option, + pub issues: Vec, +} + +impl TargetState { + pub fn selected_profile(&self) -> Option<&TargetProfile> { + let selected = self.selected_profile_id.as_deref()?; + self.profiles.iter().find(|profile| profile.id == selected) + } +} + +#[derive(Clone, Debug)] +pub struct ManagedRuntimeProjection { + pub runtime_profile: String, + pub executable: PathBuf, + pub data_root: PathBuf, + pub repository_root: PathBuf, + pub model_directory: PathBuf, + pub package_version: String, + pub capabilities: Vec, + pub surfaces: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +struct ProbeRuntime { + python_executable: PathBuf, + python_version: String, + implementation: String, + prefix: PathBuf, + base_prefix: PathBuf, +} + +#[derive(Debug, Deserialize, Serialize)] +struct ProbeLaunchContract { + protocol_version: u32, + surface: String, + command: String, +} + +#[derive(Debug, Default, Deserialize, Serialize)] +struct ProbeCapabilities { + #[serde(default)] + frontend: FrontendCapability, +} + +#[derive(Debug, Deserialize, Serialize)] +struct ProbeDocument { + product: String, + product_version: String, + schema_version: u32, + protocol_version: u32, + launch_contract: ProbeLaunchContract, + request_id: String, + launcher: PathBuf, + runtime: ProbeRuntime, + data_root: PathBuf, + repository_root: PathBuf, + model_root: PathBuf, + #[serde(default)] + capabilities: ProbeCapabilities, +} + +#[derive(Clone, Debug, Default)] +struct DecodedState { + profiles: Vec, + selected_profile_id: Option, + issues: Vec, + changed: bool, +} + +struct ProbeOutput { + success: bool, + stdout: Vec, + stderr: Vec, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum InspectionState { + ReadyToUse, + UpdateRequired, + CannotStart, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct TargetInspection { + pub state: InspectionState, + pub adoptable: bool, + pub executable: PathBuf, + pub reported_version: Option, + pub probe_compatible: bool, + pub launch_compatible: bool, + pub validated: Option, + pub message: String, + pub remediation: String, + pub technical_details: Option, +} + +fn unix_timestamp() -> Result { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_secs()) + .map_err(|error| { + TargetError::new( + TargetErrorCode::ValidationRequired, + format!("The system clock is invalid: {error}"), + ) + }) +} + +fn canonical_executable(path: &Path) -> Result { + if !path.exists() { + return Err(TargetError::new( + TargetErrorCode::ExecutableMissing, + format!( + "The selected VidXP executable no longer exists at {}.", + path.display() + ), + )); + } + if !path.is_file() { + return Err(TargetError::new( + TargetErrorCode::ExecutableInvalid, + "The selected VidXP path is not an executable file.", + )); + } + fs::canonicalize(path).map_err(|error| { + TargetError::new( + TargetErrorCode::ExecutableInvalid, + format!("The selected VidXP executable could not be resolved: {error}"), + ) + }) +} + +fn canonical_reported_launcher(reported: &Path, selected: &Path) -> Result { + if let Ok(canonical) = canonical_executable(reported) { + return (canonical == selected).then_some(canonical).ok_or_else(|| { + TargetError::new( + TargetErrorCode::LauncherIdentityMismatch, + "The probe response belongs to a different launcher than the selected executable.", + ) + }); + } + #[cfg(windows)] + if !reported.exists() && reported.extension().is_none() { + let reported_parent = reported + .parent() + .and_then(|parent| fs::canonicalize(parent).ok()); + let selected_parent = selected + .parent() + .and_then(|parent| fs::canonicalize(parent).ok()); + if reported_parent == selected_parent && reported.file_name() == selected.file_stem() { + return Ok(selected.to_path_buf()); + } + } + Err(TargetError::new( + TargetErrorCode::LauncherIdentityMismatch, + "The probe did not report a usable VidXP launcher identity.", + )) +} + +fn challenge_for(executable: &Path) -> Result { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|error| { + TargetError::new( + TargetErrorCode::ValidationRequired, + format!("The system clock is invalid: {error}"), + ) + })? + .as_nanos(); + let seed = format!("{}:{now}:{}", executable.display(), std::process::id()); + Ok(hex::encode(Sha256::digest(seed.as_bytes()))) +} + +fn collect_command_output( + executable: &Path, + arguments: &[&str], + operation: &str, + cancellation: Option<&CancellationToken>, +) -> Result { + let mut command = Command::new(executable); + command.args(arguments); + collect_prepared_command_output(command, operation, cancellation) +} + +fn collect_prepared_command_output( + command: Command, + operation: &str, + cancellation: Option<&CancellationToken>, +) -> Result { + let BackgroundOutput { + status, + stdout, + stderr, + } = background_process::run( + command, + BackgroundPolicy { + timeout: PROBE_TIMEOUT, + max_output_bytes: MAX_PROBE_STREAM_BYTES, + }, + cancellation, + ) + .map_err(|error| { + let code = match error.kind { + BackgroundErrorKind::Start => TargetErrorCode::ProbeCouldNotStart, + BackgroundErrorKind::Timeout => TargetErrorCode::ProbeTimeout, + BackgroundErrorKind::OutputTooLarge => TargetErrorCode::ProbeOutputTooLarge, + _ => TargetErrorCode::ProbeFailed, + }; + TargetError::new(code, format!("{operation} failed: {}.", error.detail)) + })?; + Ok(ProbeOutput { + success: status.success(), + stdout, + stderr, + }) +} + +pub(crate) fn validate_executable_using( + path: &Path, + desktop_version: &str, + cancellation: Option<&CancellationToken>, + command_for: impl FnOnce(&Path) -> Command, +) -> Result { + validate_executable_with(path, desktop_version, |canonical, version, request_id| { + let mut command = command_for(canonical); + command.args([ + "desktop-probe", + "--json", + "--desktop-version", + version, + "--request-id", + request_id, + ]); + collect_prepared_command_output( + command, + "The managed VidXP compatibility probe", + cancellation, + ) + }) +} + +fn collect_probe_output( + executable: &Path, + desktop_version: &str, + request_id: &str, +) -> Result { + collect_command_output( + executable, + &[ + "desktop-probe", + "--json", + "--desktop-version", + desktop_version, + "--request-id", + request_id, + ], + "The VidXP compatibility probe", + None, + ) +} + +#[cfg(test)] +fn collect_version_output(executable: &Path) -> Result { + collect_command_output(executable, &["--version"], "The VidXP version check", None) +} + +fn validate_probe_document( + canonical: &Path, + request_id: &str, + document: ProbeDocument, + now: u64, +) -> Result { + if document.product != PRODUCT_ID { + return Err(TargetError::new( + TargetErrorCode::NotVidxp, + "The selected executable did not identify itself as VidXP.", + )); + } + if document.request_id != request_id { + return Err(TargetError::new( + TargetErrorCode::ProbeChallengeMismatch, + "The selected executable did not return the desktop validation challenge.", + )); + } + if document.schema_version != SUPPORTED_PROBE_SCHEMA_VERSION { + return Err(TargetError::new( + TargetErrorCode::UnsupportedProbeSchema, + format!( + "This executable uses desktop probe schema {}; VidXP desktop supports schema {}.", + document.schema_version, SUPPORTED_PROBE_SCHEMA_VERSION + ), + )); + } + if document.protocol_version != SUPPORTED_PROBE_PROTOCOL_VERSION { + return Err(TargetError::new( + TargetErrorCode::UnsupportedProbeProtocol, + format!( + "This executable uses desktop probe protocol {}; VidXP desktop supports protocol {}.", + document.protocol_version, SUPPORTED_PROBE_PROTOCOL_VERSION + ), + )); + } + if document.launch_contract.protocol_version != SUPPORTED_LAUNCH_PROTOCOL_VERSION { + return Err(TargetError::new( + TargetErrorCode::UnsupportedLaunchProtocol, + format!( + "This executable uses desktop launch protocol {}; VidXP desktop supports protocol {}.", + document.launch_contract.protocol_version, SUPPORTED_LAUNCH_PROTOCOL_VERSION + ), + )); + } + if document.launch_contract.surface != "browser" || document.launch_contract.command != "ui" { + return Err(TargetError::new( + TargetErrorCode::UnsupportedLaunchContract, + "This executable does not provide the supported VidXP browser launch contract.", + )); + } + canonical_reported_launcher(&document.launcher, canonical)?; + for (label, path) in [ + ("data", &document.data_root), + ("repository", &document.repository_root), + ("model", &document.model_root), + ("Python executable", &document.runtime.python_executable), + ("Python prefix", &document.runtime.prefix), + ("Python base prefix", &document.runtime.base_prefix), + ] { + if !path.is_absolute() { + return Err(TargetError::new( + TargetErrorCode::InvalidDataRoot, + format!("The probe reported a non-absolute {label} path."), + )); + } + } + Ok(ValidatedTarget { + executable: canonical.to_path_buf(), + product_version: document.product_version, + probe_schema_version: document.schema_version, + probe_protocol_version: document.protocol_version, + launch_protocol_version: document.launch_contract.protocol_version, + runtime: RuntimeIdentity { + python_executable: document.runtime.python_executable, + python_version: document.runtime.python_version, + implementation: document.runtime.implementation, + prefix: document.runtime.prefix, + base_prefix: document.runtime.base_prefix, + }, + data_root: document.data_root, + repository_root: document.repository_root, + model_root: document.model_root, + frontend: document.capabilities.frontend, + validated_at: now, + }) +} + +fn validate_executable_with( + path: &Path, + desktop_version: &str, + run_probe: impl FnOnce(&Path, &str, &str) -> Result, +) -> Result { + let canonical = canonical_executable(path)?; + let request_id = challenge_for(&canonical)?; + let output = run_probe(&canonical, desktop_version, &request_id)?; + if !output.success { + return Err(TargetError::new( + TargetErrorCode::ProbeFailed, + "The selected executable rejected the VidXP compatibility probe.", + )); + } + let document: ProbeDocument = serde_json::from_slice(&output.stdout).map_err(|_| { + TargetError::new( + TargetErrorCode::MalformedProbe, + "The selected executable did not return a valid VidXP compatibility response.", + ) + })?; + validate_probe_document(&canonical, &request_id, document, unix_timestamp()?) +} + +fn inspect_executable_with( + path: &Path, + desktop_version: &str, + run_probe: impl FnOnce(&Path, &str, &str) -> Result, + run_version: impl FnOnce(&Path) -> Result, +) -> Result { + let canonical = canonical_executable(path)?; + let request_id = challenge_for(&canonical)?; + let probe_result = run_probe(&canonical, desktop_version, &request_id).and_then(|output| { + if !output.success { + let detail = String::from_utf8_lossy(&output.stderr).trim().to_owned(); + return Err(TargetError::new( + TargetErrorCode::ProbeFailed, + if detail.is_empty() { + "The selected executable rejected the VidXP compatibility probe.".into() + } else { + format!("The compatibility probe failed: {detail}") + }, + )); + } + let document: ProbeDocument = serde_json::from_slice(&output.stdout).map_err(|_| { + TargetError::new( + TargetErrorCode::MalformedProbe, + "The selected executable did not return a valid VidXP compatibility response.", + ) + })?; + validate_probe_document(&canonical, &request_id, document, unix_timestamp()?) + }); + match probe_result { + Ok(validated) => Ok(TargetInspection { + state: InspectionState::ReadyToUse, + adoptable: true, + executable: canonical, + reported_version: Some(validated.product_version.clone()), + probe_compatible: true, + launch_compatible: true, + validated: Some(validated), + message: "This installation supports the Desktop compatibility and launch contracts." + .into(), + remediation: String::new(), + technical_details: None, + }), + Err(probe_error) => { + let version = run_version(&canonical); + match version { + Ok(output) if output.success && !output.stdout.is_empty() => { + let reported = String::from_utf8_lossy(&output.stdout).trim().to_owned(); + Ok(TargetInspection { + state: InspectionState::UpdateRequired, + adoptable: false, + executable: canonical, + reported_version: Some(reported.strip_prefix("VidXP ").unwrap_or(&reported).to_owned()), + probe_compatible: false, + launch_compatible: false, + validated: None, + message: "This VidXP installation does not provide a compatible Desktop probe and launch contract.".into(), + remediation: "Update this external installation with its own package-management workflow, then check it again.".into(), + technical_details: Some(probe_error.message), + }) + } + Ok(output) => { + let detail = String::from_utf8_lossy(&output.stderr).trim().to_owned(); + Ok(TargetInspection { + state: InspectionState::CannotStart, + adoptable: false, + executable: canonical, + reported_version: None, + probe_compatible: false, + launch_compatible: false, + validated: None, + message: "This executable could not start well enough to report its version.".into(), + remediation: "Repair this external installation with its own package-management workflow, then check it again.".into(), + technical_details: Some(if detail.is_empty() { probe_error.message } else { detail }), + }) + } + Err(version_error) => Ok(TargetInspection { + state: InspectionState::CannotStart, + adoptable: false, + executable: canonical, + reported_version: None, + probe_compatible: false, + launch_compatible: false, + validated: None, + message: "This executable could not start well enough to report its version.".into(), + remediation: "Repair this external installation with its own package-management workflow, then check it again.".into(), + technical_details: Some(format!("{} {}", probe_error.message, version_error.message)), + }), + } + } + } +} + +pub fn validate_executable( + path: &Path, + desktop_version: &str, +) -> Result { + validate_executable_with(path, desktop_version, collect_probe_output) +} + +#[cfg(test)] +fn inspect_executable(path: &Path, desktop_version: &str) -> Result { + inspect_executable_with( + path, + desktop_version, + collect_probe_output, + collect_version_output, + ) +} + +pub fn inspect_executable_with_cancellation( + path: &Path, + desktop_version: &str, + cancellation: &CancellationToken, +) -> Result { + inspect_executable_with( + path, + desktop_version, + |executable, version, request_id| { + collect_command_output( + executable, + &[ + "desktop-probe", + "--json", + "--desktop-version", + version, + "--request-id", + request_id, + ], + "The VidXP compatibility probe", + Some(cancellation), + ) + }, + |executable| { + collect_command_output( + executable, + &["--version"], + "The VidXP version check", + Some(cancellation), + ) + }, + ) +} + +pub fn discover_local_targets() -> Vec { + let mut seen = HashSet::new(); + let mut discovered = which::which_all("vidxp") + .into_iter() + .flatten() + .filter_map(|candidate| canonical_executable(&candidate).ok()) + .filter(|candidate| seen.insert(candidate.clone())) + .map(|executable| DiscoveredTarget { executable }) + .collect::>(); + discovered.sort_by(|left, right| left.executable.cmp(&right.executable)); + discovered +} + +pub fn authorize_lifecycle( + profile: &TargetProfile, + action: LifecycleAction, +) -> Result<(), TargetError> { + let structurally_valid = matches!( + (&profile.kind, &profile.lifecycle_ownership), + (TargetKind::ExistingLocal, LifecycleOwnership::External) + | (TargetKind::Managed, LifecycleOwnership::Desktop) + ); + if !structurally_valid { + return Err(TargetError::new( + TargetErrorCode::ProfileMalformed, + "The target profile has an invalid lifecycle ownership declaration.", + )); + } + if profile.lifecycle_ownership == LifecycleOwnership::External + && matches!( + action, + LifecycleAction::Install | LifecycleAction::BroadProcessStop + ) + { + return Err(TargetError::new( + TargetErrorCode::LifecycleForbidden, + "This VidXP installation is externally owned and cannot be changed or broadly stopped by the desktop.", + )); + } + Ok(()) +} + +pub fn authorize_managed_runtime_action( + profile: &TargetProfile, + runtime_profile: &str, +) -> Result<(), TargetError> { + authorize_lifecycle(profile, LifecycleAction::Install)?; + if profile.kind != TargetKind::Managed + || profile.managed_runtime_profile.as_deref() != Some(runtime_profile) + { + return Err(TargetError::new( + TargetErrorCode::ValidationRequired, + "The selected managed target no longer matches the active Desktop runtime.", + )); + } + Ok(()) +} + +fn stable_local_profile_id(executable: &Path) -> String { + let digest = hex::encode(Sha256::digest(executable.to_string_lossy().as_bytes())); + format!("local-{}", &digest[..24]) +} + +fn local_profile(validated: ValidatedTarget, display_name: Option) -> TargetProfile { + let default_name = validated + .executable + .file_name() + .and_then(|name| name.to_str()) + .map_or_else( + || "Local VidXP".into(), + |name| format!("Local VidXP ({name})"), + ); + TargetProfile { + id: stable_local_profile_id(&validated.executable), + display_name: display_name + .map(|name| name.trim().to_owned()) + .filter(|name| !name.is_empty()) + .unwrap_or(default_name), + schema_version: CURRENT_PROFILE_SCHEMA_VERSION, + kind: TargetKind::ExistingLocal, + lifecycle_ownership: LifecycleOwnership::External, + executable: validated.executable, + data_root: validated.data_root, + repository_root: validated.repository_root, + observed_vidxp_version: validated.product_version, + probe_schema_version: validated.probe_schema_version, + probe_protocol_version: validated.probe_protocol_version, + launch_protocol_version: validated.launch_protocol_version, + runtime: Some(validated.runtime), + frontend: validated.frontend, + last_successful_validation_at: Some(validated.validated_at), + validation_error: None, + managed_runtime_profile: None, + capabilities: Vec::new(), + surfaces: Vec::new(), + model_directory: None, + } +} + +fn managed_profile(managed: ManagedRuntimeProjection) -> TargetProfile { + TargetProfile { + id: format!("managed-{}", managed.runtime_profile), + display_name: "Desktop-managed VidXP".into(), + schema_version: CURRENT_PROFILE_SCHEMA_VERSION, + kind: TargetKind::Managed, + lifecycle_ownership: LifecycleOwnership::Desktop, + executable: managed.executable, + data_root: managed.data_root, + repository_root: managed.repository_root, + observed_vidxp_version: managed.package_version, + probe_schema_version: 0, + probe_protocol_version: 0, + launch_protocol_version: 0, + runtime: None, + frontend: FrontendCapability { + available: managed.surfaces.iter().any(|surface| surface == "browser"), + launchable: false, + optional: true, + code: "validation_required".into(), + message: "The managed runtime must be revalidated before use.".into(), + remediation: "Complete managed runtime validation before launch.".into(), + }, + last_successful_validation_at: None, + validation_error: Some(TargetError::new( + TargetErrorCode::ValidationRequired, + "The migrated managed runtime must be revalidated before use.", + )), + managed_runtime_profile: Some(managed.runtime_profile), + capabilities: managed.capabilities, + surfaces: managed.surfaces, + model_directory: Some(managed.model_directory), + } +} + +fn reconcile_managed_profile( + existing: Option<&TargetProfile>, + managed: ManagedRuntimeProjection, +) -> TargetProfile { + let mut profile = managed_profile(managed); + if let Some(existing) = existing { + profile.display_name = existing.display_name.clone(); + } + profile +} + +fn validate_profile_structure(profile: &TargetProfile) -> Result<(), TargetError> { + if profile.schema_version != CURRENT_PROFILE_SCHEMA_VERSION { + return Err(TargetError::new( + TargetErrorCode::UnsupportedProfileSchema, + format!( + "Target profile {} uses unsupported schema version {}.", + profile.id, profile.schema_version + ), + )); + } + if profile.id.trim().is_empty() || profile.display_name.trim().is_empty() { + return Err(TargetError::new( + TargetErrorCode::ProfileMalformed, + "A stored target profile is missing its stable identity or display name.", + )); + } + authorize_lifecycle(profile, LifecycleAction::Validate)?; + if profile.kind == TargetKind::Managed && profile.managed_runtime_profile.is_none() { + return Err(TargetError::new( + TargetErrorCode::ProfileMalformed, + "A managed target profile is missing its runtime identity.", + )); + } + Ok(()) +} + +fn migrate_profile_value(mut value: Value) -> Result<(TargetProfile, bool), TargetError> { + let object = value.as_object_mut().ok_or_else(|| { + TargetError::new( + TargetErrorCode::ProfileMalformed, + "A stored target profile is not a JSON object.", + ) + })?; + let schema = object + .get("schema_version") + .and_then(Value::as_u64) + .unwrap_or(0); + let mut changed = false; + match schema { + 0 => { + object.insert( + "schema_version".into(), + Value::from(CURRENT_PROFILE_SCHEMA_VERSION), + ); + if !object.contains_key("lifecycle_ownership") { + let ownership = match object.get("kind").and_then(Value::as_str) { + Some("managed") => "desktop", + _ => "external", + }; + object.insert("lifecycle_ownership".into(), Value::from(ownership)); + } + changed = true; + } + value if value == u64::from(CURRENT_PROFILE_SCHEMA_VERSION) => {} + other => { + return Err(TargetError::new( + TargetErrorCode::UnsupportedProfileSchema, + format!("A stored target profile uses unsupported schema version {other}."), + )); + } + } + let profile: TargetProfile = serde_json::from_value(value).map_err(|_| { + TargetError::new( + TargetErrorCode::ProfileMalformed, + "A stored target profile is malformed and could not be restored.", + ) + })?; + validate_profile_structure(&profile)?; + Ok((profile, changed)) +} + +fn decode_state( + store_schema: Option, + profiles: Option, + selected: Option, +) -> Result { + let schema = store_schema.as_ref().and_then(Value::as_u64).unwrap_or(0); + if schema > u64::from(CURRENT_STORE_SCHEMA_VERSION) { + return Err(TargetError::new( + TargetErrorCode::UnsupportedStoreSchema, + format!("Target profile storage uses unsupported schema version {schema}."), + )); + } + let mut decoded = DecodedState { + changed: schema != u64::from(CURRENT_STORE_SCHEMA_VERSION), + ..DecodedState::default() + }; + let values = match profiles { + None => Vec::new(), + Some(Value::Array(values)) => values, + Some(_) => { + return Err(TargetError::new( + TargetErrorCode::StoreCorrupt, + "Stored target profiles are malformed.", + )); + } + }; + let mut ids = BTreeSet::new(); + for value in values { + let (profile, migrated) = migrate_profile_value(value)?; + if !ids.insert(profile.id.clone()) { + return Err(TargetError::new( + TargetErrorCode::StoreCorrupt, + "Stored target profiles contain duplicate identities.", + )); + } + decoded.changed |= migrated; + decoded.profiles.push(profile); + } + decoded.selected_profile_id = match selected { + None | Some(Value::Null) => None, + Some(Value::String(value)) if !value.trim().is_empty() => Some(value), + Some(_) => { + decoded.changed = true; + decoded.issues.push(TargetError::new( + TargetErrorCode::SelectedProfileMissing, + "The selected target identity was malformed and has been cleared.", + )); + None + } + }; + if decoded + .selected_profile_id + .as_ref() + .is_some_and(|selected| !ids.contains(selected)) + { + decoded.selected_profile_id = None; + decoded.changed = true; + decoded.issues.push(TargetError::new( + TargetErrorCode::SelectedProfileMissing, + "The selected target no longer exists and has been cleared.", + )); + } + Ok(decoded) +} + +type ProfileStore = Arc>; + +fn open_store(app: &AppHandle) -> Result<(ProfileStore, Option), TargetError> { + match app.store(STORE_FILE) { + Ok(store) => Ok((store, None)), + Err(error) => { + log::warn!("Recovering malformed target profile store: {error}"); + let store = + app.store_builder(STORE_FILE) + .create_new() + .build() + .map_err(|recovery_error| { + TargetError::new( + TargetErrorCode::StoreUnavailable, + format!( + "Target profile storage could not be recovered: {recovery_error}" + ), + ) + })?; + Ok(( + store, + Some(TargetError::new( + TargetErrorCode::StoreCorrupt, + "Target profile storage was corrupt and has been reset. Choose a target again.", + )), + )) + } + } +} + +fn persist_state(store: &ProfileStore, decoded: &DecodedState) -> Result<(), TargetError> { + store.set(STORE_SCHEMA_KEY, Value::from(CURRENT_STORE_SCHEMA_VERSION)); + store.set( + PROFILES_KEY, + serde_json::to_value(&decoded.profiles).map_err(|error| { + TargetError::new( + TargetErrorCode::StoreUnavailable, + format!("Target profiles could not be serialized: {error}"), + ) + })?, + ); + match &decoded.selected_profile_id { + Some(selected) => store.set(SELECTED_PROFILE_KEY, Value::from(selected.clone())), + None => { + store.delete(SELECTED_PROFILE_KEY); + } + } + store.save().map_err(|error| { + TargetError::new( + TargetErrorCode::StoreUnavailable, + format!("Target profiles could not be saved: {error}"), + ) + }) +} + +fn load_state(app: &AppHandle) -> Result<(ProfileStore, DecodedState), TargetError> { + let (store, load_issue) = open_store(app)?; + let mut decoded = match decode_state( + store.get(STORE_SCHEMA_KEY), + store.get(PROFILES_KEY), + store.get(SELECTED_PROFILE_KEY), + ) { + Ok(decoded) => decoded, + Err(error) if error.code != TargetErrorCode::UnsupportedStoreSchema => { + let mut recovered = DecodedState { + changed: true, + ..DecodedState::default() + }; + recovered.issues.push(error); + recovered + } + Err(error) => return Err(error), + }; + if let Some(issue) = load_issue { + decoded.issues.push(issue); + decoded.changed = true; + } + if decoded.changed { + persist_state(&store, &decoded)?; + decoded.changed = false; + } + Ok((store, decoded)) +} + +fn state_snapshot(decoded: DecodedState) -> TargetState { + TargetState { + profiles: decoded.profiles, + selected_profile_id: decoded.selected_profile_id, + issues: decoded.issues, + } +} + +fn upsert_profile(decoded: &mut DecodedState, profile: TargetProfile) { + if let Some(existing) = decoded + .profiles + .iter_mut() + .find(|existing| existing.id == profile.id) + { + *existing = profile; + } else { + decoded.profiles.push(profile); + } + decoded + .profiles + .sort_by(|left, right| left.id.cmp(&right.id)); + decoded.changed = true; +} + +pub fn initialize( + app: &AppHandle, + managed_runtime: Option, + _desktop_version: &str, +) -> Result { + let (store, mut decoded) = load_state(app)?; + let selected_was_managed = decoded + .selected_profile_id + .as_ref() + .is_some_and(|selected| { + decoded + .profiles + .iter() + .any(|profile| profile.id == *selected && profile.kind == TargetKind::Managed) + }); + if let Some(managed_runtime) = managed_runtime { + let profile = reconcile_managed_profile( + decoded + .profiles + .iter() + .find(|existing| existing.kind == TargetKind::Managed), + managed_runtime, + ); + let id = profile.id.clone(); + let was_empty = decoded.profiles.is_empty(); + decoded + .profiles + .retain(|existing| existing.kind != TargetKind::Managed); + upsert_profile(&mut decoded, profile); + if selected_was_managed || (was_empty && decoded.selected_profile_id.is_none()) { + decoded.selected_profile_id = Some(id); + decoded.changed = true; + } + } else { + let previous_length = decoded.profiles.len(); + decoded + .profiles + .retain(|profile| profile.kind != TargetKind::Managed); + if decoded.profiles.len() != previous_length { + decoded.changed = true; + } + if selected_was_managed { + decoded.selected_profile_id = None; + decoded.changed = true; + } + } + if decoded.changed { + persist_state(&store, &decoded)?; + } + Ok(state_snapshot(decoded)) +} + +fn apply_validation(profile: &mut TargetProfile, validated: ValidatedTarget) { + profile.executable = validated.executable; + profile.data_root = validated.data_root; + profile.repository_root = validated.repository_root; + profile.observed_vidxp_version = validated.product_version; + profile.probe_schema_version = validated.probe_schema_version; + profile.probe_protocol_version = validated.probe_protocol_version; + profile.launch_protocol_version = validated.launch_protocol_version; + profile.runtime = Some(validated.runtime); + profile.frontend = validated.frontend; + profile.last_successful_validation_at = Some(validated.validated_at); + profile.validation_error = None; +} + +pub fn current_state(app: &AppHandle) -> Result { + let (_, decoded) = load_state(app)?; + Ok(state_snapshot(decoded)) +} + +pub fn replace_state(app: &AppHandle, state: TargetState) -> Result<(), TargetError> { + let (store, _) = load_state(app)?; + let decoded = DecodedState { + profiles: state.profiles, + selected_profile_id: state.selected_profile_id, + issues: state.issues, + changed: true, + }; + persist_state(&store, &decoded) +} + +pub fn selected_profile(app: &AppHandle) -> Result { + let state = current_state(app)?; + state.selected_profile().cloned().ok_or_else(|| { + TargetError::new( + TargetErrorCode::SelectedProfileMissing, + "Choose a VidXP target before continuing.", + ) + }) +} + +pub fn validated_selected_profile_with_cancellation( + app: &AppHandle, + desktop_version: &str, + cancellation: Option<&CancellationToken>, +) -> Result { + let profile = selected_profile(app)?; + let validated = validate_executable_with( + &profile.executable, + desktop_version, + |path, version, request_id| { + collect_command_output( + path, + &[ + "desktop-probe", + "--json", + "--desktop-version", + version, + "--request-id", + request_id, + ], + "The VidXP compatibility probe", + cancellation, + ) + }, + ); + persist_selected_validation(app, validated) +} + +pub(crate) fn persist_selected_validation( + app: &AppHandle, + validated: Result, +) -> Result { + let (store, mut decoded) = load_state(app)?; + let selected = decoded.selected_profile_id.clone().ok_or_else(|| { + TargetError::new( + TargetErrorCode::SelectedProfileMissing, + "Choose a VidXP target before continuing.", + ) + })?; + let profile = decoded + .profiles + .iter_mut() + .find(|profile| profile.id == selected) + .ok_or_else(|| { + TargetError::new( + TargetErrorCode::ProfileNotFound, + "The selected VidXP target no longer exists.", + ) + })?; + match validated { + Ok(validated) => { + apply_validation(profile, validated); + let result = profile.clone(); + decoded.changed = true; + persist_state(&store, &decoded)?; + Ok(result) + } + Err(error) => { + profile.validation_error = Some(error.clone()); + decoded.changed = true; + persist_state(&store, &decoded)?; + Err(error) + } + } +} + +pub fn adopt_validated( + app: &AppHandle, + validated: ValidatedTarget, + display_name: Option, +) -> Result { + let profile = local_profile(validated, display_name); + let (store, mut decoded) = load_state(app)?; + upsert_profile(&mut decoded, profile.clone()); + decoded.selected_profile_id = Some(profile.id.clone()); + persist_state(&store, &decoded)?; + Ok(state_snapshot(decoded)) +} + +pub fn select_profile( + app: &AppHandle, + profile_id: &str, + desktop_version: &str, +) -> Result { + let profile = current_state(app)? + .profiles + .into_iter() + .find(|profile| profile.id == profile_id) + .ok_or_else(|| { + TargetError::new( + TargetErrorCode::ProfileNotFound, + "The selected VidXP target no longer exists.", + ) + })?; + let validated = validate_executable(&profile.executable, desktop_version)?; + select_validated_profile(app, profile_id, validated) +} + +pub(crate) fn select_validated_profile( + app: &AppHandle, + profile_id: &str, + validated: ValidatedTarget, +) -> Result { + let (store, mut decoded) = load_state(app)?; + let profile = decoded + .profiles + .iter_mut() + .find(|profile| profile.id == profile_id) + .ok_or_else(|| { + TargetError::new( + TargetErrorCode::ProfileNotFound, + "The selected VidXP target no longer exists.", + ) + })?; + apply_validation(profile, validated); + decoded.selected_profile_id = Some(profile_id.to_owned()); + decoded.changed = true; + persist_state(&store, &decoded)?; + Ok(state_snapshot(decoded)) +} + +pub fn delete_profile(app: &AppHandle, profile_id: &str) -> Result { + let (store, mut decoded) = load_state(app)?; + if decoded + .profiles + .iter() + .any(|profile| profile.id == profile_id && profile.kind == TargetKind::Managed) + { + return Err(TargetError::new( + TargetErrorCode::ManagedProfileOwned, + "The active Desktop-managed target cannot be forgotten. Create or select another target instead.", + )); + } + let original_length = decoded.profiles.len(); + decoded.profiles.retain(|profile| profile.id != profile_id); + if decoded.profiles.len() == original_length { + return Err(TargetError::new( + TargetErrorCode::ProfileNotFound, + "The target profile no longer exists.", + )); + } + if decoded.selected_profile_id.as_deref() == Some(profile_id) { + decoded.selected_profile_id = None; + } + decoded.changed = true; + persist_state(&store, &decoded)?; + Ok(state_snapshot(decoded)) +} + +pub fn prepare_managed_activation( + app: &AppHandle, + managed_runtime: ManagedRuntimeProjection, + validated: ValidatedTarget, +) -> Result { + let (_, mut decoded) = load_state(app)?; + prepare_managed_state(&mut decoded, managed_runtime, Ok(validated)) +} + +fn prepare_managed_state( + decoded: &mut DecodedState, + managed_runtime: ManagedRuntimeProjection, + validated: Result, +) -> Result { + let mut profile = reconcile_managed_profile( + decoded + .profiles + .iter() + .find(|existing| existing.kind == TargetKind::Managed), + managed_runtime, + ); + apply_validation(&mut profile, validated?); + decoded + .profiles + .retain(|existing| existing.kind != TargetKind::Managed); + upsert_profile(decoded, profile.clone()); + decoded.selected_profile_id = Some(profile.id.clone()); + Ok(state_snapshot(decoded.clone())) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn profile(kind: TargetKind, ownership: LifecycleOwnership) -> TargetProfile { + TargetProfile { + id: "profile-1".into(), + display_name: "Target".into(), + schema_version: CURRENT_PROFILE_SCHEMA_VERSION, + kind: kind.clone(), + lifecycle_ownership: ownership, + executable: PathBuf::from("/vidxp"), + data_root: PathBuf::from("/data"), + repository_root: PathBuf::from("/data/repositories/default"), + observed_vidxp_version: "0.4.0-b".into(), + probe_schema_version: 1, + probe_protocol_version: 1, + launch_protocol_version: 1, + runtime: None, + frontend: FrontendCapability::default(), + last_successful_validation_at: Some(100), + validation_error: None, + managed_runtime_profile: (kind == TargetKind::Managed).then(|| "runtime-1".into()), + capabilities: Vec::new(), + surfaces: Vec::new(), + model_directory: None, + } + } + + #[test] + fn tagged_profiles_round_trip_with_explicit_ownership() { + for expected in [ + profile(TargetKind::ExistingLocal, LifecycleOwnership::External), + profile(TargetKind::Managed, LifecycleOwnership::Desktop), + ] { + let json = serde_json::to_value(&expected).expect("serialize profile"); + assert_eq!( + json.get("kind").and_then(Value::as_str), + Some(match expected.kind { + TargetKind::ExistingLocal => "existing_local", + TargetKind::Managed => "managed", + }) + ); + assert_eq!( + serde_json::from_value::(json).expect("deserialize profile"), + expected + ); + } + } + + #[test] + fn schema_zero_profile_migrates_ownership() { + let mut value = serde_json::to_value(profile( + TargetKind::ExistingLocal, + LifecycleOwnership::External, + )) + .expect("serialize"); + let object = value.as_object_mut().expect("object"); + object.remove("schema_version"); + object.remove("lifecycle_ownership"); + + let (migrated, changed) = migrate_profile_value(value).expect("migration"); + + assert!(changed); + assert_eq!(migrated.schema_version, CURRENT_PROFILE_SCHEMA_VERSION); + assert_eq!(migrated.lifecycle_ownership, LifecycleOwnership::External); + } + + #[test] + fn corrupted_store_and_duplicate_profiles_are_rejected() { + assert_eq!( + decode_state(Some(Value::from(1)), Some(Value::from("bad")), None) + .expect_err("corrupt") + .code, + TargetErrorCode::StoreCorrupt + ); + let value = serde_json::to_value(profile( + TargetKind::ExistingLocal, + LifecycleOwnership::External, + )) + .expect("profile"); + assert_eq!( + decode_state( + Some(Value::from(1)), + Some(Value::Array(vec![value.clone(), value])), + None, + ) + .expect_err("duplicate") + .code, + TargetErrorCode::StoreCorrupt + ); + } + + #[test] + fn missing_or_deleted_selected_profile_is_cleared() { + let decoded = decode_state( + Some(Value::from(1)), + Some(Value::Array(Vec::new())), + Some(Value::from("deleted")), + ) + .expect("decode"); + + assert_eq!(decoded.selected_profile_id, None); + assert_eq!( + decoded.issues[0].code, + TargetErrorCode::SelectedProfileMissing + ); + assert!(decoded.changed); + } + + #[test] + fn lifecycle_guards_block_actual_external_mutation_and_broad_stop() { + let external = profile(TargetKind::ExistingLocal, LifecycleOwnership::External); + for action in [LifecycleAction::Install, LifecycleAction::BroadProcessStop] { + assert_eq!( + authorize_lifecycle(&external, action) + .expect_err("external mutation") + .code, + TargetErrorCode::LifecycleForbidden + ); + } + authorize_lifecycle(&external, LifecycleAction::Validate).expect("validation"); + authorize_lifecycle(&external, LifecycleAction::Launch).expect("launch"); + + let managed = profile(TargetKind::Managed, LifecycleOwnership::Desktop); + authorize_lifecycle(&managed, LifecycleAction::Install).expect("managed install"); + authorize_lifecycle(&managed, LifecycleAction::BroadProcessStop).expect("managed stop"); + } + + #[test] + fn external_selection_cannot_act_on_an_installed_managed_runtime() { + let external = profile(TargetKind::ExistingLocal, LifecycleOwnership::External); + let error = authorize_managed_runtime_action(&external, "runtime-1") + .expect_err("external selection must not control the managed runtime"); + assert_eq!(error.code, TargetErrorCode::LifecycleForbidden); + + let managed = profile(TargetKind::Managed, LifecycleOwnership::Desktop); + authorize_managed_runtime_action(&managed, "runtime-1") + .expect("matching managed selection"); + let mismatch = authorize_managed_runtime_action(&managed, "runtime-2") + .expect_err("a different managed runtime must not be controlled"); + assert_eq!(mismatch.code, TargetErrorCode::ValidationRequired); + } + + #[test] + fn mismatched_kind_and_ownership_is_malformed() { + let invalid = profile(TargetKind::ExistingLocal, LifecycleOwnership::Desktop); + assert_eq!( + authorize_lifecycle(&invalid, LifecycleAction::Validate) + .expect_err("mismatch") + .code, + TargetErrorCode::ProfileMalformed + ); + } + + #[test] + fn stale_or_failed_validation_is_not_ready() { + let mut target = profile(TargetKind::ExistingLocal, LifecycleOwnership::External); + assert!(target.is_ready(100 + VALIDATION_MAX_AGE.as_secs())); + assert!(!target.is_ready(101 + VALIDATION_MAX_AGE.as_secs())); + target.validation_error = Some(TargetError::new( + TargetErrorCode::UnsupportedLaunchProtocol, + "unsupported launch protocol", + )); + assert!(!target.is_ready(100)); + } + + fn document(canonical: &Path, request_id: &str) -> ProbeDocument { + let root = std::env::current_dir().expect("current directory"); + ProbeDocument { + product: PRODUCT_ID.into(), + product_version: "0.4.0-b".into(), + schema_version: 1, + protocol_version: 1, + launch_contract: ProbeLaunchContract { + protocol_version: 2, + surface: "browser".into(), + command: "ui".into(), + }, + request_id: request_id.into(), + launcher: canonical.into(), + runtime: ProbeRuntime { + python_executable: root.join("python"), + python_version: "3.14.6".into(), + implementation: "CPython".into(), + prefix: root.join("prefix"), + base_prefix: root.join("base-prefix"), + }, + data_root: root.join("data"), + repository_root: root.join("data").join("repositories").join("default"), + model_root: root.join("data").join("models"), + capabilities: ProbeCapabilities::default(), + } + } + + #[test] + fn valid_probe_accepts_missing_optional_frontend() { + let executable = std::env::current_exe().expect("current executable"); + let canonical = fs::canonicalize(executable).expect("canonical executable"); + let validated = + validate_probe_document(&canonical, "nonce", document(&canonical, "nonce"), 100) + .expect("valid probe"); + + assert!(!validated.frontend.available); + assert!(!validated.frontend.launchable); + } + + #[test] + fn validation_pipeline_reports_missing_malformed_timeout_and_failed_probes() { + let missing = std::env::temp_dir().join("vidxp-missing-probe-executable"); + assert_eq!( + validate_executable_with(&missing, "0.4.0-b", |_, _, _| { + panic!("a missing executable must not be launched") + }) + .expect_err("missing") + .code, + TargetErrorCode::ExecutableMissing + ); + + let executable = std::env::current_exe().expect("current executable"); + assert_eq!( + validate_executable_with(&executable, "0.4.0-b", |_, _, _| { + Ok(ProbeOutput { + success: true, + stdout: b"not json".to_vec(), + stderr: Vec::new(), + }) + }) + .expect_err("malformed") + .code, + TargetErrorCode::MalformedProbe + ); + assert_eq!( + validate_executable_with(&executable, "0.4.0-b", |_, _, _| { + Err(TargetError::new(TargetErrorCode::ProbeTimeout, "timed out")) + }) + .expect_err("timeout") + .code, + TargetErrorCode::ProbeTimeout + ); + assert_eq!( + validate_executable_with(&executable, "0.4.0-b", |_, _, _| { + Ok(ProbeOutput { + success: false, + stdout: Vec::new(), + stderr: Vec::new(), + }) + }) + .expect_err("failed") + .code, + TargetErrorCode::ProbeFailed + ); + } + + #[test] + fn probe_rejects_identity_and_probe_contract_mismatches() { + let executable = std::env::current_exe().expect("current executable"); + let canonical = fs::canonicalize(executable).expect("canonical executable"); + + let mut non_vidxp = document(&canonical, "nonce"); + non_vidxp.product = "other".into(); + assert_eq!( + validate_probe_document(&canonical, "nonce", non_vidxp, 100) + .expect_err("product") + .code, + TargetErrorCode::NotVidxp + ); + + let wrong_nonce = document(&canonical, "wrong"); + assert_eq!( + validate_probe_document(&canonical, "nonce", wrong_nonce, 100) + .expect_err("nonce") + .code, + TargetErrorCode::ProbeChallengeMismatch + ); + + let mut wrong_launcher = document(&canonical, "nonce"); + wrong_launcher.launcher = fs::canonicalize(file!()).expect("source file"); + assert_eq!( + validate_probe_document(&canonical, "nonce", wrong_launcher, 100) + .expect_err("launcher") + .code, + TargetErrorCode::LauncherIdentityMismatch + ); + + let mut wrong_schema = document(&canonical, "nonce"); + wrong_schema.schema_version = 2; + assert_eq!( + validate_probe_document(&canonical, "nonce", wrong_schema, 100) + .expect_err("schema") + .code, + TargetErrorCode::UnsupportedProbeSchema + ); + + let mut wrong_protocol = document(&canonical, "nonce"); + wrong_protocol.protocol_version = 2; + assert_eq!( + validate_probe_document(&canonical, "nonce", wrong_protocol, 100) + .expect_err("protocol") + .code, + TargetErrorCode::UnsupportedProbeProtocol + ); + } + + #[test] + fn compatible_probe_accepts_a_different_reported_package_version() { + let executable = std::env::current_exe().expect("current executable"); + let canonical = fs::canonicalize(executable).expect("canonical executable"); + let mut compatible = document(&canonical, "nonce"); + compatible.product_version = "0.3.0".into(); + + let validated = validate_probe_document(&canonical, "nonce", compatible, 100) + .expect("compatible contract"); + + assert_eq!(validated.product_version, "0.3.0"); + assert_eq!(validated.launch_protocol_version, 2); + } + + #[test] + fn exact_and_canonical_symlink_launchers_preserve_selected_identity() { + let executable = std::env::current_exe().expect("current executable"); + let canonical = fs::canonicalize(&executable).expect("canonical executable"); + let exact = document(&canonical, "nonce"); + assert!(validate_probe_document(&canonical, "nonce", exact, 100).is_ok()); + + let link = std::env::temp_dir().join(format!( + "vidxp-launcher-link-{}{}", + std::process::id(), + std::env::consts::EXE_SUFFIX + )); + #[cfg(windows)] + let linked = std::os::windows::fs::symlink_file(&canonical, &link); + #[cfg(unix)] + let linked = std::os::unix::fs::symlink(&canonical, &link); + if linked.is_err() { + return; + } + let linked_document = document(&link, "nonce"); + assert!(validate_probe_document(&canonical, "nonce", linked_document, 100).is_ok()); + fs::remove_file(link).expect("remove launcher symlink"); + } + + #[cfg(windows)] + #[test] + fn extensionless_windows_console_script_identity_resolves_only_selected_shim() { + let root = + std::env::temp_dir().join(format!("vidxp-launcher-identity-{}", std::process::id())); + fs::create_dir_all(&root).expect("launcher test directory"); + let selected = root.join("vidxp.exe"); + let colliding = root.join("vidxp.com"); + let similar = root.join("vidxp-helper.exe"); + fs::write(&selected, b"shim").expect("selected shim"); + fs::write(&colliding, b"different PATHEXT sibling").expect("colliding shim"); + fs::write(&similar, b"other").expect("similar shim"); + let canonical = fs::canonicalize(&selected).expect("canonical selected shim"); + + let mut extensionless = document(&canonical, "nonce"); + extensionless.launcher = root.join("vidxp"); + assert!(validate_probe_document(&canonical, "nonce", extensionless, 100).is_ok()); + + let mut unrelated = document(&canonical, "nonce"); + unrelated.launcher = similar; + assert_eq!( + validate_probe_document(&canonical, "nonce", unrelated, 100) + .expect_err("similar sibling") + .code, + TargetErrorCode::LauncherIdentityMismatch + ); + + let mut missing = document(&canonical, "nonce"); + missing.launcher = root.join("missing"); + assert_eq!( + validate_probe_document(&canonical, "nonce", missing, 100) + .expect_err("missing launcher") + .code, + TargetErrorCode::LauncherIdentityMismatch + ); + fs::remove_dir_all(root).expect("remove launcher test directory"); + } + + #[cfg(not(windows))] + #[test] + fn non_windows_launcher_identity_does_not_resolve_executable_suffixes() { + let root = + std::env::temp_dir().join(format!("vidxp-launcher-identity-{}", std::process::id())); + fs::create_dir_all(&root).expect("launcher test directory"); + let selected = root.join("vidxp.exe"); + fs::write(&selected, b"shim").expect("selected shim"); + let canonical = fs::canonicalize(&selected).expect("canonical selected shim"); + let mut extensionless = document(&canonical, "nonce"); + extensionless.launcher = root.join("vidxp"); + + assert_eq!( + validate_probe_document(&canonical, "nonce", extensionless, 100) + .expect_err("ordinary non-Windows path") + .code, + TargetErrorCode::LauncherIdentityMismatch + ); + fs::remove_dir_all(root).expect("remove launcher test directory"); + } + + #[test] + fn probe_rejects_an_incompatible_launch_protocol() { + let executable = std::env::current_exe().expect("current executable"); + let canonical = fs::canonicalize(executable).expect("canonical executable"); + let mut incompatible = document(&canonical, "nonce"); + incompatible.launch_contract.protocol_version = 1; + + assert_eq!( + validate_probe_document(&canonical, "nonce", incompatible, 100) + .expect_err("launch protocol") + .code, + TargetErrorCode::UnsupportedLaunchProtocol + ); + } + + #[test] + fn inspection_accepts_a_compatible_contract_with_a_different_package_version() { + let executable = std::env::current_exe().expect("current executable"); + let inspected = inspect_executable_with( + &executable, + "0.4.0-b", + |canonical, _, request_id| { + let mut payload = document(canonical, request_id); + payload.product_version = "0.3.0".into(); + Ok(ProbeOutput { + success: true, + stdout: serde_json::to_vec(&payload).expect("probe json"), + stderr: Vec::new(), + }) + }, + |_| panic!("a compatible probe must not fall back to package version"), + ) + .expect("inspection"); + + assert_eq!(inspected.state, InspectionState::ReadyToUse); + assert!(inspected.adoptable); + assert_eq!(inspected.reported_version.as_deref(), Some("0.3.0")); + assert!(inspected.probe_compatible); + assert!(inspected.launch_compatible); + } + + #[test] + fn version_fallback_is_diagnostic_only_and_cannot_make_a_target_adoptable() { + let executable = std::env::current_exe().expect("current executable"); + let inspected = inspect_executable_with( + &executable, + "0.4.0-b", + |_, _, _| { + Ok(ProbeOutput { + success: false, + stdout: Vec::new(), + stderr: b"No such command: desktop-probe".to_vec(), + }) + }, + |_| { + Ok(ProbeOutput { + success: true, + stdout: b"VidXP 0.4.0b0\n".to_vec(), + stderr: Vec::new(), + }) + }, + ) + .expect("inspection"); + + assert_eq!(inspected.state, InspectionState::UpdateRequired); + assert!(!inspected.adoptable); + assert_eq!(inspected.reported_version.as_deref(), Some("0.4.0b0")); + assert!(!inspected.probe_compatible); + assert!(inspected.validated.is_none()); + } + + #[test] + fn executable_that_cannot_report_a_version_is_not_adoptable() { + let executable = std::env::current_exe().expect("current executable"); + let inspected = inspect_executable_with( + &executable, + "0.4.0-b", + |_, _, _| { + Ok(ProbeOutput { + success: false, + stdout: Vec::new(), + stderr: b"missing dependency".to_vec(), + }) + }, + |_| { + Ok(ProbeOutput { + success: false, + stdout: Vec::new(), + stderr: b"ModuleNotFoundError: SQLAlchemy".to_vec(), + }) + }, + ) + .expect("inspection"); + + assert_eq!(inspected.state, InspectionState::CannotStart); + assert!(!inspected.adoptable); + assert!( + inspected + .remediation + .contains("package-management workflow") + ); + } + + #[cfg(windows)] + #[test] + fn real_windows_console_script_passes_the_desktop_inspection_path_when_requested() { + let Some(executable) = std::env::var_os("VIDXP_DESKTOP_INTEGRATION_EXECUTABLE") else { + return; + }; + let inspected = + inspect_executable(Path::new(&executable), "0.4.0-b").expect("real Desktop inspection"); + let validated = inspected.validated.expect("validated target"); + + assert_eq!(inspected.state, InspectionState::ReadyToUse); + assert!(inspected.adoptable); + assert_eq!(validated.product_version, "0.4.0b0"); + assert_eq!(validated.probe_protocol_version, 1); + assert_eq!(validated.launch_protocol_version, 2); + assert_eq!(validated.runtime.python_version, "3.14.0"); + assert!(validated.frontend.launchable); + let expected_data_root = + std::env::var_os("VIDXP_DESKTOP_INTEGRATION_DATA_ROOT").expect("integration data root"); + assert_eq!(validated.data_root, PathBuf::from(expected_data_root)); + } + + #[test] + fn managed_projection_preserves_current_runtime_identity_without_claiming_validation() { + let projected = managed_profile(ManagedRuntimeProjection { + runtime_profile: "abc-123".into(), + executable: PathBuf::from("/runtime/bin/vidxp"), + data_root: PathBuf::from("/data"), + repository_root: PathBuf::from("/data/repositories/default"), + model_directory: PathBuf::from("/models"), + package_version: "0.4.0-b".into(), + capabilities: vec!["scene".into()], + surfaces: vec!["browser".into()], + }); + + assert_eq!(projected.id, "managed-abc-123"); + assert_eq!(projected.kind, TargetKind::Managed); + assert_eq!(projected.lifecycle_ownership, LifecycleOwnership::Desktop); + assert_eq!( + projected.managed_runtime_profile.as_deref(), + Some("abc-123") + ); + assert!(projected.last_successful_validation_at.is_none()); + assert_eq!( + projected.validation_error.expect("validation error").code, + TargetErrorCode::ValidationRequired + ); + } + + #[test] + fn managed_reconciliation_refreshes_authoritative_fields_and_preserves_name() { + let mut existing = profile(TargetKind::Managed, LifecycleOwnership::Desktop); + existing.id = "managed-runtime-2".into(); + existing.display_name = "Editing workstation".into(); + existing.executable = PathBuf::from("/stale/vidxp"); + existing.model_directory = Some(PathBuf::from("/legacy/models")); + existing.capabilities = vec!["dialogue".into()]; + + let reconciled = reconcile_managed_profile( + Some(&existing), + ManagedRuntimeProjection { + runtime_profile: "runtime-2".into(), + executable: PathBuf::from("/current/vidxp"), + data_root: PathBuf::from("/current/data"), + repository_root: PathBuf::from("/current/data/repositories/default"), + model_directory: PathBuf::from("/current/models"), + package_version: "0.5.0".into(), + capabilities: vec!["scene".into()], + surfaces: vec!["browser".into()], + }, + ); + + assert_eq!(reconciled.display_name, "Editing workstation"); + assert_eq!(reconciled.executable, PathBuf::from("/current/vidxp")); + assert_eq!(reconciled.data_root, PathBuf::from("/current/data")); + assert_eq!( + reconciled.model_directory, + Some(PathBuf::from("/current/models")) + ); + assert_eq!(reconciled.capabilities, ["scene"]); + assert_eq!(reconciled.surfaces, ["browser"]); + assert_eq!(reconciled.observed_vidxp_version, "0.5.0"); + } + + fn managed_projection_for_test() -> ManagedRuntimeProjection { + ManagedRuntimeProjection { + runtime_profile: "runtime-new".into(), + executable: PathBuf::from("/runtime/bin/vidxp"), + data_root: PathBuf::from("/data"), + repository_root: PathBuf::from("/data/repositories/default"), + model_directory: PathBuf::from("/models"), + package_version: "0.5.0".into(), + capabilities: vec!["scene".into()], + surfaces: vec!["browser".into()], + } + } + + fn validated_managed_target() -> ValidatedTarget { + ValidatedTarget { + executable: PathBuf::from("/runtime/bin/vidxp"), + product_version: "0.5.0".into(), + probe_schema_version: 1, + probe_protocol_version: 1, + launch_protocol_version: 2, + runtime: RuntimeIdentity { + python_executable: PathBuf::from("/runtime/bin/python"), + python_version: "3.14.6".into(), + implementation: "CPython".into(), + prefix: PathBuf::from("/runtime"), + base_prefix: PathBuf::from("/python"), + }, + data_root: PathBuf::from("/data"), + repository_root: PathBuf::from("/data/repositories/default"), + model_root: PathBuf::from("/models"), + frontend: FrontendCapability { + available: true, + launchable: true, + optional: true, + code: "frontend_available".into(), + message: "Available".into(), + remediation: String::new(), + }, + validated_at: 200, + } + } + + #[test] + fn failed_managed_candidate_probe_cannot_replace_target_state() { + let old = profile(TargetKind::Managed, LifecycleOwnership::Desktop); + let mut decoded = DecodedState { + profiles: vec![old.clone()], + selected_profile_id: Some(old.id.clone()), + ..DecodedState::default() + }; + + let error = prepare_managed_state( + &mut decoded, + managed_projection_for_test(), + Err(TargetError::new( + TargetErrorCode::UnsupportedLaunchProtocol, + "candidate launch contract failed", + )), + ) + .expect_err("candidate must not activate"); + + assert_eq!(error.code, TargetErrorCode::UnsupportedLaunchProtocol); + assert_eq!(decoded.profiles.as_slice(), std::slice::from_ref(&old)); + assert_eq!( + decoded.selected_profile_id.as_deref(), + Some(old.id.as_str()) + ); + } + + #[test] + fn managed_activation_replaces_all_stale_managed_profiles() { + let external = profile(TargetKind::ExistingLocal, LifecycleOwnership::External); + let mut old = profile(TargetKind::Managed, LifecycleOwnership::Desktop); + old.id = "managed-old".into(); + old.display_name = "Editing workstation".into(); + let mut stale = old.clone(); + stale.id = "managed-stale".into(); + let mut decoded = DecodedState { + profiles: vec![external.clone(), old, stale], + selected_profile_id: Some("managed-old".into()), + ..DecodedState::default() + }; + + let state = prepare_managed_state( + &mut decoded, + managed_projection_for_test(), + Ok(validated_managed_target()), + ) + .expect("managed candidate"); + + let managed: Vec<_> = state + .profiles + .iter() + .filter(|profile| profile.kind == TargetKind::Managed) + .collect(); + assert_eq!(managed.len(), 1); + assert_eq!(managed[0].id, "managed-runtime-new"); + assert_eq!(managed[0].display_name, "Editing workstation"); + assert_eq!( + state.selected_profile_id.as_deref(), + Some("managed-runtime-new") + ); + assert!(state.profiles.contains(&external)); + } +} diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index 663588c..0d5239b 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -4,12 +4,16 @@ "version": "0.4.0-b", "identifier": "dev.grayhat.vidxp", "build": { - "frontendDist": "../web" + "beforeDevCommand": "npm run dev", + "beforeBuildCommand": "npm run build", + "devUrl": "http://localhost:5173", + "frontendDist": "../dist" }, "app": { - "withGlobalTauri": true, + "withGlobalTauri": false, "security": { - "csp": "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src ipc: http://ipc.localhost" + "csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src ipc: http://ipc.localhost", + "devCsp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src ipc: http://ipc.localhost ws:" }, "windows": [ { @@ -20,13 +24,20 @@ "minWidth": 680, "minHeight": 600, "visible": false, - "resizable": true + "resizable": true, + "decorations": true, + "shadow": true, + "transparent": false } ] }, "bundle": { "active": true, "targets": "all", + "resources": [ + "../THIRD_PARTY_NOTICES.txt", + "../../LICENSE" + ], "externalBin": [ "binaries/uv" ], diff --git a/desktop/src-tauri/tauri.windows.conf.json b/desktop/src-tauri/tauri.windows.conf.json new file mode 100644 index 0000000..7693ba8 --- /dev/null +++ b/desktop/src-tauri/tauri.windows.conf.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "app": { + "windows": [ + { + "label": "main", + "title": "VidXP", + "width": 920, + "height": 760, + "minWidth": 680, + "minHeight": 600, + "visible": false, + "resizable": true, + "decorations": false, + "shadow": false, + "transparent": false + } + ] + } +} diff --git a/desktop/src/App.test.tsx b/desktop/src/App.test.tsx new file mode 100644 index 0000000..fd07763 --- /dev/null +++ b/desktop/src/App.test.tsx @@ -0,0 +1,381 @@ +import { MantineProvider } from '@mantine/core'; +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { StrictMode } from 'react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + targetSetupState: vi.fn(), recheckTargetState: vi.fn(), discoverLocalTargets: vi.fn(), + chooseLocalExecutable: vi.fn(), inspectLocalTarget: vi.fn(), activateLocalTarget: vi.fn(), + selectTargetProfile: vi.fn(), deleteTargetProfile: vi.fn(), confirmForgetTarget: vi.fn(), beginManagedSetup: vi.fn(), + cancelManagedSetup: vi.fn(), installMediaRuntime: vi.fn(), installRuntime: vi.fn(), + prepareManagedModels: vi.fn(), + runtimeManifest: vi.fn(), runtimeStatus: vi.fn(), launchUi: vi.fn(), + chooseModelDirectory: vi.fn(), modelDirectoryInventory: vi.fn(), +})); + +const windowMocks = vi.hoisted(() => ({ + close: vi.fn(), isMaximized: vi.fn(), minimize: vi.fn(), onResized: vi.fn(), toggleMaximize: vi.fn(), +})); + +vi.mock('@tauri-apps/api/window', () => ({ getCurrentWindow: () => windowMocks })); +vi.mock('./tauri', () => ({ + ...mocks, + selectedProfile: (state: any) => state.profiles.find((profile: any) => profile.id === state.selected_profile_id) ?? null, + errorMessage: (error: unknown, fallback: string) => typeof error === 'string' ? error : fallback, + displayPath: (path: string) => path, +})); + +import { App } from './App'; + +const frontend = { available: true, launchable: true, optional: true, code: 'frontend_available', message: 'Available.', remediation: '' }; +const localProfile = { + id: 'local-1', display_name: 'Studio VidXP', schema_version: 1, kind: 'existing_local', + lifecycle_ownership: 'external', executable: 'C:\\Tools\\VidXP\\vidxp.exe', + display_executable: 'C:\\Tools\\VidXP\\vidxp.exe', data_root: 'C:\\Data', display_data_root: 'C:\\Data', + repository_root: 'C:\\Data\\repositories\\default', display_repository_root: 'C:\\Data\\repositories\\default', + observed_vidxp_version: '0.4.0', probe_schema_version: 1, probe_protocol_version: 1, + launch_protocol_version: 1, runtime: null, frontend, last_successful_validation_at: 1, + last_validated_at: '2026-08-01T10:00:00Z', validation_error: null, capabilities: [], surfaces: ['browser'], +}; +const managedProfile = { + ...localProfile, id: 'managed-a', display_name: 'Managed VidXP', kind: 'managed', + lifecycle_ownership: 'desktop', managed_runtime_profile: 'runtime-a', capabilities: ['scene'], surfaces: [], + frontend: { ...frontend, available: false, launchable: false, code: 'frontend_unavailable', message: 'Browser interface is not installed.', remediation: 'Return to managed setup.' }, +}; +const emptyState = { profiles: [], selected_profile_id: null, issues: [] }; +const localState = { profiles: [localProfile], selected_profile_id: localProfile.id, issues: [] }; + +function deferred() { + let resolve!: (value: T) => void; + let reject!: (reason?: unknown) => void; + const promise = new Promise((done, fail) => { resolve = done; reject = fail; }); + return { promise, resolve, reject }; +} + +function renderApp() { + return render(); +} + +async function enterLocal(user: ReturnType) { + await screen.findByRole('heading', { name: 'Where should VidXP run?' }); + await user.click(screen.getByRole('radio', { name: /Use an existing installation/i })); + await user.click(screen.getByRole('button', { name: 'Continue' })); +} + +async function enterManaged(user: ReturnType) { + await screen.findByRole('heading', { name: 'Where should VidXP run?' }); + await user.click(screen.getByRole('radio', { name: /Set up VidXP for me/i })); + await user.click(screen.getByRole('button', { name: 'Continue' })); + await user.click(screen.getByRole('button', { name: 'Continue to setup' })); + await screen.findByRole('heading', { name: 'Set up local processing' }); +} + +describe('desktop target lifecycle', () => { + beforeEach(() => { + vi.resetAllMocks(); + windowMocks.isMaximized.mockResolvedValue(false); + windowMocks.onResized.mockResolvedValue(vi.fn()); + mocks.targetSetupState.mockResolvedValue(emptyState); + mocks.recheckTargetState.mockResolvedValue(emptyState); + mocks.discoverLocalTargets.mockResolvedValue([{ executable: 'C:\\Tools\\VidXP\\vidxp.exe', display_path: 'C:\\Tools\\VidXP\\vidxp.exe', source: 'PATH' }]); + mocks.inspectLocalTarget.mockResolvedValue({ + state: 'ready_to_use', adoptable: true, executable: 'C:\\Tools\\VidXP\\vidxp.exe', reported_version: '0.4.0', + probe_compatible: true, launch_compatible: true, message: 'Compatible contracts.', remediation: '', technical_details: null, + validation: { canonical_executable: 'C:\\Tools\\VidXP\\vidxp.exe', protocol_version: 1, launch_protocol_version: 1, python_version: '3.14', display_data_root: 'C:\\Data', can_launch_frontend: true, frontend }, + }); + mocks.beginManagedSetup.mockResolvedValue({ id: 'draft-1', previous_profile_id: null }); + mocks.cancelManagedSetup.mockResolvedValue(emptyState); + mocks.confirmForgetTarget.mockResolvedValue(true); + mocks.runtimeManifest.mockResolvedValue({ package_version: '0.4.0', capabilities: { scene: { extra: 'scene', label: 'Visual scene search' } }, surfaces: { browser: { extra: 'frontend', label: 'Browser interface', description: 'Browser UI', default: true } } }); + mocks.runtimeStatus.mockResolvedValue({ state: 'never_configured', ready: false, runtime_profile: null, package_version: '0.4.0', capabilities: [], surfaces: [], model_directory: 'C:\\Models', detail: 'No managed runtime yet.' }); + mocks.modelDirectoryInventory.mockResolvedValue({ directory: 'C:\\Models', exists: false, readable: true, total_bytes: 0, file_count: 0, recognized_models: [], empty: true, verification_required: false, truncated: false, detail: 'Empty.' }); + mocks.installMediaRuntime.mockResolvedValue({ ready: true }); + mocks.installRuntime.mockResolvedValue({ + install: { package_version: '0.4.0', capabilities: ['scene'], surfaces: ['browser'], model_directory: 'C:\\Models', prepared: true }, + setup: { profiles: [managedProfile], selected_profile_id: managedProfile.id, issues: [] }, + }); + mocks.prepareManagedModels.mockResolvedValue({ profiles: [managedProfile], selected_profile_id: managedProfile.id, issues: [] }); + mocks.launchUi.mockResolvedValue(undefined); + }); + + it('shows the target-first choice without a remote placeholder', async () => { + renderApp(); + expect(await screen.findByRole('radio', { name: /Use an existing installation/i })).toBeVisible(); + expect(screen.getByRole('radio', { name: /Set up VidXP for me/i })).toBeVisible(); + expect(screen.queryByText(/remote server/i)).not.toBeInTheDocument(); + }); + + it('shows the restored control panel immediately while one startup recheck is pending', async () => { + let resolve!: (value: typeof localState) => void; + mocks.targetSetupState.mockResolvedValue(localState); + mocks.recheckTargetState.mockReturnValue(new Promise((done) => { resolve = done; })); + renderApp(); + expect(await screen.findByRole('heading', { name: 'Studio VidXP' })).toBeVisible(); + expect(screen.getByRole('button', { name: 'Recheck target' })).toHaveAttribute('data-loading'); + expect(mocks.recheckTargetState).toHaveBeenCalledTimes(1); + resolve(localState); + await waitFor(() => expect(screen.getByRole('button', { name: 'Recheck target' })).not.toHaveAttribute('data-loading')); + }); + + it('opens the browser once and settles its loading state', async () => { + mocks.targetSetupState.mockResolvedValue(localState); + mocks.recheckTargetState.mockResolvedValue(localState); + const user = userEvent.setup(); renderApp(); + const open = await screen.findByRole('button', { name: 'Open VidXP' }); + await user.click(open); + expect(mocks.launchUi).toHaveBeenCalledTimes(1); + await waitFor(() => expect(open).not.toHaveAttribute('data-loading')); + }); + + it('uses the parent exclusive operation while browser startup is pending', async () => { + const browserManaged = { + ...managedProfile, + frontend, + surfaces: ['browser'], + }; + const setup = { profiles: [browserManaged], selected_profile_id: browserManaged.id, issues: [] }; + const opening = deferred(); + mocks.targetSetupState.mockResolvedValue(setup); + mocks.recheckTargetState.mockResolvedValue(setup); + mocks.launchUi.mockReturnValue(opening.promise); + const user = userEvent.setup(); renderApp(); + const open = await screen.findByRole('button', { name: 'Open VidXP' }); + await user.click(open); + expect(open).toHaveAttribute('data-loading'); + expect(screen.getByRole('button', { name: 'Manage targets' })).toBeDisabled(); + expect(screen.getByRole('button', { name: 'Recheck target' })).toBeDisabled(); + expect(screen.getByRole('button', { name: 'Manage setup' })).toBeDisabled(); + opening.resolve(); + await waitFor(() => expect(open).not.toHaveAttribute('data-loading')); + }); + + it('adopts the inspected candidate without an installation action', async () => { + mocks.activateLocalTarget.mockResolvedValue(localState); + const user = userEvent.setup(); renderApp(); await enterLocal(user); + await user.click(await screen.findByRole('radio', { name: /VidXP executable/i })); + await user.click(await screen.findByRole('button', { name: 'Use this installation' })); + expect(mocks.inspectLocalTarget).toHaveBeenCalledTimes(1); + expect(mocks.activateLocalTarget).toHaveBeenCalledTimes(1); + expect(mocks.installRuntime).not.toHaveBeenCalled(); + }); + + it('keeps fresh discovery fields authoritative while retaining inspection UI', async () => { + const user = userEvent.setup(); renderApp(); await enterLocal(user); + await user.click(await screen.findByRole('radio', { name: /VidXP executable/i })); + await screen.findByText('Compatible contracts.'); + mocks.discoverLocalTargets.mockResolvedValue([{ executable: 'C:\\Tools\\VidXP\\vidxp.exe', display_path: 'C:\\New\\display.exe', source: 'Fresh scan' }]); + await user.click(screen.getByRole('button', { name: 'Scan again' })); + expect(await screen.findByText('C:\\New\\display.exe')).toBeVisible(); + expect(screen.getByText('Compatible contracts.')).toBeVisible(); + expect(screen.getByText('Discovered via Fresh scan')).toBeVisible(); + }); + + it('cancels managed setup back to the still-selected target', async () => { + mocks.targetSetupState.mockResolvedValue(localState); + mocks.recheckTargetState.mockResolvedValue(localState); + mocks.beginManagedSetup.mockResolvedValue({ id: 'draft-1', previous_profile_id: localProfile.id }); + mocks.cancelManagedSetup.mockResolvedValue(localState); + const user = userEvent.setup(); renderApp(); + await user.click(await screen.findByRole('button', { name: 'Manage targets' })); + await user.click(screen.getByRole('radio', { name: /Set up VidXP for me/i })); + await user.click(screen.getByRole('button', { name: 'Continue' })); + await user.click(screen.getByRole('button', { name: 'Continue to setup' })); + await user.click(await screen.findByRole('button', { name: 'Back' })); + expect(mocks.cancelManagedSetup).toHaveBeenCalledWith('draft-1'); + expect(await screen.findByRole('heading', { name: 'Studio VidXP' })).toBeVisible(); + }); + + it('supports selecting and forgetting saved profiles', async () => { + const saved = { ...localProfile, id: 'local-2', display_name: 'Other VidXP' }; + const state = { profiles: [localProfile, saved], selected_profile_id: localProfile.id, issues: [] }; + mocks.targetSetupState.mockResolvedValue(state); mocks.recheckTargetState.mockResolvedValue(state); + mocks.selectTargetProfile.mockResolvedValue({ ...state, selected_profile_id: saved.id }); + mocks.deleteTargetProfile.mockResolvedValue({ profiles: [saved], selected_profile_id: saved.id, issues: [] }); + const user = userEvent.setup(); renderApp(); + await user.click(await screen.findByRole('button', { name: 'Manage targets' })); + await user.click(screen.getAllByRole('button', { name: 'Select' }).find((button) => !button.hasAttribute('disabled'))!); + expect(mocks.selectTargetProfile).toHaveBeenCalledWith(saved.id); + await user.click(screen.getByRole('button', { name: 'Manage targets' })); + await user.click(screen.getAllByRole('button', { name: 'Forget' })[0]); + expect(mocks.deleteTargetProfile).toHaveBeenCalled(); + }); + + it('offers recheck recovery for an invalid restored target', async () => { + const invalid = { ...localProfile, validation_error: { code: 'probe_timeout', message: 'Timed out.' } }; + const invalidState = { profiles: [invalid], selected_profile_id: invalid.id, issues: [] }; + mocks.targetSetupState.mockResolvedValue(invalidState); mocks.recheckTargetState.mockResolvedValue(invalidState); + const user = userEvent.setup(); renderApp(); + expect(await screen.findByText('Timed out.')).toBeVisible(); + await user.click(screen.getByRole('button', { name: 'Recheck target' })); + expect(mocks.recheckTargetState).toHaveBeenCalledTimes(2); + }); + + it('directs a managed target without browser surface back to managed setup', async () => { + const setup = { profiles: [managedProfile], selected_profile_id: managedProfile.id, issues: [] }; + mocks.targetSetupState.mockResolvedValue(setup); mocks.recheckTargetState.mockResolvedValue(setup); + renderApp(); + expect(await screen.findByText('Unavailable · return to managed setup to enable the browser surface')).toBeVisible(); + expect(screen.getByRole('button', { name: 'Manage setup' })).toBeEnabled(); + }); + + it('keeps ready managed settings read-only until a draft is dirty, then offers Apply and Reset', async () => { + mocks.runtimeStatus.mockResolvedValue({ state: 'ready', ready: true, runtime_profile: 'runtime-a', package_version: '0.4.0', capabilities: ['scene'], surfaces: [], model_directory: 'C:\\Models', detail: 'Ready.' }); + const user = userEvent.setup(); renderApp(); await enterManaged(user); + const apply = await screen.findByRole('button', { name: 'Apply update' }); + expect(apply).toBeDisabled(); + await user.click(screen.getByRole('checkbox', { name: /Browser interface/i })); + expect(apply).toBeEnabled(); + expect(screen.getByText(/installed runtime remains active while Desktop creates/i)).toBeVisible(); + await user.click(screen.getByRole('button', { name: 'Reset changes' })); + expect(apply).toBeDisabled(); + await waitFor(() => expect(mocks.modelDirectoryInventory).toHaveBeenCalledTimes(2)); + }); + + it('preserves a broken managed runtime draft instead of selecting every option', async () => { + mocks.runtimeManifest.mockResolvedValue({ + package_version: '0.4.0', + capabilities: { + actor: { extra: 'actor', label: 'Actor recognition' }, + scene: { extra: 'scene', label: 'Visual scene search' }, + }, + surfaces: { browser: { extra: 'frontend', label: 'Browser interface', description: 'Browser UI', default: true } }, + }); + mocks.runtimeStatus.mockResolvedValue({ + state: 'broken', ready: false, runtime_profile: 'runtime-a', package_version: '0.4.0', + capabilities: ['scene'], surfaces: [], model_directory: 'D:\\CustomModels', detail: 'FFmpeg was not found.', + }); + const user = userEvent.setup(); renderApp(); await enterManaged(user); + expect(screen.getByRole('checkbox', { name: /Visual scene search/i })).toBeChecked(); + expect(screen.getByRole('checkbox', { name: /Actor recognition/i })).not.toBeChecked(); + expect(screen.getByRole('checkbox', { name: /Browser interface/i })).not.toBeChecked(); + expect(screen.getByText('D:\\CustomModels')).toBeVisible(); + expect(screen.getByRole('button', { name: 'Repair VidXP' })).toBeEnabled(); + }); + + it('uses manifest defaults and submits a replacement for a corrupt runtime pointer', async () => { + mocks.runtimeManifest.mockResolvedValue({ + package_version: '0.4.0', + capabilities: { + actor: { extra: 'actor', label: 'Actor recognition' }, + scene: { extra: 'scene', label: 'Visual scene search' }, + }, + surfaces: { browser: { extra: 'frontend', label: 'Browser interface', description: 'Browser UI', default: true } }, + }); + mocks.runtimeStatus.mockResolvedValue({ + state: 'broken', ready: false, runtime_profile: null, package_version: '0.4.0', + capabilities: [], surfaces: [], model_directory: 'C:\\Models', detail: 'The active runtime pointer is invalid.', + }); + const user = userEvent.setup(); renderApp(); await enterManaged(user); + expect(screen.getByRole('checkbox', { name: /Actor recognition/i })).toBeChecked(); + expect(screen.getByRole('checkbox', { name: /Visual scene search/i })).toBeChecked(); + expect(screen.getByRole('checkbox', { name: /Browser interface/i })).toBeChecked(); + expect(screen.getByText(/cannot recover settings from the unreadable pointer/i)).toBeVisible(); + await user.click(screen.getByRole('button', { name: 'Configure replacement' })); + await waitFor(() => expect(mocks.installRuntime).toHaveBeenCalledWith(expect.objectContaining({ + capabilities: expect.arrayContaining(['actor', 'scene']), + surfaces: ['browser'], + draft_id: 'draft-1', + }))); + expect(screen.queryByText('Select at least one capability.')).not.toBeInTheDocument(); + }); + + it('passes the scoped draft through first-time installation and does not auto-open the browser', async () => { + const user = userEvent.setup(); renderApp(); await enterManaged(user); + await user.click(await screen.findByRole('button', { name: 'Configure VidXP' })); + await waitFor(() => expect(mocks.installRuntime).toHaveBeenCalledWith(expect.objectContaining({ draft_id: 'draft-1' }))); + expect(mocks.installMediaRuntime).toHaveBeenCalledWith('draft-1'); + expect(mocks.launchUi).not.toHaveBeenCalled(); + }); + + it('coalesces duplicate managed Continue actions', async () => { + const pending = deferred<{ id: string; previous_profile_id: null }>(); + mocks.beginManagedSetup.mockReturnValue(pending.promise); + const user = userEvent.setup(); renderApp(); + await screen.findByRole('heading', { name: 'Where should VidXP run?' }); + await user.click(screen.getByRole('radio', { name: /Set up VidXP for me/i })); + await user.click(screen.getByRole('button', { name: 'Continue' })); + const continueButton = screen.getByRole('button', { name: 'Continue to setup' }); + await user.dblClick(continueButton); + expect(mocks.beginManagedSetup).toHaveBeenCalledTimes(1); + pending.resolve({ id: 'draft-1', previous_profile_id: null }); + expect(await screen.findByRole('heading', { name: 'Set up local processing' })).toBeVisible(); + }); + + it('freezes managed controls and coalesces Apply while a replacement is running', async () => { + mocks.runtimeStatus.mockResolvedValue({ state: 'ready', ready: true, runtime_profile: 'runtime-a', package_version: '0.4.0', capabilities: ['scene'], surfaces: [], model_directory: 'C:\\Models', detail: 'Ready.' }); + const media = deferred<{ ready: boolean }>(); + mocks.installMediaRuntime.mockReturnValue(media.promise); + const user = userEvent.setup(); renderApp(); await enterManaged(user); + const browser = screen.getByRole('checkbox', { name: /Browser interface/i }); + await user.click(browser); + const apply = screen.getByRole('button', { name: 'Apply update' }); + await user.dblClick(apply); + expect(mocks.installMediaRuntime).toHaveBeenCalledTimes(1); + expect(screen.getByRole('button', { name: 'Back' })).toBeDisabled(); + expect(browser).toBeDisabled(); + expect(screen.getByRole('button', { name: 'Choose folder…' })).toBeDisabled(); + expect(screen.getByRole('button', { name: 'Reset changes' })).toBeDisabled(); + expect(screen.getByRole('button', { name: 'Prepare / verify models' })).toBeDisabled(); + expect(screen.getByRole('button', { name: 'Open VidXP' })).toBeDisabled(); + media.resolve({ ready: true }); + expect(await screen.findByRole('heading', { name: 'Managed VidXP' })).toBeVisible(); + }); + + it('prepares models for an unchanged ready runtime without making the draft dirty', async () => { + const setup = { profiles: [managedProfile], selected_profile_id: managedProfile.id, issues: [] }; + mocks.targetSetupState.mockResolvedValue(setup); + mocks.recheckTargetState.mockResolvedValue(setup); + mocks.runtimeStatus.mockResolvedValue({ state: 'ready', ready: true, runtime_profile: 'runtime-a', package_version: '0.4.0', capabilities: ['scene'], surfaces: [], model_directory: 'C:\\Models', detail: 'Ready.' }); + const user = userEvent.setup(); renderApp(); + await user.click(await screen.findByRole('button', { name: 'Manage setup' })); + await user.click(screen.getByRole('button', { name: 'Continue to setup' })); + await screen.findByRole('heading', { name: 'Set up local processing' }); + expect(screen.getByRole('button', { name: 'Apply update' })).toBeDisabled(); + await user.click(screen.getByRole('button', { name: 'Prepare / verify models' })); + expect(mocks.prepareManagedModels).toHaveBeenCalledWith('draft-1'); + }); + + it('disables managed runtime actions while an external target is selected', async () => { + const setup = { + profiles: [localProfile, managedProfile], + selected_profile_id: localProfile.id, + issues: [], + }; + mocks.targetSetupState.mockResolvedValue(setup); + mocks.recheckTargetState.mockResolvedValue(setup); + mocks.runtimeStatus.mockResolvedValue({ state: 'ready', ready: true, runtime_profile: 'runtime-a', package_version: '0.4.0', capabilities: ['scene'], surfaces: ['browser'], model_directory: 'C:\\Models', detail: 'Ready.' }); + const user = userEvent.setup(); renderApp(); + await user.click(await screen.findByRole('button', { name: 'Manage targets' })); + await enterManaged(user); + expect(screen.getByText(/another target is currently selected/i)).toBeVisible(); + expect(screen.getByRole('button', { name: 'Prepare / verify models' })).toBeDisabled(); + expect(screen.getByRole('button', { name: 'Open VidXP' })).toBeDisabled(); + expect(mocks.prepareManagedModels).not.toHaveBeenCalled(); + expect(mocks.launchUi).not.toHaveBeenCalled(); + }); + + it('uses the committed install state without a fallible status refresh', async () => { + const user = userEvent.setup(); renderApp(); await enterManaged(user); + await user.click(screen.getByRole('button', { name: 'Configure VidXP' })); + expect(await screen.findByRole('heading', { name: 'Managed VidXP' })).toBeVisible(); + expect(mocks.runtimeStatus).toHaveBeenCalledTimes(1); + expect(mocks.targetSetupState).toHaveBeenCalledTimes(1); + }); + + it('coalesces duplicate saved-profile Select actions', async () => { + const saved = { ...localProfile, id: 'local-2', display_name: 'Other VidXP' }; + const state = { profiles: [localProfile, saved], selected_profile_id: localProfile.id, issues: [] }; + const selection = deferred(); + mocks.targetSetupState.mockResolvedValue(state); + mocks.recheckTargetState.mockResolvedValue(state); + mocks.selectTargetProfile.mockReturnValue(selection.promise); + const user = userEvent.setup(); renderApp(); + await user.click(await screen.findByRole('button', { name: 'Manage targets' })); + const select = screen.getAllByRole('button', { name: 'Select' }).find((button) => !button.hasAttribute('disabled'))!; + await user.dblClick(select); + expect(mocks.selectTargetProfile).toHaveBeenCalledTimes(1); + selection.resolve({ ...state, selected_profile_id: saved.id }); + expect(await screen.findByRole('heading', { name: 'Other VidXP' })).toBeVisible(); + }); +}); diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx new file mode 100644 index 0000000..c06d714 --- /dev/null +++ b/desktop/src/App.tsx @@ -0,0 +1,237 @@ +import { Alert, Badge, Button, Group, Loader, Stack, Text, ThemeIcon, Title } from '@mantine/core'; +import { IconAlertCircle, IconArrowLeft, IconDownload, IconTrash } from '@tabler/icons-react'; +import { useCallback, useEffect, useReducer, useRef } from 'react'; + +import { LocalSetup } from './components/LocalSetup'; +import { ManagedSetup } from './components/ManagedSetup'; +import { TargetChoice } from './components/TargetChoice'; +import { TargetSummary } from './components/TargetSummary'; +import { DesktopViewport } from './components/TitleBar'; +import { + beginManagedSetup, + cancelManagedSetup, + confirmForgetTarget, + deleteTargetProfile, + errorMessage, + launchUi, + recheckTargetState, + selectTargetProfile, + selectedProfile, + targetSetupState, + type ManagedSetupDraft, + type TargetKind, + type TargetSetupState, +} from './tauri'; +import { useExclusiveOperation } from './useAsyncAction'; + +type Stage = 'loading' | 'choice' | 'local' | 'managed-confirm' | 'managed' | 'summary'; +type AppOperation = 'startup-check' | 'recheck' | 'begin-managed' | 'cancel-managed' | 'select-profile' | 'forget-profile' | 'open-browser'; + +interface LifecycleState { + stage: Stage; + choice: TargetKind | null; + setup: TargetSetupState | null; + draft: ManagedSetupDraft | null; + failure: string | null; + operation: AppOperation | null; + operationProfile: string | null; +} + +type Action = + | { type: 'navigate'; stage: Stage; choice?: TargetKind | null } + | { type: 'choice'; choice: TargetKind | null } + | { type: 'loaded'; setup: TargetSetupState } + | { type: 'loadFailed'; failure: string } + | { type: 'operationStarted'; operation: AppOperation; profileId?: string } + | { type: 'operationFailed'; failure: string } + | { type: 'operationSettled'; setup?: TargetSetupState; stage?: Stage; draft?: ManagedSetupDraft | null }; + +const initialState: LifecycleState = { + stage: 'loading', choice: null, setup: null, draft: null, failure: null, + operation: null, operationProfile: null, +}; + +function reducer(state: LifecycleState, action: Action): LifecycleState { + switch (action.type) { + case 'navigate': + return { ...state, stage: action.stage, choice: action.choice === undefined ? state.choice : action.choice }; + case 'choice': + return { ...state, choice: action.choice }; + case 'loaded': + return { ...state, setup: action.setup, stage: selectedProfile(action.setup) ? 'summary' : 'choice', failure: null }; + case 'loadFailed': + return { ...state, stage: 'choice', failure: action.failure }; + case 'operationStarted': + return { ...state, operation: action.operation, operationProfile: action.profileId ?? null, failure: null }; + case 'operationFailed': + return { ...state, operation: null, operationProfile: null, failure: action.failure }; + case 'operationSettled': + return { + ...state, + setup: action.setup ?? state.setup, + stage: action.stage ?? state.stage, + draft: action.draft === undefined ? state.draft : action.draft, + operation: null, + operationProfile: null, + failure: null, + }; + } +} + +export function App() { + const [state, dispatch] = useReducer(reducer, initialState); + const operations = useExclusiveOperation(); + const startupLoad = useRef | null>(null); + + const startOperation = useCallback((operation: AppOperation, profileId?: string): number | null => { + const current = operations.begin(operation); + if (current === null) return null; + dispatch({ type: 'operationStarted', operation, profileId }); + return current; + }, [operations]); + + const settleOperation = useCallback((current: number, action: Action) => { + if (!operations.settle(current)) return; + dispatch(action); + }, [operations]); + + const recheck = useCallback(async (operation: 'startup-check' | 'recheck' = 'recheck') => { + const current = startOperation(operation); + if (current === null) return; + try { + const setup = await recheckTargetState(); + settleOperation(current, { type: 'operationSettled', setup }); + } catch (error) { + settleOperation(current, { + type: 'operationFailed', + failure: errorMessage(error, 'The active target could not be checked.'), + }); + } + }, [settleOperation, startOperation]); + + useEffect(() => { + let mounted = true; + const request = startupLoad.current ?? targetSetupState(); + startupLoad.current = request; + void request + .then((setup) => { + if (!mounted) return; + dispatch({ type: 'loaded', setup }); + if (selectedProfile(setup)) void recheck('startup-check'); + }) + .catch((error: unknown) => { + if (!mounted) return; + dispatch({ + type: 'loadFailed', + failure: errorMessage(error, 'VidXP Desktop could not load its target profiles.'), + }); + }); + return () => { + mounted = false; + }; + }, [recheck]); + + async function beginManaged() { + const current = startOperation('begin-managed'); + if (current === null) return; + try { + const draft = await beginManagedSetup(); + settleOperation(current, { type: 'operationSettled', draft, stage: 'managed' }); + } catch (error) { + settleOperation(current, { + type: 'operationFailed', + failure: errorMessage(error, 'Managed setup could not be started.'), + }); + } + } + + async function cancelManaged() { + if (!state.draft) return; + const current = startOperation('cancel-managed'); + if (current === null) return; + try { + const setup = await cancelManagedSetup(state.draft.id); + settleOperation(current, { + type: 'operationSettled', setup, draft: null, + stage: selectedProfile(setup) ? 'summary' : 'choice', + }); + } catch (error) { + settleOperation(current, { + type: 'operationFailed', + failure: errorMessage(error, 'Managed setup could not be cancelled.'), + }); + } + } + + async function selectSaved(id: string) { + const current = startOperation('select-profile', id); + if (current === null) return; + try { + const setup = await selectTargetProfile(id); + settleOperation(current, { type: 'operationSettled', setup, stage: 'summary' }); + } catch (error) { + settleOperation(current, { + type: 'operationFailed', + failure: errorMessage(error, 'The saved target could not be selected.'), + }); + } + } + + async function forgetSaved(id: string, name: string) { + if (!await confirmForgetTarget(name)) return; + const current = startOperation('forget-profile', id); + if (current === null) return; + try { + const setup = await deleteTargetProfile(id); + settleOperation(current, { type: 'operationSettled', setup }); + } catch (error) { + settleOperation(current, { + type: 'operationFailed', + failure: errorMessage(error, 'The saved target could not be forgotten.'), + }); + } + } + + async function openBrowser() { + const current = startOperation('open-browser'); + if (current === null) return; + try { + await launchUi(); + settleOperation(current, { type: 'operationSettled' }); + } catch (error) { + settleOperation(current, { + type: 'operationFailed', + failure: errorMessage(error, 'VidXP could not be opened.'), + }); + } + } + + const profile = state.setup ? selectedProfile(state.setup) : null; + const operationPending = state.operation !== null; + return ( + +