diff --git a/.gitattributes b/.gitattributes
index 905a8ead7..10cf9919c 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,4 @@
src/InfiniFrame.Native/Dependencies/* linguist-vendored
-src/InfiniFrame.Native/Dependencies/**/* linguist-vendored
\ No newline at end of file
+src/InfiniFrame.Native/Dependencies/**/* linguist-vendored
+
+*.sh text eol=lf
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 49bbe5474..56c3e2bb5 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -20,3 +20,7 @@ updates:
directory: "/"
schedule:
interval: "weekly"
+ commit-message:
+ prefix: "Chore(deps): "
+ include: "scope"
+
diff --git a/.github/scripts/bump_version.py b/.github/scripts/bump_version.py
index 0dc70227e..526ae5b27 100644
--- a/.github/scripts/bump_version.py
+++ b/.github/scripts/bump_version.py
@@ -6,6 +6,7 @@
import xml.etree.ElementTree as Et
from pathlib import Path
from typing import Final, Literal, Never
+import json
# Resolve paths from the repository root: .github/scripts -> repo root is three levels up.
REPO_ROOT: Final[Path] = Path(__file__).parent.parent.parent
@@ -14,7 +15,6 @@
VERSION_PATTERN: Final[re.Pattern[str]] = re.compile(r"^\d+\.\d+\.\d+(-preview\.\d+)?$")
BumpPart = Literal["major", "minor", "patch", "preview"]
-
def fail(message: str) -> Never:
print(message)
raise SystemExit(1)
@@ -82,6 +82,60 @@ def update_cmake_version(cmake_path: Path, new_version: str) -> None:
fail("Error: Could not find InfiniFrame.Native version in CMakeLists.txt")
cmake_path.write_text(updated, encoding="utf-8")
+def update_package_json_version(pkg_path: Path, new_version: str) -> None:
+ """
+ Updates:
+ - package.json "version"
+ - optionally replaces version placeholders inside scripts
+ """
+ data = json.loads(pkg_path.read_text(encoding="utf-8"))
+
+ # Update top-level version
+ if "version" in data:
+ data["version"] = new_version
+
+ # Optional: update version strings inside scripts
+ scripts = data.get("scripts", {})
+ for key, value in scripts.items():
+ if isinstance(value, str):
+ scripts[key] = _replace_version_in_string(value, new_version)
+
+ data["scripts"] = scripts
+
+ pkg_path.write_text(
+ json.dumps(data, indent=2, ensure_ascii=False) + "\n",
+ encoding="utf-8",
+ )
+
+def _replace_version_in_string(text: str, new_version: str) -> str:
+ """
+ Replace common version patterns inside arbitrary strings.
+ Extend this as needed per repo conventions.
+ """
+ # Example patterns you might have in scripts:
+ # - --version 1.2.3
+ # - v1.2.3
+ # - 1.2.3-preview.4
+
+ text = re.sub(
+ r"\d+\.\d+\.\d+(?:-preview\.\d+)?",
+ new_version,
+ text,
+ )
+ return text
+
+def update_all_package_json_files(repo_root: Path, new_version: str) -> None:
+ """
+ Recursively updates all package.json files in the repository.
+ """
+ for pkg_path in repo_root.rglob("package.json"):
+ # skip certain folders just in case
+ if "node_modules" in pkg_path.parts:
+ continue
+ if "InfiniFrame.Native" in pkg_path.parts:
+ continue
+
+ update_package_json_version(pkg_path, new_version)
def main() -> int:
if len(sys.argv) < 2:
@@ -122,6 +176,7 @@ def main() -> int:
version_elem.text = new_version
tree.write(FILE, encoding="utf-8", xml_declaration=True)
update_cmake_version(CMAKE_FILE, new_version)
+ update_all_package_json_files(REPO_ROOT, new_version)
print(f"Bumped version: {old_version} -> {new_version}")
print(new_version) # Output for GitHub Actions to capture
diff --git a/.github/scripts/test_bump_version.py b/.github/scripts/test_bump_version.py
index bd3317721..d60098b58 100644
--- a/.github/scripts/test_bump_version.py
+++ b/.github/scripts/test_bump_version.py
@@ -1,7 +1,9 @@
#!/usr/bin/env python3
from __future__ import annotations
+import json
import sys
+import tempfile
from pathlib import Path
import pytest
@@ -10,7 +12,12 @@
if str(SCRIPT_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPT_DIR))
-from bump_version import bump, validate_version
+from bump_version import (
+ bump,
+ validate_version,
+ update_package_json_version,
+ _replace_version_in_string,
+)
@pytest.mark.parametrize(
@@ -49,3 +56,37 @@ def test_bump_unknown_part_raises_value_error() -> None:
with pytest.raises(ValueError):
# noinspection PyTypeChecker
bump("1.2.3", "banana")
+
+def test_replace_version_in_string() -> None:
+ assert _replace_version_in_string(
+ "build --version 1.2.3",
+ "9.8.7",
+ ) == "build --version 9.8.7"
+
+ assert _replace_version_in_string(
+ "tool v1.2.3-preview.4 run",
+ "0.0.1",
+ ) == "tool v0.0.1 run"
+
+def test_update_package_json_version_updates_version_and_scripts() -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ pkg_path = Path(tmp) / "package.json"
+
+ original = {
+ "name": "test",
+ "version": "1.0.0",
+ "scripts": {
+ "build": "echo 1.0.0",
+ "deploy": "echo deploying v1.0.0-preview.3",
+ },
+ }
+
+ pkg_path.write_text(json.dumps(original), encoding="utf-8")
+
+ update_package_json_version(pkg_path, "2.3.4")
+
+ updated = json.loads(pkg_path.read_text(encoding="utf-8"))
+
+ assert updated["version"] == "2.3.4"
+ assert updated["scripts"]["build"] == "echo 2.3.4"
+ assert updated["scripts"]["deploy"] == "echo deploying v2.3.4"
diff --git a/.github/scripts/update_native_vendor_deps.py b/.github/scripts/update_native_vendor_deps.py
index 113a55ccd..471733ee1 100644
--- a/.github/scripts/update_native_vendor_deps.py
+++ b/.github/scripts/update_native_vendor_deps.py
@@ -5,6 +5,7 @@
import json
import os
import urllib.request
+import urllib.error
from pathlib import Path
from typing import Any
@@ -39,27 +40,31 @@ def download_file(url: str, destination: Path, token: str) -> None:
with urllib.request.urlopen(request, timeout=120) as response:
destination.write_bytes(response.read())
-
-def get_latest_release(repo: str, token: str) -> tuple[str, dict[str, str]]:
+def get_latest_release(repo: str, token: str) -> tuple[str | None, dict[str, str]]:
url = f"https://api.github.com/repos/{repo}/releases/latest"
- payload = request_json(url, token)
+ try:
+ payload = request_json(url, token)
+ except urllib.error.HTTPError as e:
+ if e.code == 404:
+ # No releases → fallback mode
+ return None, {}
+ raise
tag = payload.get("tag_name")
if not isinstance(tag, str) or not tag:
- raise RuntimeError(f"Missing tag_name for {repo}")
+ return None, {}
assets_payload = payload.get("assets", [])
- if not isinstance(assets_payload, list):
- raise RuntimeError(f"Unexpected assets payload for {repo}")
-
assets: dict[str, str] = {}
- for asset in assets_payload:
- if not isinstance(asset, dict):
- continue
- name = asset.get("name")
- download_url = asset.get("browser_download_url")
- if isinstance(name, str) and isinstance(download_url, str):
- assets[name] = download_url
+
+ if isinstance(assets_payload, list):
+ for asset in assets_payload:
+ if not isinstance(asset, dict):
+ continue
+ name = asset.get("name")
+ download_url = asset.get("browser_download_url")
+ if isinstance(name, str) and isinstance(download_url, str):
+ assets[name] = download_url
return tag, assets
@@ -92,6 +97,10 @@ def update_manifest(manifest_path: Path, check_only: bool) -> int:
latest_tag, latest_assets = get_latest_release(repo, token)
+ # If no releases, stick to manifest tag (or empty)
+ if latest_tag is None:
+ latest_tag = current_tag or ""
+
has_missing_files = False
for entries in (assets, source_files, licenses):
for asset_entry in entries:
@@ -104,7 +113,8 @@ def update_manifest(manifest_path: Path, check_only: bool) -> int:
if has_missing_files:
break
- needs_update = latest_tag != current_tag
+ has_release = latest_tag is not None and latest_tag != ""
+ needs_update = has_release and latest_tag != current_tag
needs_download = needs_update or has_missing_files
if not needs_download:
@@ -117,22 +127,23 @@ def update_manifest(manifest_path: Path, check_only: bool) -> int:
if check_only:
continue
- for asset_entry in assets:
- if not isinstance(asset_entry, dict):
- raise RuntimeError(f"Library {name} has invalid asset entry")
-
- asset_name = asset_entry.get("asset")
- destination = asset_entry.get("destination")
- if not isinstance(asset_name, str) or not isinstance(destination, str):
- raise RuntimeError(f"Library {name} has invalid asset definition")
-
- download_url = latest_assets.get(asset_name)
- if not download_url:
- raise RuntimeError(f"Asset '{asset_name}' not found in latest release for {repo}")
-
- destination_path = REPO_ROOT / destination
- print(f" downloading {asset_name} -> {destination}")
- download_file(download_url, destination_path, token)
+ if latest_assets:
+ for asset_entry in assets:
+ if not isinstance(asset_entry, dict):
+ raise RuntimeError(f"Library {name} has invalid asset entry")
+
+ asset_name = asset_entry.get("asset")
+ destination = asset_entry.get("destination")
+ if not isinstance(asset_name, str) or not isinstance(destination, str):
+ raise RuntimeError(f"Library {name} has invalid asset definition")
+
+ download_url = latest_assets.get(asset_name)
+ if not download_url:
+ raise RuntimeError(f"Asset '{asset_name}' not found in latest release for {repo}")
+
+ destination_path = REPO_ROOT / destination
+ print(f" downloading {asset_name} -> {destination}")
+ download_file(download_url, destination_path, token)
for source_entry in source_files:
if not isinstance(source_entry, dict):
diff --git a/.github/workflows/shared-testing-windows-playwright.yml b/.github/workflows/shared-testing-windows-playwright.yml
index a816e964c..cbcb96fd7 100644
--- a/.github/workflows/shared-testing-windows-playwright.yml
+++ b/.github/workflows/shared-testing-windows-playwright.yml
@@ -1,4 +1,5 @@
name: "Shared: Windows Playwright Tests"
+
on:
workflow_call:
inputs:
@@ -16,24 +17,74 @@ on:
type: string
required: true
+permissions:
+ contents: read
+ checks: write
+ statuses: write
+
jobs:
+ windows-playwright-pending:
+ name: CI Testing - Windows Playwright (Pending)
+ runs-on: windows-latest
+ timeout-minutes: 10
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+ with:
+ ref: ${{ inputs.checkout_ref != '' && inputs.checkout_ref || (inputs.pr_number != '' && format('refs/pull/{0}/merge', inputs.pr_number) || github.ref) }}
+ fetch-depth: 1
+
+ - name: Set Playwright Check Pending
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ # noinspection UndefinedAction
+ uses: ./.github/actions/sync-check
+ # noinspection UndefinedParamsPresent
+ with:
+ repo: ${{ github.repository }}
+ sha: ${{ inputs.target_sha }}
+ context: CI Testing - Windows Playwright
+ state: pending
+ status-description: Windows Playwright tests in progress
+ target-url: ${{ inputs.workflow_url }}
+ allow-status-422: 'true'
+
windows-playwright:
- name: CI Testing - Windows Playwright
+ name: CI Testing - Windows Playwright ${{ matrix.project.name }}
runs-on: windows-latest
timeout-minutes: 75
+
+ needs: windows-playwright-pending
+
+ strategy:
+ fail-fast: false
+ matrix:
+ project:
+ - name: WebApp Vue
+ path: InfiniFrameTests.Playwright.WebApp.Vue
+ sourceFolder: tests/InfiniFrameTests.Playwright.WebApp.Vue/Sources/infiniframe-playwright-vue
+ - name: WebApp React
+ path: InfiniFrameTests.Playwright.WebApp.React
+ sourceFolder: tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react
+ - name: Blazor MudBlazor
+ path: InfiniFrameTests.Playwright.BlazorWebView.MudBlazor
+ sourceFolder: ""
+
env:
TARGET_SHA: ${{ inputs.target_sha }}
WORKFLOW_URL: ${{ inputs.workflow_url }}
+
permissions:
contents: read
statuses: write
checks: write
-
+
steps:
- name: Checkout
uses: actions/checkout@v6
with:
- ref: ${{ inputs.checkout_ref != '' && inputs.checkout_ref || (inputs.pr_number != '' && inputs.pr_number != null && format('refs/pull/{0}/merge', inputs.pr_number) || github.ref) }}
+ ref: ${{ inputs.checkout_ref != '' && inputs.checkout_ref || (inputs.pr_number != '' && format('refs/pull/{0}/merge', inputs.pr_number) || github.ref) }}
fetch-depth: 1
- name: Set Playwright Check Pending
@@ -45,9 +96,9 @@ jobs:
with:
repo: ${{ github.repository }}
sha: ${{ inputs.target_sha }}
- context: CI Testing - Windows Playwright
+ context: CI Testing - Windows Playwright ${{ matrix.project.name }}
state: pending
- status-description: Windows Playwright tests in progress (net8.0, net9.0, net10.0)
+ status-description: Windows Playwright tests in progress
target-url: ${{ inputs.workflow_url }}
allow-status-422: 'true'
@@ -59,33 +110,31 @@ jobs:
9.x
10.x
- - name: Cache NuGet
- uses: actions/cache@v5
- with:
- path: ~\.nuget\packages
- key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/Directory.Packages.props') }}
- restore-keys: |
- ${{ runner.os }}-nuget-
-
- name: Setup Native dependencies
# noinspection UndefinedAction
uses: ./.github/actions/setup-dependencies-native
# noinspection UndefinedParamsPresent
with:
apt-cache-version: 1.0
- brew-cache-key: ${{ matrix.os }}-${{ matrix.arch }}-brew-native-${{ hashFiles('.github/actions/setup-dependencies-native/action.yml', '.github/workflows/shared-testing-windows-playwright.yml') }}
- brew-restore-key: ${{ matrix.os }}-${{ matrix.arch }}-brew-native-
+ brew-cache-key: ${{ runner.os }}-brew-native-${{ hashFiles('.github/actions/setup-dependencies-native/action.yml') }}
+ brew-restore-key: ${{ runner.os }}-brew-native-
+
+ - name: Setup Node
+ uses: actions/setup-node@v6
+ with:
+ node-version: 20
+ cache: 'npm'
+ cache-dependency-path: '**/package-lock.json'
- name: Prepare WebView2 Cache Directory
shell: pwsh
run: New-Item -ItemType Directory -Path ".cache\webview2" -Force | Out-Null
- name: Cache WebView2
- id: cache-webview2
uses: actions/cache@v5
with:
path: .cache\webview2
- key: webview2-installer-${{ runner.os }}
+ key: webview2-installer-${{ runner.os }}-v1
- name: Install WebView2 Runtime
shell: pwsh
@@ -95,66 +144,79 @@ jobs:
Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile $installer
}
Start-Process -FilePath $installer -ArgumentList "/silent", "/install" -Wait
+
+ - name: NPM Install
+ if: ${{ matrix.project.sourceFolder != '' }}
+ shell: pwsh
+ run: |
+ $dir = "${{ matrix.project.sourceFolder }}"
+
+ if ([string]::IsNullOrWhiteSpace($dir)) {
+ throw "sourceFolder is empty for ${{ matrix.project.name }}"
+ }
+
+ Write-Host "Installing npm dependencies in $dir"
+
+ if (!(Test-Path $dir)) {
+ throw "Directory not found: $dir"
+ }
+
+ Push-Location $dir
+ npm ci
+ Pop-Location
- name: Restore
- run: dotnet restore InfiniFrame.GitHubActions.Testing.Playwright.slnf /p:NoWarn=NU1503
+ run: |
+ dotnet restore InfiniFrame.GitHubActions.Testing.Playwright.slnf `
+ /p:NoWarn=NU1503
- - name: Build Release
+ - name: Build Native
run: |
dotnet build src/InfiniFrame.Native/InfiniFrame.Native.proj `
--configuration Release `
--no-restore `
/p:SolutionDir=$GITHUB_WORKSPACE/
- dotnet build InfiniFrame.GitHubActions.Testing.Playwright.slnf `
+ - name: Build Tests
+ run: |
+ dotnet build tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj `
--configuration Release `
--no-restore `
+ --framework net8.0 `
+ /p:SolutionDir=$GITHUB_WORKSPACE/
+
+ dotnet build tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj `
+ --configuration Release `
+ --no-restore `
+ --framework net9.0 `
+ /p:SolutionDir=$GITHUB_WORKSPACE/
+
+ dotnet build tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj `
+ --configuration Release `
+ --no-restore `
+ --framework net10.0 `
/p:SolutionDir=$GITHUB_WORKSPACE/
-
- - name: Cache Playwright Browsers
- id: cache-playwright
- uses: actions/cache@v5
- with:
- path: ~\AppData\Local\ms-playwright
- key: playwright-${{ runner.os }}-${{ hashFiles('tests/InfiniFrameTests.Playwright.WebApp.React/InfiniFrameTests.Playwright.WebApp.React.csproj', 'tests/InfiniFrameTests.Playwright.WebApp.Vue/InfiniFrameTests.Playwright.WebApp.Vue.csproj', 'tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor.csproj') }}
- restore-keys: |
- playwright-${{ runner.os }}-
- name: Install Playwright Browsers
- if: steps.cache-playwright.outputs.cache-hit != 'true'
- working-directory: tests/InfiniFrameTests.Playwright.WebApp.Vue
shell: pwsh
run: |
- $scripts = @(
- "bin/Release/net10.0/playwright.ps1",
- "bin/Release/net9.0/playwright.ps1",
- "bin/Release/net8.0/playwright.ps1"
- )
- $script = $scripts | Where-Object { Test-Path $_ } | Select-Object -First 1
- if (-Not $script) {
- throw "Playwright install script not found in bin/Release/(net8.0|net9.0|net10.0)"
+ $script = "tests/${{ matrix.project.path }}/bin/Release/net10.0/playwright.ps1"
+
+ if (-not (Test-Path $script)) {
+ throw "Playwright script not found: $script"
}
-
+
pwsh $script install
- name: Verify Native Binaries
shell: pwsh
run: |
- $frameworks = @("net8.0", "net9.0", "net10.0")
- $projects = @(
- "tests/InfiniFrameTests.Playwright.WebApp.React",
- "tests/InfiniFrameTests.Playwright.WebApp.Vue",
- "tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor"
- )
- foreach ($framework in $frameworks) {
- foreach ($project in $projects) {
- $binPath = "$project/bin/Release/$framework"
- foreach ($dll in @("InfiniFrame.Native.dll", "WebView2Loader.dll")) {
- $fullPath = Join-Path $binPath $dll
- if (-Not (Test-Path $fullPath)) {
- throw "$dll missing in $binPath"
- }
- }
+ $binPath = "tests/${{ matrix.project.path }}/bin/Release/net10.0"
+
+ foreach ($dll in @("InfiniFrame.Native.dll", "WebView2Loader.dll")) {
+ $fullPath = Join-Path $binPath $dll
+ if (-Not (Test-Path $fullPath)) {
+ throw "$dll missing in $binPath"
}
}
@@ -165,73 +227,78 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env['ACTIONS_RUNTIME_TOKEN']);
core.exportVariable('ACTIONS_RESULTS_URL', process.env['ACTIONS_RESULTS_URL']);
- - name: Run Playwright tests (all frameworks/projects)
- id: playwright_all
- continue-on-error: true
+ - name: Run Playwright tests NET 8.0
shell: pwsh
run: |
- $frameworks = @(
- "net8.0",
- "net9.0",
- "net10.0"
- )
-
- $projects = @(
- "tests/InfiniFrameTests.Playwright.WebApp.Vue",
- "tests/InfiniFrameTests.Playwright.WebApp.React",
- "tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor"
- )
-
- $failed = @()
-
- foreach ($framework in $frameworks) {
- foreach ($project in $projects) {
- Write-Host ""
- Write-Host "▶ Running $project ($framework)"
-
- dotnet test --project $project `
- --configuration Release `
- --no-build `
- --no-restore `
- --framework $framework
-
- if ($LASTEXITCODE -ne 0) {
- Write-Host "❌ Failed: $project ($framework)"
- $failed += "$project ($framework)"
- } else {
- Write-Host "✅ Passed: $project ($framework)"
- }
- }
- }
-
- if ($failed.Count -gt 0) {
- $msg = "Windows Playwright tests failed: $($failed -join ', ')"
- "failure_message=$msg" >> $env:GITHUB_OUTPUT
- "success_message=" >> $env:GITHUB_OUTPUT
-
- Write-Host "❌ $msg"
- exit 1
- }
-
- $msg = "Windows Playwright tests passed (all frameworks/projects)"
- "success_message=$msg" >> $env:GITHUB_OUTPUT
- "failure_message=" >> $env:GITHUB_OUTPUT
-
- Write-Host "✅ All tests passed"
-
+ dotnet test `
+ --project tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj `
+ --configuration Release `
+ --no-build `
+ --no-restore `
+ --framework net8.0
+
+ - name: Run Playwright tests NET 9.0
+ shell: pwsh
+ run: |
+ dotnet test `
+ --project tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj `
+ --configuration Release `
+ --no-build `
+ --no-restore `
+ --framework net9.0
+
+ - name: Run Playwright tests NET 10.0
+ shell: pwsh
+ run: |
+ dotnet test `
+ --project tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj `
+ --configuration Release `
+ --no-build `
+ --no-restore `
+ --framework net10.0
- name: Update Playwright Check
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- # noinspection UndefinedAction, UndefinedParamsPresent
+ # noinspection UndefinedAction
uses: ./.github/actions/sync-check-finalize
# noinspection UndefinedParamsPresent
with:
repo: ${{ github.repository }}
sha: ${{ inputs.target_sha }}
- context: CI Testing - Windows Playwright
+ context: CI Testing - Windows Playwright ${{ matrix.project.name }}
target-url: ${{ inputs.workflow_url }}
job-status: ${{ job.status }}
- success-description: ${{ steps.playwright_all.outputs.success_message }}
- failure-description: ${{ steps.playwright_all.outputs.failure_message }}
+ success-description: Windows Playwright tests passed
+ failure-description: Windows Playwright tests failed
+
+ windows-playwright-finalize:
+ name: CI Testing - Windows Playwright (Finalize)
+ runs-on: windows-latest
+ timeout-minutes: 10
+
+ needs: windows-playwright
+ if: always()
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+ with:
+ ref: ${{ inputs.checkout_ref != '' && inputs.checkout_ref || (inputs.pr_number != '' && format('refs/pull/{0}/merge', inputs.pr_number) || github.ref) }}
+ fetch-depth: 1
+
+ - name: Set Playwright Check Final Status
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ # noinspection UndefinedAction
+ uses: ./.github/actions/sync-check-finalize
+ # noinspection UndefinedParamsPresent
+ with:
+ repo: ${{ github.repository }}
+ sha: ${{ inputs.target_sha }}
+ context: CI Testing - Windows Playwright
+ target-url: ${{ inputs.workflow_url }}
+ job-status: ${{ needs.windows-playwright.result == 'success' && 'success' || 'failure' }}
+ success-description: Windows Playwright tests passed
+ failure-description: Windows Playwright tests failed
diff --git a/.gitignore b/.gitignore
index fe2ddda26..041f3c111 100644
--- a/.gitignore
+++ b/.gitignore
@@ -348,6 +348,7 @@ healthchecksdb
/src/InfiniFrame.Native/cmake-build-debug/
/src/InfiniFrame.Native/cmake-build-debug-linux/
/src/InfiniFrame.Native/cmake-build-debug-windows/
+/src/InfiniFrame.Native/Embedded/InfiniFrameHostJs/InfiniFrameHostJs.cpp
# wwwroot folders from js web based projects
/examples/InfiniFrameExample.WebApp.React/wwwroot/
diff --git a/.run/docker-compose-linux.sh.run.xml b/.run/docker-compose-linux.sh.run.xml
index 51e1c02f0..2cc12f377 100644
--- a/.run/docker-compose-linux.sh.run.xml
+++ b/.run/docker-compose-linux.sh.run.xml
@@ -1,6 +1,6 @@
-
+
@@ -14,4 +14,4 @@
-
+
\ No newline at end of file
diff --git a/.run/docker-linux-run-blazorwebview.sh.run.xml b/.run/docker-linux-run-blazorwebview.sh.run.xml
new file mode 100644
index 000000000..4749efb00
--- /dev/null
+++ b/.run/docker-linux-run-blazorwebview.sh.run.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/docker-run-linux-playwright.sh.run.xml b/.run/docker-linux-run-playwright.sh.run.xml
similarity index 78%
rename from .run/docker-run-linux-playwright.sh.run.xml
rename to .run/docker-linux-run-playwright.sh.run.xml
index 2e99f6b49..759f838ef 100644
--- a/.run/docker-run-linux-playwright.sh.run.xml
+++ b/.run/docker-linux-run-playwright.sh.run.xml
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/.run/docker-run-linux-tests.sh.run.xml b/.run/docker-linux-run-tests.sh.run.xml
similarity index 86%
rename from .run/docker-run-linux-tests.sh.run.xml
rename to .run/docker-linux-run-tests.sh.run.xml
index 39fc7efa3..a441b66e2 100644
--- a/.run/docker-run-linux-tests.sh.run.xml
+++ b/.run/docker-linux-run-tests.sh.run.xml
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/.run/example-blazorwebview.sh.run.xml b/.run/example-blazorwebview.sh.run.xml
new file mode 100644
index 000000000..b7fc988db
--- /dev/null
+++ b/.run/example-blazorwebview.sh.run.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 96412bc14..4a3f7fa3d 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -36,9 +36,9 @@
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/InfiniFrame.GitHubActions.Testing.Playwright.slnf b/InfiniFrame.GitHubActions.Testing.Playwright.slnf
index 351f6516d..a764320c8 100644
--- a/InfiniFrame.GitHubActions.Testing.Playwright.slnf
+++ b/InfiniFrame.GitHubActions.Testing.Playwright.slnf
@@ -7,7 +7,6 @@
"src\\InfiniFrame.BlazorWebView\\InfiniFrame.BlazorWebView.csproj",
"src\\InfiniFrame.Js\\InfiniFrame.Js.csproj",
"src\\InfiniFrame.Shared\\InfiniFrame.Shared.csproj",
- "src\\InfiniFrame.Tools.Pack\\InfiniFrame.Tools.Pack.csproj",
"src\\InfiniFrame.WebServer\\InfiniFrame.WebServer.csproj",
"tests\\InfiniFrameTests.Shared\\InfiniFrameTests.Shared.csproj",
diff --git a/InfiniFrame.slnx b/InfiniFrame.slnx
index b9426b218..33b6660a8 100644
--- a/InfiniFrame.slnx
+++ b/InfiniFrame.slnx
@@ -14,8 +14,9 @@
-
-
+
+
+
diff --git a/docker/compose/infiniframe-linux.yml b/docker/compose/infiniframe-linux.yml
new file mode 100644
index 000000000..a97c08d4a
--- /dev/null
+++ b/docker/compose/infiniframe-linux.yml
@@ -0,0 +1,78 @@
+name: infiniframe
+
+services:
+ linux-tests:
+ image: infiniframe/linux:latest
+ build:
+ context: ../..
+ dockerfile: docker/infiniframe-linux/Dockerfile
+ working_dir: /work
+ volumes:
+ - ../../:/src:ro
+ - infiniframe_linux_tests_work:/work
+ environment:
+ - SOLUTION_FILTER=InfiniFrame.GitHubActions.Testing.slnf
+ - CONFIGURATION=Release
+ - NATIVE_PLATFORM=x64
+ - USE_HOST_DISPLAY=${USE_HOST_DISPLAY:-0}
+ - DISPLAY=${DISPLAY:-}
+ - NUGET_PACKAGES=/root/.nuget/packages
+ - NUGET_FALLBACK_PACKAGES=
+ entrypoint: ["/bin/bash", "-lc"]
+ command:
+ - "bash /src/docker/infiniframe-linux/bootstrap-workspace.sh /src /work && bash /work/scripts/nuget-install.sh && exec bash /work/docker/infiniframe-linux/tests.sh"
+ stdin_open: true
+ tty: true
+
+ linux-tests-playwright:
+ image: infiniframe/linux:latest
+ build:
+ context: ../..
+ dockerfile: docker/infiniframe-linux/Dockerfile
+ working_dir: /work
+ volumes:
+ - ../../:/src:ro
+ - infiniframe_linux_playwright_work:/work
+ environment:
+ - SOLUTION_FILTER=InfiniFrame.GitHubActions.Testing.Playwright.slnf
+ - CONFIGURATION=Release
+ - NATIVE_PLATFORM=x64
+ - FRAMEWORKS=net8.0 net9.0 net10.0
+ - USE_HOST_DISPLAY=${USE_HOST_DISPLAY:-0}
+ - DISPLAY=${DISPLAY:-}
+ - NUGET_PACKAGES=/root/.nuget/packages
+ - NUGET_FALLBACK_PACKAGES=
+ entrypoint: ["/bin/bash", "-lc"]
+ command:
+ - "bash /src/docker/infiniframe-linux/bootstrap-workspace.sh /src /work && bash /work/scripts/nuget-install.sh && exec bash /work/docker/infiniframe-linux/playwright.sh"
+ stdin_open: true
+ tty: true
+
+ linux-example-blazorwebview:
+ image: infiniframe/linux:latest
+ build:
+ context: ../..
+ dockerfile: docker/infiniframe-linux/Dockerfile
+ working_dir: /work
+ volumes:
+ - ../../:/src:ro
+ - infiniframe_linux_examples_work:/work
+ environment:
+ - SOLUTION_FILTER=InfiniFrame.slnx
+ - CONFIGURATION=Release
+ - NATIVE_PLATFORM=x64
+ - FRAMEWORKS=net8.0 net9.0 net10.0
+ - USE_HOST_DISPLAY=${USE_HOST_DISPLAY:-0}
+ - DISPLAY=${DISPLAY:-}
+ - NUGET_PACKAGES=/root/.nuget/packages
+ - NUGET_FALLBACK_PACKAGES=
+ entrypoint: ["/bin/bash", "-lc"]
+ command:
+ - "bash /src/docker/infiniframe-linux/bootstrap-workspace.sh /src /work && bash /work/scripts/nuget-install.sh && exec bash /work/docker/infiniframe-linux/example-blazorwebview.sh"
+ stdin_open: true
+ tty: true
+
+volumes:
+ infiniframe_linux_tests_work:
+ infiniframe_linux_playwright_work:
+ infiniframe_linux_examples_work:
\ No newline at end of file
diff --git a/docker/compose/linux-tests.yml b/docker/compose/linux-tests.yml
deleted file mode 100644
index 74c1f9f11..000000000
--- a/docker/compose/linux-tests.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-name: infiniframe
-
-services:
- linux-tests:
- image: infiniframe/testing-linux:latest
- build:
- context: ../..
- dockerfile: docker/linux-tests/Dockerfile
- working_dir: /work
- volumes:
- - ../../:/src:ro
- - infiniframe_linux_work:/work
- environment:
- - SOLUTION_FILTER=InfiniFrame.GitHubActions.Testing.slnf
- - CONFIGURATION=Release
- - NATIVE_PLATFORM=x64
- - USE_HOST_DISPLAY=${USE_HOST_DISPLAY:-0}
- - DISPLAY=${DISPLAY:-}
- - NUGET_PACKAGES=/root/.nuget/packages
- - NUGET_FALLBACK_PACKAGES=
- entrypoint: ["/bin/bash", "-lc"]
- command:
- - "bash /src/docker/linux-tests/bootstrap-workspace.sh /src /work && bash /work/scripts/nuget-install.sh && exec bash /work/docker/linux-tests/tests.sh"
- stdin_open: true
- tty: true
- linux-playwright-tests:
- image: infiniframe/testing-linux:latest
- build:
- context: ../..
- dockerfile: docker/linux-tests/Dockerfile
- working_dir: /work
- volumes:
- - ../../:/src:ro
- - infiniframe_linux_work:/work
- environment:
- - SOLUTION_FILTER=InfiniFrame.GitHubActions.Testing.Playwright.slnf
- - CONFIGURATION=Release
- - NATIVE_PLATFORM=x64
- - FRAMEWORKS=net8.0 net9.0 net10.0
- - USE_HOST_DISPLAY=${USE_HOST_DISPLAY:-0}
- - DISPLAY=${DISPLAY:-}
- - NUGET_PACKAGES=/root/.nuget/packages
- - NUGET_FALLBACK_PACKAGES=
- entrypoint: ["/bin/bash", "-lc"]
- command:
- - "bash /src/docker/linux-tests/bootstrap-workspace.sh /src /work && bash /work/scripts/nuget-install.sh && exec bash /work/docker/linux-tests/playwright.sh"
- stdin_open: true
- tty: true
-
-volumes:
- infiniframe_linux_work:
diff --git a/docker/linux-tests/Dockerfile b/docker/infiniframe-linux/Dockerfile
similarity index 100%
rename from docker/linux-tests/Dockerfile
rename to docker/infiniframe-linux/Dockerfile
diff --git a/docker/linux-tests/NuGet.Config b/docker/infiniframe-linux/NuGet.Config
similarity index 100%
rename from docker/linux-tests/NuGet.Config
rename to docker/infiniframe-linux/NuGet.Config
diff --git a/docker/linux-tests/bootstrap-workspace.sh b/docker/infiniframe-linux/bootstrap-workspace.sh
similarity index 100%
rename from docker/linux-tests/bootstrap-workspace.sh
rename to docker/infiniframe-linux/bootstrap-workspace.sh
diff --git a/docker/linux-tests/common.sh b/docker/infiniframe-linux/common.sh
similarity index 97%
rename from docker/linux-tests/common.sh
rename to docker/infiniframe-linux/common.sh
index 6bf4ece01..e3c95de38 100644
--- a/docker/linux-tests/common.sh
+++ b/docker/infiniframe-linux/common.sh
@@ -6,7 +6,7 @@ init_common_defaults() {
NATIVE_PLATFORM="${NATIVE_PLATFORM:-x64}"
USE_HOST_DISPLAY="${USE_HOST_DISPLAY:-0}"
CMAKE_BUILD_DIR="${CMAKE_BUILD_DIR:-/tmp/infiniframe-cmake/${NATIVE_PLATFORM}/${CONFIGURATION}}"
- NUGET_CONFIG_FILE="${NUGET_CONFIG_FILE:-/work/docker/linux-tests/NuGet.Config}"
+ NUGET_CONFIG_FILE="${NUGET_CONFIG_FILE:-/work/docker/infiniframe-linux/NuGet.Config}"
NUGET_PACKAGES_DIR="${NUGET_PACKAGES:-/root/.nuget/packages}"
DOTNET_MAX_CPU_COUNT="${DOTNET_MAX_CPU_COUNT:-1}"
diff --git a/docker/infiniframe-linux/example-blazorwebview.sh b/docker/infiniframe-linux/example-blazorwebview.sh
new file mode 100644
index 000000000..0962db227
--- /dev/null
+++ b/docker/infiniframe-linux/example-blazorwebview.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "${SCRIPT_DIR}/common.sh"
+
+SOLUTION="${SOLUTION:-InfiniFrame.slnx}"
+init_common_defaults
+setup_cleanup_trap
+setup_display_mode "/tmp/xvfb.log" "/tmp/mutter.log"
+restore_solution_filter "${SOLUTION}"
+build_native_project
+build_solution_filter "${SOLUTION}" "tests"
+
+echo "Running Blazor Webview Example..."
+dotnet run \
+ --project examples/InfiniFrameExample.BlazorWebView/InfiniFrameExample.BlazorWebView.csproj \
+ --configuration "${CONFIGURATION}" \
+ --no-build \
+ --no-restore \
+ /p:UseAppHost=false \
+ "${COMMON_DOTNET_PROPS[@]}"
diff --git a/docker/linux-tests/playwright.sh b/docker/infiniframe-linux/playwright.sh
similarity index 100%
rename from docker/linux-tests/playwright.sh
rename to docker/infiniframe-linux/playwright.sh
diff --git a/docker/linux-tests/tests.sh b/docker/infiniframe-linux/tests.sh
similarity index 100%
rename from docker/linux-tests/tests.sh
rename to docker/infiniframe-linux/tests.sh
diff --git a/docs/InfiniFrame.Docs.proj b/docs/InfiniFrame.Docs.proj
index bcbd11b19..576d84415 100644
--- a/docs/InfiniFrame.Docs.proj
+++ b/docs/InfiniFrame.Docs.proj
@@ -1,6 +1,7 @@
+ None
true
false
@@ -29,4 +30,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/docs/guides/core-window.md b/docs/docs/guides/core-window.md
index ae5de7678..c003fb3df 100644
--- a/docs/docs/guides/core-window.md
+++ b/docs/docs/guides/core-window.md
@@ -301,7 +301,7 @@ await window.SendWebMessageAsync("async hello");
In JavaScript, listen with:
```js
-window.infiniframe.host.receiveMessage(function(message) {
+window.infiniframe.host.receiveCallback(function(message) {
console.log("Received:", message);
});
```
@@ -311,7 +311,7 @@ window.infiniframe.host.receiveMessage(function(message) {
In JavaScript, send with:
```js
-window.infiniframe.host.postMessage({ id: "hello", data: "from JS", version: 1 });
+window.infiniframe.host.postData({ id: "hello", command: "Post", data: "from JS", version: 2 });
```
In C#, handle with:
diff --git a/docs/docs/guides/javascript-interop.md b/docs/docs/guides/javascript-interop.md
index cb67a1162..e3b2732f4 100644
--- a/docs/docs/guides/javascript-interop.md
+++ b/docs/docs/guides/javascript-interop.md
@@ -18,16 +18,16 @@ The messaging channel works the same way regardless of whether you are using pla
Use this JavaScript API as the primary send path:
```js
-window.infiniframe.host.postMessage({ id: "my:event", data: { value: 1 }, version: 1 });
+window.infiniframe.host.postData({ id: "my:event", command: "Post", data: { value: 1 }, version: 2 });
```
Messages are validated against a versioned envelope contract:
```json
-{ "id": "", "data": , "version": 1, "channel": "" }
+{ "id": "", "command": "Post|Get", "data": , "version": 2, "requestId": "", "channel": "" }
```
-`id` and `version` are required. `version` must be `1`.
+`id`, `command`, and `version` are required. `version` must be `2`.
Legacy `id;payload` messaging is out of support. The JSON envelope contract is the only supported protocol.
@@ -41,7 +41,7 @@ await window.SendWebMessageAsync("async hello");
In the browser:
```js
-window.infiniframe.host.receiveMessage(function(message) {
+window.infiniframe.host.receiveCallback(function(message) {
console.log("Received from C#:", message);
});
```
@@ -49,7 +49,7 @@ window.infiniframe.host.receiveMessage(function(message) {
### Sending from JavaScript to C#
```js
-window.infiniframe.host.postMessage({ id: "action", data: 42, version: 1 });
+window.infiniframe.host.postData({ id: "action", command: "Post", data: 42, version: 2 });
```
In C#:
@@ -81,8 +81,8 @@ window.MessageHandlers.RegisterMessageHandler("set-title", (window, title) => {
```
```js
-window.infiniframe.host.postMessage({ id: "ping", data: null, version: 1 });
-window.infiniframe.host.postMessage({ id: "set-title", data: "New Title", version: 1 });
+window.infiniframe.host.postData({ id: "ping", command: "Post", data: null, version: 2 });
+window.infiniframe.host.postData({ id: "set-title", command: "Post", data: "New Title", version: 2 });
```
## InfiniFrame.Js
@@ -169,19 +169,19 @@ These are used internally by `InfiniFrameWindowDragArea`, `InfiniFrameWindowButt
All messages follow a versioned JSON envelope:
```js
-window.infiniframe.host.postMessage({ id: "__infiniframe:window:minimize", data: null, version: 1 });
-window.infiniframe.host.postMessage({ id: "__infiniframe:window:maximize", data: null, version: 1 });
-window.infiniframe.host.postMessage({ id: "__infiniframe:window:close", data: null, version: 1 });
+window.infiniframe.host.postData({ id: "__infiniframe:window:minimize", command: "Post", data: null, version: 2 });
+window.infiniframe.host.postData({ id: "__infiniframe:window:maximize", command: "Post", data: null, version: 2 });
+window.infiniframe.host.postData({ id: "__infiniframe:window:close", command: "Post", data: null, version: 2 });
```
```js
// Title data is the new title string
-window.infiniframe.host.postMessage({ id: "__infiniframe:title:change", data: "New Window Title", version: 1 });
+window.infiniframe.host.postData({ id: "__infiniframe:title:change", command: "Post", data: "New Window Title", version: 2 });
```
```js
-window.infiniframe.host.postMessage({ id: "__infiniframe:fullscreen:enter", data: null, version: 1 });
-window.infiniframe.host.postMessage({ id: "__infiniframe:fullscreen:exit", data: null, version: 1 });
+window.infiniframe.host.postData({ id: "__infiniframe:fullscreen:enter", command: "Post", data: null, version: 2 });
+window.infiniframe.host.postData({ id: "__infiniframe:fullscreen:exit", command: "Post", data: null, version: 2 });
```
When using `InfiniFrame.js`, you can go through its API instead:
@@ -200,13 +200,14 @@ The message channel uses a JSON envelope, so structured data can be placed direc
```csharp
window.SendWebMessage(JsonSerializer.Serialize(new {
id = "update",
+ command = "Post",
data = new { count = 42 },
- version = 1
+ version = 2
}));
```
```js
-window.infiniframe.host.receiveMessage(function(raw) {
+window.infiniframe.host.receiveCallback(function(raw) {
const envelope = JSON.parse(raw);
if (envelope.id === "update") {
updateUI(envelope.data.count);
@@ -217,10 +218,11 @@ window.infiniframe.host.receiveMessage(function(raw) {
**JS → C#:**
```js
-window.infiniframe.host.postMessage({
+window.infiniframe.host.postData({
id: "log",
+ command: "Post",
data: { message: "hello" },
- version: 1
+ version: 2
});
```
diff --git a/docs/docs/guides/scripts.md b/docs/docs/guides/scripts.md
index 9d6c77e02..3b1ecd94a 100644
--- a/docs/docs/guides/scripts.md
+++ b/docs/docs/guides/scripts.md
@@ -10,7 +10,7 @@ This page documents the repository scripts in `scripts/` and what each command d
### `docker-compose-linux.sh`
-Builds the Linux testing image from `docker/compose/linux-tests.yml` with a no-cache build.
+Builds the Linux testing image from `docker/compose/infiniframe-linux.yml` with a no-cache build.
```bash
bash ./scripts/docker-compose-linux.sh
diff --git a/docs/docs/migration/breaking-changes-from-photino.md b/docs/docs/migration/breaking-changes-from-photino.md
index 68d6e8bba..4a96dfa7e 100644
--- a/docs/docs/migration/breaking-changes-from-photino.md
+++ b/docs/docs/migration/breaking-changes-from-photino.md
@@ -164,7 +164,7 @@ One handler. The full message string is passed as-is.
### InfiniFrame — typed message routing
-InfiniFrame uses a versioned JSON envelope protocol. Messages sent from JavaScript as `{"id":"myEvent","data":"some data","version":1}` are dispatched to the handler registered for `"myEvent"`:
+InfiniFrame uses a versioned JSON envelope protocol. Messages sent from JavaScript as `{"id":"myEvent","command":"Post","data":"some data","version":2}` are dispatched to the handler registered for `"myEvent"`:
```csharp
window.MessageHandlers.RegisterMessageHandler("myEvent", (window, payload) => {
@@ -177,7 +177,7 @@ window.MessageHandlers.RegisterMessageHandler("myEvent", (window, payload) => {
The JavaScript side must use the envelope format:
```js
-window.infiniframe.host.postMessage({ id: "myEvent", data: "some data", version: 1 });
+window.infiniframe.host.postData({ id: "myEvent", command: "Post", data: "some data", version: 2 });
```
Legacy `messageId;payload` is out of support and not supported by InfiniFrame messaging contracts.
diff --git a/examples/InfiniFrameExample.BlazorWebView/Program.cs b/examples/InfiniFrameExample.BlazorWebView/Program.cs
index 57d53894f..da6058d51 100644
--- a/examples/InfiniFrameExample.BlazorWebView/Program.cs
+++ b/examples/InfiniFrameExample.BlazorWebView/Program.cs
@@ -3,6 +3,7 @@
// ---------------------------------------------------------------------------------------------------------------------
using InfiniFrame;
using InfiniFrame.BlazorWebView;
+using InfiniFrame.Js.Interop.MessageHandlers;
using InfiniFrameExample.BlazorWebView.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -33,6 +34,7 @@ private static void Main(string[] args) {
appBuilder.WithInfiniFrameWindowBuilder(builder => {
builder
+ .RegisterStandardGetWebMessageHandler()
// .SetTransparent(true)
// .SetChromeless(true)
// .SetResizable(true)
diff --git a/examples/InfiniFrameExample.TrimAotSmoke/InfiniFrameExample.TrimAotSmoke.csproj b/examples/InfiniFrameExample.TrimAotSmoke/InfiniFrameExample.TrimAotSmoke.csproj
index 1d7bf5e44..c8da83ccf 100644
--- a/examples/InfiniFrameExample.TrimAotSmoke/InfiniFrameExample.TrimAotSmoke.csproj
+++ b/examples/InfiniFrameExample.TrimAotSmoke/InfiniFrameExample.TrimAotSmoke.csproj
@@ -6,10 +6,9 @@
enable
false
- true
true
- true
win-x64
+ true
diff --git a/examples/InfiniFrameExample.WebApp.React/Source/InfiniFrame.React/package-lock.json b/examples/InfiniFrameExample.WebApp.React/Source/InfiniFrame.React/package-lock.json
index a254d8af3..f0d545985 100644
--- a/examples/InfiniFrameExample.WebApp.React/Source/InfiniFrame.React/package-lock.json
+++ b/examples/InfiniFrameExample.WebApp.React/Source/InfiniFrame.React/package-lock.json
@@ -8,32 +8,32 @@
"name": "InfiniFrame-react",
"version": "0.0.0",
"dependencies": {
- "react": "^19.1.1",
- "react-dom": "^19.1.1"
+ "react": "^19.2.5",
+ "react-dom": "^19.2.5"
},
"devDependencies": {
- "@eslint/js": "^9.35.0",
- "@types/node": "^20.12.5",
- "@types/react": "^19.1.13",
- "@types/react-dom": "^19.1.9",
- "@vitejs/plugin-react": "^5.0.2",
- "eslint": "^9.35.0",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
- "globals": "^16.4.0",
- "typescript": "~5.8.3",
- "typescript-eslint": "^8.43.0",
- "vite": "^7.3.2"
+ "@eslint/js": "^10.0.1",
+ "@types/node": "^25.6.0",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "eslint": "^10.2.1",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.2",
+ "globals": "^17.5.0",
+ "typescript": "^6.0.3",
+ "typescript-eslint": "^8.59.1",
+ "vite": "^8.0.10"
}
},
"node_modules/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -42,9 +42,9 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz",
- "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
+ "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -52,21 +52,21 @@
}
},
"node_modules/@babel/core": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
- "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
- "@babel/helper-compilation-targets": "^7.27.2",
- "@babel/helper-module-transforms": "^7.28.3",
- "@babel/helpers": "^7.28.4",
- "@babel/parser": "^7.28.4",
- "@babel/template": "^7.27.2",
- "@babel/traverse": "^7.28.4",
- "@babel/types": "^7.28.4",
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
@@ -83,14 +83,14 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
- "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.3",
- "@babel/types": "^7.28.2",
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -100,13 +100,13 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
- "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.27.2",
+ "@babel/compat-data": "^7.28.6",
"@babel/helper-validator-option": "^7.27.1",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
@@ -127,29 +127,29 @@
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
- "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
- "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.28.3"
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
@@ -158,16 +158,6 @@
"@babel/core": "^7.0.0"
}
},
- "node_modules/@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
@@ -179,9 +169,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
- "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -199,27 +189,27 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
- "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
+ "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.4"
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
- "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
+ "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.4"
+ "@babel/types": "^7.29.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -228,66 +218,34 @@
"node": ">=6.0.0"
}
},
- "node_modules/@babel/plugin-transform-react-jsx-self": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
- "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-react-jsx-source": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
- "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
"node_modules/@babel/template": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
- "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/parser": "^7.27.2",
- "@babel/types": "^7.27.1"
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
- "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
"@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.4",
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.4",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
"debug": "^4.3.1"
},
"engines": {
@@ -295,465 +253,57 @@
}
},
"node_modules/@babel/types": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
- "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
+ "@babel/helper-validator-identifier": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
}
},
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
- "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
- "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
- "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
- "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
- "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
- "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
- "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
- "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
- "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
- "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
- "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
- "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
- "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
- "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
- "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
- "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
- "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
- "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
- "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
- "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
- "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
- "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
- "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
- "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
}
},
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
- "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
- "cpu": [
- "ia32"
- ],
+ "node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "tslib": "^2.4.0"
}
},
- "node_modules/@esbuild/win32-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
- "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "tslib": "^2.4.0"
}
},
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.9.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
- "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -783,9 +333,9 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "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": {
@@ -793,141 +343,129 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.21.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
- "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
+ "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": "^2.1.6",
+ "@eslint/object-schema": "^3.0.5",
"debug": "^4.3.1",
- "minimatch": "^3.1.2"
+ "minimatch": "^10.2.4"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/config-helpers": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
- "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz",
+ "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==",
"dev": true,
"license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^1.2.1"
+ },
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/core": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
- "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "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": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/eslintrc": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
- "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/js": {
- "version": "9.35.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz",
- "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==",
+ "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": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "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": "2.1.6",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
- "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "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": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
- "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz",
+ "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.15.2",
+ "@eslint/core": "^1.2.1",
"levn": "^0.4.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@humanfs/core": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
- "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "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.7",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
- "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "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.1",
+ "@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",
@@ -1001,74 +539,44 @@
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
}
},
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
+ "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
"dev": true,
"license": "MIT",
+ "optional": true,
"dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
+ "@tybys/wasm-util": "^0.10.1"
},
- "engines": {
- "node": ">= 8"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
}
},
- "node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.35",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.35.tgz",
- "integrity": "sha512-slYrCpoxJUqzFDDNlvrOYRazQUNRvWPjXA17dAOISY3rDMxX6k8K4cj2H+hEYMHF81HO3uNd5rHVigAWRM5dSg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
- "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@oxc-project/types": {
+ "version": "0.127.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz",
+ "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
},
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
- "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==",
"cpu": [
"arm64"
],
@@ -1077,12 +585,15 @@
"optional": true,
"os": [
"android"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
- "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==",
"cpu": [
"arm64"
],
@@ -1091,12 +602,15 @@
"optional": true,
"os": [
"darwin"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
- "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==",
"cpu": [
"x64"
],
@@ -1105,26 +619,15 @@
"optional": true,
"os": [
"darwin"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
- "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
- "cpu": [
- "arm64"
],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
- "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==",
"cpu": [
"x64"
],
@@ -1133,26 +636,15 @@
"optional": true,
"os": [
"freebsd"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
- "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
- "cpu": [
- "arm"
],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
- "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz",
+ "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==",
"cpu": [
"arm"
],
@@ -1161,180 +653,135 @@
"optional": true,
"os": [
"linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
- "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
- "cpu": [
- "arm64"
],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
- "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==",
"cpu": [
"arm64"
],
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
- "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
- "cpu": [
- "loong64"
+ "libc": [
+ "glibc"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
- "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
- "cpu": [
- "loong64"
],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
- "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz",
+ "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==",
"cpu": [
- "ppc64"
+ "arm64"
],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
- "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==",
"cpu": [
"ppc64"
],
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
- "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
- "cpu": [
- "riscv64"
+ "libc": [
+ "glibc"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
- "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
- "cpu": [
- "riscv64"
],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
- "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==",
"cpu": [
"s390x"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
- "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==",
"cpu": [
"x64"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
- "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz",
+ "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==",
"cpu": [
"x64"
],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
- ]
- },
- "node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
- "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
- "cpu": [
- "x64"
],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ]
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
- "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==",
"cpu": [
"arm64"
],
@@ -1343,54 +790,51 @@
"optional": true,
"os": [
"openharmony"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
- "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
- "cpu": [
- "arm64"
],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
- "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz",
+ "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==",
"cpu": [
- "ia32"
+ "wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "win32"
- ]
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
- "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
+ "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==",
"cpu": [
- "x64"
+ "arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
- "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz",
+ "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==",
"cpu": [
"x64"
],
@@ -1399,52 +843,35 @@
"optional": true,
"os": [
"win32"
- ]
- },
- "node_modules/@types/babel__core": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
- "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.20.7",
- "@babel/types": "^7.20.7",
- "@types/babel__generator": "*",
- "@types/babel__template": "*",
- "@types/babel__traverse": "*"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@types/babel__generator": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
- "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.7",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz",
+ "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.0.0"
- }
+ "license": "MIT"
},
- "node_modules/@types/babel__template": {
- "version": "7.4.4",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
- "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
"dev": true,
"license": "MIT",
+ "optional": true,
"dependencies": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
+ "tslib": "^2.4.0"
}
},
- "node_modules/@types/babel__traverse": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
- "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "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",
- "dependencies": {
- "@babel/types": "^7.28.2"
- }
+ "license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.8",
@@ -1461,51 +888,50 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "20.19.17",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz",
- "integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==",
+ "version": "25.6.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
+ "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~6.21.0"
+ "undici-types": "~7.19.0"
}
},
"node_modules/@types/react": {
- "version": "19.1.13",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz",
- "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==",
+ "version": "19.2.14",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
+ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "csstype": "^3.0.2"
+ "csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
- "version": "19.1.9",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz",
- "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
- "@types/react": "^19.0.0"
+ "@types/react": "^19.2.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz",
- "integrity": "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz",
+ "integrity": "sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/type-utils": "8.44.0",
- "@typescript-eslint/utils": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
- "graphemer": "^1.4.0",
- "ignore": "^7.0.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.59.1",
+ "@typescript-eslint/type-utils": "8.59.1",
+ "@typescript-eslint/utils": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1",
+ "ignore": "^7.0.5",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^2.1.0"
+ "ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1515,9 +941,9 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.44.0",
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "@typescript-eslint/parser": "^8.59.1",
+ "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": {
@@ -1531,17 +957,17 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.0.tgz",
- "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.1.tgz",
+ "integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
- "debug": "^4.3.4"
+ "@typescript-eslint/scope-manager": "8.59.1",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1",
+ "debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1551,20 +977,20 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "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.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.0.tgz",
- "integrity": "sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz",
+ "integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.44.0",
- "@typescript-eslint/types": "^8.44.0",
- "debug": "^4.3.4"
+ "@typescript-eslint/tsconfig-utils": "^8.59.1",
+ "@typescript-eslint/types": "^8.59.1",
+ "debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1574,18 +1000,18 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz",
- "integrity": "sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz",
+ "integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0"
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1596,9 +1022,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz",
- "integrity": "sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz",
+ "integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1609,21 +1035,21 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz",
- "integrity": "sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz",
+ "integrity": "sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/utils": "8.44.0",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.1.0"
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1",
+ "@typescript-eslint/utils": "8.59.1",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1633,14 +1059,14 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz",
- "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz",
+ "integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1652,22 +1078,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz",
- "integrity": "sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz",
+ "integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.44.0",
- "@typescript-eslint/tsconfig-utils": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.1.0"
+ "@typescript-eslint/project-service": "8.59.1",
+ "@typescript-eslint/tsconfig-utils": "8.59.1",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1",
+ "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"
@@ -1677,39 +1102,13 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
- "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/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"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -1720,16 +1119,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.0.tgz",
- "integrity": "sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz",
+ "integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0"
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.59.1",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1739,19 +1138,19 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "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.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz",
- "integrity": "sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz",
+ "integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.44.0",
- "eslint-visitor-keys": "^4.2.1"
+ "@typescript-eslint/types": "8.59.1",
+ "eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1762,30 +1161,35 @@
}
},
"node_modules/@vitejs/plugin-react": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.3.tgz",
- "integrity": "sha512-PFVHhosKkofGH0Yzrw1BipSedTH68BFF8ZWy1kfUpCtJcouXXY0+racG8sExw7hw0HoX36813ga5o3LTWZ4FUg==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
+ "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/core": "^7.28.4",
- "@babel/plugin-transform-react-jsx-self": "^7.27.1",
- "@babel/plugin-transform-react-jsx-source": "^7.27.1",
- "@rolldown/pluginutils": "1.0.0-beta.35",
- "@types/babel__core": "^7.20.5",
- "react-refresh": "^0.17.0"
+ "@rolldown/pluginutils": "1.0.0-rc.7"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
- "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ "@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/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"bin": {
@@ -1822,74 +1226,46 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "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==",
+ "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",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
"engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true,
- "license": "Python-2.0"
- },
- "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/baseline-browser-mapping": {
- "version": "2.8.6",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz",
- "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==",
+ "version": "2.10.24",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.24.tgz",
+ "integrity": "sha512-I2NkZOOrj2XuguvWCK6OVh9GavsNjZjK908Rq3mIBK25+GD8vPX5w2WdxVqnQ7xx3SrZJiCiZFu+/Oz50oSYSA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
- "baseline-browser-mapping": "dist/cli.js"
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
"node_modules/brace-expansion": {
- "version": "1.1.14",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
- "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "fill-range": "^7.1.1"
+ "balanced-match": "^4.0.2"
},
"engines": {
- "node": ">=8"
+ "node": "18 || 20 || >=22"
}
},
"node_modules/browserslist": {
- "version": "4.26.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz",
- "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==",
+ "version": "4.28.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
+ "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
"dev": true,
"funding": [
{
@@ -1907,11 +1283,11 @@
],
"license": "MIT",
"dependencies": {
- "baseline-browser-mapping": "^2.8.3",
- "caniuse-lite": "^1.0.30001741",
- "electron-to-chromium": "^1.5.218",
- "node-releases": "^2.0.21",
- "update-browserslist-db": "^1.1.3"
+ "baseline-browser-mapping": "^2.10.12",
+ "caniuse-lite": "^1.0.30001782",
+ "electron-to-chromium": "^1.5.328",
+ "node-releases": "^2.0.36",
+ "update-browserslist-db": "^1.2.3"
},
"bin": {
"browserslist": "cli.js"
@@ -1920,80 +1296,26 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/caniuse-lite": {
- "version": "1.0.30001743",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz",
- "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==",
+ "version": "1.0.30001791",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz",
+ "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==",
"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/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/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/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT"
+ {
+ "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/convert-source-map": {
"version": "2.0.0",
@@ -2018,9 +1340,9 @@
}
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"dev": true,
"license": "MIT"
},
@@ -2049,55 +1371,23 @@
"dev": true,
"license": "MIT"
},
- "node_modules/electron-to-chromium": {
- "version": "1.5.222",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz",
- "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/esbuild": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
- "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
+ "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,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
+ "license": "Apache-2.0",
"engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.7",
- "@esbuild/android-arm": "0.27.7",
- "@esbuild/android-arm64": "0.27.7",
- "@esbuild/android-x64": "0.27.7",
- "@esbuild/darwin-arm64": "0.27.7",
- "@esbuild/darwin-x64": "0.27.7",
- "@esbuild/freebsd-arm64": "0.27.7",
- "@esbuild/freebsd-x64": "0.27.7",
- "@esbuild/linux-arm": "0.27.7",
- "@esbuild/linux-arm64": "0.27.7",
- "@esbuild/linux-ia32": "0.27.7",
- "@esbuild/linux-loong64": "0.27.7",
- "@esbuild/linux-mips64el": "0.27.7",
- "@esbuild/linux-ppc64": "0.27.7",
- "@esbuild/linux-riscv64": "0.27.7",
- "@esbuild/linux-s390x": "0.27.7",
- "@esbuild/linux-x64": "0.27.7",
- "@esbuild/netbsd-arm64": "0.27.7",
- "@esbuild/netbsd-x64": "0.27.7",
- "@esbuild/openbsd-arm64": "0.27.7",
- "@esbuild/openbsd-x64": "0.27.7",
- "@esbuild/openharmony-arm64": "0.27.7",
- "@esbuild/sunos-x64": "0.27.7",
- "@esbuild/win32-arm64": "0.27.7",
- "@esbuild/win32-ia32": "0.27.7",
- "@esbuild/win32-x64": "0.27.7"
+ "node": ">=8"
}
},
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.345",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.345.tgz",
+ "integrity": "sha512-F9JXQGiMrz6yVNPI2qOVPvB9HzjH5cGzhs8oJ6A28V5L/YnzN/0KsuiibqF+F1Fd9qxFzD1BUnYSd8JfULxTwg==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
@@ -2122,34 +1412,30 @@
}
},
"node_modules/eslint": {
- "version": "9.35.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz",
- "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==",
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.1.tgz",
+ "integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.0",
- "@eslint/config-helpers": "^0.3.1",
- "@eslint/core": "^0.15.2",
- "@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.35.0",
- "@eslint/plugin-kit": "^0.3.5",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@eslint/config-array": "^0.23.5",
+ "@eslint/config-helpers": "^0.5.5",
+ "@eslint/core": "^1.2.1",
+ "@eslint/plugin-kit": "^0.7.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
- "@types/json-schema": "^7.0.15",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
+ "ajv": "^6.14.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.4.0",
- "eslint-visitor-keys": "^4.2.1",
- "espree": "^10.4.0",
- "esquery": "^1.5.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",
@@ -2159,8 +1445,7 @@
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
+ "minimatch": "^10.2.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
@@ -2168,7 +1453,7 @@
"eslint": "bin/eslint.js"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://eslint.org/donate"
@@ -2183,80 +1468,89 @@
}
},
"node_modules/eslint-plugin-react-hooks": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
- "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "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": ">=10"
+ "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"
+ "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.4.20",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
- "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz",
+ "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
- "eslint": ">=8.40"
+ "eslint": "^9 || ^10"
}
},
"node_modules/eslint-scope": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
- "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "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": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "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": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/espree": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
- "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "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.15.0",
+ "acorn": "^8.16.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.1"
+ "eslint-visitor-keys": "^5.0.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "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": {
@@ -2306,36 +1600,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/fast-glob/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"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",
@@ -2350,14 +1614,22 @@
"dev": true,
"license": "MIT"
},
- "node_modules/fastq": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
- "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "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": "ISC",
- "dependencies": {
- "reusify": "^1.0.4"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
}
},
"node_modules/file-entry-cache": {
@@ -2373,19 +1645,6 @@
"node": ">=16.0.0"
}
},
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -2463,9 +1722,9 @@
}
},
"node_modules/globals": {
- "version": "16.4.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
- "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
+ "version": "17.5.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz",
+ "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2475,21 +1734,21 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "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/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "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",
- "engines": {
- "node": ">=8"
+ "dependencies": {
+ "hermes-estree": "0.25.1"
}
},
"node_modules/ignore": {
@@ -2502,23 +1761,6 @@
"node": ">= 4"
}
},
- "node_modules/import-fresh": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -2552,16 +1794,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -2576,19 +1808,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/js-yaml": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
- "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -2621,43 +1840,316 @@
"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"
+ "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/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "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.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
},
- "node_modules/json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "MIT",
- "bin": {
- "json5": "lib/cli.js"
- },
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=6"
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "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==",
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "license": "MIT",
- "dependencies": {
- "json-buffer": "3.0.1"
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "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==",
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "MIT",
- "dependencies": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- },
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">= 0.8.0"
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
"node_modules/locate-path": {
@@ -2676,13 +2168,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "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",
@@ -2693,41 +2178,20 @@
"yallist": "^3.0.2"
}
},
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/micromatch": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
- "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "braces": "^3.0.3",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
"node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "brace-expansion": "^5.0.5"
},
"engines": {
- "node": "*"
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/ms": {
@@ -2764,9 +2228,9 @@
"license": "MIT"
},
"node_modules/node-releases": {
- "version": "2.0.21",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz",
- "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==",
+ "version": "2.0.38",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz",
+ "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==",
"dev": true,
"license": "MIT"
},
@@ -2820,19 +2284,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "callsites": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -2861,22 +2312,22 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
- "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=8.6"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
- "version": "8.5.10",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
- "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
+ "version": "8.5.12",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz",
+ "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==",
"dev": true,
"funding": [
{
@@ -2922,152 +2373,72 @@
"node": ">=6"
}
},
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
"node_modules/react": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
- "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz",
+ "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
- "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
+ "version": "19.2.5",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz",
+ "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==",
"license": "MIT",
"dependencies": {
- "scheduler": "^0.26.0"
+ "scheduler": "^0.27.0"
},
"peerDependencies": {
- "react": "^19.1.1"
- }
- },
- "node_modules/react-refresh": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
- "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
+ "react": "^19.2.5"
}
},
- "node_modules/reusify": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
- "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
- "node_modules/rollup": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
- "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
+ "node_modules/rolldown": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz",
+ "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@types/estree": "1.0.8"
+ "@oxc-project/types": "=0.127.0",
+ "@rolldown/pluginutils": "1.0.0-rc.17"
},
"bin": {
- "rollup": "dist/bin/rollup"
+ "rolldown": "bin/cli.mjs"
},
"engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.59.0",
- "@rollup/rollup-android-arm64": "4.59.0",
- "@rollup/rollup-darwin-arm64": "4.59.0",
- "@rollup/rollup-darwin-x64": "4.59.0",
- "@rollup/rollup-freebsd-arm64": "4.59.0",
- "@rollup/rollup-freebsd-x64": "4.59.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
- "@rollup/rollup-linux-arm64-gnu": "4.59.0",
- "@rollup/rollup-linux-arm64-musl": "4.59.0",
- "@rollup/rollup-linux-loong64-gnu": "4.59.0",
- "@rollup/rollup-linux-loong64-musl": "4.59.0",
- "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
- "@rollup/rollup-linux-ppc64-musl": "4.59.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
- "@rollup/rollup-linux-riscv64-musl": "4.59.0",
- "@rollup/rollup-linux-s390x-gnu": "4.59.0",
- "@rollup/rollup-linux-x64-gnu": "4.59.0",
- "@rollup/rollup-linux-x64-musl": "4.59.0",
- "@rollup/rollup-openbsd-x64": "4.59.0",
- "@rollup/rollup-openharmony-arm64": "4.59.0",
- "@rollup/rollup-win32-arm64-msvc": "4.59.0",
- "@rollup/rollup-win32-ia32-msvc": "4.59.0",
- "@rollup/rollup-win32-x64-gnu": "4.59.0",
- "@rollup/rollup-win32-x64-msvc": "4.59.0",
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "@rolldown/binding-android-arm64": "1.0.0-rc.17",
+ "@rolldown/binding-darwin-arm64": "1.0.0-rc.17",
+ "@rolldown/binding-darwin-x64": "1.0.0-rc.17",
+ "@rolldown/binding-freebsd-x64": "1.0.0-rc.17",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17",
+ "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17",
+ "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17",
+ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17"
+ }
+ },
+ "node_modules/rolldown/node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz",
+ "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==",
"dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
+ "license": "MIT"
},
"node_modules/scheduler": {
- "version": "0.26.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
- "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "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": {
@@ -3113,41 +2484,15 @@
"node": ">=0.10.0"
}
},
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "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/tinyglobby": {
- "version": "0.2.15",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
- "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "version": "0.2.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
- "picomatch": "^4.0.3"
+ "picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
@@ -3156,54 +2501,10 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "node_modules/tinyglobby/node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/tinyglobby/node_modules/picomatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
- "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
"node_modules/ts-api-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
- "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "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": {
@@ -3213,6 +2514,14 @@
"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==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -3227,9 +2536,9 @@
}
},
"node_modules/typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "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": {
@@ -3241,16 +2550,16 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.0.tgz",
- "integrity": "sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.1.tgz",
+ "integrity": "sha512-xqDcFVBmlrltH64lklOVp1wYxgJr6LVdg3NamBgH2OOQDLFdTKfIZXF5PfghrnXQKXZGTQs8tr1vL7fJvq8CTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.44.0",
- "@typescript-eslint/parser": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/utils": "8.44.0"
+ "@typescript-eslint/eslint-plugin": "8.59.1",
+ "@typescript-eslint/parser": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1",
+ "@typescript-eslint/utils": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3260,21 +2569,21 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/undici-types": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "version": "7.19.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
+ "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
"dev": true,
"license": "MIT"
},
"node_modules/update-browserslist-db": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
- "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
"funding": [
{
@@ -3313,18 +2622,17 @@
}
},
"node_modules/vite": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
- "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
+ "version": "8.0.10",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
+ "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.27.0",
- "fdir": "^6.5.0",
- "picomatch": "^4.0.3",
- "postcss": "^8.5.6",
- "rollup": "^4.43.0",
- "tinyglobby": "^0.2.15"
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.10",
+ "rolldown": "1.0.0-rc.17",
+ "tinyglobby": "^0.2.16"
},
"bin": {
"vite": "bin/vite.js"
@@ -3340,9 +2648,10 @@
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.1.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
- "lightningcss": "^1.21.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
@@ -3355,13 +2664,16 @@
"@types/node": {
"optional": true
},
- "jiti": {
+ "@vitejs/devtools": {
"optional": true
},
- "less": {
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
"optional": true
},
- "lightningcss": {
+ "less": {
"optional": true
},
"sass": {
@@ -3387,37 +2699,6 @@
}
}
},
- "node_modules/vite/node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/vite/node_modules/picomatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
- "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -3463,6 +2744,29 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.1.tgz",
+ "integrity": "sha512-a6ENMBBGZBsnlSebQ/eKCguSBeGKSf4O7BPnqVPmYGtpBYI7VSqoVqw+QcB7kPRjbqPwhYTpFbVj/RqNz/CT0Q==",
+ "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/examples/InfiniFrameExample.WebApp.React/Source/InfiniFrame.React/package.json b/examples/InfiniFrameExample.WebApp.React/Source/InfiniFrame.React/package.json
index 9932eb93f..37992d945 100644
--- a/examples/InfiniFrameExample.WebApp.React/Source/InfiniFrame.React/package.json
+++ b/examples/InfiniFrameExample.WebApp.React/Source/InfiniFrame.React/package.json
@@ -9,22 +9,22 @@
"react - lint": "eslint .",
"react - preview": "vite preview"
},
- "dependencies": {
- "react": "^19.1.1",
- "react-dom": "^19.1.1"
- },
- "devDependencies": {
- "@eslint/js": "^9.35.0",
- "@types/react": "^19.1.13",
- "@types/react-dom": "^19.1.9",
- "@vitejs/plugin-react": "^5.0.2",
- "@types/node": "^20.12.5",
- "eslint": "^9.35.0",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
- "globals": "^16.4.0",
- "typescript": "~5.8.3",
- "typescript-eslint": "^8.43.0",
- "vite": "^7.3.2"
- }
+ "dependencies": {
+ "react": "^19.2.5",
+ "react-dom": "^19.2.5"
+ },
+ "devDependencies": {
+ "@eslint/js": "^10.0.1",
+ "@types/node": "^25.6.0",
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "eslint": "^10.2.1",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.2",
+ "globals": "^17.5.0",
+ "typescript": "^6.0.3",
+ "typescript-eslint": "^8.59.1",
+ "vite": "^8.0.10"
+ }
}
diff --git a/examples/InfiniFrameExample.WebApp.Vue/Source/InfiniFrame.Vue/package-lock.json b/examples/InfiniFrameExample.WebApp.Vue/Source/InfiniFrame.Vue/package-lock.json
index f757692bb..8217f39ea 100644
--- a/examples/InfiniFrameExample.WebApp.Vue/Source/InfiniFrame.Vue/package-lock.json
+++ b/examples/InfiniFrameExample.WebApp.Vue/Source/InfiniFrame.Vue/package-lock.json
@@ -8,15 +8,15 @@
"name": "InfiniFrame-vue",
"version": "0.0.0",
"dependencies": {
- "vue": "^3.5.21"
+ "vue": "^3.5.33"
},
"devDependencies": {
- "@types/node": "^20.12.5",
- "@vitejs/plugin-vue": "^6.0.1",
- "@vue/tsconfig": "^0.8.1",
- "typescript": "~5.8.3",
- "vite": "^7.3.2",
- "vue-tsc": "^3.0.7"
+ "@types/node": "^25.6.0",
+ "@vitejs/plugin-vue": "^6.0.6",
+ "@vue/tsconfig": "^0.9.1",
+ "typescript": "6.0.3",
+ "vite": "^8.0.10",
+ "vue-tsc": "^3.2.7"
}
},
"node_modules/@babel/helper-string-parser": {
@@ -29,21 +29,21 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
- "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
- "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
+ "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.4"
+ "@babel/types": "^7.29.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -53,124 +53,91 @@
}
},
"node_modules/@babel/types": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
- "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
+ "@babel/helper-validator-identifier": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
}
},
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
- "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
- "cpu": [
- "ppc64"
- ],
+ "node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
}
},
- "node_modules/@esbuild/android-arm": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
- "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "tslib": "^2.4.0"
}
},
- "node_modules/@esbuild/android-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
- "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "tslib": "^2.4.0"
}
},
- "node_modules/@esbuild/android-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
- "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
+ "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==",
+ "license": "MIT"
},
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
- "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
+ "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
}
},
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
- "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@oxc-project/types": {
+ "version": "0.127.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz",
+ "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
}
},
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
- "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==",
"cpu": [
"arm64"
],
@@ -178,50 +145,16 @@
"license": "MIT",
"optional": true,
"os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
- "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
- "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
+ "android"
],
"engines": {
- "node": ">=18"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
- "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==",
"cpu": [
"arm64"
],
@@ -229,118 +162,16 @@
"license": "MIT",
"optional": true,
"os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
- "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
- "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
- "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
- "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
- "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
- "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
+ "darwin"
],
"engines": {
- "node": ">=18"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@esbuild/linux-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
- "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==",
"cpu": [
"x64"
],
@@ -348,33 +179,16 @@
"license": "MIT",
"optional": true,
"os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
- "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
+ "darwin"
],
"engines": {
- "node": ">=18"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
- "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==",
"cpu": [
"x64"
],
@@ -382,428 +196,153 @@
"license": "MIT",
"optional": true,
"os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
- "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
+ "freebsd"
],
"engines": {
- "node": ">=18"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
- "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz",
+ "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==",
"cpu": [
- "x64"
+ "arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
- "openbsd"
+ "linux"
],
"engines": {
- "node": ">=18"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
- "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==",
"cpu": [
"arm64"
],
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
- "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
- "cpu": [
- "x64"
+ "libc": [
+ "glibc"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
- "sunos"
+ "linux"
],
"engines": {
- "node": ">=18"
+ "node": "^20.19.0 || >=22.12.0"
}
},
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
- "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz",
+ "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==",
"cpu": [
"arm64"
],
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
- "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
- "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
- "cpu": [
- "x64"
+ "libc": [
+ "musl"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
- "win32"
+ "linux"
],
"engines": {
- "node": ">=18"
+ "node": "^20.19.0 || >=22.12.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==",
- "license": "MIT"
- },
- "node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.29",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz",
- "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
- "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
- "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
- "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
- "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
- "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
- "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
- "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
- "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
- "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
- "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
- "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
- "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
- "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
- "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
- "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
- "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
- "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==",
"cpu": [
- "s390x"
+ "ppc64"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
- "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==",
"cpu": [
- "x64"
+ "s390x"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
- "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz",
+ "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==",
"cpu": [
"x64"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
- "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz",
+ "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==",
"cpu": [
"x64"
],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
- "openbsd"
- ]
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
- "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz",
+ "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==",
"cpu": [
"arm64"
],
@@ -812,54 +351,51 @@
"optional": true,
"os": [
"openharmony"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
- "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
- "cpu": [
- "arm64"
],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
- "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz",
+ "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==",
"cpu": [
- "ia32"
+ "wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "win32"
- ]
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
- "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
+ "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==",
"cpu": [
- "x64"
+ "arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
- "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz",
+ "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==",
"cpu": [
"x64"
],
@@ -868,215 +404,209 @@
"optional": true,
"os": [
"win32"
- ]
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
},
- "node_modules/@types/estree": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
- "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.13",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.13.tgz",
+ "integrity": "sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==",
"dev": true,
"license": "MIT"
},
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@types/node": {
- "version": "20.19.17",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz",
- "integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==",
+ "version": "25.6.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
+ "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~6.21.0"
+ "undici-types": "~7.19.0"
}
},
"node_modules/@vitejs/plugin-vue": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz",
- "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==",
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.6.tgz",
+ "integrity": "sha512-u9HHgfrq3AjXlysn0eINFnWQOJQLO9WN6VprZ8FXl7A2bYisv3Hui9Ij+7QZ41F/WYWarHjwBbXtD7dKg3uxbg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@rolldown/pluginutils": "1.0.0-beta.29"
+ "@rolldown/pluginutils": "1.0.0-rc.13"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
"vue": "^3.2.25"
}
},
"node_modules/@volar/language-core": {
- "version": "2.4.23",
- "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz",
- "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==",
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz",
+ "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@volar/source-map": "2.4.23"
+ "@volar/source-map": "2.4.28"
}
},
"node_modules/@volar/source-map": {
- "version": "2.4.23",
- "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz",
- "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==",
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz",
+ "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@volar/typescript": {
- "version": "2.4.23",
- "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz",
- "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==",
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz",
+ "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@volar/language-core": "2.4.23",
+ "@volar/language-core": "2.4.28",
"path-browserify": "^1.0.1",
"vscode-uri": "^3.0.8"
}
},
"node_modules/@vue/compiler-core": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
- "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==",
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.33.tgz",
+ "integrity": "sha512-3PZLQwFw4Za3TC8t0FvTy3wI16Kt+pmwcgNZca4Pj9iWL2E72a/gZlpBtAJvEdDMdCxdG/qq0C7PN0bsJuv0Rw==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.3",
- "@vue/shared": "3.5.21",
- "entities": "^4.5.0",
+ "@babel/parser": "^7.29.2",
+ "@vue/shared": "3.5.33",
+ "entities": "^7.0.1",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz",
- "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==",
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.33.tgz",
+ "integrity": "sha512-PXq0yrfCLzzL07rbXO4awtXY1Z06LG2eu6Adg3RJFa/j3Cii217XxxLXG22N330gw7GmALCY0Z8RgXEviwgpjA==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-core": "3.5.21",
- "@vue/shared": "3.5.21"
+ "@vue/compiler-core": "3.5.33",
+ "@vue/shared": "3.5.33"
}
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
- "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.33.tgz",
+ "integrity": "sha512-UTUvRO9cY+rROrx/pvN9P5Z7FgA6QGfokUCfhQE4EnmUj3rVnK+CHI0LsEO1pg+I7//iRYMUfcNcCPe7tg0CoA==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.3",
- "@vue/compiler-core": "3.5.21",
- "@vue/compiler-dom": "3.5.21",
- "@vue/compiler-ssr": "3.5.21",
- "@vue/shared": "3.5.21",
+ "@babel/parser": "^7.29.2",
+ "@vue/compiler-core": "3.5.33",
+ "@vue/compiler-dom": "3.5.33",
+ "@vue/compiler-ssr": "3.5.33",
+ "@vue/shared": "3.5.33",
"estree-walker": "^2.0.2",
- "magic-string": "^0.30.18",
- "postcss": "^8.5.6",
+ "magic-string": "^0.30.21",
+ "postcss": "^8.5.10",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz",
- "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==",
- "license": "MIT",
- "dependencies": {
- "@vue/compiler-dom": "3.5.21",
- "@vue/shared": "3.5.21"
- }
- },
- "node_modules/@vue/compiler-vue2": {
- "version": "2.7.16",
- "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
- "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
- "dev": true,
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.33.tgz",
+ "integrity": "sha512-IErjYdnj1qIupG5xxiVIYiiRvDhGWV4zuh/RCrwfYpuL+HWQzeU6lCk/nF9r7olWMnjKxCAkOctT2qFWFkzb1A==",
"license": "MIT",
"dependencies": {
- "de-indent": "^1.0.2",
- "he": "^1.2.0"
+ "@vue/compiler-dom": "3.5.33",
+ "@vue/shared": "3.5.33"
}
},
"node_modules/@vue/language-core": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.0.7.tgz",
- "integrity": "sha512-0sqqyqJ0Gn33JH3TdIsZLCZZ8Gr4kwlg8iYOnOrDDkJKSjFurlQY/bEFQx5zs7SX2C/bjMkmPYq/NiyY1fTOkw==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.2.7.tgz",
+ "integrity": "sha512-Gn4q/tRxbpVGLEuARQ43p3YELlNAFgRUVCgW9U5Cr+5q4vfD2bWDWpl3ABbJMXUt5xlE1dF8dkigg2aUq7JYYw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@volar/language-core": "2.4.23",
+ "@volar/language-core": "2.4.28",
"@vue/compiler-dom": "^3.5.0",
- "@vue/compiler-vue2": "^2.7.16",
"@vue/shared": "^3.5.0",
- "alien-signals": "^2.0.5",
+ "alien-signals": "^3.1.2",
"muggle-string": "^0.4.1",
"path-browserify": "^1.0.1",
- "picomatch": "^4.0.2"
- },
- "peerDependencies": {
- "typescript": "*"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
+ "picomatch": "^4.0.4"
}
},
"node_modules/@vue/reactivity": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz",
- "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==",
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.33.tgz",
+ "integrity": "sha512-p8UfIqyIhb0rYGlSgSBV+lPhF2iUSBcRy7enhTmPqKWadHy9kcOFYF1AejYBP9P+avnd3OBbD49DU4pLWX/94A==",
"license": "MIT",
"dependencies": {
- "@vue/shared": "3.5.21"
+ "@vue/shared": "3.5.33"
}
},
"node_modules/@vue/runtime-core": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz",
- "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==",
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.33.tgz",
+ "integrity": "sha512-UpFF45RI9//a7rvq7RdOQblb4tup7hHG9QsmIrxkFQLzQ7R8/iNQ5LE15NhLZ1/WcHMU2b47u6P33CPUelHyIQ==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.5.21",
- "@vue/shared": "3.5.21"
+ "@vue/reactivity": "3.5.33",
+ "@vue/shared": "3.5.33"
}
},
"node_modules/@vue/runtime-dom": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz",
- "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==",
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.33.tgz",
+ "integrity": "sha512-IOxMsAOwquhfITgmOgaPYl7/j8gKUxUFoflRc+u4LxyD3+783xne8vNta1PONVCvCV9A0w7hkyEepINDqfO0tw==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.5.21",
- "@vue/runtime-core": "3.5.21",
- "@vue/shared": "3.5.21",
- "csstype": "^3.1.3"
+ "@vue/reactivity": "3.5.33",
+ "@vue/runtime-core": "3.5.33",
+ "@vue/shared": "3.5.33",
+ "csstype": "^3.2.3"
}
},
"node_modules/@vue/server-renderer": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz",
- "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==",
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.33.tgz",
+ "integrity": "sha512-0xylq/8/h44lVG0pZFknv1XIdEgymq2E9n59uTWJBG+dIgiT0TMCSsxrN7nO16Z0MU0MPjFcguBbZV8Itk52Hw==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-ssr": "3.5.21",
- "@vue/shared": "3.5.21"
+ "@vue/compiler-ssr": "3.5.33",
+ "@vue/shared": "3.5.33"
},
"peerDependencies": {
- "vue": "3.5.21"
+ "vue": "3.5.33"
}
},
"node_modules/@vue/shared": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz",
- "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==",
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.33.tgz",
+ "integrity": "sha512-5vR2QIlmaLG77Ygd4pMP6+SGQ5yox9VhtnbDWTy9DzMzdmeLxZ1QqxrywEZ9sa1AVubfIJyaCG3ytyWU81ufcQ==",
"license": "MIT"
},
"node_modules/@vue/tsconfig": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.8.1.tgz",
- "integrity": "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==",
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.9.1.tgz",
+ "integrity": "sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==",
"dev": true,
"license": "MIT",
"peerDependencies": {
- "typescript": "5.x",
+ "typescript": ">= 5.8",
"vue": "^3.4.0"
},
"peerDependenciesMeta": {
@@ -1089,29 +619,32 @@
}
},
"node_modules/alien-signals": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.7.tgz",
- "integrity": "sha512-wE7y3jmYeb0+h6mr5BOovuqhFv22O/MV9j5p0ndJsa7z1zJNPGQ4ph5pQk/kTTCWRC3xsA4SmtwmkzQO+7NCNg==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.2.tgz",
+ "integrity": "sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==",
"dev": true,
"license": "MIT"
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
- "node_modules/de-indent": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
- "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+ "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": "MIT"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
},
"node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -1120,48 +653,6 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
- "node_modules/esbuild": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
- "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.7",
- "@esbuild/android-arm": "0.27.7",
- "@esbuild/android-arm64": "0.27.7",
- "@esbuild/android-x64": "0.27.7",
- "@esbuild/darwin-arm64": "0.27.7",
- "@esbuild/darwin-x64": "0.27.7",
- "@esbuild/freebsd-arm64": "0.27.7",
- "@esbuild/freebsd-x64": "0.27.7",
- "@esbuild/linux-arm": "0.27.7",
- "@esbuild/linux-arm64": "0.27.7",
- "@esbuild/linux-ia32": "0.27.7",
- "@esbuild/linux-loong64": "0.27.7",
- "@esbuild/linux-mips64el": "0.27.7",
- "@esbuild/linux-ppc64": "0.27.7",
- "@esbuild/linux-riscv64": "0.27.7",
- "@esbuild/linux-s390x": "0.27.7",
- "@esbuild/linux-x64": "0.27.7",
- "@esbuild/netbsd-arm64": "0.27.7",
- "@esbuild/netbsd-x64": "0.27.7",
- "@esbuild/openbsd-arm64": "0.27.7",
- "@esbuild/openbsd-x64": "0.27.7",
- "@esbuild/openharmony-arm64": "0.27.7",
- "@esbuild/sunos-x64": "0.27.7",
- "@esbuild/win32-arm64": "0.27.7",
- "@esbuild/win32-ia32": "0.27.7",
- "@esbuild/win32-x64": "0.27.7"
- }
- },
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
@@ -1201,20 +692,283 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
- "node_modules/he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
"dev": true,
- "license": "MIT",
- "bin": {
- "he": "bin/he"
+ "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.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "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.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "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/magic-string": {
- "version": "0.30.19",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
- "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
@@ -1299,51 +1053,47 @@
"node": "^10 || ^12 || >=14"
}
},
- "node_modules/rollup": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
- "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
+ "node_modules/rolldown": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz",
+ "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@types/estree": "1.0.8"
+ "@oxc-project/types": "=0.127.0",
+ "@rolldown/pluginutils": "1.0.0-rc.17"
},
"bin": {
- "rollup": "dist/bin/rollup"
+ "rolldown": "bin/cli.mjs"
},
"engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.59.0",
- "@rollup/rollup-android-arm64": "4.59.0",
- "@rollup/rollup-darwin-arm64": "4.59.0",
- "@rollup/rollup-darwin-x64": "4.59.0",
- "@rollup/rollup-freebsd-arm64": "4.59.0",
- "@rollup/rollup-freebsd-x64": "4.59.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
- "@rollup/rollup-linux-arm64-gnu": "4.59.0",
- "@rollup/rollup-linux-arm64-musl": "4.59.0",
- "@rollup/rollup-linux-loong64-gnu": "4.59.0",
- "@rollup/rollup-linux-loong64-musl": "4.59.0",
- "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
- "@rollup/rollup-linux-ppc64-musl": "4.59.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
- "@rollup/rollup-linux-riscv64-musl": "4.59.0",
- "@rollup/rollup-linux-s390x-gnu": "4.59.0",
- "@rollup/rollup-linux-x64-gnu": "4.59.0",
- "@rollup/rollup-linux-x64-musl": "4.59.0",
- "@rollup/rollup-openbsd-x64": "4.59.0",
- "@rollup/rollup-openharmony-arm64": "4.59.0",
- "@rollup/rollup-win32-arm64-msvc": "4.59.0",
- "@rollup/rollup-win32-ia32-msvc": "4.59.0",
- "@rollup/rollup-win32-x64-gnu": "4.59.0",
- "@rollup/rollup-win32-x64-msvc": "4.59.0",
- "fsevents": "~2.3.2"
+ "@rolldown/binding-android-arm64": "1.0.0-rc.17",
+ "@rolldown/binding-darwin-arm64": "1.0.0-rc.17",
+ "@rolldown/binding-darwin-x64": "1.0.0-rc.17",
+ "@rolldown/binding-freebsd-x64": "1.0.0-rc.17",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17",
+ "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17",
+ "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17",
+ "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17",
+ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17"
}
},
+ "node_modules/rolldown/node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.17",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz",
+ "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1354,14 +1104,14 @@
}
},
"node_modules/tinyglobby": {
- "version": "0.2.15",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
- "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "version": "0.2.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
- "picomatch": "^4.0.3"
+ "picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
@@ -1370,10 +1120,18 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
+ "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==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
"node_modules/typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
@@ -1385,25 +1143,24 @@
}
},
"node_modules/undici-types": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "version": "7.19.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
+ "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
"dev": true,
"license": "MIT"
},
"node_modules/vite": {
- "version": "7.3.2",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
- "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
+ "version": "8.0.10",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
+ "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.27.0",
- "fdir": "^6.5.0",
- "picomatch": "^4.0.3",
- "postcss": "^8.5.6",
- "rollup": "^4.43.0",
- "tinyglobby": "^0.2.15"
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.10",
+ "rolldown": "1.0.0-rc.17",
+ "tinyglobby": "^0.2.16"
},
"bin": {
"vite": "bin/vite.js"
@@ -1419,9 +1176,10 @@
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.1.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
- "lightningcss": "^1.21.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
@@ -1434,13 +1192,16 @@
"@types/node": {
"optional": true
},
- "jiti": {
+ "@vitejs/devtools": {
"optional": true
},
- "less": {
+ "esbuild": {
"optional": true
},
- "lightningcss": {
+ "jiti": {
+ "optional": true
+ },
+ "less": {
"optional": true
},
"sass": {
@@ -1474,16 +1235,16 @@
"license": "MIT"
},
"node_modules/vue": {
- "version": "3.5.21",
- "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz",
- "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
+ "version": "3.5.33",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.33.tgz",
+ "integrity": "sha512-1AgChhx5w3ALgT4oK3acm2Es/7jyZhWSVUfs3rOBlGQC0rjEDkS7G4lWlJJGGNQD+BV3reCwbQrOe1mPNwKHBQ==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.21",
- "@vue/compiler-sfc": "3.5.21",
- "@vue/runtime-dom": "3.5.21",
- "@vue/server-renderer": "3.5.21",
- "@vue/shared": "3.5.21"
+ "@vue/compiler-dom": "3.5.33",
+ "@vue/compiler-sfc": "3.5.33",
+ "@vue/runtime-dom": "3.5.33",
+ "@vue/server-renderer": "3.5.33",
+ "@vue/shared": "3.5.33"
},
"peerDependencies": {
"typescript": "*"
@@ -1495,14 +1256,14 @@
}
},
"node_modules/vue-tsc": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.0.7.tgz",
- "integrity": "sha512-BSMmW8GGEgHykrv7mRk6zfTdK+tw4MBZY/x6fFa7IkdXK3s/8hQRacPjG9/8YKFDIWGhBocwi6PlkQQ/93OgIQ==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.2.7.tgz",
+ "integrity": "sha512-zc1tL3HoQni1zGTGrwBVRQb7rGP5SWdu/m4rGB6JcnAC5MT5LFZIxF7Y+EJEnt4hGF23d60rXH7gRjHGb5KQQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@volar/typescript": "2.4.23",
- "@vue/language-core": "3.0.7"
+ "@volar/typescript": "2.4.28",
+ "@vue/language-core": "3.2.7"
},
"bin": {
"vue-tsc": "bin/vue-tsc.js"
diff --git a/examples/InfiniFrameExample.WebApp.Vue/Source/InfiniFrame.Vue/package.json b/examples/InfiniFrameExample.WebApp.Vue/Source/InfiniFrame.Vue/package.json
index abf33f797..b842b444d 100644
--- a/examples/InfiniFrameExample.WebApp.Vue/Source/InfiniFrame.Vue/package.json
+++ b/examples/InfiniFrameExample.WebApp.Vue/Source/InfiniFrame.Vue/package.json
@@ -9,14 +9,14 @@
"vue.fullscreen-preview": "vite preview"
},
"dependencies": {
- "vue": "^3.5.21"
+ "vue": "^3.5.33"
},
"devDependencies": {
- "@vitejs/plugin-vue": "^6.0.1",
- "@vue/tsconfig": "^0.8.1",
- "@types/node": "^20.12.5",
- "typescript": "~5.8.3",
- "vite": "^7.3.2",
- "vue-tsc": "^3.0.7"
+ "@vitejs/plugin-vue": "^6.0.6",
+ "@vue/tsconfig": "^0.9.1",
+ "@types/node": "^25.6.0",
+ "typescript": "6.0.3",
+ "vite": "^8.0.10",
+ "vue-tsc": "^3.2.7"
}
}
diff --git a/scripts/clion-linux-environment.sh b/scripts/clion-linux-environment.sh
new file mode 100644
index 000000000..e18653c1d
--- /dev/null
+++ b/scripts/clion-linux-environment.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+set -e
+
+echo "Updating package lists..."
+sudo apt update
+
+echo "Installing base dependencies..."
+sudo apt install -y \
+ apt-transport-https \
+ ca-certificates \
+ gnupg \
+ software-properties-common \
+ wget \
+ build-essential \
+ pkg-config \
+ lsb-release
+
+# ----------------------------------------------------------------------------------------------------------------------
+# CMake (latest via Kitware)
+# ----------------------------------------------------------------------------------------------------------------------
+echo "Installing latest CMake..."
+
+# Install key safely (no overwrite prompt, idempotent)
+wget -O- https://apt.kitware.com/keys/kitware-archive-latest.asc | \
+sudo gpg --batch --yes --dearmor \
+ -o /usr/share/keyrings/kitware-archive-keyring.gpg.tmp && \
+sudo mv /usr/share/keyrings/kitware-archive-keyring.gpg.tmp \
+ /usr/share/keyrings/kitware-archive-keyring.gpg
+
+. /etc/os-release
+UBUNTU_CODENAME=${VERSION_CODENAME:-$(lsb_release -cs)}
+
+# Avoid duplicate repo entries
+if [ ! -f /etc/apt/sources.list.d/kitware.list ]; then
+ echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ $UBUNTU_CODENAME main" | \
+ sudo tee /etc/apt/sources.list.d/kitware.list
+fi
+
+sudo apt update
+sudo apt install -y cmake
+
+# ----------------------------------------------------------------------------------------------------------------------
+# Modern GCC (required for C++23 )
+# ----------------------------------------------------------------------------------------------------------------------
+echo "Installing modern GCC..."
+
+if grep -qi ubuntu /etc/os-release; then
+ sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
+ sudo apt update
+ sudo apt install -y g++-13 gcc-13
+
+ echo "✅ GCC 13 installed"
+
+ # Safely register alternatives (non-fatal if already exists)
+ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100 || true
+ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 100 || true
+
+ # Prefer GCC 13
+ sudo update-alternatives --set gcc /usr/bin/gcc-13 || true
+ sudo update-alternatives --set g++ /usr/bin/g++-13 || true
+else
+ echo "⚠️ Non-Ubuntu system detected — skipping GCC PPA"
+ echo "👉 You may need to install a newer compiler manually"
+fi
+
+# Verify GCC 13 availability
+if command -v g++-13 >/dev/null 2>&1; then
+ echo "✅ GCC 13 available"
+else
+ echo "❌ GCC 13 NOT found — build may fail for C++23 ()"
+fi
+
+# ----------------------------------------------------------------------------------------------------------------------
+# Clang + libc++ (fallback / alternative toolchain)
+# ----------------------------------------------------------------------------------------------------------------------
+echo "Installing Clang toolchain (optional but recommended)..."
+
+sudo apt install -y clang libc++-dev libc++abi-dev || true
+
+# ----------------------------------------------------------------------------------------------------------------------
+# GTK / WebKit / Native deps
+# ----------------------------------------------------------------------------------------------------------------------
+echo "Installing GTK/WebKit dependencies..."
+
+sudo apt install -y \
+ libgtk-3-dev \
+ libwebkit2gtk-4.1-dev \
+ libssl-dev \
+ libcurl4-openssl-dev \
+ zlib1g-dev \
+ libnotify-dev
+
+# ----------------------------------------------------------------------------------------------------------------------
+# Toolchain environment (important for CMake correctness)
+# ----------------------------------------------------------------------------------------------------------------------
+echo "Setting compiler environment..."
+
+export CC=gcc-13
+export CXX=g++-13
+
+# ----------------------------------------------------------------------------------------------------------------------
+# Verification
+# ----------------------------------------------------------------------------------------------------------------------
+echo "Verifying toolchain..."
+
+echo "CMake version:"
+cmake --version
+
+echo "GCC version:"
+gcc --version || true
+
+echo "G++ version:"
+g++ --version || true
+
+echo "Clang version:"
+clang++ --version || true
+
+echo ""
+echo "Setup complete!"
\ No newline at end of file
diff --git a/scripts/docker-compose-linux.sh b/scripts/docker-compose-linux.sh
deleted file mode 100644
index 64d8acbb3..000000000
--- a/scripts/docker-compose-linux.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-docker compose -f ../docker/compose/linux-tests.yml build --no-cache linux-tests
\ No newline at end of file
diff --git a/scripts/docker-linux-compose.sh b/scripts/docker-linux-compose.sh
new file mode 100644
index 000000000..c2c914178
--- /dev/null
+++ b/scripts/docker-linux-compose.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+COMPOSE_FILE="$SCRIPT_DIR/../docker/compose/infiniframe-linux.yml"
+
+docker compose -f "$COMPOSE_FILE" build --no-cache \
+ linux-tests \
+ linux-tests-playwright \
+ linux-example-blazorwebview
\ No newline at end of file
diff --git a/scripts/docker-linux-run-blazorwebview.sh b/scripts/docker-linux-run-blazorwebview.sh
new file mode 100644
index 000000000..d4d447e14
--- /dev/null
+++ b/scripts/docker-linux-run-blazorwebview.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+COMPOSE_FILE="${REPO_ROOT}/docker/compose/infiniframe-linux.yml"
+
+DISPLAY_VALUE="${DISPLAY:-:0}"
+
+docker compose -f "${COMPOSE_FILE}" run --rm \
+ -e USE_HOST_DISPLAY=1 \
+ -e DISPLAY="${DISPLAY_VALUE}" \
+ -v /tmp/.X11-unix:/tmp/.X11-unix \
+ linux-example-blazorwebview
diff --git a/scripts/docker-run-linux-playwright.sh b/scripts/docker-linux-run-playwrighttests.sh
similarity index 96%
rename from scripts/docker-run-linux-playwright.sh
rename to scripts/docker-linux-run-playwrighttests.sh
index 91111f63a..6e25d45d8 100644
--- a/scripts/docker-run-linux-playwright.sh
+++ b/scripts/docker-linux-run-playwrighttests.sh
@@ -15,4 +15,4 @@ docker compose -f "${COMPOSE_FILE}" run --rm \
-e PLAYWRIGHT_VISIBLE_DEBUG="${PLAYWRIGHT_VISIBLE_DEBUG_VALUE}" \
-e PLAYWRIGHT_VISIBLE_DEBUG_SECONDS="${PLAYWRIGHT_VISIBLE_DEBUG_SECONDS_VALUE}" \
-v /tmp/.X11-unix:/tmp/.X11-unix \
- linux-playwright-tests
+ linux-tests-playwright
diff --git a/scripts/docker-run-linux-tests.sh b/scripts/docker-linux-run-tests.sh
similarity index 83%
rename from scripts/docker-run-linux-tests.sh
rename to scripts/docker-linux-run-tests.sh
index a9aea3467..92ae38226 100644
--- a/scripts/docker-run-linux-tests.sh
+++ b/scripts/docker-linux-run-tests.sh
@@ -3,7 +3,7 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
-COMPOSE_FILE="${REPO_ROOT}/docker/compose/linux-tests.yml"
+COMPOSE_FILE="${REPO_ROOT}/docker/compose/infiniframe-linux.yml"
DISPLAY_VALUE="${DISPLAY:-:0}"
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 81620249c..1a44f6ec7 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,27 +1,40 @@
+
net8.0;net9.0;net10.0
12.0
13.0
14.0
+
true
latest
+ true
+
enable
enable
- true
true
+
+
+ true
+ true
+
+
embedded
- true
- true
true
+ true
+ true
+
+
+ true
+ true
+
true
false
- true
- true
+
0.10.0
InfiniFrame, TryPhotino
LICENSE
diff --git a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorApp.cs b/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorApp.cs
index cc4ea35f7..99f0d51f1 100644
--- a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorApp.cs
+++ b/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorApp.cs
@@ -64,34 +64,30 @@ public void Run() {
private static bool IsNonFatalException(Exception exception)
=> exception is not (OutOfMemoryException or AccessViolationException);
-
+
public async ValueTask DisposeAsync() {
if (_disposed) return;
_disposed = true;
+ ILogger? logger = null;
+
try {
+ logger = ServiceProvider.GetService>();
+
UnhandledExceptionRegistration?.Dispose();
switch (ServiceProvider) {
- case ServiceProvider serviceProvider: {
- await serviceProvider.DisposeAsync();
- break;
- }
-
- case IAsyncDisposable asyncDisposable: {
+ case IAsyncDisposable asyncDisposable:
await asyncDisposable.DisposeAsync();
break;
- }
- case IDisposable disposable: {
+ case IDisposable disposable:
disposable.Dispose();
break;
- }
}
}
catch (Exception e) when (IsNonFatalException(e)) {
- var logger = ServiceProvider.GetService>();
logger?.LogError(e, "Error disposing of InfiniFrameBlazorApp");
}
diff --git a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilder.cs b/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilder.cs
index 0d1c9a382..a20d0f9c7 100644
--- a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilder.cs
+++ b/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilder.cs
@@ -149,10 +149,22 @@ public InfiniFrameBlazorApp Build(IServiceProvider serviceProvider) {
private static IDisposable RegisterUnhandledExceptionHandler(IServiceProvider serviceProvider) {
var exceptionSource = serviceProvider.GetRequiredService();
+
return exceptionSource.Register((_, error) => {
- serviceProvider
- .GetService()?
- .ShowMessage("Fatal exception", error.ExceptionObject.ToString());
+ try {
+ var window = serviceProvider.GetService();
+
+ // Only interact if safe
+ window?.Invoke(() => {
+ window.ShowMessage(
+ "Fatal exception",
+ error.ExceptionObject.ToString()
+ );
+ });
+ }
+ catch (Exception) {
+ // Never throw from global exception handler
+ }
});
}
}
diff --git a/src/InfiniFrame.BlazorWebView/InfiniFrameWebViewManager.cs b/src/InfiniFrame.BlazorWebView/InfiniFrameWebViewManager.cs
index ee5b566e6..cf222f5f9 100644
--- a/src/InfiniFrame.BlazorWebView/InfiniFrameWebViewManager.cs
+++ b/src/InfiniFrame.BlazorWebView/InfiniFrameWebViewManager.cs
@@ -23,7 +23,13 @@ public class InfiniFrameWebViewManager : WebViewManager, IInfiniFrameWebViewMana
public const string BlazorAppScheme = "app";
public const string AppBaseUri = $"{BlazorAppScheme}://localhost/";
- private readonly Channel _channel = Channel.CreateUnbounded(new UnboundedChannelOptions { SingleReader = true, SingleWriter = false, AllowSynchronousContinuations = false });
+ private readonly Channel _channel =
+ Channel.CreateUnbounded(new UnboundedChannelOptions {
+ SingleReader = true,
+ SingleWriter = false,
+ AllowSynchronousContinuations = false
+ });
+
private readonly Task _messagePumpTask;
private readonly SynchronousTaskScheduler _syncScheduler = new();
private readonly InfiniFrameUriSecurityPolicy _uriSecurityPolicy;
@@ -49,42 +55,84 @@ IOptions config
builder.RegisterWebMessageReceivedHandler((_, message) => {
string? origin = InfiniFrameWebMessageContext.CurrentOrigin;
- LazyLogger.Value?.LogDebug("Web message callback from native. Origin: {Origin}, Message: {Message}", origin, message);
- // On some platforms, we need to move off the browser UI thread
+ LazyLogger.Value?.LogDebug(
+ "Web message callback from native. Origin: {Origin}, Message: {Message}",
+ origin,
+ message);
+
Task.Factory.StartNew(
- action: state => HandleWebMessage(((string Message, string? Origin))state!),
+ state => HandleWebMessage(((string Message, string? Origin))state!),
(Message: message, Origin: origin),
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
_syncScheduler);
});
- // Start the reader and observe/await it during disposal.
_messagePumpTask = Task.Run(MessagePump);
}
+
private Lazy LazyWindow { get; }
private Lazy?> LazyLogger { get; }
- public Stream? HandleWebRequest(object? sender, string? schema, string? url, out string? contentType) {
+ // -----------------------------------------------------------------------------------------------------------------
+ // Web Requests
+ // -----------------------------------------------------------------------------------------------------------------
+ public Stream? HandleWebRequest(
+ object? sender,
+ string? schema,
+ string? url,
+ out string? contentType
+ ) {
+ contentType = null;
+
if (string.IsNullOrWhiteSpace(url)) {
- LazyLogger.Value?.LogWarning("Rejected web request because URL is null or empty. Schema: {Schema}", schema);
- contentType = null;
+ LazyLogger.Value?.LogWarning(
+ "Rejected web request because URL is null or empty. Schema: {Schema}",
+ schema
+ );
return null;
}
if (!Uri.TryCreate(url, UriKind.Absolute, out Uri? requestUri)) {
- LazyLogger.Value?.LogWarning("Rejected web request because URL parsing failed. Url: {Url}, Schema: {Schema}", url, schema);
- contentType = null;
+ LazyLogger.Value?.LogWarning(
+ "Rejected web request because URL parsing failed. Url: {Url}, Schema: {Schema}",
+ url, schema
+ );
return null;
}
+ // ---------------------------------------------------------------------
+ // IMPORTANT FIX: allow Blazor + app internal scheme BEFORE validation
+ // ---------------------------------------------------------------------
+ if (requestUri.Scheme == BlazorAppScheme) {
+ // no security policy, no warnings — this is framework/app internal traffic
+
+ string localPath = requestUri.LocalPath;
+ bool hasFileExtension = Path.HasExtension(localPath);
+
+ Uri sanitizedUri = new UriBuilder(requestUri) {
+ Query = string.Empty,
+ Fragment = string.Empty
+ }.Uri;
+
+ if (!TryGetResponseContent(sanitizedUri.AbsoluteUri, !hasFileExtension, out _, out _, out Stream content, out IDictionary headers))
+ return null;
+
+ headers.TryGetValue("Content-Type", out contentType);
+ contentType ??= GetFallbackContentType(sanitizedUri.LocalPath);
+ return content;
+
+ }
+
+ // ---------------------------------------------------------------------
+ // External / non-Blazor traffic (secure path)
+ // ---------------------------------------------------------------------
if (!_uriSecurityPolicy.IsNavigationSchemeAllowed(requestUri.Scheme)) {
LazyLogger.Value?.LogWarning(
"Rejected web request due to disallowed URI scheme. Scheme: {Scheme}, Url: {Url}",
requestUri.Scheme,
requestUri);
- contentType = null;
return null;
}
@@ -93,59 +141,70 @@ IOptions config
"Rejected web request due to untrusted origin. RequestOrigin: {RequestOrigin}, TrustedOrigins: {TrustedOrigins}",
requestUri,
_uriSecurityPolicy.TrustedOrigins);
- contentType = null;
return null;
}
- if (!string.IsNullOrWhiteSpace(schema)
- && !string.Equals(schema, requestUri.Scheme, StringComparison.OrdinalIgnoreCase)) {
+ if (!string.IsNullOrWhiteSpace(schema) &&
+ !string.Equals(schema, requestUri.Scheme, StringComparison.OrdinalIgnoreCase)) {
LazyLogger.Value?.LogWarning(
"Rejected web request due to schema mismatch. ReportedSchema: {ReportedSchema}, UriScheme: {UriScheme}, Url: {Url}",
schema,
requestUri.Scheme,
url);
- contentType = null;
return null;
}
- // It would be better if we were told whether this is a navigation request, but
- // since we're not, guess.
- string localPath = requestUri.LocalPath;
- bool hasFileExtension = localPath.LastIndexOf('.') > localPath.LastIndexOf('/');
+ string localPath2 = requestUri.LocalPath;
+ bool hasFileExtension2 = Path.HasExtension(localPath2);
- // Remove query/fragment before attempting to retrieve the file.
- Uri sanitizedUri = new UriBuilder(requestUri) { Query = string.Empty, Fragment = string.Empty }.Uri;
- string sanitizedUrl = sanitizedUri.AbsoluteUri;
+ Uri sanitizedUri2 = new UriBuilder(requestUri) {
+ Query = string.Empty,
+ Fragment = string.Empty
+ }.Uri;
- if (TryGetResponseContent(sanitizedUrl, !hasFileExtension, out _, out _,
- out Stream content, out IDictionary headers)) {
- headers.TryGetValue("Content-Type", out contentType);
- contentType ??= GetFallbackContentType(sanitizedUri.LocalPath);
- return content;
+ if (TryGetResponseContent(
+ sanitizedUri2.AbsoluteUri,
+ !hasFileExtension2,
+ out _,
+ out _,
+ out Stream content2,
+ out IDictionary headers2)) {
+ headers2.TryGetValue("Content-Type", out contentType);
+ contentType ??= GetFallbackContentType(sanitizedUri2.LocalPath);
+ return content2;
}
- LazyLogger.Value?.LogWarning("No web content found for trusted URL. Url: {Url}", sanitizedUrl);
- contentType = null;
+ LazyLogger.Value?.LogWarning(
+ "No web content found for trusted URL. Url: {Url}",
+ sanitizedUri2);
+
return null;
}
// -----------------------------------------------------------------------------------------------------------------
- // Methods
+ // Web message handling
// -----------------------------------------------------------------------------------------------------------------
private void HandleWebMessage((string Message, string? Origin) state) {
Uri? messageOriginUrl;
+
if (!string.IsNullOrWhiteSpace(state.Origin)) {
if (!Uri.TryCreate(state.Origin, UriKind.Absolute, out messageOriginUrl)) {
- LazyLogger.Value?.LogWarning("Rejected web message because origin parsing failed. Origin: {Origin}", state.Origin);
+ LazyLogger.Value?.LogWarning(
+ "Rejected web message because origin parsing failed. Origin: {Origin}",
+ state.Origin);
return;
}
}
- else if (Uri.TryCreate(AppBaseUri, UriKind.Absolute, out Uri? fallbackOriginUrl)) {
- messageOriginUrl = fallbackOriginUrl;
- LazyLogger.Value?.LogDebug("Web message origin missing. Falling back to AppBaseUri origin: {FallbackOrigin}", fallbackOriginUrl);
+ else if (Uri.TryCreate(AppBaseUri, UriKind.Absolute, out Uri? fallback)) {
+ messageOriginUrl = fallback;
+
+ LazyLogger.Value?.LogDebug(
+ "Web message origin missing. Falling back to AppBaseUri origin: {FallbackOrigin}",
+ fallback);
}
else {
- LazyLogger.Value?.LogWarning("Rejected web message because origin is missing or unknown.");
+ LazyLogger.Value?.LogWarning(
+ "Rejected web message because origin is missing or unknown.");
return;
}
@@ -160,8 +219,11 @@ private void HandleWebMessage((string Message, string? Origin) state) {
MessageReceived(messageOriginUrl, state.Message);
}
+ // -----------------------------------------------------------------------------------------------------------------
+ // Navigation
+ // -----------------------------------------------------------------------------------------------------------------
protected override void NavigateCore(Uri absoluteUri) {
- LazyWindow.Value.Load(absoluteUri);// TODO handle exceptions
+ LazyWindow.Value.Load(absoluteUri);
}
protected override void SendMessage(string message) {
@@ -171,18 +233,16 @@ protected override void SendMessage(string message) {
}
private async Task MessagePump() {
- ChannelReader reader = _channel.Reader;
try {
- while (true) {
- string message = await reader.ReadAsync();
+ while (await _channel.Reader.ReadAsync() is { } message) {
await LazyWindow.Value.SendWebMessageAsync(message);
}
}
- catch (ChannelClosedException) {
- // ignored
+ catch (ChannelClosedException ex) {
+ LazyLogger.Value?.LogDebug(ex, "WebView message channel closed; stopping message pump.");
}
catch (OperationCanceledException) {
- // ignored
+ LazyLogger.Value?.LogDebug("WebView message pump cancellation requested.");
}
catch (Exception ex) when (IsNonFatalException(ex)) {
LazyLogger.Value?.LogError(ex, "Unhandled exception in WebView message pump.");
@@ -191,23 +251,19 @@ private async Task MessagePump() {
}
protected override async ValueTask DisposeAsyncCore() {
- //complete channel
try { _channel.Writer.Complete(); }
- catch (ChannelClosedException) {
- // ignored
+ catch (ChannelClosedException ex) {
+ LazyLogger.Value?.LogDebug(ex, "Channel was already closed during dispose.");
}
try {
await _messagePumpTask.WaitAsync(TimeSpan.FromSeconds(5));
}
- catch (ChannelClosedException) {
- // ignored
- }
catch (TimeoutException) {
- LazyLogger.Value?.LogWarning("Timed out while waiting for WebView message pump shutdown.");
+ LazyLogger.Value?.LogWarning(
+ "Timed out while waiting for WebView message pump shutdown.");
}
- //continue disposing
await base.DisposeAsyncCore();
}
@@ -216,6 +272,7 @@ private static bool IsNonFatalException(Exception exception)
private static string GetFallbackContentType(string localPath) {
string extension = Path.GetExtension(localPath);
+
if (string.IsNullOrWhiteSpace(extension)) return "application/octet-stream";
return extension.ToLowerInvariant() switch {
diff --git a/src/InfiniFrame.Js/HandlerNames.cs b/src/InfiniFrame.Js/HandlerNames.cs
index 07a161237..f21e65427 100644
--- a/src/InfiniFrame.Js/HandlerNames.cs
+++ b/src/InfiniFrame.Js/HandlerNames.cs
@@ -21,13 +21,12 @@ public static class HandlerNames {
internal const string RegisterTitleChange = $"{InfiniFramePrefix}:register:title:change";
internal const string WindowReady = $"{InfiniFramePrefix}:ready";
-
+ internal const string GetRequest = $"{InfiniFramePrefix}:get";
+ internal const string GetResponse = $"{InfiniFramePrefix}:get:response";
internal const string WindowMinimize = $"{InfiniFramePrefix}:window:minimize";
internal const string WindowMaximize = $"{InfiniFramePrefix}:window:maximize";
internal const string WindowClose = $"{InfiniFramePrefix}:window:close";
- // internal const string WindowOpen = $"{InfiniFramePrefix}:window:open";
internal const string RegisterWindowClose = $"{InfiniFramePrefix}:register:window:close";
- // internal const string RegisterWindowOpen = $"{InfiniFramePrefix}:register:window:open";
}
diff --git a/src/InfiniFrame.Js/InfiniFrame.Js.csproj b/src/InfiniFrame.Js/InfiniFrame.Js.csproj
index ffb1f603a..ab5c27b15 100644
--- a/src/InfiniFrame.Js/InfiniFrame.Js.csproj
+++ b/src/InfiniFrame.Js/InfiniFrame.Js.csproj
@@ -28,6 +28,12 @@
+
+ Never
+ Never
+ false
+ true
+
diff --git a/src/InfiniFrame.Js/InfiniFrameJs.cs b/src/InfiniFrame.Js/InfiniFrameJs.cs
index a803643fd..d39bf66d1 100644
--- a/src/InfiniFrame.Js/InfiniFrameJs.cs
+++ b/src/InfiniFrame.Js/InfiniFrameJs.cs
@@ -12,7 +12,7 @@ namespace InfiniFrame.Js;
public class InfiniFrameJs(IJSRuntime jsRuntime, ILogger logger) : IInfiniFrameJs {
public async Task SetPointerCaptureAsync(ElementReference elementReference, long pointerId, CancellationToken ct = default) {
try {
- await jsRuntime.InvokeVoidAsync("infiniFrame.setPointerCapture", ct, elementReference, pointerId);
+ await jsRuntime.InvokeVoidAsync("infiniframe.utils.setPointerCapture", ct, elementReference, pointerId);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested) {
// ignore cancellation
@@ -27,7 +27,7 @@ public async Task SetPointerCaptureAsync(ElementReference elementReference, long
public async Task ReleasePointerCaptureAsync(ElementReference elementReference, long pointerId, CancellationToken ct = default) {
try {
- await jsRuntime.InvokeVoidAsync("infiniFrame.releasePointerCapture", ct, elementReference, pointerId);
+ await jsRuntime.InvokeVoidAsync("infiniframe.utils.releasePointerCapture", ct, elementReference, pointerId);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested) {
// ignore cancellation
diff --git a/src/InfiniFrame.Js/Interop/InteropEnvelopeProtocol.cs b/src/InfiniFrame.Js/Interop/InteropEnvelopeProtocol.cs
index 39104fdbd..6beac420e 100644
--- a/src/InfiniFrame.Js/Interop/InteropEnvelopeProtocol.cs
+++ b/src/InfiniFrame.Js/Interop/InteropEnvelopeProtocol.cs
@@ -10,8 +10,10 @@ namespace InfiniFrame.Js.Interop;
// Code
// ---------------------------------------------------------------------------------------------------------------------
internal static class InteropEnvelopeProtocol {
- internal const int CurrentVersion = 1;
+ internal const int CurrentVersion = 2;
internal const int MaxMessageSizeBytes = 1024 * 1024;
+ internal const string PostCommand = "Post";
+ internal const string GetCommand = "Get";
private static readonly JsonDocumentOptions JsonDocumentOptions = new() {
AllowTrailingCommas = false,
@@ -19,14 +21,18 @@ internal static class InteropEnvelopeProtocol {
MaxDepth = 64
};
- internal static string CreateEnvelopeMessage(string id, string? data = null) {
+ internal static string CreateEnvelopeMessage(string id, string? data = null, string command = PostCommand, string? requestId = null) {
ArgumentException.ThrowIfNullOrWhiteSpace(id);
+ ArgumentException.ThrowIfNullOrWhiteSpace(command);
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
writer.WriteStartObject();
writer.WriteString("id", id);
+ writer.WriteString("command", command);
+ if (!string.IsNullOrWhiteSpace(requestId))
+ writer.WriteString("requestId", requestId);
if (data is null)
writer.WriteNull("data");
else
@@ -41,6 +47,9 @@ internal static string CreateEnvelopeMessage(string id, string? data = null) {
internal static InteropEnvelopeParseResult ParseIncomingMessage(string message) {
if (string.IsNullOrWhiteSpace(message))
return InteropEnvelopeParseResult.CreateFailure("Message is empty.");
+
+ if (message.StartsWith("__bwv:", StringComparison.Ordinal))
+ return InteropEnvelopeParseResult.Ignored;
message = TryUnwrapJsonEncodedString(message);
@@ -49,7 +58,7 @@ internal static InteropEnvelopeParseResult ParseIncomingMessage(string message)
return InteropEnvelopeParseResult.CreateFailure($"Message exceeds max size of {MaxMessageSizeBytes} bytes.");
if (!LooksLikeJsonObject(message))
- return ParseLegacyMessage(message);
+ return InteropEnvelopeParseResult.CreateFailure("Envelope root must be a JSON object.");
try {
using JsonDocument jsonDocument = JsonDocument.Parse(message, JsonDocumentOptions);
@@ -73,11 +82,26 @@ internal static InteropEnvelopeParseResult ParseIncomingMessage(string message)
if (version != CurrentVersion)
return InteropEnvelopeParseResult.CreateFailure($"Unsupported envelope version '{version}'.");
+ if (!root.TryGetProperty("command", out JsonElement commandElement) || commandElement.ValueKind != JsonValueKind.String)
+ return InteropEnvelopeParseResult.CreateFailure("Envelope 'command' is required and must be a string.");
+
+ string? command = commandElement.GetString();
+ if (!IsSupportedCommand(command))
+ return InteropEnvelopeParseResult.CreateFailure("Envelope 'command' must be 'Post' or 'Get'.");
+
string? payload = null;
if (root.TryGetProperty("data", out JsonElement dataElement))
payload = ConvertDataToPayload(dataElement);
- return InteropEnvelopeParseResult.CreateSuccess(messageId, payload);
+ string? requestId = null;
+ if (root.TryGetProperty("requestId", out JsonElement requestIdElement)) {
+ if (requestIdElement.ValueKind != JsonValueKind.String)
+ return InteropEnvelopeParseResult.CreateFailure("Envelope 'requestId' must be a string.");
+
+ requestId = requestIdElement.GetString();
+ }
+
+ return InteropEnvelopeParseResult.CreateSuccess(messageId, payload, command, requestId);
}
catch (JsonException) {
return InteropEnvelopeParseResult.CreateFailure("Envelope JSON is malformed.");
@@ -101,17 +125,9 @@ private static string TryUnwrapJsonEncodedString(string message) {
}
}
- private static InteropEnvelopeParseResult ParseLegacyMessage(string message) {
- int separatorIndex = message.IndexOf(';');
- bool hasSeparator = separatorIndex >= 0;
-
- string messageId = (hasSeparator ? message[..separatorIndex] : message).Trim();
- if (string.IsNullOrWhiteSpace(messageId))
- return InteropEnvelopeParseResult.CreateFailure("Legacy message has an empty message ID.");
-
- string? payload = hasSeparator ? message[(separatorIndex + 1)..] : null;
- return InteropEnvelopeParseResult.CreateSuccess(messageId, payload, true);
- }
+ private static bool IsSupportedCommand(string? command)
+ => string.Equals(command, PostCommand, StringComparison.Ordinal)
+ || string.Equals(command, GetCommand, StringComparison.Ordinal);
private static bool LooksLikeJsonObject(string message) {
ReadOnlySpan span = message.AsSpan().TrimStart();
diff --git a/src/InfiniFrame.Js/Interop/MessageHandlers/FullscreenWebMessageHandler.cs b/src/InfiniFrame.Js/Interop/MessageHandlers/FullscreenWebMessageHandler.cs
index e3b2da884..2c0879a4e 100644
--- a/src/InfiniFrame.Js/Interop/MessageHandlers/FullscreenWebMessageHandler.cs
+++ b/src/InfiniFrame.Js/Interop/MessageHandlers/FullscreenWebMessageHandler.cs
@@ -7,19 +7,19 @@ namespace InfiniFrame.Js.Interop.MessageHandlers;
// ---------------------------------------------------------------------------------------------------------------------
public static class FullscreenWebMessageHandler {
public static T RegisterFullScreenWebMessageHandler(this T builder) where T : class, IInfiniFrameWindowBuilder {
- RegisterWindowCreatedUtility.RegisterMessageHandler(builder,
+ builder.MessageHandlers.RegisterHandler(
HandlerNames.FullscreenEnter,
- handler: static window => window.SetFullScreen(true)
+ (window, _) => window.SetFullScreen(true)
);
-
- RegisterWindowCreatedUtility.RegisterMessageHandler(builder,
+
+ builder.MessageHandlers.RegisterHandler(
HandlerNames.FullscreenExit,
- handler: static window => window.SetFullScreen(false)
+ (window, _) => window.SetFullScreen(false)
);
-
- RegisterWindowCreatedUtility.RegisterMessageHandler(builder,
+
+ builder.MessageHandlers.RegisterHandler(
HandlerNames.FullscreenToggle,
- handler: static window => window.SetFullScreen(!window.FullScreen)
+ (window, _) => window.SetFullScreen(!window.FullScreen)
);
RegisterWindowCreatedUtility.RegisterWindowCreatedWebMessage(builder, HandlerNames.RegisterFullScreenChange);
diff --git a/src/InfiniFrame.Js/Interop/MessageHandlers/OpenExternalTargetWebMessageHandler.cs b/src/InfiniFrame.Js/Interop/MessageHandlers/OpenExternalTargetWebMessageHandler.cs
index 878fc9aec..9084a57a8 100644
--- a/src/InfiniFrame.Js/Interop/MessageHandlers/OpenExternalTargetWebMessageHandler.cs
+++ b/src/InfiniFrame.Js/Interop/MessageHandlers/OpenExternalTargetWebMessageHandler.cs
@@ -14,7 +14,7 @@ public static class OpenExternalTargetWebMessageHandler {
// Methods
// -----------------------------------------------------------------------------------------------------------------
public static T RegisterOpenExternalTargetWebMessageHandler(this T builder) where T : class, IInfiniFrameWindowBuilder {
- RegisterWindowCreatedUtility.RegisterMessageHandler(builder, HandlerNames.OpenExternal, HandleWebMessage);
+ builder.MessageHandlers.RegisterHandler(HandlerNames.OpenExternal, HandleWebMessage);
RegisterWindowCreatedUtility.RegisterWindowCreatedWebMessage(builder, HandlerNames.RegisterOpenExternal);
return builder;
}
diff --git a/src/InfiniFrame.Js/Interop/MessageHandlers/StandardGetWebMessageHandler.cs b/src/InfiniFrame.Js/Interop/MessageHandlers/StandardGetWebMessageHandler.cs
new file mode 100644
index 000000000..7dec06906
--- /dev/null
+++ b/src/InfiniFrame.Js/Interop/MessageHandlers/StandardGetWebMessageHandler.cs
@@ -0,0 +1,68 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+using System.Globalization;
+using System.Text.Json;
+
+namespace InfiniFrame.Js.Interop.MessageHandlers;
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+public static class StandardGetWebMessageHandler {
+ public static T RegisterStandardGetWebMessageHandler(this T builder) where T : class, IInfiniFrameWindowBuilder {
+ builder.MessageHandlers.RegisterHandler(HandlerNames.GetRequest, HandleGetRequest);
+ return builder;
+ }
+
+ private static string? HandleGetRequest(IInfiniFrameWindow window, string? payload) {
+ // ReSharper disable once UnusedVariable
+ if (!TryParseGetRequest(payload, out string? command, out JsonElement? args))
+ return null;
+
+ return command switch {
+ "title" => window.Title,
+ "width" => window.Width.ToString(CultureInfo.InvariantCulture),
+ "height" => window.Height.ToString(CultureInfo.InvariantCulture),
+ "left" => window.Left.ToString(CultureInfo.InvariantCulture),
+ "top" => window.Top.ToString(CultureInfo.InvariantCulture),
+ "maximized" => window.Maximized.ToString(),
+ "minimized" => window.Minimized.ToString(),
+ "fullscreen" => window.FullScreen.ToString(),
+ "focused" => window.Focused.ToString(),
+ "resizable" => window.Resizable.ToString(),
+ "zoom" => window.Zoom.ToString(CultureInfo.InvariantCulture),
+ _ => null
+ };
+ }
+
+ private static bool TryParseGetRequest(string? payload, out string? command, out JsonElement? args) {
+ command = null;
+ args = null;
+
+ if (string.IsNullOrWhiteSpace(payload))
+ return false;
+
+ try {
+ using JsonDocument document = JsonDocument.Parse(payload);
+ if (document.RootElement.ValueKind != JsonValueKind.Object)
+ return false;
+
+ if (!document.RootElement.TryGetProperty("command", out JsonElement commandElement)
+ || commandElement.ValueKind != JsonValueKind.String) {
+ return false;
+ }
+
+ command = commandElement.GetString();
+ if (string.IsNullOrWhiteSpace(command))
+ return false;
+
+ if (document.RootElement.TryGetProperty("args", out JsonElement argsElement))
+ args = argsElement.Clone();
+
+ return true;
+ }
+ catch (JsonException) {
+ return false;
+ }
+ }
+}
diff --git a/src/InfiniFrame.Js/Interop/MessageHandlers/TitleChangedWebMessageHandler.cs b/src/InfiniFrame.Js/Interop/MessageHandlers/TitleChangedWebMessageHandler.cs
index b5ba3de11..3c9238faf 100644
--- a/src/InfiniFrame.Js/Interop/MessageHandlers/TitleChangedWebMessageHandler.cs
+++ b/src/InfiniFrame.Js/Interop/MessageHandlers/TitleChangedWebMessageHandler.cs
@@ -9,7 +9,7 @@ namespace InfiniFrame.Js.Interop.MessageHandlers;
// ---------------------------------------------------------------------------------------------------------------------
public static class TitleChangedWebMessageHandler {
public static T RegisterTitleChangedWebMessageHandler(this T builder) where T : class, IInfiniFrameWindowBuilder {
- RegisterWindowCreatedUtility.RegisterMessageHandler(builder, HandlerNames.TitleChanged, HandleWebMessage);
+ builder.MessageHandlers.RegisterHandler(HandlerNames.TitleChanged, HandleWebMessage);
RegisterWindowCreatedUtility.RegisterWindowCreatedWebMessage(builder, HandlerNames.RegisterTitleChange);
return builder;
}
diff --git a/src/InfiniFrame.Js/Interop/MessageHandlers/WindowManagementWebMessageHandler.cs b/src/InfiniFrame.Js/Interop/MessageHandlers/WindowManagementWebMessageHandler.cs
index 4f8cc2d21..505340d8d 100644
--- a/src/InfiniFrame.Js/Interop/MessageHandlers/WindowManagementWebMessageHandler.cs
+++ b/src/InfiniFrame.Js/Interop/MessageHandlers/WindowManagementWebMessageHandler.cs
@@ -7,17 +7,17 @@ namespace InfiniFrame.Js.Interop.MessageHandlers;
// ---------------------------------------------------------------------------------------------------------------------
public static class WindowManagementWebMessageHandler {
public static T RegisterWindowManagementWebMessageHandler(this T builder) where T : class, IInfiniFrameWindowBuilder {
- RegisterWindowCreatedUtility.RegisterMessageHandler(builder,
+ builder.MessageHandlers.RegisterHandler(
HandlerNames.WindowMinimize,
- handler: static window => window.SetMinimized(true));
+ (window, _) => window.SetMinimized(true));
- RegisterWindowCreatedUtility.RegisterMessageHandler(builder,
+ builder.MessageHandlers.RegisterHandler(
HandlerNames.WindowMaximize,
- handler: static window => window.SetMaximized(true));
+ (window, _) => window.SetMaximized(true));
- RegisterWindowCreatedUtility.RegisterMessageHandler(builder,
+ builder.MessageHandlers.RegisterHandler(
HandlerNames.WindowClose,
- handler: static window => window.Close());
+ (window, _) => window.Close());
RegisterWindowCreatedUtility.RegisterWindowCreatedWebMessage(builder, HandlerNames.RegisterWindowClose);
return builder;
diff --git a/src/InfiniFrame.Js/Interop/RegisterWindowCreatedUtility.cs b/src/InfiniFrame.Js/Interop/RegisterWindowCreatedUtility.cs
index f188a66aa..ee83bb51b 100644
--- a/src/InfiniFrame.Js/Interop/RegisterWindowCreatedUtility.cs
+++ b/src/InfiniFrame.Js/Interop/RegisterWindowCreatedUtility.cs
@@ -18,14 +18,6 @@ public static class RegisterWindowCreatedUtility {
// -----------------------------------------------------------------------------------------------------------------
// Methods
// -----------------------------------------------------------------------------------------------------------------
- public static void RegisterMessageHandler(IInfiniFrameWindowBuilder builder, string messageId, Action handler) {
- builder.MessageHandlers.RegisterMessageHandler(messageId, handler);
- }
-
- public static void RegisterMessageHandler(IInfiniFrameWindowBuilder builder, string messageId, Action handler) {
- builder.MessageHandlers.RegisterMessageHandler(messageId, handler: (w, _) => handler(w));
- }
-
public static void RegisterWindowCreatedWebMessage(IInfiniFrameWindowBuilder builder, string messageId) {
WindowReadyRegistrationState registrationState = RegistrationStates.GetOrCreateValue(builder);
@@ -65,7 +57,7 @@ private static void EnsureReadyHandler(IInfiniFrameWindowBuilder builder, Window
state.ReadyHandlerRegistered = true;
}
- RegisterMessageHandler(builder, HandlerNames.WindowReady, handler: (window, payload) => {
+ builder.MessageHandlers.RegisterHandler(HandlerNames.WindowReady, handler: (window, payload) => {
WindowRegistrationState windowState;
string[] registrationMessages;
lock (state.Lock) {
diff --git a/src/InfiniFrame.Js/TypeScript/Contracts/IInteropEnvelope.ts b/src/InfiniFrame.Js/TypeScript/Contracts/EnvelopeProtocol.ts
similarity index 77%
rename from src/InfiniFrame.Js/TypeScript/Contracts/IInteropEnvelope.ts
rename to src/InfiniFrame.Js/TypeScript/Contracts/EnvelopeProtocol.ts
index 846aad2ef..c3b643b98 100644
--- a/src/InfiniFrame.Js/TypeScript/Contracts/IInteropEnvelope.ts
+++ b/src/InfiniFrame.Js/TypeScript/Contracts/EnvelopeProtocol.ts
@@ -9,15 +9,20 @@ export interface InteropEnvelopeV1 {
id: string;
data?: unknown;
version: number;
+ command?: InteropEnvelopeCommand;
+ requestId?: string;
channel?: string;
}
export interface ParsedInteropMessage {
messageId: string;
payload?: string;
- isLegacyProtocol?: boolean;
+ command?: InteropEnvelopeCommand;
+ requestId?: string;
}
-export interface ParseError {
+export interface InteropParseError {
error: string;
}
+
+export type InteropEnvelopeCommand = "Post" | "Get";
diff --git a/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrame.ts b/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrame.ts
index bf6bc9380..6cc1dc027 100644
--- a/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrame.ts
+++ b/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrame.ts
@@ -1,17 +1,15 @@
// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
-import {IHostMessaging, SendToHostMessageId} from "./IHostMessaging";
+import {IInfiniFrameHostMessaging} from "./IInfiniFrameHostMessaging";
+import {IInfiniFrameUtils} from "./IInfiniFrameUtils";
+import {IInfiniFrameWindow} from "./IInfiniFrameWindow";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
export interface IInfiniFrame {
- HostMessaging: IHostMessaging;
-
- sendMessageToHost(id: SendToHostMessageId, data?: unknown): void;
-
- setPointerCapture(element: Element, pointerId: number): void;
-
- releasePointerCapture(element: Element, pointerId: number): void;
+ messaging: IInfiniFrameHostMessaging;
+ window: IInfiniFrameWindow;
+ utils: IInfiniFrameUtils;
}
diff --git a/src/InfiniFrame.Js/TypeScript/Contracts/IHostMessaging.ts b/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrameHostMessaging.ts
similarity index 53%
rename from src/InfiniFrame.Js/TypeScript/Contracts/IHostMessaging.ts
rename to src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrameHostMessaging.ts
index 6504a474c..35811511b 100644
--- a/src/InfiniFrame.Js/TypeScript/Contracts/IHostMessaging.ts
+++ b/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrameHostMessaging.ts
@@ -1,33 +1,37 @@
// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
+import {InteropEnvelopeV1} from "./EnvelopeProtocol";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
-const infiniFrame: string = "__infiniframe";
+const infiniframe: string = "__infiniframe";
export const SendToHostMessageIds = {
- titleChange: `${infiniFrame}:title:change`,
- fullscreenEnter: `${infiniFrame}:fullscreen:enter`,
- fullscreenExit: `${infiniFrame}:fullscreen:exit`,
- openExternalLink: `${infiniFrame}:open:external`,
- windowClose: `${infiniFrame}:window:close`,
- ready: `${infiniFrame}:ready`,
+ getRequest: `${infiniframe}:get`,
+ titleChange: `${infiniframe}:title:change`,
+ fullscreenEnter: `${infiniframe}:fullscreen:enter`,
+ fullscreenExit: `${infiniframe}:fullscreen:exit`,
+ openExternalLink: `${infiniframe}:open:external`,
+ windowClose: `${infiniframe}:window:close`,
+ ready: `${infiniframe}:ready`,
}
export const ReceiveFromHostMessageIds = {
- registerOpenExternal: `${infiniFrame}:register:open:external`,
- registerFullscreenChange: `${infiniFrame}:register:fullscreen:change`,
- registerTitleChange: `${infiniFrame}:register:title:change`,
- registerWindowClose: `${infiniFrame}:register:window:close`,
+ registerOpenExternal: `${infiniframe}:register:open:external`,
+ registerFullscreenChange: `${infiniframe}:register:fullscreen:change`,
+ registerTitleChange: `${infiniframe}:register:title:change`,
+ registerWindowClose: `${infiniframe}:register:window:close`,
+ getMessageResponse: `${infiniframe}:get:response`,
}
export type SendToHostMessageId = typeof SendToHostMessageIds[keyof typeof SendToHostMessageIds];
export type MessageCallback = (data?: string) => void;
-export interface IHostMessaging {
+export interface IInfiniFrameHostMessaging {
sendMessageToHost(id: SendToHostMessageId | string, data?: unknown): void;
+ getMessageFromHostAsync(message: InteropEnvelopeV1 | string): Promise;
assignMessageReceivedHandler(messageId: string, callback: MessageCallback): void;
diff --git a/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrameUtils.ts b/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrameUtils.ts
new file mode 100644
index 000000000..8020d9a83
--- /dev/null
+++ b/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrameUtils.ts
@@ -0,0 +1,11 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+export interface IInfiniFrameUtils {
+ setPointerCapture(element: Element, pointerId: number): void;
+ releasePointerCapture(element: Element, pointerId: number): void;
+}
\ No newline at end of file
diff --git a/src/InfiniFrame.Shared/IInfiniFrameWindowMessageHandlers.cs b/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrameWindow.ts
similarity index 66%
rename from src/InfiniFrame.Shared/IInfiniFrameWindowMessageHandlers.cs
rename to src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrameWindow.ts
index f38c88458..d41a6e655 100644
--- a/src/InfiniFrame.Shared/IInfiniFrameWindowMessageHandlers.cs
+++ b/src/InfiniFrame.Js/TypeScript/Contracts/IInfiniFrameWindow.ts
@@ -1,13 +1,11 @@
// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
-namespace InfiniFrame;
+
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
-public interface IInfiniFrameWindowMessageHandlers {
- bool IsEmpty { get; }
-
- void RegisterMessageHandler(string messageId, Action handler);
- void Handle(IInfiniFrameWindow sender, string message);
+export interface IInfiniFrameWindow {
+ setTitle(title: string): void;
+ getTitleAsync(): Promise;
}
diff --git a/src/InfiniFrame.Js/TypeScript/Contracts/global.ts b/src/InfiniFrame.Js/TypeScript/Contracts/global.ts
index 74fc3af9f..e676c827e 100644
--- a/src/InfiniFrame.Js/TypeScript/Contracts/global.ts
+++ b/src/InfiniFrame.Js/TypeScript/Contracts/global.ts
@@ -1,8 +1,8 @@
-// ---------------------------------------------------------------------------------------------------------------------
+// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
import {IInfiniFrame} from "./IInfiniFrame";
-import {InteropEnvelopeV1} from "./IInteropEnvelope";
+import {InteropEnvelopeV1} from "./EnvelopeProtocol";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
@@ -10,18 +10,29 @@ export {}
declare global {
// noinspection JSUnusedGlobalSymbols
interface Window {
+ infiniframe : IInfiniFrame;
+
+ // Managed by InfiniFrame.Native
+ __infiniframe?: {
+ host?: {
+ postData(envelope: InteropEnvelopeV1 | string): void;
+ receiveCallback(callback: (message: string) => void): void;
+ getDataAsync?(message: InteropEnvelopeV1 | string): Promise;
+ };
+ };
+ __dispatchMessageCallback?: (message: string) => void;
chrome?: {
webview?: {
postMessage(message: string): void;
- addEventListener(type: 'message', listener: (event: { data: string }) => void): void;
+ addEventListener(type: "message", listener: (event: { data: string }) => void): void;
};
};
- infiniframe?: {
- host?: {
- postMessage(envelope: InteropEnvelopeV1 | string): void;
- receiveMessage(callback: (message: string) => void): void;
+ webkit?: {
+ messageHandlers?: {
+ infiniFrameInterop?: {
+ postMessage(message: string): void;
+ };
};
};
- infiniFrame: IInfiniFrame
}
}
diff --git a/src/InfiniFrame.Js/TypeScript/Contracts/index.ts b/src/InfiniFrame.Js/TypeScript/Contracts/index.ts
new file mode 100644
index 000000000..3ef2f1466
--- /dev/null
+++ b/src/InfiniFrame.Js/TypeScript/Contracts/index.ts
@@ -0,0 +1,9 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Exports
+// ---------------------------------------------------------------------------------------------------------------------
+export * from "./EnvelopeProtocol";
+export * from "./global";
+export * from "./IInfiniFrame";
+export * from "./IInfiniFrameHostMessaging";
+export * from "./IInfiniFrameUtils";
+export * from "./IInfiniFrameWindow";
\ No newline at end of file
diff --git a/src/InfiniFrame.Js/TypeScript/Index.ts b/src/InfiniFrame.Js/TypeScript/Index.ts
index 4fe97003c..ee12b20e4 100644
--- a/src/InfiniFrame.Js/TypeScript/Index.ts
+++ b/src/InfiniFrame.Js/TypeScript/Index.ts
@@ -2,10 +2,11 @@
// Imports
// ---------------------------------------------------------------------------------------------------------------------
import InfiniFrame from "./InfiniFrame";
-import {installHostBridge} from "./Interop/HostBridge";
+import {installHostBridge} from "./Interop/NativeHost/HostBridge";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
export {};
installHostBridge();
-window.infiniFrame = new InfiniFrame();
+
+window.infiniframe = new InfiniFrame();
diff --git a/src/InfiniFrame.Js/TypeScript/InfiniFrame.test.ts b/src/InfiniFrame.Js/TypeScript/InfiniFrame.test.ts
index 84f66cf91..9308b47b4 100644
--- a/src/InfiniFrame.Js/TypeScript/InfiniFrame.test.ts
+++ b/src/InfiniFrame.Js/TypeScript/InfiniFrame.test.ts
@@ -1,49 +1,46 @@
// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
-import {beforeEach, describe, expect, it, vi} from "vitest";
-import {SendToHostMessageIds} from "./Contracts/IHostMessaging";
+import {describe, it, expect, vi} from "vitest";
+import {InfiniFrame} from "./InfiniFrame";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
describe("InfiniFrame", () => {
- beforeEach(() => {
- vi.resetModules();
- });
- it("delegates sendMessageToHost to HostMessaging", async () => {
- const sendMessageToHost = vi.fn();
+ it("initializes HostMessaging and Utils", () => {
+ const instance = new InfiniFrame();
- vi.doMock("./HostMessaging", () => ({
- default: class {
- public sendMessageToHost = sendMessageToHost;
- }
- }));
+ expect(instance.messaging).toBeDefined();
+ expect(instance.utils).toBeDefined();
+ });
- const {InfiniFrame} = await import("./InfiniFrame");
- const instance = new InfiniFrame();
- instance.sendMessageToHost(SendToHostMessageIds.ready, "payload");
+ it("creates independent instances", () => {
+ const a = new InfiniFrame();
+ const b = new InfiniFrame();
- expect(sendMessageToHost).toHaveBeenCalledTimes(1);
- expect(sendMessageToHost).toHaveBeenCalledWith(SendToHostMessageIds.ready, "payload");
+ expect(a.messaging).not.toBe(b.messaging);
+ expect(a.utils).not.toBe(b.utils);
});
- it("forwards pointer capture APIs to element", async () => {
- const {InfiniFrame} = await import("./InfiniFrame");
- const instance = new InfiniFrame();
+ it("does not define or mutate window.__infiniframe.host", async () => {
+ const setSpy = vi.spyOn(Object, "defineProperty");
+
+ const win = window as any;
+
+ // ensure clean state
+ delete win.infiniframe;
+
+ await import("./InfiniFrame");
- const setPointerCapture = vi.fn();
- const releasePointerCapture = vi.fn();
- const element = {
- setPointerCapture,
- releasePointerCapture
- } as unknown as Element;
+ // library should not define host via Object.defineProperty
+ const hostDefinitionCalls = setSpy.mock.calls.filter(call =>
+ String(call[1])?.includes?.("host")
+ );
- instance.setPointerCapture(element, 10);
- instance.releasePointerCapture(element, 10);
+ expect(hostDefinitionCalls.length).toBe(0);
- expect(setPointerCapture).toHaveBeenCalledWith(10);
- expect(releasePointerCapture).toHaveBeenCalledWith(10);
+ setSpy.mockRestore();
});
-});
+});
\ No newline at end of file
diff --git a/src/InfiniFrame.Js/TypeScript/InfiniFrame.ts b/src/InfiniFrame.Js/TypeScript/InfiniFrame.ts
index b59028991..39d011eb1 100644
--- a/src/InfiniFrame.Js/TypeScript/InfiniFrame.ts
+++ b/src/InfiniFrame.Js/TypeScript/InfiniFrame.ts
@@ -1,27 +1,18 @@
// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
-import {IInfiniFrame} from "./Contracts/IInfiniFrame";
-import {IHostMessaging, SendToHostMessageId} from "./Contracts/IHostMessaging";
-import HostMessaging from "./HostMessaging";
+import {IInfiniFrame, IInfiniFrameHostMessaging, IInfiniFrameUtils, IInfiniFrameWindow} from "./Contracts";
+import InfiniFrameHostMessaging from "./InfiniFrameHostMessaging";
+import {InfiniFrameUtils} from "./InfiniFrameUtils";
+import {InfiniFrameWindow} from "./InfiniFrameWindow";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
export class InfiniFrame implements IInfiniFrame {
- HostMessaging: IHostMessaging = new HostMessaging();
-
- // Overload to make a dev's life easier instead of having to go to the HostMessaging class
- sendMessageToHost(id: SendToHostMessageId, data?: unknown) {
- this.HostMessaging.sendMessageToHost(id, data);
- }
-
- setPointerCapture(element: Element, pointerId: number) {
- element.setPointerCapture(pointerId);
- }
-
- releasePointerCapture(element: Element, pointerId: number) {
- element.releasePointerCapture(pointerId);
- }
+ messaging: IInfiniFrameHostMessaging = new InfiniFrameHostMessaging();
+ window: IInfiniFrameWindow = new InfiniFrameWindow();
+
+ utils: IInfiniFrameUtils = new InfiniFrameUtils()
}
export default InfiniFrame
diff --git a/src/InfiniFrame.Js/TypeScript/HostMessaging.test.ts b/src/InfiniFrame.Js/TypeScript/InfiniFrameHostMessaging.test.ts
similarity index 67%
rename from src/InfiniFrame.Js/TypeScript/HostMessaging.test.ts
rename to src/InfiniFrame.Js/TypeScript/InfiniFrameHostMessaging.test.ts
index ac351a27d..7f58db314 100644
--- a/src/InfiniFrame.Js/TypeScript/HostMessaging.test.ts
+++ b/src/InfiniFrame.Js/TypeScript/InfiniFrameHostMessaging.test.ts
@@ -2,7 +2,7 @@
// Imports
// ---------------------------------------------------------------------------------------------------------------------
import {beforeEach, describe, expect, it, vi} from "vitest";
-import {ReceiveFromHostMessageIds, SendToHostMessageIds} from "./Contracts/IHostMessaging";
+import {ReceiveFromHostMessageIds, SendToHostMessageIds} from "./Contracts";
// ---------------------------------------------------------------------------------------------------------------------
// Code
@@ -10,15 +10,15 @@ import {ReceiveFromHostMessageIds, SendToHostMessageIds} from "./Contracts/IHost
type ReceiveMessageCallback = (message: string) => void;
type TestWindow = Window & {
- infiniframe?: {
+ __infiniframe?: {
host?: {
- postMessage: (message: unknown) => void;
- receiveMessage: (callback: ReceiveMessageCallback) => void;
+ postData: (message: unknown) => void;
+ receiveCallback: (callback: ReceiveMessageCallback) => void;
};
};
};
-describe("HostMessaging", () => {
+describe("InfiniFrameHostMessaging", () => {
const testWindow = window as TestWindow;
beforeEach(() => {
@@ -27,49 +27,50 @@ describe("HostMessaging", () => {
});
async function setupHostMessaging() {
- const postMessage = vi.fn();
- let receiveCallback: ReceiveMessageCallback | null = null;
- const receiveMessage = vi.fn((callback: ReceiveMessageCallback) => {
- receiveCallback = callback;
+ const postData = vi.fn();
+ let receiveCallbackInner: ReceiveMessageCallback | null = null;
+ const receiveCallback = vi.fn((callback: ReceiveMessageCallback) => {
+ receiveCallbackInner = callback;
});
- testWindow.infiniframe = {
+ testWindow.__infiniframe = {
host: {
- postMessage,
- receiveMessage
+ postData,
+ receiveCallback
}
};
const blankTargetHandler = vi.fn();
const titleObserverObserve = vi.fn();
- vi.doMock("./BlankTargetHandler", () => ({blankTargetHandler}));
- vi.doMock("./Observers", () => ({
+ vi.doMock("./Utils/BlankTargetHandler", () => ({blankTargetHandler}));
+ vi.doMock("./Utils/Observers", () => ({
getTitleObserverTarget: vi.fn(() => document.querySelector("title")),
getTitleObserver: vi.fn(() => ({observe: titleObserverObserve}))
}));
- const module = await import("./HostMessaging");
+ const module = await import("././InfiniFrameHostMessaging");
const messaging = new module.default();
return {
messaging,
- postMessage,
- receiveMessage,
- getReceiveCallback: () => receiveCallback!,
+ postData,
+ receiveCallback,
+ getReceiveCallback: () => receiveCallbackInner!,
blankTargetHandler,
titleObserverObserve
};
}
it("sends ready message on startup and wires receive callback", async () => {
- const {postMessage, receiveMessage} = await setupHostMessaging();
+ const {postData, receiveCallback} = await setupHostMessaging();
- expect(receiveMessage).toHaveBeenCalledTimes(1);
- expect(postMessage).toHaveBeenCalled();
- expect(postMessage.mock.calls[0][0]).toMatchObject({
+ expect(receiveCallback).toHaveBeenCalledTimes(1);
+ expect(postData).toHaveBeenCalled();
+ expect(postData.mock.calls[0][0]).toMatchObject({
id: SendToHostMessageIds.ready,
- version: 1
+ command: "Post",
+ version: 2
});
});
@@ -78,26 +79,12 @@ describe("HostMessaging", () => {
const handler = vi.fn();
messaging.assignMessageReceivedHandler("custom:event", handler);
- getReceiveCallback()(JSON.stringify({id: "custom:event", data: "payload", version: 1}));
+ getReceiveCallback()(JSON.stringify({id: "custom:event", command: "Post", data: "payload", version: 2}));
expect(handler).toHaveBeenCalledTimes(1);
expect(handler).toHaveBeenCalledWith("payload");
});
- it("logs legacy warning only once for repeated legacy messages", async () => {
- const {messaging, getReceiveCallback} = await setupHostMessaging();
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
- messaging.assignMessageReceivedHandler("legacy", vi.fn());
-
- getReceiveCallback()("legacy;a");
- getReceiveCallback()("legacy;b");
-
- const legacyWarnings = warnSpy.mock.calls.filter(call =>
- String(call[0]).includes("legacy inbound host message format")
- );
- expect(legacyWarnings.length).toBe(1);
- });
-
it("ignores BlazorWebView internal __bwv messages without warning", async () => {
const {getReceiveCallback} = await setupHostMessaging();
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
@@ -110,7 +97,7 @@ describe("HostMessaging", () => {
it("registers open-external click handler only once", async () => {
const {getReceiveCallback, blankTargetHandler} = await setupHostMessaging();
const addEventListenerSpy = vi.spyOn(document, "addEventListener");
- const registerMessage = JSON.stringify({id: ReceiveFromHostMessageIds.registerOpenExternal, version: 1});
+ const registerMessage = JSON.stringify({id: ReceiveFromHostMessageIds.registerOpenExternal, command: "Post", version: 2});
getReceiveCallback()(registerMessage);
getReceiveCallback()(registerMessage);
@@ -126,19 +113,19 @@ describe("HostMessaging", () => {
document.head.appendChild(title);
const {getReceiveCallback, titleObserverObserve} = await setupHostMessaging();
- getReceiveCallback()(JSON.stringify({id: ReceiveFromHostMessageIds.registerTitleChange, version: 1}));
+ getReceiveCallback()(JSON.stringify({id: ReceiveFromHostMessageIds.registerTitleChange, command: "Post", version: 2}));
expect(titleObserverObserve).toHaveBeenCalledWith(title, {childList: true});
});
it("overrides window.close after registerWindowClose and routes to host", async () => {
- const {getReceiveCallback, postMessage} = await setupHostMessaging();
+ const {getReceiveCallback, postData} = await setupHostMessaging();
const originalClose = window.close;
- getReceiveCallback()(JSON.stringify({id: ReceiveFromHostMessageIds.registerWindowClose, version: 1}));
+ getReceiveCallback()(JSON.stringify({id: ReceiveFromHostMessageIds.registerWindowClose, command: "Post", version: 2}));
window.close();
- const closeMessages = postMessage.mock.calls
+ const closeMessages = postData.mock.calls
.map(call => call[0])
.filter(
message => typeof message === "object"
diff --git a/src/InfiniFrame.Js/TypeScript/HostMessaging.ts b/src/InfiniFrame.Js/TypeScript/InfiniFrameHostMessaging.ts
similarity index 77%
rename from src/InfiniFrame.Js/TypeScript/HostMessaging.ts
rename to src/InfiniFrame.Js/TypeScript/InfiniFrameHostMessaging.ts
index 7448a3b5c..5e0aa05f4 100644
--- a/src/InfiniFrame.Js/TypeScript/HostMessaging.ts
+++ b/src/InfiniFrame.Js/TypeScript/InfiniFrameHostMessaging.ts
@@ -2,20 +2,21 @@
// Imports
// ---------------------------------------------------------------------------------------------------------------------
import {
- IHostMessaging,
+ IInfiniFrameHostMessaging,
+ InteropEnvelopeV1,
MessageCallback,
ReceiveFromHostMessageIds,
SendToHostMessageId,
SendToHostMessageIds
-} from "./Contracts/IHostMessaging";
-import {createEnvelope, parseIncomingMessage} from "./Interop/InteropEnvelopeProtocol";
-import {blankTargetHandler} from "./BlankTargetHandler";
-import {getTitleObserver, getTitleObserverTarget} from "./Observers";
+} from "./Contracts";
+import {createEnvelope, InteropGetCommand, parseIncomingMessage} from "./Interop/EnvelopeProtocol/InteropEnvelopeProtocol";
+import {blankTargetHandler} from "./Utils/BlankTargetHandler";
+import {getTitleObserver, getTitleObserverTarget} from "./Utils/Observers";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
-class HostMessaging implements IHostMessaging {
+class InfiniFrameHostMessaging implements IInfiniFrameHostMessaging {
private static readonly BlazorWebViewMessagePrefix = "__bwv:";
private static readonly ReadyHandshakeRetryIntervalMs = 1000;
private static readonly MaxReadyHandshakeAttempts = 20;
@@ -24,11 +25,10 @@ class HostMessaging implements IHostMessaging {
private fullscreenRegistered = false;
private titleRegistered = false;
private windowCloseRegistered = false;
- private legacyInboundWarningLogged = false;
private readyHandshakeAttempts = 0;
private readyHandshakeAcknowledged = false;
private readyHandshakeRetryTimer: number | null = null;
-
+
constructor() {
this.assignWebMessageReceiver();
@@ -58,12 +58,24 @@ class HostMessaging implements IHostMessaging {
public sendMessageToHost(id: SendToHostMessageId | string, data?: unknown) {
const envelope = createEnvelope(id, data);
- if (window.infiniframe?.host?.postMessage) {
- window.infiniframe.host.postMessage(envelope);
+ if (window.__infiniframe?.host?.postData) {
+ window.__infiniframe.host.postData(envelope);
} else {
console.warn("Message to host failed. Host bridge API is not initialized.");
+ return;
}
}
+
+ public async getMessageFromHostAsync(message: InteropEnvelopeV1 | string): Promise {
+ const host = window.__infiniframe?.host;
+ if (!host?.getDataAsync) throw new Error("Message to host failed. Host getDataAsync API is not initialized.");
+
+ const envelope = typeof message === "string"
+ ? createEnvelope(message, undefined, undefined, InteropGetCommand)
+ : message;
+
+ return await host.getDataAsync(envelope);
+ }
public assignMessageReceivedHandler(messageId: string, callback: MessageCallback) {
this.messageHandlers.set(messageId, callback);
@@ -74,11 +86,15 @@ class HostMessaging implements IHostMessaging {
}
private assignWebMessageReceiver() {
- if (window.infiniframe?.host?.receiveMessage) {
- window.infiniframe.host.receiveMessage((message: string) => {
+ if (window.__infiniframe?.host?.receiveCallback) {
+ window.__infiniframe.host.receiveCallback((message: string) => {
this.handleInteropMessage(message);
});
}
+ else {
+ console.warn("Web message receiver failed. Host bridge API is not initialized.");
+ return;
+ }
}
private handleInteropMessage(message: any): boolean {
@@ -86,29 +102,22 @@ class HostMessaging implements IHostMessaging {
if (!message) return false;
// Route only messages that match the explicit interop envelope contract.
const parsedMessage = parseIncomingMessage(message);
- if ("error" in parsedMessage) {
- return false;
- }
+ if ("error" in parsedMessage) return false;
// Blazor WebView internal transport messages are routed by blazor.webview.js.
// They are not InfiniFrame host-message contracts and should not emit warnings.
- if (parsedMessage.messageId.startsWith(HostMessaging.BlazorWebViewMessagePrefix)) {
+ if (parsedMessage.messageId.startsWith(InfiniFrameHostMessaging.BlazorWebViewMessagePrefix)) {
return true;
}
- if (parsedMessage.isLegacyProtocol && !this.legacyInboundWarningLogged) {
- this.legacyInboundWarningLogged = true;
- console.warn("Received legacy inbound host message format. Migrate host-to-web messages to the JSON envelope contract.");
- }
-
// Execute registered handler
const handler = this.messageHandlers.get(parsedMessage.messageId);
- if (handler) {
- handler(parsedMessage.payload);
- } else {
- console.warn('No handler registered for message ID:', parsedMessage.messageId);
+ if (!handler) {
+ console.warn('No handler registered for message:', parsedMessage);
+ return false;
}
+ handler(parsedMessage.payload);
return true;
}
@@ -166,13 +175,13 @@ class HostMessaging implements IHostMessaging {
this.sendReadyHandshake();
this.readyHandshakeRetryTimer = window.setInterval(() => {
- if (this.readyHandshakeAcknowledged || this.readyHandshakeAttempts >= HostMessaging.MaxReadyHandshakeAttempts) {
+ if (this.readyHandshakeAcknowledged || this.readyHandshakeAttempts >= InfiniFrameHostMessaging.MaxReadyHandshakeAttempts) {
this.stopReadyHandshakeRetry();
return;
}
this.sendReadyHandshake();
- }, HostMessaging.ReadyHandshakeRetryIntervalMs);
+ }, InfiniFrameHostMessaging.ReadyHandshakeRetryIntervalMs);
}
private sendReadyHandshake() {
@@ -193,4 +202,4 @@ class HostMessaging implements IHostMessaging {
}
}
-export default HostMessaging
+export default InfiniFrameHostMessaging
diff --git a/src/InfiniFrame.Js/TypeScript/InfiniFrameUtils.test.ts b/src/InfiniFrame.Js/TypeScript/InfiniFrameUtils.test.ts
new file mode 100644
index 000000000..7677749bf
--- /dev/null
+++ b/src/InfiniFrame.Js/TypeScript/InfiniFrameUtils.test.ts
@@ -0,0 +1,43 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+import {describe, it, expect, vi} from "vitest";
+import {InfiniFrameUtils} from "./InfiniFrameUtils";
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+describe("InfiniFrameUtils", () => {
+ it("forwards setPointerCapture to element", () => {
+ const utils = new InfiniFrameUtils();
+
+ const setPointerCapture = vi.fn();
+ const hasPointerCapture = vi.fn(() => false);
+
+ const element = {
+ setPointerCapture,
+ hasPointerCapture
+ } as unknown as Element;
+
+ utils.setPointerCapture(element, 10);
+
+ expect(setPointerCapture).toHaveBeenCalledWith(10);
+ });
+
+ it("forwards releasePointerCapture to element", () => {
+ const utils = new InfiniFrameUtils();
+
+ const releasePointerCapture = vi.fn();
+ const hasPointerCapture = vi.fn(() => true);
+
+ const element = {
+ releasePointerCapture,
+ hasPointerCapture
+ } as unknown as Element;
+
+ utils.releasePointerCapture(element, 10);
+
+ expect(hasPointerCapture).toHaveBeenCalledWith(10);
+ expect(releasePointerCapture).toHaveBeenCalledWith(10);
+ });
+});
\ No newline at end of file
diff --git a/src/InfiniFrame.Js/TypeScript/InfiniFrameUtils.ts b/src/InfiniFrame.Js/TypeScript/InfiniFrameUtils.ts
new file mode 100644
index 000000000..022ead136
--- /dev/null
+++ b/src/InfiniFrame.Js/TypeScript/InfiniFrameUtils.ts
@@ -0,0 +1,25 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+import {IInfiniFrameUtils} from "./Contracts";
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+export class InfiniFrameUtils implements IInfiniFrameUtils {
+ setPointerCapture(element: Element, pointerId: number): void {
+ if (element === null) return;
+ if (pointerId === null) return;
+
+ if (element.hasPointerCapture(pointerId)) return;
+ element.setPointerCapture(pointerId);
+ }
+
+ releasePointerCapture(element: Element, pointerId: number): void {
+ if (element === null) return;
+ if (pointerId === null) return;
+
+ if (!element.hasPointerCapture(pointerId)) return;
+ element.releasePointerCapture(pointerId);
+ }
+}
\ No newline at end of file
diff --git a/src/InfiniFrame.Js/TypeScript/InfiniFrameWindow.test.ts b/src/InfiniFrame.Js/TypeScript/InfiniFrameWindow.test.ts
new file mode 100644
index 000000000..f61b5d6fe
--- /dev/null
+++ b/src/InfiniFrame.Js/TypeScript/InfiniFrameWindow.test.ts
@@ -0,0 +1,68 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+import {beforeEach, describe, expect, it, vi} from "vitest";
+import {IInfiniFrameHostMessaging, SendToHostMessageIds} from "./Contracts";
+import {InfiniFrameWindow} from "./InfiniFrameWindow";
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+describe("InfiniFrameWindow", () => {
+ const testWindow = window as Window;
+
+ beforeEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ function createMessagingMocks(getMessageFromHostAsync: (message: string) => Promise): IInfiniFrameHostMessaging {
+ return {
+ sendMessageToHost: vi.fn(),
+ getMessageFromHostAsync,
+ assignMessageReceivedHandler: vi.fn(),
+ unregisterMessageReceivedHandler: vi.fn()
+ };
+ }
+
+ function assignInfiniFrame(messaging: IInfiniFrameHostMessaging) {
+ testWindow.infiniframe = {
+ messaging,
+ window: {
+ setTitle: vi.fn(),
+ getTitleAsync: vi.fn(async () => ""),
+ },
+ utils: {
+ setPointerCapture: vi.fn(),
+ releasePointerCapture: vi.fn()
+ }
+ };
+ }
+
+ it("setTitle sends titleChange message", () => {
+ const messaging = createMessagingMocks(vi.fn());
+ assignInfiniFrame(messaging);
+
+ const infiniFrameWindow = new InfiniFrameWindow();
+ infiniFrameWindow.setTitle("Hello");
+
+ expect(messaging.sendMessageToHost).toHaveBeenCalledWith(SendToHostMessageIds.titleChange, "Hello");
+ });
+
+ it("getTitleAsync requests generic get envelope and returns host payload", async () => {
+ const messaging = createMessagingMocks(vi.fn().mockResolvedValue("Native Title"));
+ assignInfiniFrame(messaging);
+
+ const infiniFrameWindow = new InfiniFrameWindow();
+ const title = await infiniFrameWindow.getTitleAsync();
+
+ expect(messaging.getMessageFromHostAsync).toHaveBeenCalledWith(
+ expect.objectContaining({
+ id: SendToHostMessageIds.getRequest,
+ command: "Get",
+ data: {command: "title", args: undefined},
+ version: 2
+ })
+ );
+ expect(title).toBe("Native Title");
+ });
+});
diff --git a/src/InfiniFrame.Js/TypeScript/InfiniFrameWindow.ts b/src/InfiniFrame.Js/TypeScript/InfiniFrameWindow.ts
new file mode 100644
index 000000000..87909ab3b
--- /dev/null
+++ b/src/InfiniFrame.Js/TypeScript/InfiniFrameWindow.ts
@@ -0,0 +1,31 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+import {IInfiniFrameWindow, SendToHostMessageIds} from "./Contracts";
+import {createGetEnvelope} from "./Interop/EnvelopeProtocol/InteropEnvelopeProtocol";
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+export class InfiniFrameWindow implements IInfiniFrameWindow {
+
+ private getMessageFromHostAsync(command: string, args?: any): Promise {
+ try {
+ return window.infiniframe.messaging.getMessageFromHostAsync(
+ createGetEnvelope(command, args)
+ );
+ }
+ catch (e) {
+ console.error("Failed to get response message from host.", e);
+ return Promise.reject(e);
+ }
+ }
+
+ setTitle(title:string) {
+ window.infiniframe.messaging.sendMessageToHost(SendToHostMessageIds.titleChange, title);
+ }
+
+ async getTitleAsync(): Promise {
+ return this.getMessageFromHostAsync("title")
+ }
+}
diff --git a/src/InfiniFrame.Js/TypeScript/Interop/InteropEnvelopeProtocol.test.ts b/src/InfiniFrame.Js/TypeScript/Interop/EnvelopeProtocol/InteropEnvelopeProtocol.test.ts
similarity index 95%
rename from src/InfiniFrame.Js/TypeScript/Interop/InteropEnvelopeProtocol.test.ts
rename to src/InfiniFrame.Js/TypeScript/Interop/EnvelopeProtocol/InteropEnvelopeProtocol.test.ts
index 352efaf66..6b06d6288 100644
--- a/src/InfiniFrame.Js/TypeScript/Interop/InteropEnvelopeProtocol.test.ts
+++ b/src/InfiniFrame.Js/TypeScript/Interop/EnvelopeProtocol/InteropEnvelopeProtocol.test.ts
@@ -1,7 +1,9 @@
// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
+// @ts-ignore
import fs from "node:fs";
+// @ts-ignore
import path from "node:path";
import {describe, expect, it} from "vitest";
import {createEnvelopeMessage, InteropMessageMaxSizeBytes, parseIncomingMessage} from "./InteropEnvelopeProtocol";
@@ -22,7 +24,6 @@ type ParseVector = {
success: boolean;
messageId?: string;
payload?: string | null;
- isLegacyProtocol?: boolean;
errorContains?: string;
};
@@ -32,6 +33,7 @@ type GoldenVectors = {
};
function loadGoldenVectors(): GoldenVectors {
+ // @ts-ignore
const filePath = path.resolve(__dirname, "./interop-envelope-golden-vectors.json");
return JSON.parse(fs.readFileSync(filePath, "utf8")) as GoldenVectors;
}
@@ -58,7 +60,6 @@ describe("InteropEnvelopeProtocol", () => {
expect(vector.success, vector.name).toBe(true);
expect(result.messageId, vector.name).toBe(vector.messageId);
expect(result.payload ?? null, vector.name).toBe(vector.payload ?? null);
- expect(Boolean(result.isLegacyProtocol), vector.name).toBe(Boolean(vector.isLegacyProtocol));
}
});
diff --git a/src/InfiniFrame.Js/TypeScript/Interop/InteropEnvelopeProtocol.ts b/src/InfiniFrame.Js/TypeScript/Interop/EnvelopeProtocol/InteropEnvelopeProtocol.ts
similarity index 60%
rename from src/InfiniFrame.Js/TypeScript/Interop/InteropEnvelopeProtocol.ts
rename to src/InfiniFrame.Js/TypeScript/Interop/EnvelopeProtocol/InteropEnvelopeProtocol.ts
index 5bab6d919..ceda1a815 100644
--- a/src/InfiniFrame.Js/TypeScript/Interop/InteropEnvelopeProtocol.ts
+++ b/src/InfiniFrame.Js/TypeScript/Interop/EnvelopeProtocol/InteropEnvelopeProtocol.ts
@@ -1,34 +1,63 @@
// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
-import {InteropEnvelopeV1, ParsedInteropMessage, ParseError} from "../Contracts/IInteropEnvelope";
+import {
+ InteropEnvelopeCommand,
+ InteropEnvelopeV1,
+ ParsedInteropMessage,
+ InteropParseError,
+ SendToHostMessageIds
+} from "../../Contracts";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
-export const InteropEnvelopeVersion = 1;
+export const InteropEnvelopeVersion = 2;
export const InteropMessageMaxSizeBytes = 1024 * 1024;
-
-export function createEnvelope(id: string, data?: unknown, channel?: string): InteropEnvelopeV1 {
+export const InteropPostCommand: InteropEnvelopeCommand = "Post";
+export const InteropGetCommand: InteropEnvelopeCommand = "Get";
+
+export function createEnvelope(
+ id: string,
+ data?: unknown,
+ channel?: string,
+ command: InteropEnvelopeCommand = InteropPostCommand,
+ requestId?: string
+): InteropEnvelopeV1 {
if (!id || id.trim().length === 0) {
throw new Error("Envelope 'id' is required.");
}
return {
id,
+ command,
+ requestId,
data,
version: InteropEnvelopeVersion,
channel
};
}
-export function createEnvelopeMessage(id: string, data?: unknown, channel?: string): string {
- const envelope = createEnvelope(id, data, channel);
+export function createGetEnvelope(
+ command: string,
+ args?: unknown,
+): InteropEnvelopeV1 {
+ return createEnvelope(SendToHostMessageIds.getRequest, {command, args}, undefined, InteropGetCommand);
+}
+
+export function createEnvelopeMessage(
+ id: string,
+ data?: unknown,
+ channel?: string,
+ command: InteropEnvelopeCommand = InteropPostCommand,
+ requestId?: string
+): string {
+ const envelope = createEnvelope(id, data, channel, command, requestId);
return JSON.stringify(envelope);
}
-export function parseIncomingMessage(message: string): ParsedInteropMessage | ParseError {
+export function parseIncomingMessage(message: string): ParsedInteropMessage | InteropParseError {
if (!message || message.trim().length === 0) {
return {error: "Message is empty."};
}
@@ -37,10 +66,6 @@ export function parseIncomingMessage(message: string): ParsedInteropMessage | Pa
return {error: `Message exceeds max size of ${InteropMessageMaxSizeBytes} bytes.`};
}
- if (!looksLikeJsonObject(message)) {
- return parseLegacyMessage(message);
- }
-
try {
const parsed = JSON.parse(message) as unknown;
if (!isObject(parsed)) {
@@ -60,31 +85,26 @@ export function parseIncomingMessage(message: string): ParsedInteropMessage | Pa
}
const payload = convertDataToPayload(parsed.data);
+
+ if (!isSupportedCommand(parsed.command)) {
+ return {error: "Envelope 'command' must be 'Post' or 'Get'."};
+ }
+
+ if (parsed.requestId !== undefined && typeof parsed.requestId !== "string") {
+ return {error: "Envelope 'requestId' must be a string."};
+ }
+
return {
messageId: parsed.id,
- payload
+ payload,
+ command: parsed.command,
+ requestId: parsed.requestId
};
} catch {
return {error: "Envelope JSON is malformed."};
}
}
-function parseLegacyMessage(message: string): ParsedInteropMessage | ParseError {
- const separatorIndex = message.indexOf(";");
- const hasSeparator = separatorIndex >= 0;
- const messageId = (hasSeparator ? message.slice(0, separatorIndex) : message).trim();
-
- if (messageId.length === 0) {
- return {error: "Legacy message has an empty message ID."};
- }
-
- return {
- messageId,
- payload: hasSeparator ? message.slice(separatorIndex + 1) : undefined,
- isLegacyProtocol: true
- };
-}
-
function convertDataToPayload(data: unknown): string | undefined {
if (data === null || data === undefined) {
return undefined;
@@ -97,10 +117,6 @@ function convertDataToPayload(data: unknown): string | undefined {
return JSON.stringify(data);
}
-function looksLikeJsonObject(message: string): boolean {
- return message.replace(/^\s+/, "").startsWith("{");
-}
-
function getUtf8ByteCount(message: string): number {
return new TextEncoder().encode(message).length;
}
@@ -108,3 +124,7 @@ function getUtf8ByteCount(message: string): number {
function isObject(value: unknown): value is Record {
return typeof value === "object" && value !== null;
}
+
+function isSupportedCommand(command: unknown): command is InteropEnvelopeCommand {
+ return command === InteropPostCommand || command === InteropGetCommand;
+}
diff --git a/src/InfiniFrame.Js/TypeScript/Interop/interop-envelope-golden-vectors.json b/src/InfiniFrame.Js/TypeScript/Interop/EnvelopeProtocol/interop-envelope-golden-vectors.json
similarity index 53%
rename from src/InfiniFrame.Js/TypeScript/Interop/interop-envelope-golden-vectors.json
rename to src/InfiniFrame.Js/TypeScript/Interop/EnvelopeProtocol/interop-envelope-golden-vectors.json
index 05d193ab6..0da1406b7 100644
--- a/src/InfiniFrame.Js/TypeScript/Interop/interop-envelope-golden-vectors.json
+++ b/src/InfiniFrame.Js/TypeScript/Interop/EnvelopeProtocol/interop-envelope-golden-vectors.json
@@ -4,74 +4,74 @@
"name": "create-null-data",
"id": "ping",
"data": null,
- "expectedMessage": "{\"id\":\"ping\",\"data\":null,\"version\":1}"
+ "expectedMessage": "{\"id\":\"ping\",\"command\":\"Post\",\"data\":null,\"version\":2}"
},
{
"name": "create-string-data",
"id": "set-title",
"data": "Hello",
- "expectedMessage": "{\"id\":\"set-title\",\"data\":\"Hello\",\"version\":1}"
+ "expectedMessage": "{\"id\":\"set-title\",\"command\":\"Post\",\"data\":\"Hello\",\"version\":2}"
},
{
"name": "create-string-with-semicolons",
"id": "event",
"data": "value;with;semicolons",
- "expectedMessage": "{\"id\":\"event\",\"data\":\"value;with;semicolons\",\"version\":1}"
+ "expectedMessage": "{\"id\":\"event\",\"command\":\"Post\",\"data\":\"value;with;semicolons\",\"version\":2}"
}
],
"parseVectors": [
{
"name": "parse-envelope-string-payload",
- "message": "{\"id\":\"ping\",\"data\":\"hello\",\"version\":1}",
+ "message": "{\"id\":\"ping\",\"command\":\"Post\",\"data\":\"hello\",\"version\":2}",
"success": true,
"messageId": "ping",
- "payload": "hello",
- "isLegacyProtocol": false
+ "payload": "hello"
},
{
"name": "parse-envelope-null-payload",
- "message": "{\"id\":\"ping\",\"data\":null,\"version\":1}",
+ "message": "{\"id\":\"ping\",\"command\":\"Post\",\"data\":null,\"version\":2}",
"success": true,
"messageId": "ping",
- "payload": null,
- "isLegacyProtocol": false
+ "payload": null
},
{
"name": "parse-envelope-object-payload",
- "message": "{\"id\":\"complex\",\"data\":{\"name\":\"München\",\"values\":[1,2,3]},\"version\":1}",
+ "message": "{\"id\":\"complex\",\"command\":\"Post\",\"data\":{\"name\":\"München\",\"values\":[1,2,3]},\"version\":2}",
"success": true,
"messageId": "complex",
- "payload": "{\"name\":\"München\",\"values\":[1,2,3]}",
- "isLegacyProtocol": false
+ "payload": "{\"name\":\"München\",\"values\":[1,2,3]}"
},
{
- "name": "parse-legacy-with-payload",
- "message": "set-title;New Title",
+ "name": "parse-envelope-get-request",
+ "message": "{\"id\":\"complex\",\"command\":\"Get\",\"requestId\":\"req-1\",\"data\":\"hello\",\"version\":2}",
"success": true,
- "messageId": "set-title",
- "payload": "New Title",
- "isLegacyProtocol": true
- },
- {
- "name": "parse-legacy-without-payload",
- "message": "window-close",
- "success": true,
- "messageId": "window-close",
- "payload": null,
- "isLegacyProtocol": true
+ "messageId": "complex",
+ "payload": "hello"
},
{
"name": "parse-invalid-missing-id",
- "message": "{\"data\":\"x\",\"version\":1}",
+ "message": "{\"command\":\"Post\",\"data\":\"x\",\"version\":2}",
"success": false,
"errorContains": "id"
},
+ {
+ "name": "parse-invalid-v1-envelope",
+ "message": "{\"id\":\"legacy\",\"data\":\"hello\",\"version\":1}",
+ "success": false,
+ "errorContains": "Unsupported envelope version"
+ },
{
"name": "parse-invalid-unsupported-version",
- "message": "{\"id\":\"ping\",\"data\":\"x\",\"version\":2}",
+ "message": "{\"id\":\"ping\",\"data\":\"x\",\"version\":3}",
"success": false,
"errorContains": "Unsupported envelope version"
},
+ {
+ "name": "parse-invalid-missing-command",
+ "message": "{\"id\":\"ping\",\"data\":\"x\",\"version\":2}",
+ "success": false,
+ "errorContains": "command"
+ },
{
"name": "parse-malformed-json",
"message": "{not-json",
diff --git a/src/InfiniFrame.Js/TypeScript/Interop/HostBridge.test.ts b/src/InfiniFrame.Js/TypeScript/Interop/HostBridge.test.ts
deleted file mode 100644
index c528dbf9a..000000000
--- a/src/InfiniFrame.Js/TypeScript/Interop/HostBridge.test.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-// ---------------------------------------------------------------------------------------------------------------------
-// Imports
-// ---------------------------------------------------------------------------------------------------------------------
-import {beforeEach, describe, expect, it, vi} from "vitest";
-import {installHostBridge} from "./HostBridge";
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Code
-// ---------------------------------------------------------------------------------------------------------------------
-type TestWindow = Window & {
- infiniframe?: {
- host?: {
- postMessage?: (message: unknown) => void;
- receiveMessage?: (callback: (message: string) => void) => void;
- };
- };
- chrome?: {
- webview?: {
- postMessage: (message: string) => void;
- addEventListener: (type: "message", listener: (event: { data: string }) => void) => void;
- };
- };
-};
-
-describe("HostBridge", () => {
- const testWindow = window as TestWindow;
-
- beforeEach(() => {
- delete testWindow.infiniframe;
- delete testWindow.chrome;
- vi.restoreAllMocks();
- });
-
- it("normalizes object envelopes to string for existing postMessage handlers", () => {
- const existingPostMessage = vi.fn();
- testWindow.infiniframe = {
- host: {
- postMessage: existingPostMessage,
- receiveMessage: vi.fn()
- }
- };
-
- installHostBridge();
- testWindow.infiniframe!.host!.postMessage!({id: "ping", data: "hello", version: 1});
-
- expect(existingPostMessage).toHaveBeenCalledTimes(1);
- expect(existingPostMessage.mock.calls[0][0]).toBe("{\"id\":\"ping\",\"data\":\"hello\",\"version\":1}");
- });
-
- it("falls back to object payload when existing postMessage rejects string payloads", () => {
- const existingPostMessage = vi.fn((payload: unknown) => {
- if (typeof payload === "string") throw new Error("String payloads not supported.");
- });
- testWindow.infiniframe = {
- host: {
- postMessage: existingPostMessage,
- receiveMessage: vi.fn()
- }
- };
-
- installHostBridge();
- testWindow.infiniframe!.host!.postMessage!({id: "ping", data: "hello", version: 1});
-
- expect(existingPostMessage).toHaveBeenCalledTimes(2);
- expect(typeof existingPostMessage.mock.calls[0][0]).toBe("string");
- expect(existingPostMessage.mock.calls[1][0]).toEqual({id: "ping", data: "hello", version: 1});
- });
-
- it("uses platform transport when no existing bridge callback exists", () => {
- const postMessage = vi.fn();
- testWindow.chrome = {
- webview: {
- postMessage,
- addEventListener: vi.fn()
- }
- };
-
- installHostBridge();
- testWindow.infiniframe!.host!.postMessage!({id: "ping", data: "hello", version: 1});
-
- expect(postMessage).toHaveBeenCalledTimes(1);
- expect(postMessage.mock.calls[0][0]).toBe("{\"id\":\"ping\",\"data\":\"hello\",\"version\":1}");
- });
-});
diff --git a/src/InfiniFrame.Js/TypeScript/Interop/HostBridge.ts b/src/InfiniFrame.Js/TypeScript/Interop/HostBridge.ts
deleted file mode 100644
index 060a1f03f..000000000
--- a/src/InfiniFrame.Js/TypeScript/Interop/HostBridge.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-// ---------------------------------------------------------------------------------------------------------------------
-// Imports
-// ---------------------------------------------------------------------------------------------------------------------
-import {InteropEnvelopeV1} from "../Contracts/IInteropEnvelope";
-import {InteropEnvelopeVersion} from "./InteropEnvelopeProtocol";
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Code
-// ---------------------------------------------------------------------------------------------------------------------
-export function installHostBridge(): void {
- const root: NonNullable = window.infiniframe ?? {};
- const host = (root.host ?? {}) as NonNullable["host"]>;
- const existingPostMessage = host.postMessage;
- const existingReceiveMessage = host.receiveMessage;
-
- host.postMessage = (envelope: InteropEnvelopeV1 | string) => {
- dispatchEnvelopeToHost(envelope, existingPostMessage);
- };
- host.receiveMessage = (callback: (message: string) => void) => {
- registerWebMessageReceiver(callback, existingReceiveMessage);
- };
-
- root.host = host;
- window.infiniframe = root;
-}
-
-function dispatchEnvelopeToHost(
- envelope: InteropEnvelopeV1 | string,
- existingPostMessage?: ((envelope: InteropEnvelopeV1 | string) => void)
-): void {
- if (typeof envelope === "string") {
- const rawMessage = envelope.trim();
- if (rawMessage.length === 0) {
- console.warn("Ignoring empty host bridge payload.");
- return;
- }
-
- if (existingPostMessage) {
- try {
- existingPostMessage(rawMessage);
- return;
- } catch (error) {
- console.warn("Existing InfiniFrame host bridge failed. Falling back to platform adapters.", error);
- }
- }
-
- sendViaPlatformTransport(rawMessage);
- return;
- }
-
- const normalized = normalizeEnvelope(envelope);
- if (!normalized) {
- return;
- }
-
- const serializedEnvelope = JSON.stringify(normalized);
-
- if (existingPostMessage) {
- try {
- // Prefer the string contract for host adapters that only accept raw messages.
- existingPostMessage(serializedEnvelope);
- return;
- } catch (error) {
- try {
- // Backward compatibility for adapters that still expect an envelope object.
- existingPostMessage(normalized);
- return;
- } catch {
- console.warn("Existing InfiniFrame host bridge failed. Falling back to platform adapters.", error);
- }
- }
- }
-
- sendViaPlatformTransport(serializedEnvelope);
-}
-
-function normalizeEnvelope(envelope: InteropEnvelopeV1): InteropEnvelopeV1 | null {
- if (!envelope || typeof envelope !== "object") {
- console.warn("Host bridge payload must be an envelope object.");
- return null;
- }
-
- // noinspection SuspiciousTypeOfGuard
- if (typeof envelope.id !== "string" || envelope.id.trim().length === 0) {
- console.warn("Host bridge envelope requires a non-empty 'id'.");
- return null;
- }
-
- const version = Number.isInteger(envelope.version)
- ? envelope.version
- : InteropEnvelopeVersion;
-
- const normalized: InteropEnvelopeV1 = {
- id: envelope.id,
- data: envelope.data,
- version
- };
-
- // noinspection SuspiciousTypeOfGuard
- if (envelope.channel !== undefined && typeof envelope.channel === "string" && envelope.channel.trim().length > 0) {
- normalized.channel = envelope.channel;
- }
-
- return normalized;
-}
-
-function sendViaPlatformTransport(message: string): void {
- if (window.chrome?.webview) {
- window.chrome.webview.postMessage(message);
- return;
- }
-
- console.warn("Message to host failed. No supported host transport was found.");
-}
-
-function registerWebMessageReceiver(
- callback: (message: string) => void,
- existingReceiveMessage?: (callback: (message: string) => void) => void
-): void {
- if (existingReceiveMessage) {
- try {
- existingReceiveMessage(callback);
- return;
- } catch (error) {
- console.warn("Existing InfiniFrame host receive bridge failed. Falling back to platform adapters.", error);
- }
- }
-
- if (window.chrome?.webview) {
- window.chrome.webview.addEventListener("message", (event) => {
- callback(event.data);
- });
- return;
- }
-
- console.warn("Receive message registration failed. No supported host receive transport was found.");
-}
diff --git a/src/InfiniFrame.Js/TypeScript/Interop/NativeHost/HostBridge.test.ts b/src/InfiniFrame.Js/TypeScript/Interop/NativeHost/HostBridge.test.ts
new file mode 100644
index 000000000..6b5fff47c
--- /dev/null
+++ b/src/InfiniFrame.Js/TypeScript/Interop/NativeHost/HostBridge.test.ts
@@ -0,0 +1,84 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+import {beforeEach, describe, expect, it, vi} from "vitest";
+import {installHostBridge} from "./HostBridge";
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+type TestWindow = Window & {
+ __infiniframe?: {
+ host?: {
+ postData?: (message: unknown) => void;
+ receiveCallback?: (callback: (message: string) => void) => void;
+ };
+ };
+ chrome?: {
+ webview?: {
+ postMessage: (message: string) => void;
+ addEventListener: (type: "message", listener: (event: { data: string }) => void) => void;
+ };
+ };
+};
+
+describe("HostBridge", () => {
+ const testWindow = window as TestWindow;
+
+ beforeEach(() => {
+ delete testWindow.__infiniframe;
+ delete testWindow.chrome;
+ vi.restoreAllMocks();
+ });
+
+ it("normalizes object envelopes to string for existing postData handlers", () => {
+ const existingPostData = vi.fn();
+ testWindow.__infiniframe = {
+ host: {
+ postData: existingPostData,
+ receiveCallback: vi.fn()
+ }
+ };
+
+ installHostBridge();
+ testWindow.__infiniframe!.host!.postData!({id: "ping", command: "Post", data: "hello", version: 2});
+
+ expect(existingPostData).toHaveBeenCalledTimes(1);
+ expect(existingPostData.mock.calls[0][0]).toBe("{\"id\":\"ping\",\"command\":\"Post\",\"data\":\"hello\",\"version\":2}");
+ });
+
+ it("falls back to object payload when existing postData rejects string payloads", () => {
+ const existingPostData = vi.fn((payload: unknown) => {
+ if (typeof payload === "string") throw new Error("String payloads not supported.");
+ });
+ testWindow.__infiniframe = {
+ host: {
+ postData: existingPostData,
+ receiveCallback: vi.fn()
+ }
+ };
+
+ installHostBridge();
+ testWindow.__infiniframe!.host!.postData!({id: "ping", command: "Post", data: "hello", version: 2});
+
+ expect(existingPostData).toHaveBeenCalledTimes(2);
+ expect(typeof existingPostData.mock.calls[0][0]).toBe("string");
+ expect(existingPostData.mock.calls[1][0]).toEqual({id: "ping", command: "Post", data: "hello", version: 2});
+ });
+
+ it("uses platform transport when no existing bridge callback exists", () => {
+ const postData = vi.fn();
+ testWindow.chrome = {
+ webview: {
+ postMessage: postData,
+ addEventListener: vi.fn()
+ }
+ };
+
+ installHostBridge();
+ testWindow.__infiniframe!.host!.postData!({id: "ping", command: "Post", data: "hello", version: 2});
+
+ expect(postData).toHaveBeenCalledTimes(1);
+ expect(postData.mock.calls[0][0]).toBe("{\"id\":\"ping\",\"command\":\"Post\",\"data\":\"hello\",\"version\":2}");
+ });
+});
diff --git a/src/InfiniFrame.Js/TypeScript/Interop/NativeHost/HostBridge.ts b/src/InfiniFrame.Js/TypeScript/Interop/NativeHost/HostBridge.ts
new file mode 100644
index 000000000..15833b08d
--- /dev/null
+++ b/src/InfiniFrame.Js/TypeScript/Interop/NativeHost/HostBridge.ts
@@ -0,0 +1,315 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+import {InteropEnvelopeV1} from "../../Contracts";
+import {
+ InteropEnvelopeVersion,
+ InteropGetCommand,
+ InteropPostCommand,
+ parseIncomingMessage
+} from "../EnvelopeProtocol/InteropEnvelopeProtocol";
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+const GetMessageResponseId = "__infiniframe:get:response";
+const GetMessageTimeoutMs = 10_000;
+
+type ReceiveCallback = (message: string) => void;
+const receiveCallbacks = new Set();
+let receiveBridgeAttached = false;
+
+export function installHostBridge(): void {
+ const root: NonNullable = window.__infiniframe ?? {};
+ const host = (root.host ?? {}) as NonNullable["host"]>;
+ const existingPostData = host.postData;
+ const existingReceiveCallback = host.receiveCallback;
+ const existingGetData = host.getDataAsync;
+
+ host.postData = (envelope: InteropEnvelopeV1 | string) => {
+ dispatchEnvelopeToHost(envelope, existingPostData);
+ };
+ host.receiveCallback = (callback: (message: string) => void) => {
+ registerWebMessageReceiver(callback, existingReceiveCallback);
+ };
+ host.getDataAsync = (message: InteropEnvelopeV1 | string) => {
+ return requestMessageFromHost(message, host, existingGetData, existingReceiveCallback);
+ };
+
+ root.host = host;
+ window.__infiniframe = root;
+}
+
+function dispatchEnvelopeToHost(
+ envelope: InteropEnvelopeV1 | string,
+ existingPostData?: ((envelope: InteropEnvelopeV1 | string) => void)
+): void {
+ if (typeof envelope === "string") {
+ const rawMessage = envelope.trim();
+ if (rawMessage.length === 0) {
+ console.warn("Ignoring empty host bridge payload.");
+ return;
+ }
+
+ if (existingPostData) {
+ try {
+ existingPostData(rawMessage);
+ return;
+ } catch (error) {
+ console.warn("Existing InfiniFrame host bridge failed. Falling back to platform adapters.", error);
+ }
+ }
+
+ postToPlatform(rawMessage);
+ return;
+ }
+
+ const normalized = normalizeEnvelope(envelope);
+ if (!normalized) {
+ return;
+ }
+
+ const serializedEnvelope = JSON.stringify(normalized);
+
+ if (existingPostData) {
+ try {
+ // Prefer the string contract for host adapters that only accept raw messages.
+ existingPostData(serializedEnvelope);
+ return;
+ } catch (error) {
+ try {
+ // Backward compatibility for adapters that still expect an envelope object.
+ existingPostData(normalized);
+ return;
+ } catch {
+ console.warn("Existing InfiniFrame host bridge failed. Falling back to platform adapters.", error);
+ }
+ }
+ }
+
+ postToPlatform(serializedEnvelope);
+}
+
+function requestMessageFromHost(
+ message: InteropEnvelopeV1 | string,
+ host: NonNullable["host"]>,
+ existingGetData?: ((message: InteropEnvelopeV1 | string) => Promise | string),
+ existingReceiveCallback?: (callback: (message: string) => void) => void
+): Promise {
+ const normalizedMessage = normalizeGetMessageInput(message);
+ if (!normalizedMessage) {
+ return Promise.reject(new Error("Host getDataAsync payload is invalid."));
+ }
+
+ if (existingGetData) {
+ try {
+ const existingResult = existingGetData(normalizedMessage);
+ if (existingResult && typeof (existingResult as Promise).then === "function") {
+ return existingResult as Promise;
+ }
+
+ return Promise.resolve(String(existingResult ?? ""));
+ } catch (error) {
+ console.warn("Existing InfiniFrame getDataAsync bridge failed. Falling back to request/response transport.", error);
+ }
+ }
+
+ const requestId = createRequestId();
+
+ return new Promise((resolve, reject) => {
+ const timeout = window.setTimeout(() => {
+ unregisterWebMessageReceiver(responseCallback);
+ reject(new Error("Timed out waiting for getDataAsync response from host."));
+ }, GetMessageTimeoutMs);
+
+ const responseCallback = (rawMessage: string) => {
+ const parsed = parseIncomingMessage(rawMessage);
+ if ("error" in parsed || parsed.messageId !== GetMessageResponseId || !parsed.payload) {
+ return;
+ }
+
+ let payload: unknown;
+ try {
+ payload = JSON.parse(parsed.payload);
+ } catch {
+ return;
+ }
+
+ if (!isGetMessageResponsePayload(payload) || payload.requestId !== requestId) {
+ return;
+ }
+
+ window.clearTimeout(timeout);
+ unregisterWebMessageReceiver(responseCallback);
+
+ if (payload.success) {
+ resolve(payload.data ?? "");
+ return;
+ }
+
+ reject(new Error(payload.error ?? "Host getDataAsync failed."));
+ };
+
+ registerWebMessageReceiver(responseCallback, existingReceiveCallback);
+ const requestEnvelope = createGetRequestEnvelope(normalizedMessage, requestId);
+
+ if (!requestEnvelope) {
+ window.clearTimeout(timeout);
+ unregisterWebMessageReceiver(responseCallback);
+ reject(new Error("Host getDataAsync payload is invalid."));
+ return;
+ }
+
+ host.postData?.(requestEnvelope);
+ });
+}
+
+function createGetRequestEnvelope(normalizedMessage: InteropEnvelopeV1 | string, requestId: string): InteropEnvelopeV1 | null {
+ if (typeof normalizedMessage !== "string") {
+ return normalizeEnvelope(normalizedMessage, InteropGetCommand, requestId);
+ }
+
+ try {
+ const parsed = JSON.parse(normalizedMessage) as unknown;
+ if (isObject(parsed)) {
+ return normalizeEnvelope(parsed as unknown as InteropEnvelopeV1, InteropGetCommand, requestId);
+ }
+ } catch {
+ // A plain string is treated as the message id for a get request.
+ }
+
+ return normalizeEnvelope({id: normalizedMessage, version: InteropEnvelopeVersion}, InteropGetCommand, requestId);
+}
+
+function normalizeGetMessageInput(message: InteropEnvelopeV1 | string): InteropEnvelopeV1 | string | null {
+ if (typeof message === "string") {
+ const trimmed = message.trim();
+ if (trimmed.length === 0) {
+ return null;
+ }
+
+ return trimmed;
+ }
+
+ const normalizedEnvelope = normalizeEnvelope(message);
+ if (!normalizedEnvelope) {
+ return null;
+ }
+
+ return normalizedEnvelope;
+}
+
+function createRequestId(): string {
+ return `if_req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
+}
+
+function normalizeEnvelope(
+ envelope: InteropEnvelopeV1,
+ command = envelope.command ?? InteropPostCommand,
+ requestId = envelope.requestId
+): InteropEnvelopeV1 | null {
+ if (!envelope || typeof envelope !== "object") {
+ console.warn("Host bridge payload must be an envelope object.");
+ return null;
+ }
+
+ // noinspection SuspiciousTypeOfGuard
+ if (typeof envelope.id !== "string" || envelope.id.trim().length === 0) {
+ console.warn("Host bridge envelope requires a non-empty 'id'.");
+ return null;
+ }
+
+ const normalized: InteropEnvelopeV1 = {
+ id: envelope.id,
+ command,
+ requestId,
+ data: envelope.data,
+ version: InteropEnvelopeVersion
+ };
+
+ // noinspection SuspiciousTypeOfGuard
+ if (envelope.channel !== undefined && typeof envelope.channel === "string" && envelope.channel.trim().length > 0) {
+ normalized.channel = envelope.channel;
+ }
+
+ return normalized;
+}
+
+function registerWebMessageReceiver(
+ callback: (message: string) => void,
+ existingReceiveCallback?: (callback: (message: string) => void) => void
+): void {
+ receiveCallbacks.add(callback);
+ attachReceiveBridgeOnce(existingReceiveCallback);
+}
+
+function unregisterWebMessageReceiver(callback: ReceiveCallback): void {
+ receiveCallbacks.delete(callback);
+}
+
+function attachReceiveBridgeOnce(existingReceiveCallback?: (callback: (message: string) => void) => void): void {
+ if (receiveBridgeAttached) {
+ return;
+ }
+
+ const dispatch = (message: string) => {
+ for (const callback of receiveCallbacks) {
+ callback(message);
+ }
+ };
+
+ if (existingReceiveCallback) {
+ try {
+ existingReceiveCallback(dispatch);
+ receiveBridgeAttached = true;
+ return;
+ } catch (error) {
+ console.warn("Existing InfiniFrame host receive bridge failed. Falling back to platform adapters.", error);
+ }
+ }
+
+ if (window.chrome?.webview?.addEventListener) {
+ window.chrome.webview.addEventListener("message", event => dispatch(event.data));
+ receiveBridgeAttached = true;
+ return;
+ }
+
+ if (window.webkit?.messageHandlers?.infiniFrameInterop) {
+ window.__dispatchMessageCallback = dispatch;
+ receiveBridgeAttached = true;
+ return;
+ }
+
+ console.warn("Receive message registration failed. No supported host receive transport was found.");
+}
+
+function postToPlatform(message: string): void {
+ if (window.chrome?.webview?.postMessage) {
+ window.chrome.webview.postMessage(message);
+ return;
+ }
+
+ if (window.webkit?.messageHandlers?.infiniFrameInterop?.postMessage) {
+ window.webkit.messageHandlers.infiniFrameInterop.postMessage(message);
+ return;
+ }
+
+ console.warn("[InfiniFrame] No native bridge available:", message);
+}
+
+function isObject(value: unknown): value is Record {
+ return typeof value === "object" && value !== null;
+}
+
+function isGetMessageResponsePayload(value: unknown): value is {
+ requestId: string;
+ success: boolean;
+ data?: string;
+ error?: string;
+} {
+ return isObject(value)
+ && typeof value.requestId === "string"
+ && typeof value.success === "boolean"
+ && (value.data === undefined || typeof value.data === "string")
+ && (value.error === undefined || typeof value.error === "string");
+}
diff --git a/src/InfiniFrame.Js/TypeScript/BlankTargetHandler.test.ts b/src/InfiniFrame.Js/TypeScript/Utils/BlankTargetHandler.test.ts
similarity index 94%
rename from src/InfiniFrame.Js/TypeScript/BlankTargetHandler.test.ts
rename to src/InfiniFrame.Js/TypeScript/Utils/BlankTargetHandler.test.ts
index 8c831c3e0..05b605671 100644
--- a/src/InfiniFrame.Js/TypeScript/BlankTargetHandler.test.ts
+++ b/src/InfiniFrame.Js/TypeScript/Utils/BlankTargetHandler.test.ts
@@ -3,14 +3,14 @@
// ---------------------------------------------------------------------------------------------------------------------
import {beforeEach, describe, expect, it, vi} from "vitest";
import {blankTargetHandler} from "./BlankTargetHandler";
-import {SendToHostMessageIds} from "./Contracts/IHostMessaging";
+import {SendToHostMessageIds} from "../Contracts";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
type TestWindow = Window & {
- infiniFrame?: {
- HostMessaging: {
+ infiniframe?: {
+ messaging: {
sendMessageToHost: (id: string, data?: unknown) => void;
};
};
@@ -23,8 +23,9 @@ describe("blankTargetHandler", () => {
beforeEach(() => {
document.body.innerHTML = "";
sendMessageToHost.mockReset();
- testWindow.infiniFrame = {
- HostMessaging: {
+ testWindow.infiniframe = {
+ // @ts-ignore
+ messaging: {
sendMessageToHost
}
};
diff --git a/src/InfiniFrame.Js/TypeScript/BlankTargetHandler.ts b/src/InfiniFrame.Js/TypeScript/Utils/BlankTargetHandler.ts
similarity index 88%
rename from src/InfiniFrame.Js/TypeScript/BlankTargetHandler.ts
rename to src/InfiniFrame.Js/TypeScript/Utils/BlankTargetHandler.ts
index f00ce79a0..21d1abfad 100644
--- a/src/InfiniFrame.Js/TypeScript/BlankTargetHandler.ts
+++ b/src/InfiniFrame.Js/TypeScript/Utils/BlankTargetHandler.ts
@@ -1,7 +1,7 @@
// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
-import {SendToHostMessageIds} from "./Contracts/IHostMessaging";
+import {SendToHostMessageIds} from "../Contracts";
// ---------------------------------------------------------------------------------------------------------------------
// Code
@@ -38,7 +38,7 @@ export async function blankTargetHandler(e: MouseEvent) {
}
e.preventDefault();
- window.infiniFrame.HostMessaging.sendMessageToHost(SendToHostMessageIds.openExternalLink, anchor.href);
+ window.infiniframe.messaging.sendMessageToHost(SendToHostMessageIds.openExternalLink, anchor.href);
return;
}
}
\ No newline at end of file
diff --git a/src/InfiniFrame.Js/TypeScript/Observers.test.ts b/src/InfiniFrame.Js/TypeScript/Utils/Observers.test.ts
similarity index 93%
rename from src/InfiniFrame.Js/TypeScript/Observers.test.ts
rename to src/InfiniFrame.Js/TypeScript/Utils/Observers.test.ts
index 8879a637b..b1925b666 100644
--- a/src/InfiniFrame.Js/TypeScript/Observers.test.ts
+++ b/src/InfiniFrame.Js/TypeScript/Utils/Observers.test.ts
@@ -3,14 +3,14 @@
// ---------------------------------------------------------------------------------------------------------------------
import {afterEach, describe, expect, it, vi} from "vitest";
import {getTitleObserver, getTitleObserverTarget} from "./Observers";
-import {SendToHostMessageIds} from "./Contracts/IHostMessaging";
+import {SendToHostMessageIds} from "../Contracts";
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
type TestWindow = Window & {
- infiniFrame?: {
- HostMessaging: {
+ infiniframe?: {
+ messaging: {
sendMessageToHost: (id: string, data?: unknown) => void;
};
};
@@ -49,8 +49,9 @@ describe("Observers", () => {
}
globalThis.MutationObserver = FakeMutationObserver as unknown as typeof MutationObserver;
- testWindow.infiniFrame = {
- HostMessaging: {
+ testWindow.infiniframe = {
+ // @ts-ignore
+ messaging: {
sendMessageToHost
}
};
diff --git a/src/InfiniFrame.Js/TypeScript/Observers.ts b/src/InfiniFrame.Js/TypeScript/Utils/Observers.ts
similarity index 82%
rename from src/InfiniFrame.Js/TypeScript/Observers.ts
rename to src/InfiniFrame.Js/TypeScript/Utils/Observers.ts
index 2e2ce69cf..7d712fbf8 100644
--- a/src/InfiniFrame.Js/TypeScript/Observers.ts
+++ b/src/InfiniFrame.Js/TypeScript/Utils/Observers.ts
@@ -1,7 +1,7 @@
// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
-import {SendToHostMessageIds} from "./Contracts/IHostMessaging";
+import {SendToHostMessageIds} from "../Contracts";
// ---------------------------------------------------------------------------------------------------------------------
// Code
@@ -14,7 +14,7 @@ export function getTitleObserver(): MutationObserver {
return new MutationObserver((mutations, _) => {
mutations.forEach((mutation) => {
if (mutation.type !== "childList") return;
- window.infiniFrame.HostMessaging.sendMessageToHost(SendToHostMessageIds.titleChange, document.title);
+ window.infiniframe.messaging.sendMessageToHost(SendToHostMessageIds.titleChange, document.title);
})
})
}
diff --git a/src/InfiniFrame.Js/package-lock.json b/src/InfiniFrame.Js/package-lock.json
index 6519d6c17..eb61b8450 100644
--- a/src/InfiniFrame.Js/package-lock.json
+++ b/src/InfiniFrame.Js/package-lock.json
@@ -9,8 +9,9 @@
"version": "0.1.0",
"license": "GNUv3",
"devDependencies": {
+ "@types/node": "^25.6.0",
"@vitest/coverage-v8": "^4.1.5",
- "jsdom": "^29.0.2",
+ "jsdom": "^29.1.1",
"ts-loader": "^9.5.7",
"typescript": "^6.0.3",
"vitest": "^4.1.5",
@@ -36,9 +37,9 @@
}
},
"node_modules/@asamuzakjp/dom-selector": {
- "version": "7.0.10",
- "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.10.tgz",
- "integrity": "sha512-KyOb19eytNSELkmdqzZZUXWCU25byIlOld5qVFg0RYdS0T3tt7jeDByxk9hIAC73frclD8GKrHttr0SUjKCCdQ==",
+ "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": {
@@ -778,13 +779,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "25.0.3",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
- "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
+ "version": "25.6.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
+ "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~7.16.0"
+ "undici-types": "~7.19.0"
}
},
"node_modules/@vitest/coverage-v8": {
@@ -1481,13 +1482,13 @@
}
},
"node_modules/entities": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
- "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "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": ">=0.12"
+ "node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
@@ -1924,28 +1925,28 @@
"license": "MIT"
},
"node_modules/jsdom": {
- "version": "29.0.2",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz",
- "integrity": "sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==",
+ "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.5",
- "@asamuzakjp/dom-selector": "^7.0.6",
+ "@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.1",
+ "@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.2.7",
- "parse5": "^8.0.0",
+ "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.24.5",
+ "undici": "^7.25.0",
"w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^8.0.1",
"whatwg-mimetype": "^5.0.0",
@@ -2451,13 +2452,13 @@
}
},
"node_modules/parse5": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
- "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
+ "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": "^6.0.0"
+ "entities": "^8.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
@@ -3102,9 +3103,9 @@
}
},
"node_modules/undici-types": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
- "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "version": "7.19.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
+ "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
"dev": true,
"license": "MIT"
},
diff --git a/src/InfiniFrame.Js/package.json b/src/InfiniFrame.Js/package.json
index e67a870a8..c50dd171d 100644
--- a/src/InfiniFrame.Js/package.json
+++ b/src/InfiniFrame.Js/package.json
@@ -13,8 +13,9 @@
"license": "GNUv3",
"description": "",
"devDependencies": {
+ "@types/node": "^25.6.0",
"@vitest/coverage-v8": "^4.1.5",
- "jsdom": "^29.0.2",
+ "jsdom": "^29.1.1",
"ts-loader": "^9.5.7",
"typescript": "^6.0.3",
"vitest": "^4.1.5",
diff --git a/src/InfiniFrame.Js/webpack.config.js b/src/InfiniFrame.Js/webpack.config.js
index 3437e96e0..05c8f0140 100644
--- a/src/InfiniFrame.Js/webpack.config.js
+++ b/src/InfiniFrame.Js/webpack.config.js
@@ -13,7 +13,7 @@ module.exports = (env, _) => {
},
output: {
path: path.resolve(__dirname, './wwwroot'),
- filename: "InfiniFrame.js", // <--- Will be compiled to this single file
+ filename: "InfiniFrame.js",
},
resolve: {
extensions: [".ts", ".tsx", ".js"],
diff --git a/src/InfiniFrame.Js/wwwroot/InfiniFrame.js b/src/InfiniFrame.Js/wwwroot/InfiniFrame.js
index 5f27a6539..99166712b 100644
--- a/src/InfiniFrame.Js/wwwroot/InfiniFrame.js
+++ b/src/InfiniFrame.Js/wwwroot/InfiniFrame.js
@@ -1 +1 @@
-(()=>{"use strict";var e={372(e,t,n){var s=this&&this.__awaiter||function(e,t,n,s){return new(n||(n=Promise))(function(r,i){function o(e){try{d(s.next(e))}catch(e){i(e)}}function a(e){try{d(s.throw(e))}catch(e){i(e)}}function d(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(o,a)}d((s=s.apply(e,t||[])).next())})};Object.defineProperty(t,"__esModule",{value:!0}),t.blankTargetHandler=function(e){return s(this,void 0,void 0,function*(){var t;let n=e.target;for(;n&&n!==document.body;){if("a"!==(null===(t=n.tagName)||void 0===t?void 0:t.toLowerCase())){n=n.parentElement;continue}const s=n;if(s.href){if("_blank"===s.getAttribute("target")||s.hasAttribute("data-external")||i(s.href))return e.preventDefault(),void window.infiniFrame.HostMessaging.sendMessageToHost(r.SendToHostMessageIds.openExternalLink,s.href);n=n.parentElement}else n=n.parentElement}})};const r=n(678);function i(e){try{return new URL(e,location.href).hostname!==location.hostname}catch(e){return!1}}},678(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.ReceiveFromHostMessageIds=t.SendToHostMessageIds=void 0;const n="__infiniframe";t.SendToHostMessageIds={titleChange:`${n}:title:change`,fullscreenEnter:`${n}:fullscreen:enter`,fullscreenExit:`${n}:fullscreen:exit`,openExternalLink:`${n}:open:external`,windowClose:`${n}:window:close`,ready:`${n}:ready`},t.ReceiveFromHostMessageIds={registerOpenExternal:`${n}:register:open:external`,registerFullscreenChange:`${n}:register:fullscreen:change`,registerTitleChange:`${n}:register:title:change`,registerWindowClose:`${n}:register:window:close`}},143(e,t,n){var s=this&&this.__awaiter||function(e,t,n,s){return new(n||(n=Promise))(function(r,i){function o(e){try{d(s.next(e))}catch(e){i(e)}}function a(e){try{d(s.throw(e))}catch(e){i(e)}}function d(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(o,a)}d((s=s.apply(e,t||[])).next())})};Object.defineProperty(t,"__esModule",{value:!0});const r=n(678),i=n(934),o=n(372),a=n(966);class d{constructor(){this.messageHandlers=new Map,this.openExternalRegistered=!1,this.fullscreenRegistered=!1,this.titleRegistered=!1,this.windowCloseRegistered=!1,this.legacyInboundWarningLogged=!1,this.readyHandshakeAttempts=0,this.readyHandshakeAcknowledged=!1,this.readyHandshakeRetryTimer=null,this.assignWebMessageReceiver(),this.assignMessageReceivedHandler(r.ReceiveFromHostMessageIds.registerOpenExternal,e=>{this.markReadyHandshakeAcknowledged(),this.registerOpenExternal()}),this.assignMessageReceivedHandler(r.ReceiveFromHostMessageIds.registerFullscreenChange,e=>{this.markReadyHandshakeAcknowledged(),this.registerFullscreenChange()}),this.assignMessageReceivedHandler(r.ReceiveFromHostMessageIds.registerTitleChange,e=>{this.markReadyHandshakeAcknowledged(),this.registerTitleChange()}),this.assignMessageReceivedHandler(r.ReceiveFromHostMessageIds.registerWindowClose,e=>{this.markReadyHandshakeAcknowledged(),this.registerWindowClose()}),this.sendReadyHandshakeWithRetry()}sendMessageToHost(e,t){var n,s;const r=(0,i.createEnvelope)(e,t);(null===(s=null===(n=window.infiniframe)||void 0===n?void 0:n.host)||void 0===s?void 0:s.postMessage)?window.infiniframe.host.postMessage(r):console.warn("Message to host failed. Host bridge API is not initialized.")}assignMessageReceivedHandler(e,t){this.messageHandlers.set(e,t)}unregisterMessageReceivedHandler(e){this.messageHandlers.delete(e)}assignWebMessageReceiver(){var e,t;(null===(t=null===(e=window.infiniframe)||void 0===e?void 0:e.host)||void 0===t?void 0:t.receiveMessage)&&window.infiniframe.host.receiveMessage(e=>{this.handleInteropMessage(e)})}handleInteropMessage(e){if("string"!=typeof e)return!1;if(!e)return!1;const t=(0,i.parseIncomingMessage)(e);if("error"in t)return!1;if(t.messageId.startsWith(d.BlazorWebViewMessagePrefix))return!0;t.isLegacyProtocol&&!this.legacyInboundWarningLogged&&(this.legacyInboundWarningLogged=!0,console.warn("Received legacy inbound host message format. Migrate host-to-web messages to the JSON envelope contract."));const n=this.messageHandlers.get(t.messageId);return n?n(t.payload):console.warn("No handler registered for message ID:",t.messageId),!0}registerOpenExternal(){this.openExternalRegistered||(this.openExternalRegistered=!0,document.addEventListener("click",o.blankTargetHandler,{capture:!0}))}registerFullscreenChange(){this.fullscreenRegistered||(this.fullscreenRegistered=!0,document.addEventListener("fullscreenchange",e=>{document.fullscreenElement?this.sendMessageToHost(r.SendToHostMessageIds.fullscreenEnter):this.sendMessageToHost(r.SendToHostMessageIds.fullscreenExit)}),document.addEventListener("keydown",e=>s(this,void 0,void 0,function*(){"F11"===e.key&&(document.fullscreenElement?yield document.exitFullscreen():yield document.body.requestFullscreen())})))}registerTitleChange(){if(this.titleRegistered)return;this.titleRegistered=!0;const e=(0,a.getTitleObserverTarget)();if(e)return void(0,a.getTitleObserver)().observe(e,{childList:!0});const t=document.head||document.documentElement;if(!t)return;const n=new MutationObserver(()=>{const e=(0,a.getTitleObserverTarget)();e&&(n.disconnect(),(0,a.getTitleObserver)().observe(e,{childList:!0}))});n.observe(t,{childList:!0,subtree:!0})}registerWindowClose(){this.windowCloseRegistered||(this.windowCloseRegistered=!0,window.close=()=>{this.sendMessageToHost(r.SendToHostMessageIds.windowClose)})}sendReadyHandshakeWithRetry(){this.sendReadyHandshake(),this.readyHandshakeRetryTimer=window.setInterval(()=>{this.readyHandshakeAcknowledged||this.readyHandshakeAttempts>=d.MaxReadyHandshakeAttempts?this.stopReadyHandshakeRetry():this.sendReadyHandshake()},d.ReadyHandshakeRetryIntervalMs)}sendReadyHandshake(){this.readyHandshakeAttempts++,this.sendMessageToHost(r.SendToHostMessageIds.ready)}markReadyHandshakeAcknowledged(){this.readyHandshakeAcknowledged||(this.readyHandshakeAcknowledged=!0,this.stopReadyHandshakeRetry())}stopReadyHandshakeRetry(){null!==this.readyHandshakeRetryTimer&&(window.clearInterval(this.readyHandshakeRetryTimer),this.readyHandshakeRetryTimer=null)}}d.BlazorWebViewMessagePrefix="__bwv:",d.ReadyHandshakeRetryIntervalMs=1e3,d.MaxReadyHandshakeAttempts=20,t.default=d},297(e,t,n){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=s(n(365));(0,n(104).installHostBridge)(),window.infiniFrame=new r.default},365(e,t,n){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.InfiniFrame=void 0;const r=s(n(143));class i{constructor(){this.HostMessaging=new r.default}sendMessageToHost(e,t){this.HostMessaging.sendMessageToHost(e,t)}setPointerCapture(e,t){e.setPointerCapture(t)}releasePointerCapture(e,t){e.releasePointerCapture(t)}}t.InfiniFrame=i,t.default=i},104(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.installHostBridge=function(){var e,t;const n=null!==(e=window.infiniframe)&&void 0!==e?e:{},i=null!==(t=n.host)&&void 0!==t?t:{},o=i.postMessage,a=i.receiveMessage;i.postMessage=e=>{!function(e,t){if("string"==typeof e){const n=e.trim();if(0===n.length)return void console.warn("Ignoring empty host bridge payload.");if(t)try{return void t(n)}catch(e){console.warn("Existing InfiniFrame host bridge failed. Falling back to platform adapters.",e)}return void r(n)}const n=function(e){if(!e||"object"!=typeof e)return console.warn("Host bridge payload must be an envelope object."),null;if("string"!=typeof e.id||0===e.id.trim().length)return console.warn("Host bridge envelope requires a non-empty 'id'."),null;const t=Number.isInteger(e.version)?e.version:s.InteropEnvelopeVersion,n={id:e.id,data:e.data,version:t};return void 0!==e.channel&&"string"==typeof e.channel&&e.channel.trim().length>0&&(n.channel=e.channel),n}(e);if(!n)return;const i=JSON.stringify(n);if(t)try{return void t(i)}catch(e){try{return void t(n)}catch(t){console.warn("Existing InfiniFrame host bridge failed. Falling back to platform adapters.",e)}}r(i)}(e,o)},i.receiveMessage=e=>{!function(e,t){var n;if(t)try{return void t(e)}catch(e){console.warn("Existing InfiniFrame host receive bridge failed. Falling back to platform adapters.",e)}(null===(n=window.chrome)||void 0===n?void 0:n.webview)?window.chrome.webview.addEventListener("message",t=>{e(t.data)}):console.warn("Receive message registration failed. No supported host receive transport was found.")}(e,a)},n.host=i,window.infiniframe=n};const s=n(934);function r(e){var t;(null===(t=window.chrome)||void 0===t?void 0:t.webview)?window.chrome.webview.postMessage(e):console.warn("Message to host failed. No supported host transport was found.")}},934(e,t){function n(e,n,s){if(!e||0===e.trim().length)throw new Error("Envelope 'id' is required.");return{id:e,data:n,version:t.InteropEnvelopeVersion,channel:s}}Object.defineProperty(t,"__esModule",{value:!0}),t.InteropMessageMaxSizeBytes=t.InteropEnvelopeVersion=void 0,t.createEnvelope=n,t.createEnvelopeMessage=function(e,t,s){const r=n(e,t,s);return JSON.stringify(r)},t.parseIncomingMessage=function(e){if(!e||0===e.trim().length)return{error:"Message is empty."};if(function(e){return(new TextEncoder).encode(e).length}(e)>t.InteropMessageMaxSizeBytes)return{error:`Message exceeds max size of ${t.InteropMessageMaxSizeBytes} bytes.`};if(!function(e){return e.replace(/^\s+/,"").startsWith("{")}(e))return function(e){const t=e.indexOf(";"),n=t>=0,s=(n?e.slice(0,t):e).trim();return 0===s.length?{error:"Legacy message has an empty message ID."}:{messageId:s,payload:n?e.slice(t+1):void 0,isLegacyProtocol:!0}}(e);try{const s=JSON.parse(e);if("object"!=typeof(n=s)||null===n)return{error:"Envelope root must be a JSON object."};if("string"!=typeof s.id||0===s.id.trim().length)return{error:"Envelope 'id' is required and must be a string."};if("number"!=typeof s.version||!Number.isInteger(s.version))return{error:"Envelope 'version' is required and must be an integer."};if(s.version!==t.InteropEnvelopeVersion)return{error:`Unsupported envelope version '${s.version}'.`};const r=function(e){if(null!=e)return"string"==typeof e?e:JSON.stringify(e)}(s.data);return{messageId:s.id,payload:r}}catch(e){return{error:"Envelope JSON is malformed."}}var n},t.InteropEnvelopeVersion=1,t.InteropMessageMaxSizeBytes=1048576},966(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.getTitleObserverTarget=function(){return document.querySelector("title")},t.getTitleObserver=function(){return new MutationObserver((e,t)=>{e.forEach(e=>{"childList"===e.type&&window.infiniFrame.HostMessaging.sendMessageToHost(s.SendToHostMessageIds.titleChange,document.title)})})};const s=n(678)}},t={};!function n(s){var r=t[s];if(void 0!==r)return r.exports;var i=t[s]={exports:{}};return e[s].call(i.exports,i,i.exports,n),i.exports}(297)})();
\ No newline at end of file
+(()=>{"use strict";var e={65(e,t){Object.defineProperty(t,"__esModule",{value:!0})},864(e,t){Object.defineProperty(t,"__esModule",{value:!0})},204(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.ReceiveFromHostMessageIds=t.SendToHostMessageIds=void 0;const n="__infiniframe";t.SendToHostMessageIds={getRequest:`${n}:get`,titleChange:`${n}:title:change`,fullscreenEnter:`${n}:fullscreen:enter`,fullscreenExit:`${n}:fullscreen:exit`,openExternalLink:`${n}:open:external`,windowClose:`${n}:window:close`,ready:`${n}:ready`},t.ReceiveFromHostMessageIds={registerOpenExternal:`${n}:register:open:external`,registerFullscreenChange:`${n}:register:fullscreen:change`,registerTitleChange:`${n}:register:title:change`,registerWindowClose:`${n}:register:window:close`,getMessageResponse:`${n}:get:response`}},419(e,t){Object.defineProperty(t,"__esModule",{value:!0})},840(e,t){Object.defineProperty(t,"__esModule",{value:!0})},934(e,t){Object.defineProperty(t,"__esModule",{value:!0})},461(e,t,n){var r=this&&this.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n);var s=Object.getOwnPropertyDescriptor(t,n);s&&!("get"in s?!t.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,s)}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),s=this&&this.__exportStar||function(e,t){for(var n in e)"default"===n||Object.prototype.hasOwnProperty.call(t,n)||r(t,e,n)};Object.defineProperty(t,"__esModule",{value:!0}),s(n(65),t),s(n(934),t),s(n(864),t),s(n(204),t),s(n(419),t),s(n(840),t)},297(e,t,n){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const s=r(n(365));(0,n(514).installHostBridge)(),window.infiniframe=new s.default},365(e,t,n){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.InfiniFrame=void 0;const s=r(n(895)),i=n(136),o=n(305);class a{constructor(){this.messaging=new s.default,this.window=new o.InfiniFrameWindow,this.utils=new i.InfiniFrameUtils}}t.InfiniFrame=a,t.default=a},895(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(s,i){function o(e){try{d(r.next(e))}catch(e){i(e)}}function a(e){try{d(r.throw(e))}catch(e){i(e)}}function d(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(o,a)}d((r=r.apply(e,t||[])).next())})};Object.defineProperty(t,"__esModule",{value:!0});const s=n(461),i=n(185),o=n(270),a=n(52);class d{constructor(){this.messageHandlers=new Map,this.openExternalRegistered=!1,this.fullscreenRegistered=!1,this.titleRegistered=!1,this.windowCloseRegistered=!1,this.readyHandshakeAttempts=0,this.readyHandshakeAcknowledged=!1,this.readyHandshakeRetryTimer=null,this.assignWebMessageReceiver(),this.assignMessageReceivedHandler(s.ReceiveFromHostMessageIds.registerOpenExternal,e=>{this.markReadyHandshakeAcknowledged(),this.registerOpenExternal()}),this.assignMessageReceivedHandler(s.ReceiveFromHostMessageIds.registerFullscreenChange,e=>{this.markReadyHandshakeAcknowledged(),this.registerFullscreenChange()}),this.assignMessageReceivedHandler(s.ReceiveFromHostMessageIds.registerTitleChange,e=>{this.markReadyHandshakeAcknowledged(),this.registerTitleChange()}),this.assignMessageReceivedHandler(s.ReceiveFromHostMessageIds.registerWindowClose,e=>{this.markReadyHandshakeAcknowledged(),this.registerWindowClose()}),this.sendReadyHandshakeWithRetry()}sendMessageToHost(e,t){var n,r;const s=(0,i.createEnvelope)(e,t);(null===(r=null===(n=window.__infiniframe)||void 0===n?void 0:n.host)||void 0===r?void 0:r.postData)?window.__infiniframe.host.postData(s):console.warn("Message to host failed. Host bridge API is not initialized.")}getMessageFromHostAsync(e){return r(this,void 0,void 0,function*(){var t;const n=null===(t=window.__infiniframe)||void 0===t?void 0:t.host;if(!(null==n?void 0:n.getDataAsync))throw new Error("Message to host failed. Host getDataAsync API is not initialized.");const r="string"==typeof e?(0,i.createEnvelope)(e,void 0,void 0,i.InteropGetCommand):e;return yield n.getDataAsync(r)})}assignMessageReceivedHandler(e,t){this.messageHandlers.set(e,t)}unregisterMessageReceivedHandler(e){this.messageHandlers.delete(e)}assignWebMessageReceiver(){var e,t;(null===(t=null===(e=window.__infiniframe)||void 0===e?void 0:e.host)||void 0===t?void 0:t.receiveCallback)?window.__infiniframe.host.receiveCallback(e=>{this.handleInteropMessage(e)}):console.warn("Web message receiver failed. Host bridge API is not initialized.")}handleInteropMessage(e){if("string"!=typeof e)return!1;if(!e)return!1;const t=(0,i.parseIncomingMessage)(e);if("error"in t)return!1;if(t.messageId.startsWith(d.BlazorWebViewMessagePrefix))return!0;const n=this.messageHandlers.get(t.messageId);return n?(n(t.payload),!0):(console.warn("No handler registered for message:",t),!1)}registerOpenExternal(){this.openExternalRegistered||(this.openExternalRegistered=!0,document.addEventListener("click",o.blankTargetHandler,{capture:!0}))}registerFullscreenChange(){this.fullscreenRegistered||(this.fullscreenRegistered=!0,document.addEventListener("fullscreenchange",e=>{document.fullscreenElement?this.sendMessageToHost(s.SendToHostMessageIds.fullscreenEnter):this.sendMessageToHost(s.SendToHostMessageIds.fullscreenExit)}),document.addEventListener("keydown",e=>r(this,void 0,void 0,function*(){"F11"===e.key&&(document.fullscreenElement?yield document.exitFullscreen():yield document.body.requestFullscreen())})))}registerTitleChange(){if(this.titleRegistered)return;this.titleRegistered=!0;const e=(0,a.getTitleObserverTarget)();if(e)return void(0,a.getTitleObserver)().observe(e,{childList:!0});const t=document.head||document.documentElement;if(!t)return;const n=new MutationObserver(()=>{const e=(0,a.getTitleObserverTarget)();e&&(n.disconnect(),(0,a.getTitleObserver)().observe(e,{childList:!0}))});n.observe(t,{childList:!0,subtree:!0})}registerWindowClose(){this.windowCloseRegistered||(this.windowCloseRegistered=!0,window.close=()=>{this.sendMessageToHost(s.SendToHostMessageIds.windowClose)})}sendReadyHandshakeWithRetry(){this.sendReadyHandshake(),this.readyHandshakeRetryTimer=window.setInterval(()=>{this.readyHandshakeAcknowledged||this.readyHandshakeAttempts>=d.MaxReadyHandshakeAttempts?this.stopReadyHandshakeRetry():this.sendReadyHandshake()},d.ReadyHandshakeRetryIntervalMs)}sendReadyHandshake(){this.readyHandshakeAttempts++,this.sendMessageToHost(s.SendToHostMessageIds.ready)}markReadyHandshakeAcknowledged(){this.readyHandshakeAcknowledged||(this.readyHandshakeAcknowledged=!0,this.stopReadyHandshakeRetry())}stopReadyHandshakeRetry(){null!==this.readyHandshakeRetryTimer&&(window.clearInterval(this.readyHandshakeRetryTimer),this.readyHandshakeRetryTimer=null)}}d.BlazorWebViewMessagePrefix="__bwv:",d.ReadyHandshakeRetryIntervalMs=1e3,d.MaxReadyHandshakeAttempts=20,t.default=d},136(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.InfiniFrameUtils=void 0,t.InfiniFrameUtils=class{setPointerCapture(e,t){null!==e&&null!==t&&(e.hasPointerCapture(t)||e.setPointerCapture(t))}releasePointerCapture(e,t){null!==e&&null!==t&&e.hasPointerCapture(t)&&e.releasePointerCapture(t)}}},305(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(s,i){function o(e){try{d(r.next(e))}catch(e){i(e)}}function a(e){try{d(r.throw(e))}catch(e){i(e)}}function d(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(o,a)}d((r=r.apply(e,t||[])).next())})};Object.defineProperty(t,"__esModule",{value:!0}),t.InfiniFrameWindow=void 0;const s=n(461),i=n(185);t.InfiniFrameWindow=class{getMessageFromHostAsync(e,t){try{return window.infiniframe.messaging.getMessageFromHostAsync((0,i.createGetEnvelope)(e,t))}catch(e){return console.error("Failed to get response message from host.",e),Promise.reject(e)}}setTitle(e){window.infiniframe.messaging.sendMessageToHost(s.SendToHostMessageIds.titleChange,e)}getTitleAsync(){return r(this,void 0,void 0,function*(){return this.getMessageFromHostAsync("title")})}}},185(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.InteropGetCommand=t.InteropPostCommand=t.InteropMessageMaxSizeBytes=t.InteropEnvelopeVersion=void 0,t.createEnvelope=s,t.createGetEnvelope=function(e,n){return s(r.SendToHostMessageIds.getRequest,{command:e,args:n},void 0,t.InteropGetCommand)},t.createEnvelopeMessage=function(e,n,r,i=t.InteropPostCommand,o){const a=s(e,n,r,i,o);return JSON.stringify(a)},t.parseIncomingMessage=function(e){if(!e||0===e.trim().length)return{error:"Message is empty."};if(function(e){return(new TextEncoder).encode(e).length}(e)>t.InteropMessageMaxSizeBytes)return{error:`Message exceeds max size of ${t.InteropMessageMaxSizeBytes} bytes.`};try{const s=JSON.parse(e);if("object"!=typeof(r=s)||null===r)return{error:"Envelope root must be a JSON object."};if("string"!=typeof s.id||0===s.id.trim().length)return{error:"Envelope 'id' is required and must be a string."};if("number"!=typeof s.version||!Number.isInteger(s.version))return{error:"Envelope 'version' is required and must be an integer."};if(s.version!==t.InteropEnvelopeVersion)return{error:`Unsupported envelope version '${s.version}'.`};const i=function(e){if(null!=e)return"string"==typeof e?e:JSON.stringify(e)}(s.data);return(n=s.command)!==t.InteropPostCommand&&n!==t.InteropGetCommand?{error:"Envelope 'command' must be 'Post' or 'Get'."}:void 0!==s.requestId&&"string"!=typeof s.requestId?{error:"Envelope 'requestId' must be a string."}:{messageId:s.id,payload:i,command:s.command,requestId:s.requestId}}catch(e){return{error:"Envelope JSON is malformed."}}var n,r};const r=n(461);function s(e,n,r,s=t.InteropPostCommand,i){if(!e||0===e.trim().length)throw new Error("Envelope 'id' is required.");return{id:e,command:s,requestId:i,data:n,version:t.InteropEnvelopeVersion,channel:r}}t.InteropEnvelopeVersion=2,t.InteropMessageMaxSizeBytes=1048576,t.InteropPostCommand="Post",t.InteropGetCommand="Get"},514(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.installHostBridge=function(){var e,t;const n=null!==(e=window.__infiniframe)&&void 0!==e?e:{},i=null!==(t=n.host)&&void 0!==t?t:{},o=i.postData,g=i.receiveCallback,f=i.getDataAsync;i.postData=e=>{!function(e,t){if("string"==typeof e){const n=e.trim();if(0===n.length)return void console.warn("Ignoring empty host bridge payload.");if(t)try{return void t(n)}catch(e){console.warn("Existing InfiniFrame host bridge failed. Falling back to platform adapters.",e)}return void c(n)}const n=a(e);if(!n)return;const r=JSON.stringify(n);if(t)try{return void t(r)}catch(e){try{return void t(n)}catch(t){console.warn("Existing InfiniFrame host bridge failed. Falling back to platform adapters.",e)}}c(r)}(e,o)},i.receiveCallback=e=>{d(e,g)},i.getDataAsync=e=>function(e,t,n,i){const o=function(e){if("string"==typeof e){const t=e.trim();return 0===t.length?null:t}const t=a(e);return t||null}(e);if(!o)return Promise.reject(new Error("Host getDataAsync payload is invalid."));if(n)try{const e=n(o);return e&&"function"==typeof e.then?e:Promise.resolve(String(null!=e?e:""))}catch(e){console.warn("Existing InfiniFrame getDataAsync bridge failed. Falling back to request/response transport.",e)}const c=`if_req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`;return new Promise((e,n)=>{var g;const f=window.setTimeout(()=>{l(v),n(new Error("Timed out waiting for getDataAsync response from host."))},1e4),v=t=>{var i,o;const a=(0,r.parseIncomingMessage)(t);if("error"in a||a.messageId!==s||!a.payload)return;let d;try{d=JSON.parse(a.payload)}catch(e){return}var g;!u(g=d)||"string"!=typeof g.requestId||"boolean"!=typeof g.success||void 0!==g.data&&"string"!=typeof g.data||void 0!==g.error&&"string"!=typeof g.error||d.requestId!==c||(window.clearTimeout(f),l(v),d.success?e(null!==(i=d.data)&&void 0!==i?i:""):n(new Error(null!==(o=d.error)&&void 0!==o?o:"Host getDataAsync failed.")))};d(v,i);const h=function(e,t){if("string"!=typeof e)return a(e,r.InteropGetCommand,t);try{const n=JSON.parse(e);if(u(n))return a(n,r.InteropGetCommand,t)}catch(e){}return a({id:e,version:r.InteropEnvelopeVersion},r.InteropGetCommand,t)}(o,c);if(!h)return window.clearTimeout(f),l(v),void n(new Error("Host getDataAsync payload is invalid."));null===(g=t.postData)||void 0===g||g.call(t,h)})}(e,i,f,g),n.host=i,window.__infiniframe=n};const r=n(185),s="__infiniframe:get:response",i=new Set;let o=!1;function a(e,t,n){var s;if(void 0===t&&(t=null!==(s=e.command)&&void 0!==s?s:r.InteropPostCommand),void 0===n&&(n=e.requestId),!e||"object"!=typeof e)return console.warn("Host bridge payload must be an envelope object."),null;if("string"!=typeof e.id||0===e.id.trim().length)return console.warn("Host bridge envelope requires a non-empty 'id'."),null;const i={id:e.id,command:t,requestId:n,data:e.data,version:r.InteropEnvelopeVersion};return void 0!==e.channel&&"string"==typeof e.channel&&e.channel.trim().length>0&&(i.channel=e.channel),i}function d(e,t){i.add(e),function(e){var t,n,r,s;if(o)return;const a=e=>{for(const t of i)t(e)};if(e)try{return e(a),void(o=!0)}catch(e){console.warn("Existing InfiniFrame host receive bridge failed. Falling back to platform adapters.",e)}(null===(n=null===(t=window.chrome)||void 0===t?void 0:t.webview)||void 0===n?void 0:n.addEventListener)?(window.chrome.webview.addEventListener("message",e=>a(e.data)),o=!0):(null===(s=null===(r=window.webkit)||void 0===r?void 0:r.messageHandlers)||void 0===s?void 0:s.infiniFrameInterop)?(window.__dispatchMessageCallback=a,o=!0):console.warn("Receive message registration failed. No supported host receive transport was found.")}(t)}function l(e){i.delete(e)}function c(e){var t,n,r,s,i;(null===(n=null===(t=window.chrome)||void 0===t?void 0:t.webview)||void 0===n?void 0:n.postMessage)?window.chrome.webview.postMessage(e):(null===(i=null===(s=null===(r=window.webkit)||void 0===r?void 0:r.messageHandlers)||void 0===s?void 0:s.infiniFrameInterop)||void 0===i?void 0:i.postMessage)?window.webkit.messageHandlers.infiniFrameInterop.postMessage(e):console.warn("[InfiniFrame] No native bridge available:",e)}function u(e){return"object"==typeof e&&null!==e}},270(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(s,i){function o(e){try{d(r.next(e))}catch(e){i(e)}}function a(e){try{d(r.throw(e))}catch(e){i(e)}}function d(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(o,a)}d((r=r.apply(e,t||[])).next())})};Object.defineProperty(t,"__esModule",{value:!0}),t.blankTargetHandler=function(e){return r(this,void 0,void 0,function*(){var t;let n=e.target;for(;n&&n!==document.body;){if("a"!==(null===(t=n.tagName)||void 0===t?void 0:t.toLowerCase())){n=n.parentElement;continue}const r=n;if(r.href){if("_blank"===r.getAttribute("target")||r.hasAttribute("data-external")||i(r.href))return e.preventDefault(),void window.infiniframe.messaging.sendMessageToHost(s.SendToHostMessageIds.openExternalLink,r.href);n=n.parentElement}else n=n.parentElement}})};const s=n(461);function i(e){try{return new URL(e,location.href).hostname!==location.hostname}catch(e){return!1}}},52(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.getTitleObserverTarget=function(){return document.querySelector("title")},t.getTitleObserver=function(){return new MutationObserver((e,t)=>{e.forEach(e=>{"childList"===e.type&&window.infiniframe.messaging.sendMessageToHost(r.SendToHostMessageIds.titleChange,document.title)})})};const r=n(461)}},t={};!function n(r){var s=t[r];if(void 0!==s)return s.exports;var i=t[r]={exports:{}};return e[r].call(i.exports,i,i.exports,n),i.exports}(297)})();
\ No newline at end of file
diff --git a/src/InfiniFrame.Native/.idea/cmake.xml b/src/InfiniFrame.Native/.idea/cmake.xml
new file mode 100644
index 000000000..9dc59ef1d
--- /dev/null
+++ b/src/InfiniFrame.Native/.idea/cmake.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/InfiniFrame.Native/CMakeLists.txt b/src/InfiniFrame.Native/CMakeLists.txt
index 7cd736266..ab781cb40 100644
--- a/src/InfiniFrame.Native/CMakeLists.txt
+++ b/src/InfiniFrame.Native/CMakeLists.txt
@@ -18,8 +18,8 @@ endif ()
# ----------------------------------------------------------------------------------------------------------------------
# Dependencies
# ----------------------------------------------------------------------------------------------------------------------
-
include("${CMAKE_SOURCE_DIR}/cmake/NativeDependencies.cmake")
+include("${CMAKE_SOURCE_DIR}/cmake/Embed.InfiniFrameHostJs.cmake")
include("${CMAKE_SOURCE_DIR}/cmake/Platform.Windows.cmake")
include("${CMAKE_SOURCE_DIR}/cmake/Platform.MacOS.cmake")
@@ -30,7 +30,6 @@ infiniframe_setup_dependencies()
# ----------------------------------------------------------------------------------------------------------------------
# Source Files
# ----------------------------------------------------------------------------------------------------------------------
-
set(COMMON_SOURCES
Exports.cpp
)
@@ -65,6 +64,8 @@ set(HEADER_FILES
Core/InfiniFrame.h
Core/InfiniFrameWindow.h
Core/InfiniFrameDialog.h
+ Embedded/Embedded.h
+ Embedded/InfiniFrameHostJs/InfiniFrameHostJs.h
Types/Basic.h
Types/Dialog.h
Core/InfiniFrameInitParams.h
@@ -89,12 +90,17 @@ if (WIN32)
"${WINDOWS_SOURCES}"
"${HEADER_FILES}"
)
+
+ infiniframe_setup_embed_js(${PROJECT_NAME})
+
elseif (APPLE)
infiniframe_configure_macos_target(
${PROJECT_NAME}
"${MAC_SOURCES}"
"${HEADER_FILES}"
)
+
+ infiniframe_setup_embed_js(${PROJECT_NAME})
elseif (UNIX)
infiniframe_configure_linux_target(
${PROJECT_NAME}
@@ -103,12 +109,13 @@ elseif (UNIX)
"${LINUX_SOURCES}"
"${HEADER_FILES}"
)
+
+ infiniframe_setup_embed_js(${PROJECT_NAME})
endif ()
# ----------------------------------------------------------------------------------------------------------------------
# Sanitizers (Debug only)
# ----------------------------------------------------------------------------------------------------------------------
-
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
message(STATUS "Enabling sanitizers for Debug build")
@@ -126,7 +133,6 @@ endif ()
# ----------------------------------------------------------------------------------------------------------------------
# Precompiled Headers
# ----------------------------------------------------------------------------------------------------------------------
-
if (NOT APPLE)
target_precompile_headers(${PROJECT_NAME} PRIVATE
diff --git a/src/InfiniFrame.Native/Devtools/localdev-ubuntu.bash b/src/InfiniFrame.Native/Devtools/localdev-ubuntu.bash
deleted file mode 100644
index 302774003..000000000
--- a/src/InfiniFrame.Native/Devtools/localdev-ubuntu.bash
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-set -e
-
-# Update and install basic utilities
-sudo apt update
-sudo apt install -y apt-transport-https ca-certificates gnupg software-properties-common wget build-essential pkg-config
-
-# Add Kitware repo for latest CMake
-wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc | sudo gpg --dearmor -o /usr/share/keyrings/kitware-archive-keyring.gpg
-UBUNTU_CODENAME=$(lsb_release -cs)
-echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ $UBUNTU_CODENAME main" | sudo tee /etc/apt/sources.list.d/kitware.list
-
-# Update and install latest CMake
-sudo apt update
-sudo apt install -y cmake
-
-# Install development libraries commonly needed for GTK/WebKit projects
-sudo apt install -y \
- libgtk-3-dev \
- libwebkit2gtk-4.1-dev \
- libssl-dev \
- libcurl4-openssl-dev \
- zlib1g-dev \
- libnotify-dev
diff --git a/src/InfiniFrame.Native/Embedded/Embedded.h b/src/InfiniFrame.Native/Embedded/Embedded.h
new file mode 100644
index 000000000..f03843a70
--- /dev/null
+++ b/src/InfiniFrame.Native/Embedded/Embedded.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "InfiniFrameHostJs.h"
+#include
+
+namespace Embedded {
+ inline const std::wstring& InfiniFrameHostJsUtf16() {
+ static const std::wstring cached = [] {
+ const auto* src = reinterpret_cast(g_infiniframe_host_js_data);
+
+ std::u16string temp;
+ temp.resize(simdutf::utf16_length_from_utf8(src, g_infiniframe_host_js_size));
+
+ const size_t written = simdutf::convert_utf8_to_utf16(
+ src,
+ g_infiniframe_host_js_size,
+ temp.data()
+ );
+
+ temp.resize(written);
+
+ return std::wstring(temp.begin(), temp.end());
+ }();
+
+ return cached;
+ }
+
+ inline const std::string& InfiniFrameHostJsUtf8() {
+ static const std::string cached = [] {
+ const auto* src = reinterpret_cast(g_infiniframe_host_js_data);
+ return std::string(src, g_infiniframe_host_js_size);
+ }();
+ return cached;
+ }
+}
diff --git a/src/InfiniFrame.Native/Embedded/InfiniFrameHostJs/InfiniFrameHostJs.h b/src/InfiniFrame.Native/Embedded/InfiniFrameHostJs/InfiniFrameHostJs.h
new file mode 100644
index 000000000..23e121dcc
--- /dev/null
+++ b/src/InfiniFrame.Native/Embedded/InfiniFrameHostJs/InfiniFrameHostJs.h
@@ -0,0 +1,6 @@
+#pragma once
+// ReSharper disable once CppUnusedIncludeDirective
+#include
+
+extern const unsigned char g_infiniframe_host_js_data[];
+extern const size_t g_infiniframe_host_js_size;
diff --git a/src/InfiniFrame.Native/Embedded/InfiniFrameHostJs/infiniframe.host.js b/src/InfiniFrame.Native/Embedded/InfiniFrameHostJs/infiniframe.host.js
new file mode 100644
index 000000000..c3e1acf03
--- /dev/null
+++ b/src/InfiniFrame.Native/Embedded/InfiniFrameHostJs/infiniframe.host.js
@@ -0,0 +1,430 @@
+(function () {
+ 'use strict';
+
+ console.log('InfiniFrame WebView JavaScript bridge initialized.');
+
+ /* ============================================================================================================== */
+ /* Setup guard */
+ /* ============================================================================================================== */
+
+ window.__infiniframeSetup = window.__infiniframeSetup || {
+ messagingBridgeInitialized: false,
+ WebviewReceiveAttached: false,
+ windowExternalBridgeInitialized: false,
+ blazorModulesFetchPatchInitialized: false,
+ blazorCustomElementsPatchInitialized: false,
+ customElementsInitialized: false
+ };
+
+ /* ============================================================================================================== */
+ /* Platform detection */
+ /* ============================================================================================================== */
+
+ let PLATFORM;
+ if (window.chrome && window.chrome.webview) PLATFORM = 'webview';
+ else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.infiniFrameInterop) PLATFORM = 'webkit';
+ else PLATFORM = null;
+
+ function nativePost(message) {
+ if (PLATFORM === 'webview') {
+ window.chrome.webview.postMessage(message);
+ } else if (PLATFORM === 'webkit') {
+ window.webkit.messageHandlers.infiniFrameInterop.postMessage(message);
+ } else {
+ console.warn('[InfiniFrame] No native bridge available:', message);
+ }
+ }
+
+ function normalizeEnvelope(envelope, command, requestId) {
+ if (!envelope || typeof envelope !== 'object') return null;
+ if (typeof envelope.id !== 'string' || envelope.id.trim().length === 0) return null;
+
+ const normalized = {
+ id: envelope.id,
+ command: command || envelope.command || 'Post',
+ data: envelope.data,
+ version: 2
+ };
+
+ const resolvedRequestId = requestId || envelope.requestId;
+ if (typeof resolvedRequestId === 'string' && resolvedRequestId.length > 0) {
+ normalized.requestId = resolvedRequestId;
+ }
+
+ if (typeof envelope.channel === 'string' && envelope.channel.trim().length > 0) {
+ normalized.channel = envelope.channel;
+ }
+
+ return normalized;
+ }
+
+ function createGetEnvelope(message, requestId) {
+ if (typeof message !== 'string') return normalizeEnvelope(message, 'Get', requestId);
+
+ const trimmed = message.trim();
+ if (!trimmed) return null;
+
+ try {
+ const parsed = JSON.parse(trimmed);
+ if (parsed && typeof parsed === 'object') return normalizeEnvelope(parsed, 'Get', requestId);
+ } catch (_) {
+ // Plain strings are treated as get message IDs.
+ }
+
+ return normalizeEnvelope({ id: trimmed }, 'Get', requestId);
+ }
+
+ /* ============================================================================================================== */
+ /* 1. Messaging bridge */
+ /* ============================================================================================================== */
+
+ if (!window.__infiniframeSetup.messagingBridgeInitialized) {
+ window.__infiniframeSetup.messagingBridgeInitialized = true;
+
+ window.__infiniframe = {
+ onReceiveMessageCallbacks: [],
+ host: {
+ postData(envelope) {
+ const normalized = typeof envelope === 'string' ? envelope : normalizeEnvelope(envelope, 'Post');
+ if (!normalized) return;
+ const message = typeof normalized === 'string' ? normalized : JSON.stringify(normalized);
+ nativePost(message);
+ },
+
+ receiveCallback(cb) {
+ window.__infiniframe.onReceiveMessageCallbacks.push(cb);
+ },
+
+ getDataAsync(message) {
+ const requestId =
+ 'if_req_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2);
+
+ const getEnvelope = createGetEnvelope(message, requestId);
+ if (!getEnvelope) return Promise.reject(new Error('Host getDataAsync payload is invalid.'));
+
+ return new Promise((resolve, reject) => {
+
+ const callback = function (raw) {
+ try {
+ const env = JSON.parse(raw);
+ if (!env || env.id !== '__infiniframe:get:response') return;
+
+ const payload = JSON.parse(env.data || '{}');
+ if (!payload || payload.requestId !== requestId) return;
+
+ const idx = window.__infiniframe.onReceiveMessageCallbacks.indexOf(callback);
+ if (idx >= 0) window.__infiniframe.onReceiveMessageCallbacks.splice(idx, 1);
+
+ payload.success
+ ? resolve(payload.data || '')
+ : reject(new Error(payload.error || 'getDataAsync failed'));
+ } catch (_) {}
+ };
+
+ window.__infiniframe.host.receiveCallback(callback);
+
+ window.__infiniframe.host.postData(getEnvelope);
+ });
+ }
+ }
+ };
+
+ /* native receive */
+ if (!window.__infiniframeSetup.WebviewReceiveAttached) {
+ window.__infiniframeSetup.WebviewReceiveAttached = true;
+
+ function dispatch(data) {
+ for (const cb of window.__infiniframe.onReceiveMessageCallbacks) {
+ try { cb(data); } catch (_) {}
+ }
+ }
+
+ if (PLATFORM === 'webview') {
+ window.chrome.webview.addEventListener('message', e => dispatch(e.data));
+ } else if (PLATFORM === 'webkit') {
+ window.__dispatchMessageCallback = dispatch;
+ }
+ }
+ }
+
+ /* ============================================================================================================== */
+ /* 2. window.external bridge (Blazor compatibility) */
+ /* ============================================================================================================== */
+ if (!window.__infiniframeSetup.windowExternalBridgeInitialized) {
+ window.__infiniframeSetup.windowExternalBridgeInitialized = true;
+
+ window.external = window.external || {};
+ window.__blazor_callbacks = window.__blazor_callbacks || [];
+
+ window.external.receiveMessage = function (callback) {
+ window.__blazor_callbacks.push(callback);
+ };
+
+ window.external.receiveCallback = window.external.receiveMessage;
+
+ window.external.sendMessage = function (message) {
+ nativePost(message);
+ };
+
+ window.external.postMessage = window.external.sendMessage;
+
+ // hook Blazor callbacks into main dispatcher
+ if (!window.__blazor_dispatch_hooked) {
+ window.__blazor_dispatch_hooked = true;
+
+ window.__infiniframe.onReceiveMessageCallbacks.push(function (message) {
+ for (let i = 0; i < window.__blazor_callbacks.length; i++) {
+ try {
+ window.__blazor_callbacks[i](message);
+ } catch (_) {
+ }
+ }
+ });
+ }
+ }
+
+ /* ============================================================================================================== */
+ /* 3. Blazor modules fetch patch */
+ /* ============================================================================================================== */
+ if (!window.__infiniframeSetup.blazorModulesFetchPatchInitialized) {
+ window.__infiniframeSetup.blazorModulesFetchPatchInitialized = true;
+
+ const originalFetch = window.fetch;
+
+ window.fetch = function (input, init) {
+ try {
+ const requestUrl = typeof input === 'string' ? input : (input && input.url ? input.url : '');
+ if (requestUrl) {
+ const absoluteUrl = new URL(requestUrl, window.location.href).href;
+
+ const isBlazorModulesJson =
+ absoluteUrl === 'http://localhost/_framework/blazor.modules.json' ||
+ absoluteUrl === 'http://localhost/_framework/blazor.modules.json/' ||
+ absoluteUrl === 'app://localhost/_framework/blazor.modules.json' ||
+ absoluteUrl === 'app://localhost/_framework/blazor.modules.json/';
+
+ if (isBlazorModulesJson) {
+ return Promise.resolve(new Response('[]', {
+ status: 200,
+ statusText: 'OK',
+ headers: {'Content-Type': 'application/json'}
+ }));
+ }
+ }
+ } catch (_) {
+ }
+
+ return originalFetch.call(this, input, init);
+ };
+ }
+
+ function toKebabCase(name) {
+ return String(name)
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
+ .replace(/_/g, '-')
+ .toLowerCase();
+ }
+
+ function toParameterValue(rawValue, typeName) {
+ if (typeName === 'bool' || typeName === 'boolean') {
+ if (rawValue === null) return false;
+ if (rawValue === '') return true;
+ return String(rawValue).toLowerCase() !== 'false';
+ }
+
+ if (['number', 'int', 'float', 'double', 'decimal'].includes(typeName)) {
+ const n = Number(rawValue);
+ return Number.isNaN(n) ? rawValue : n;
+ }
+
+ return rawValue;
+ }
+
+ const pendingAutoCustomElementRegistrations = [];
+ let autoCustomElementRegistrationScheduled = false;
+
+ function scheduleAutoRegisterMissingInitializerCustomElements(defs, initMap) {
+ if (!defs) return;
+
+ pendingAutoCustomElementRegistrations.push({ defs, initMap });
+
+ if (autoCustomElementRegistrationScheduled) return;
+ autoCustomElementRegistrationScheduled = true;
+
+ window.setTimeout(function () {
+ autoCustomElementRegistrationScheduled = false;
+ flushAutoRegisterMissingInitializerCustomElements();
+ }, 0);
+ }
+
+ function flushAutoRegisterMissingInitializerCustomElements() {
+ if (typeof window.registerBlazorCustomElement !== 'function') {
+ return;
+ }
+
+ while (pendingAutoCustomElementRegistrations.length > 0) {
+ const item = pendingAutoCustomElementRegistrations.shift();
+
+ try {
+ autoRegisterMissingInitializerCustomElements(item.defs, item.initMap);
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ }
+
+ function autoRegisterMissingInitializerCustomElements(defs, initMap) {
+ const initialized = {};
+
+ for (const list of Object.values(initMap || {})) {
+ if (!Array.isArray(list)) continue;
+ for (const id of list) initialized[id] = true;
+ }
+
+ for (const [id, def] of Object.entries(defs || {})) {
+ if (initialized[id]) continue;
+ window.registerBlazorCustomElement(id, def);
+ }
+ }
+
+ /* ============================================================================================================== */
+ /* 4. Blazor custom elements + interop patch */
+ /* ============================================================================================================== */
+ if (!window.__infiniframeSetup.blazorCustomElementsPatchInitialized) {
+ window.__infiniframeSetup.blazorCustomElementsPatchInitialized = true;
+
+ function patchAttachWebRendererInteropIfAvailable() {
+ const blazor = window.Blazor;
+
+ if (!blazor || !blazor._internal || typeof blazor._internal.attachWebRendererInterop !== 'function') {
+ return false;
+ }
+
+ if (blazor._internal.__infiniframeAttachWebRendererInteropPatched) {
+ return true;
+ }
+
+ const original = blazor._internal.attachWebRendererInterop;
+
+ blazor._internal.attachWebRendererInterop = function () {
+ const result = original.apply(this, arguments);
+ scheduleAutoRegisterMissingInitializerCustomElements(arguments[2], arguments[3]);
+ return result;
+ };
+
+ blazor._internal.__infiniframeAttachWebRendererInteropPatched = true;
+ return true;
+ }
+
+ if (!patchAttachWebRendererInteropIfAvailable()) {
+ const descriptor = Object.getOwnPropertyDescriptor(window, 'Blazor');
+
+ if (!descriptor || descriptor.configurable) {
+ let value = window.Blazor;
+
+ Object.defineProperty(window, 'Blazor', {
+ configurable: true,
+ enumerable: true,
+ get: function () {
+ return value;
+ },
+ set: function (v) {
+ value = v;
+ patchAttachWebRendererInteropIfAvailable();
+ }
+ });
+
+ if (value) {
+ patchAttachWebRendererInteropIfAvailable();
+ }
+ }
+ }
+ }
+
+ /* =================================================================================================== */
+ /* 5. Custom elements */
+ /* =================================================================================================== */
+ if (!window.__infiniframeSetup.customElementsInitialized) {
+ window.__infiniframeSetup.customElementsInitialized = true;
+
+ window.registerBlazorCustomElement = function (identifier, parameterDefinitions) {
+ if (!window.Blazor?.rootComponents?.add) return;
+ if (!window.customElements?.define) return;
+ if (window.customElements.get(identifier)) return;
+
+ const defs = Array.isArray(parameterDefinitions) ? parameterDefinitions : [];
+ const map = {};
+
+ for (const def of defs) {
+ if (!def?.name) continue;
+ const type = String(def.type || '').toLowerCase();
+ if (type === 'eventcallback') continue;
+
+ const attr = toKebabCase(def.name);
+ map[attr] = {name: def.name, type};
+ }
+
+ const observed = Object.keys(map);
+
+ class Host extends HTMLElement {
+ constructor() {
+ super();
+ this._component = null;
+ this._isDisconnected = false;
+ }
+
+ static get observedAttributes() {
+ return observed;
+ }
+
+ connectedCallback() {
+ this._isDisconnected = false;
+
+ window.Blazor.rootComponents.add(this, identifier, this._getParams())
+ .then(c => {
+ this._component = c;
+ if (this._isDisconnected && c) {
+ this._component = null;
+ return c.dispose();
+ }
+ })
+ .catch(console.error);
+ }
+
+ disconnectedCallback() {
+ this._isDisconnected = true;
+ const c = this._component;
+ this._component = null;
+ if (c?.dispose) Promise.resolve(c.dispose()).catch(() => {
+ });
+ }
+
+ attributeChangedCallback(name, oldValue, newValue) {
+ if (oldValue === newValue) return;
+ if (!this._component?.setParameters) return;
+
+ const info = map[name];
+ if (!info) return;
+
+ const p = {};
+ p[info.name] = toParameterValue(newValue, info.type);
+
+ this._component.setParameters(p).catch(console.error);
+ }
+
+ _getParams() {
+ const p = {};
+ for (const attr of observed) {
+ if (!this.hasAttribute(attr)) continue;
+ const info = map[attr];
+ p[info.name] = toParameterValue(this.getAttribute(attr), info.type);
+ }
+ return p;
+ }
+ }
+
+ window.customElements.define(identifier, Host);
+ };
+ }
+
+})();
diff --git a/src/InfiniFrame.Native/InfiniFrame.Native.proj b/src/InfiniFrame.Native/InfiniFrame.Native.proj
index 5d5b7fdf8..efca56857 100644
--- a/src/InfiniFrame.Native/InfiniFrame.Native.proj
+++ b/src/InfiniFrame.Native/InfiniFrame.Native.proj
@@ -44,7 +44,7 @@
-
+
diff --git a/src/InfiniFrame.Native/Platform/Linux/Window.cpp b/src/InfiniFrame.Native/Platform/Linux/Window.cpp
index 229e149b4..7de503c1b 100644
--- a/src/InfiniFrame.Native/Platform/Linux/Window.cpp
+++ b/src/InfiniFrame.Native/Platform/Linux/Window.cpp
@@ -16,6 +16,7 @@
#include
#include
#include
+#include "Embedded/Embedded.h"
std::mutex invokeLockMutex;
@@ -1007,178 +1008,25 @@ void InfiniFrameWindow::Show(bool isAlreadyShown) {
gtk_container_add(GTK_CONTAINER(m_impl->_window), m_impl->_webview);
+ auto js = Embedded::InfiniFrameHostJsUtf8();
+
WebKitUserScript* script = webkit_user_script_new(
- "window.__receiveMessageCallbacks = [];"
- "window.__dispatchMessageCallback = function(message) {"
- " window.__receiveMessageCallbacks.forEach(function(callback) { callback(message); });"
- "};"
- "window.infiniframe = window.infiniframe || {};"
- "window.infiniframe.host = window.infiniframe.host || {};"
- "window.infiniframe.host.postMessage = window.infiniframe.host.postMessage || function(envelope) {"
- " var message = (typeof envelope === 'string') ? envelope : JSON.stringify(envelope);"
- " window.webkit.messageHandlers.InfiniFrameInterop.postMessage(message);"
- "};"
- "window.infiniframe.host.receiveMessage = window.infiniframe.host.receiveMessage || function(callback) {"
- " window.__receiveMessageCallbacks.push(callback);"
- "};"
- "(function(){"
- " if(window.__infiniframeRegisterBlazorCustomElement){return;}"
- " window.__infiniframeRegisterBlazorCustomElement=true;"
- " function toKebabCase(name){"
- " return String(name).replace(/([a-z0-9])([A-Z])/g,'$1-$2').replace(/_/g,'-').toLowerCase();"
- " }"
- " function toParameterValue(rawValue,typeName){"
- " if(typeName==='bool'||typeName==='boolean'){"
- " if(rawValue===null){return false;}"
- " if(rawValue===''){return true;}"
- " return String(rawValue).toLowerCase()!=='false';"
- " }"
- " if(typeName==='number'||typeName==='int'||typeName==='float'||typeName==='double'||typeName==='decimal'){"
- " var numericValue=Number(rawValue);"
- " return Number.isNaN(numericValue)?rawValue:numericValue;"
- " }"
- " return rawValue;"
- " }"
- " window.registerBlazorCustomElement=window.registerBlazorCustomElement||function(identifier,parameterDefinitions){"
- " if(!window.Blazor||!window.Blazor.rootComponents||!window.Blazor.rootComponents.add){"
- " console.warn('registerBlazorCustomElement skipped: Blazor.rootComponents is unavailable.');"
- " return;"
- " }"
- " if(!window.customElements||typeof window.customElements.define!=='function'){"
- " console.warn('registerBlazorCustomElement skipped: customElements API is unavailable.');"
- " return;"
- " }"
- " if(window.customElements.get(identifier)){return;}"
- " var definitions=Array.isArray(parameterDefinitions)?parameterDefinitions:[];"
- " var parametersByAttribute={};"
- " for(var index=0;index{"
- " this._component=component;"
- " if(this._isDisconnected&&this._component){"
- " var detachedComponent=this._component;"
- " this._component=null;"
- " return detachedComponent.dispose();"
- " }"
- " return null;"
- " }).catch((error)=>{"
- " console.error('Failed to attach custom element component.',error);"
- " });"
- " }"
- " disconnectedCallback(){"
- " this._isDisconnected=true;"
- " var component=this._component;"
- " this._component=null;"
- " if(component&&typeof component.dispose==='function'){"
- " Promise.resolve(component.dispose()).catch(function(){});"
- " }"
- " }"
- " attributeChangedCallback(attributeName,oldValue,newValue){"
- " if(oldValue===newValue){return;}"
- " if(!this._component||typeof this._component.setParameters!=='function'){return;}"
- " var parameterInfo=parametersByAttribute[String(attributeName).toLowerCase()];"
- " if(!parameterInfo){return;}"
- " var nextParameters={};"
- " nextParameters[parameterInfo.name]=toParameterValue(newValue,parameterInfo.type);"
- " var updateResult=this._component.setParameters(nextParameters);"
- " if(updateResult&&typeof updateResult.catch==='function'){"
- " updateResult.catch(function(error){"
- " console.error('Failed to update custom element parameters.',error);"
- " });"
- " }"
- " }"
- " _getCurrentParameters(){"
- " var parameters={};"
- " for(var index=0;index(m_impl->_webMessageReceivedCallback)
);
- webkit_user_content_manager_register_script_message_handler(contentManager, "InfiniFrameInterop");
+ webkit_user_content_manager_register_script_message_handler(contentManager, "infiniFrameInterop");
if (!m_impl->_startUrl.empty())
NavigateToUrl(const_cast(m_impl->_startUrl.c_str()));
diff --git a/src/InfiniFrame.Native/Platform/Mac/Window.mm b/src/InfiniFrame.Native/Platform/Mac/Window.mm
index a9479eac2..018b6b4ae 100644
--- a/src/InfiniFrame.Native/Platform/Mac/Window.mm
+++ b/src/InfiniFrame.Native/Platform/Mac/Window.mm
@@ -2,6 +2,7 @@
#include "Core/InfiniFrameWindow.h"
#include "Core/InfiniFrameDialog.h"
#include "Core/InfiniFrameWindowImpl.h"
+#include "Embedded/Embedded.h"
#include "Utils/Common.h"
#include "AppDelegate.h"
#include "UiDelegate.h"
@@ -981,175 +982,19 @@
void InfiniFrameWindow::AttachWebView()
{
- NSString *initScriptSource = @"window.__receiveMessageCallbacks = [];"
- "window.__dispatchMessageCallback = function(message) {"
- " window.__receiveMessageCallbacks.forEach(function(callback) { callback(message); });"
- "};"
- "window.infiniframe = window.infiniframe || {};"
- "window.infiniframe.host = window.infiniframe.host || {};"
- "window.infiniframe.host.postMessage = window.infiniframe.host.postMessage || function(envelope) {"
- " var message = (typeof envelope === 'string') ? envelope : JSON.stringify(envelope);"
- " window.webkit.messageHandlers.infiniFrameInterop.postMessage(message);"
- "};"
- "window.infiniframe.host.receiveMessage = window.infiniframe.host.receiveMessage || function(callback) {"
- " window.__receiveMessageCallbacks.push(callback);"
- "};"
- "(function(){"
- " if(window.__infiniframeRegisterBlazorCustomElement){return;}"
- " window.__infiniframeRegisterBlazorCustomElement=true;"
- " function toKebabCase(name){"
- " return String(name).replace(/([a-z0-9])([A-Z])/g,'$1-$2').replace(/_/g,'-').toLowerCase();"
- " }"
- " function toParameterValue(rawValue,typeName){"
- " if(typeName==='bool'||typeName==='boolean'){"
- " if(rawValue===null){return false;}"
- " if(rawValue===''){return true;}"
- " return String(rawValue).toLowerCase()!=='false';"
- " }"
- " if(typeName==='number'||typeName==='int'||typeName==='float'||typeName==='double'||typeName==='decimal'){"
- " var numericValue=Number(rawValue);"
- " return Number.isNaN(numericValue)?rawValue:numericValue;"
- " }"
- " return rawValue;"
- " }"
- " window.registerBlazorCustomElement=window.registerBlazorCustomElement||function(identifier,parameterDefinitions){"
- " if(!window.Blazor||!window.Blazor.rootComponents||!window.Blazor.rootComponents.add){"
- " console.warn('registerBlazorCustomElement skipped: Blazor.rootComponents is unavailable.');"
- " return;"
- " }"
- " if(!window.customElements||typeof window.customElements.define!=='function'){"
- " console.warn('registerBlazorCustomElement skipped: customElements API is unavailable.');"
- " return;"
- " }"
- " if(window.customElements.get(identifier)){return;}"
- " var definitions=Array.isArray(parameterDefinitions)?parameterDefinitions:[];"
- " var parametersByAttribute={};"
- " for(var index=0;index{"
- " this._component=component;"
- " if(this._isDisconnected&&this._component){"
- " var detachedComponent=this._component;"
- " this._component=null;"
- " return detachedComponent.dispose();"
- " }"
- " return null;"
- " }).catch((error)=>{"
- " console.error('Failed to attach custom element component.',error);"
- " });"
- " }"
- " disconnectedCallback(){"
- " this._isDisconnected=true;"
- " var component=this._component;"
- " this._component=null;"
- " if(component&&typeof component.dispose==='function'){"
- " Promise.resolve(component.dispose()).catch(function(){});"
- " }"
- " }"
- " attributeChangedCallback(attributeName,oldValue,newValue){"
- " if(oldValue===newValue){return;}"
- " if(!this._component||typeof this._component.setParameters!=='function'){return;}"
- " var parameterInfo=parametersByAttribute[String(attributeName).toLowerCase()];"
- " if(!parameterInfo){return;}"
- " var nextParameters={};"
- " nextParameters[parameterInfo.name]=toParameterValue(newValue,parameterInfo.type);"
- " var updateResult=this._component.setParameters(nextParameters);"
- " if(updateResult&&typeof updateResult.catch==='function'){"
- " updateResult.catch(function(error){"
- " console.error('Failed to update custom element parameters.',error);"
- " });"
- " }"
- " }"
- " _getCurrentParameters(){"
- " var parameters={};"
- " for(var index=0;index_webviewConfiguration.userContentController = userContentController;
m_impl->_webview = [
diff --git a/src/InfiniFrame.Native/Platform/Windows/Window.cpp b/src/InfiniFrame.Native/Platform/Windows/Window.cpp
index 591a49fe1..18797937f 100644
--- a/src/InfiniFrame.Native/Platform/Windows/Window.cpp
+++ b/src/InfiniFrame.Native/Platform/Windows/Window.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
@@ -20,6 +21,8 @@
#include "ToastHandler.h"
#include "Utils/Common.h"
+#include "Embedded/Embedded.h"
+
#pragma comment(lib, "Shcore.lib")
#pragma comment(lib, "Urlmon.lib")
@@ -1255,10 +1258,56 @@ void InfiniFrameWindow::AttachWebView() {
if (envResult != S_OK) {
return envResult;
}
- m_impl->_webviewController->
- get_CoreWebView2(
- &m_impl->_webviewWindow
- );
+ m_impl->_webviewController->get_CoreWebView2(&m_impl->_webviewWindow);
+
+ const auto js_wide = Embedded::InfiniFrameHostJsUtf16();
+ OutputDebugStringW(std::format(L"[InfiniFrame] Bridge script length: {} chars\n", js_wide.size()).c_str());
+
+ // AddScriptToExecuteOnDocumentCreated is async: the script is not
+ // registered in the browser process until the completion callback fires.
+ // We must not navigate until then, otherwise fast local navigations
+ // (e.g., app://localhost/) reach ContentLoading before the bridge script
+ // exists, and Blazor's Boot.WebView.ts throws because
+ // window.external.receiveMessage is undefined.
+ //
+ // If script registration fails for any reason (e.g., empty resource),
+ // we fall through and navigate anyway so the page still loads.
+ struct NavigateOnce {
+ InfiniFrameWindow* self;
+ bool fired = false;
+ void navigate() {
+ if (fired) return;
+ fired = true;
+ if (!self->m_impl->_startUrl.empty())
+ self->m_impl->_webviewWindow->Navigate(self->m_impl->_startUrl.c_str());
+ else if (!self->m_impl->_startString.empty())
+ self->m_impl->_webviewWindow->NavigateToString(self->m_impl->_startString.c_str());
+ else {
+ MessageBox(nullptr,
+ L"Neither StartUrl nor StartString was specified",
+ L"Native Initialization Failed", MB_OK);
+ exit(0);
+ }
+ }
+ };
+ auto nav = std::make_shared(NavigateOnce{this});
+
+ HRESULT addScriptHr = m_impl->_webviewWindow->AddScriptToExecuteOnDocumentCreated(
+ js_wide.c_str(),
+ Callback(
+ [nav](HRESULT errorCode, LPCWSTR id) -> HRESULT {
+ OutputDebugStringW(std::format(L"[InfiniFrame] AddScriptToExecuteOnDocumentCreated callback: hr=0x{:08X} id={}\n", (unsigned)errorCode, id ? id : L"(null)").c_str());
+ nav->navigate();
+ return S_OK;
+ }
+ ).Get()
+ );
+
+ // If AddScriptToExecuteOnDocumentCreated itself failed synchronously
+ // (e.g., empty script string on some WebView2 versions), navigate now
+ // so the page is not left blank.
+ if (FAILED(addScriptHr))
+ nav->navigate();
wil::com_ptr
settings;
@@ -1289,202 +1338,7 @@ void InfiniFrameWindow::AttachWebView() {
EventRegistrationToken
webMessageToken;
- m_impl->_webviewWindow->
- AddScriptToExecuteOnDocumentCreated(
- L"(function(){window.infiniframe=window.infiniframe||{};window.infiniframe.host=window.infiniframe.host||{};window.infiniframe.host.postMessage=window.infiniframe.host.postMessage||function(envelope){var message=(typeof envelope==='string')?envelope:JSON.stringify(envelope);window.chrome.webview.postMessage(message);};window.infiniframe.host.receiveMessage=window.infiniframe.host.receiveMessage||function(callback){window.chrome.webview.addEventListener('message',function(e){callback(e.data);});};window.external=window.external||{};window.external.sendMessage=window.external.sendMessage||function(message){window.chrome.webview.postMessage(message);};window.external.postMessage=window.external.postMessage||window.external.sendMessage;window.external.receiveMessage=window.external.receiveMessage||function(callback){window.chrome.webview.addEventListener('message',function(e){callback(e.data);});};})();",
- nullptr
- );
- m_impl->_webviewWindow->
- AddScriptToExecuteOnDocumentCreated(
- L"(function(){if(window.__infiniframeBlazorModulesPatched){return;}window.__infiniframeBlazorModulesPatched=true;var originalFetch=window.fetch;window.fetch=function(input,init){try{var requestUrl=typeof input==='string'?input:(input&&input.url?input.url:'');if(requestUrl){var absoluteUrl=new URL(requestUrl,window.location.href).href;var isBlazorModulesJson=absoluteUrl==='http://localhost/_framework/blazor.modules.json'||absoluteUrl==='http://localhost/_framework/blazor.modules.json/'||absoluteUrl==='app://localhost/_framework/blazor.modules.json'||absoluteUrl==='app://localhost/_framework/blazor.modules.json/';if(isBlazorModulesJson){return Promise.resolve(new Response('[]',{status:200,statusText:'OK',headers:{'Content-Type':'application/json'}}));}}}catch(_){ }return originalFetch.call(this,input,init);};})();",
- nullptr
- );
- m_impl->_webviewWindow->
- AddScriptToExecuteOnDocumentCreated(
- LR"JS((function(){
- if(window.__infiniframeRegisterBlazorCustomElement){return;}
- window.__infiniframeRegisterBlazorCustomElement=true;
-
- function toKebabCase(name){
- return String(name)
- .replace(/([a-z0-9])([A-Z])/g,'$1-$2')
- .replace(/_/g,'-')
- .toLowerCase();
- }
-
- function toParameterValue(rawValue, typeName){
- if(typeName==='bool'||typeName==='boolean'){
- if(rawValue===null){return false;}
- if(rawValue===''){return true;}
- return String(rawValue).toLowerCase()!=='false';
- }
- if(typeName==='number'||typeName==='int'||typeName==='float'||typeName==='double'||typeName==='decimal'){
- var numericValue=Number(rawValue);
- return Number.isNaN(numericValue)?rawValue:numericValue;
- }
- return rawValue;
- }
-
- window.registerBlazorCustomElement=window.registerBlazorCustomElement||function(identifier, parameterDefinitions){
- if(!window.Blazor||!window.Blazor.rootComponents||!window.Blazor.rootComponents.add){
- console.warn('registerBlazorCustomElement skipped: Blazor.rootComponents is unavailable.');
- return;
- }
- if(!window.customElements||typeof window.customElements.define!=='function'){
- console.warn('registerBlazorCustomElement skipped: customElements API is unavailable.');
- return;
- }
- if(window.customElements.get(identifier)){
- return;
- }
-
- var definitions=Array.isArray(parameterDefinitions)?parameterDefinitions:[];
- var parametersByAttribute={};
- for(var index=0;index{
- this._component=component;
- if(this._isDisconnected&&this._component){
- var detachedComponent=this._component;
- this._component=null;
- return detachedComponent.dispose();
- }
- return null;
- })
- .catch((error)=>{
- console.error('Failed to attach custom element component.', error);
- });
- }
-
- disconnectedCallback(){
- this._isDisconnected=true;
- var component=this._component;
- this._component=null;
- if(component&&typeof component.dispose==='function'){
- Promise.resolve(component.dispose()).catch(function(){});
- }
- }
-
- attributeChangedCallback(attributeName, oldValue, newValue){
- if(oldValue===newValue){return;}
- if(!this._component||typeof this._component.setParameters!=='function'){return;}
- var parameterInfo=parametersByAttribute[String(attributeName).toLowerCase()];
- if(!parameterInfo){return;}
-
- var nextParameters={};
- nextParameters[parameterInfo.name]=toParameterValue(newValue, parameterInfo.type);
-
- var updateResult=this._component.setParameters(nextParameters);
- if(updateResult&&typeof updateResult.catch==='function'){
- updateResult.catch(function(error){
- console.error('Failed to update custom element parameters.', error);
- });
- }
- }
-
- _getCurrentParameters(){
- var parameters={};
- for(var index=0;index_webviewWindow->
add_WebMessageReceived(
Callback<
@@ -1789,29 +1643,6 @@ void InfiniFrameWindow::AttachWebView() {
&permissionRequestedToken
);
- if (!m_impl->_startUrl.empty())
- m_impl->_webviewWindow->
- Navigate(
- m_impl->_startUrl.
- c_str()
- );
- else if (!m_impl->_startString.
- empty())
- m_impl->_webviewWindow->
- NavigateToString(
- m_impl->_startString.
- c_str()
- );
- else {
- MessageBox(
- nullptr,
- L"Neither StartUrl nor StartString was specified",
- L"Native Initialization Failed",
- MB_OK
- );
- exit(0);
- }
-
if (m_impl->_contextMenuEnabled ==
false)
SetContextMenuEnabled(false);
@@ -2076,4 +1907,4 @@ void InfiniFrameWindow::InvokeRestored() const noexcept {
void InfiniFrameWindow::InvokeMinimized() const noexcept {
if (m_impl->_minimizedCallback)
m_impl->_minimizedCallback();
-}
+}
\ No newline at end of file
diff --git a/src/InfiniFrame.Native/cmake/Embed.InfiniFrameHostJs.Impl.cmake b/src/InfiniFrame.Native/cmake/Embed.InfiniFrameHostJs.Impl.cmake
new file mode 100644
index 000000000..5c0f9e2aa
--- /dev/null
+++ b/src/InfiniFrame.Native/cmake/Embed.InfiniFrameHostJs.Impl.cmake
@@ -0,0 +1,43 @@
+file(READ "${INPUT}" JS_CONTENT HEX)
+
+string(LENGTH "${JS_CONTENT}" LEN)
+math(EXPR LAST "${LEN} - 2")
+
+set(BYTES "")
+set(COUNT 0)
+
+foreach(i RANGE 0 ${LAST} 2)
+ string(SUBSTRING "${JS_CONTENT}" ${i} 2 BYTE)
+
+ if(i EQUAL ${LAST})
+ string(APPEND BYTES "0x${BYTE}")
+ else()
+ string(APPEND BYTES "0x${BYTE},")
+ endif()
+
+ math(EXPR COUNT "${COUNT} + 1")
+
+ if(COUNT EQUAL 16)
+ string(APPEND BYTES "\n")
+ set(COUNT 0)
+ endif()
+endforeach()
+
+# Header file
+file(WRITE "${OUTPUT_HEADER}" "#pragma once
+// ReSharper disable once CppUnusedIncludeDirective
+#include
+
+extern const unsigned char g_infiniframe_host_js_data[];
+extern const size_t g_infiniframe_host_js_size;
+")
+
+# Source file
+file(WRITE "${OUTPUT_SOURCE}" "#include \"InfiniFrameHostJs.h\"
+
+alignas(16) const unsigned char g_infiniframe_host_js_data[] = {
+${BYTES}
+};
+
+const size_t g_infiniframe_host_js_size = sizeof(g_infiniframe_host_js_data);
+")
\ No newline at end of file
diff --git a/src/InfiniFrame.Native/cmake/Embed.InfiniFrameHostJs.cmake b/src/InfiniFrame.Native/cmake/Embed.InfiniFrameHostJs.cmake
new file mode 100644
index 000000000..92a21404c
--- /dev/null
+++ b/src/InfiniFrame.Native/cmake/Embed.InfiniFrameHostJs.cmake
@@ -0,0 +1,31 @@
+function(infiniframe_setup_embed_js target_name)
+ set(js_input "${CMAKE_SOURCE_DIR}/Embedded/InfiniFrameHostJs/infiniframe.host.js")
+
+ set(header_output "${CMAKE_SOURCE_DIR}/Embedded/InfiniFrameHostJs/InfiniFrameHostJs.h")
+ set(source_output "${CMAKE_SOURCE_DIR}/Embedded/InfiniFrameHostJs/InfiniFrameHostJs.cpp")
+
+ add_custom_command(
+ OUTPUT ${header_output} ${source_output}
+ COMMAND ${CMAKE_COMMAND}
+ -DINPUT=${js_input}
+ -DOUTPUT_HEADER=${header_output}
+ -DOUTPUT_SOURCE=${source_output}
+ -P ${CMAKE_SOURCE_DIR}/cmake/Embed.InfiniFrameHostJs.Impl.cmake
+ DEPENDS ${js_input} ${CMAKE_SOURCE_DIR}/cmake/Embed.InfiniFrameHostJs.Impl.cmake
+ COMMENT "Embedding JS: ${js_input}"
+ VERBATIM
+ )
+
+ set(gen_target "${target_name}_InfiniFrameHostJsGen")
+
+ add_custom_target(${gen_target}
+ DEPENDS ${header_output} ${source_output}
+ )
+
+ add_dependencies(${target_name} ${gen_target})
+
+ target_sources(${target_name} PRIVATE ${source_output})
+ target_include_directories(${target_name} PRIVATE
+ "${CMAKE_SOURCE_DIR}/Embedded/InfiniFrameHostJs"
+ )
+endfunction()
\ No newline at end of file
diff --git a/src/InfiniFrame.Native/cmake/Platform.Linux.cmake b/src/InfiniFrame.Native/cmake/Platform.Linux.cmake
index feff5e50e..65eb1aaec 100644
--- a/src/InfiniFrame.Native/cmake/Platform.Linux.cmake
+++ b/src/InfiniFrame.Native/cmake/Platform.Linux.cmake
@@ -38,6 +38,7 @@ function(infiniframe_configure_linux_target target_name common_sources test_sour
)
target_link_libraries(${target_name} PRIVATE
+ simdutf::simdutf
simdjson::simdjson
${GTK3_LIBRARIES}
${WEBKIT2_LIBRARIES}
diff --git a/src/InfiniFrame.Native/cmake/Platform.MacOS.cmake b/src/InfiniFrame.Native/cmake/Platform.MacOS.cmake
index 02d67ba65..8be141301 100644
--- a/src/InfiniFrame.Native/cmake/Platform.MacOS.cmake
+++ b/src/InfiniFrame.Native/cmake/Platform.MacOS.cmake
@@ -24,6 +24,7 @@ function(infiniframe_configure_macos_target target_name mac_sources header_files
)
target_link_libraries(${target_name} PRIVATE
+ simdutf::simdutf
simdjson::simdjson
"-framework Cocoa"
"-framework WebKit"
diff --git a/src/InfiniFrame.Native/cmake/Platform.Windows.cmake b/src/InfiniFrame.Native/cmake/Platform.Windows.cmake
index e966b1ebd..9d327792c 100644
--- a/src/InfiniFrame.Native/cmake/Platform.Windows.cmake
+++ b/src/InfiniFrame.Native/cmake/Platform.Windows.cmake
@@ -6,7 +6,9 @@
# - windows_sources: list of Windows-only source files
# - header_files: list of header files for IDE organization
function(infiniframe_configure_windows_target target_name common_sources test_sources windows_sources header_files)
- add_library(${target_name} SHARED
+ add_library(${target_name} SHARED)
+
+ target_sources(${target_name} PRIVATE
${common_sources}
${test_sources}
${windows_sources}
@@ -65,4 +67,4 @@ function(infiniframe_configure_windows_target target_name common_sources test_so
"$/WebView2Loader.dll"
COMMENT "Copying WebView2Loader.dll"
)
-endfunction()
+endfunction()
\ No newline at end of file
diff --git a/src/InfiniFrame.Shared/BuilderSnapshots/InfiniFrameWindowMessageHandlersSnapshot.cs b/src/InfiniFrame.Shared/BuilderSnapshots/InfiniFrameWindowMessageHandlersSnapshot.cs
index d5d4bf6d5..7ebc35494 100644
--- a/src/InfiniFrame.Shared/BuilderSnapshots/InfiniFrameWindowMessageHandlersSnapshot.cs
+++ b/src/InfiniFrame.Shared/BuilderSnapshots/InfiniFrameWindowMessageHandlersSnapshot.cs
@@ -6,5 +6,6 @@ namespace InfiniFrame.BuilderSnapshots;
// Code
// ---------------------------------------------------------------------------------------------------------------------
internal readonly record struct InfiniFrameWindowMessageHandlersSnapshot(
- KeyValuePair>[] Handlers
+ KeyValuePair>[] PostDataHandlers,
+ KeyValuePair>[] GetDataHandlers
);
diff --git a/src/InfiniFrame.Shared/FluentApi/InfiniWindowEventsExtensions.cs b/src/InfiniFrame.Shared/FluentApi/InfiniWindowEventsExtensions.cs
index c8bf8795a..643fb96c8 100644
--- a/src/InfiniFrame.Shared/FluentApi/InfiniWindowEventsExtensions.cs
+++ b/src/InfiniFrame.Shared/FluentApi/InfiniWindowEventsExtensions.cs
@@ -1,4 +1,4 @@
-// ---------------------------------------------------------------------------------------------------------------------
+// ---------------------------------------------------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------------------------------------------------
using Microsoft.Extensions.DependencyInjection;
@@ -113,7 +113,7 @@ public static T RegisterMinimizedHandler(this T builder, Action
///
/// Messages should be sent from JavaScript via
- /// window.infiniframe.host.postMessage({ id: "...", data: ..., version: 1 }).
+ /// window.__infiniframe.host.postData({ id: "...", command: "Post", data: ..., version: 2 }).
///
/// Handler invoked with the window and message.
/// The builder to register the handler for.
@@ -131,7 +131,7 @@ public static T RegisterWebMessageReceivedHandler(this T builder, Action
///
/// Messages should be sent from JavaScript via
- /// window.infiniframe.host.postMessage({ id: "...", data: ..., version: 1 }).
+ /// window.__infiniframe.host.postData({ id: "...", command: "Post", data: ..., version: 2 }).
///
/// Handler that receives the resolved service and web message data.
/// The builder to register the handler for.
diff --git a/src/InfiniFrame.Shared/IInfiniFrameWindow.cs b/src/InfiniFrame.Shared/IInfiniFrameWindow.cs
index 17576e319..a0c86c071 100644
--- a/src/InfiniFrame.Shared/IInfiniFrameWindow.cs
+++ b/src/InfiniFrame.Shared/IInfiniFrameWindow.cs
@@ -12,7 +12,7 @@ namespace InfiniFrame;
public interface IInfiniFrameWindow : IHasInfiniFrameProperties, IHasInfiniFrameEvents {
ILogger Logger { get; }
IServiceProvider? ServiceProvider { get; }
- IInfiniFrameWindowMessageHandlers MessageHandlers { get; }
+ IInfiniFrameWindowMessageHandler MessageHandlers { get; }
IntPtr InstanceHandle { get; }
IntPtr WindowHandle { get; }
diff --git a/src/InfiniFrame.Shared/IInfiniFrameWindowBuilder.cs b/src/InfiniFrame.Shared/IInfiniFrameWindowBuilder.cs
index d5911816e..777d15786 100644
--- a/src/InfiniFrame.Shared/IInfiniFrameWindowBuilder.cs
+++ b/src/InfiniFrame.Shared/IInfiniFrameWindowBuilder.cs
@@ -9,7 +9,7 @@ public interface IInfiniFrameWindowBuilder : IHasInfiniFrameEvents {
StaticAssetSettings? StaticAssets { get; set; }
IInfiniFrameWindowNativeParameterBuilder Configuration { get; }
- IInfiniFrameWindowMessageHandlers MessageHandlers { get; }
+ IInfiniFrameWindowMessageHandler MessageHandlers { get; }
IInfiniFrameWindowCustomSchemeHandlers CustomSchemeHandlers { get; }
IInfiniFrameWindow Build(IServiceProvider? provider = null);
diff --git a/src/InfiniFrame.Shared/IInfiniFrameWindowMessageHandler.cs b/src/InfiniFrame.Shared/IInfiniFrameWindowMessageHandler.cs
new file mode 100644
index 000000000..9b04d581c
--- /dev/null
+++ b/src/InfiniFrame.Shared/IInfiniFrameWindowMessageHandler.cs
@@ -0,0 +1,17 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+namespace InfiniFrame;
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+public interface IInfiniFrameWindowMessageHandler {
+ bool IsEmpty { get; }
+ int Count { get; }
+
+ void RegisterHandler(string messageId, Action handler);
+ void RegisterHandler(string messageId, Func handler);
+
+ bool TryHandlePostDataRequest(IInfiniFrameWindow sender, string message);
+ bool TryHandleGetDataRequest(IInfiniFrameWindow window, string message, out string? response);
+}
diff --git a/src/InfiniFrame.Shared/Interop/InteropEnvelopeParseResult.cs b/src/InfiniFrame.Shared/Interop/InteropEnvelopeParseResult.cs
index d7caf757f..c240a7915 100644
--- a/src/InfiniFrame.Shared/Interop/InteropEnvelopeParseResult.cs
+++ b/src/InfiniFrame.Shared/Interop/InteropEnvelopeParseResult.cs
@@ -7,26 +7,24 @@ namespace InfiniFrame.Interop;
// ---------------------------------------------------------------------------------------------------------------------
internal readonly record struct InteropEnvelopeParseResult(
bool Success,
+ bool IsIgnored,
string? MessageId,
string? Payload,
- string? Error,
- bool IsLegacyProtocol
-) {
- public static InteropEnvelopeParseResult CreateSuccess(string messageId, string? payload, bool isLegacyProtocol = false)
- => new(
- true,
- messageId,
- payload,
- null,
- isLegacyProtocol
- );
+ string? Command,
+ string? RequestId,
+ string? Error
+)
+{
+ public static InteropEnvelopeParseResult Ignored => new(false, true, null, null, null, null, null);
+
+ public static InteropEnvelopeParseResult CreateSuccess(
+ string messageId,
+ string? payload,
+ string? command = null,
+ string? requestId = null
+ )
+ => new(true, false, messageId, payload, command, requestId, null);
public static InteropEnvelopeParseResult CreateFailure(string error)
- => new(
- false,
- null,
- null,
- error,
- false
- );
-}
+ => new(false, false, null, null, null, null, error);
+}
\ No newline at end of file
diff --git a/src/InfiniFrame/HostMessaging/GetMessageErrorResponse.cs b/src/InfiniFrame/HostMessaging/GetMessageErrorResponse.cs
new file mode 100644
index 000000000..61c83ec53
--- /dev/null
+++ b/src/InfiniFrame/HostMessaging/GetMessageErrorResponse.cs
@@ -0,0 +1,12 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+namespace InfiniFrame.HostMessaging;
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+internal sealed class GetMessageErrorResponse {
+ public string? RequestId { get; init; }
+ public bool Success { get; init; }
+ public string? Error { get; init; }
+}
diff --git a/src/InfiniFrame/HostMessaging/GetMessageSuccessResponse.cs b/src/InfiniFrame/HostMessaging/GetMessageSuccessResponse.cs
new file mode 100644
index 000000000..0cbba4ea1
--- /dev/null
+++ b/src/InfiniFrame/HostMessaging/GetMessageSuccessResponse.cs
@@ -0,0 +1,12 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+namespace InfiniFrame.HostMessaging;
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+internal sealed class GetMessageSuccessResponse {
+ public string? RequestId { get; init; }
+ public bool Success { get; init; }
+ public string? Data { get; init; }
+}
diff --git a/src/InfiniFrame/HostMessaging/InfiniFrameWindowMessageHandler.cs b/src/InfiniFrame/HostMessaging/InfiniFrameWindowMessageHandler.cs
new file mode 100644
index 000000000..220f8e913
--- /dev/null
+++ b/src/InfiniFrame/HostMessaging/InfiniFrameWindowMessageHandler.cs
@@ -0,0 +1,194 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+using InfiniFrame.BuilderSnapshots;
+using InfiniFrame.Interop;
+using InfiniFrame.Js;
+using InfiniFrame.Js.Interop;
+using Microsoft.Extensions.Logging;
+using System.Collections.Concurrent;
+using System.Text.Json;
+
+namespace InfiniFrame.HostMessaging;
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+public class InfiniFrameWindowMessageHandler : IInfiniFrameWindowMessageHandler {
+
+ private ConcurrentDictionary> PostDataHandlers { get; } = new();
+ private readonly ConcurrentDictionary> GetDataHandlers = new();
+
+ public bool IsEmpty => PostDataHandlers.IsEmpty && GetDataHandlers.IsEmpty;
+ public int Count => PostDataHandlers.Count + GetDataHandlers.Count;
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Methods
+ // -----------------------------------------------------------------------------------------------------------------
+ public void RegisterHandler(string messageId, Action handler) {
+ PostDataHandlers.AddOrUpdate(messageId, handler, updateValueFactory: (_, _) => handler);
+ }
+
+ public void RegisterHandler(string messageId, Func handler) {
+ GetDataHandlers.AddOrUpdate(messageId, handler, static (_, updated) => updated);
+ }
+
+ public bool TryHandlePostDataRequest(IInfiniFrameWindow window, string message) {
+ if (IsEmpty) return false;
+ if (string.IsNullOrWhiteSpace(message)) return false;
+
+ // ReSharper disable once UseDeconstruction
+ InteropEnvelopeParseResult parseResult = InteropEnvelopeProtocol.ParseIncomingMessage(message);
+ if (parseResult == InteropEnvelopeParseResult.Ignored) return false;
+
+ if (!parseResult.Success) {
+ window.Logger.LogWarning("Rejected invalid web message: {Reason}", parseResult.Error ?? "Unknown error");
+ return false;
+ }
+
+ string messageId = parseResult.MessageId!;
+ string? payload = parseResult.Payload;
+
+ if (!string.Equals(parseResult.Command, InteropEnvelopeProtocol.PostCommand, StringComparison.Ordinal))
+ return false;
+
+ if (!PostDataHandlers.TryGetValue(messageId, out Action? handler)) return false;
+
+ try {
+ handler(window, payload);
+ return true;
+ }
+ catch (Exception ex) when (IsNonFatalException(ex)) {
+ window.Logger.LogError(ex, "Unhandled exception while processing web message '{Message}'", message);
+ return false;
+ }
+ }
+
+ public bool TryHandleGetDataRequest(IInfiniFrameWindow window, string message, out string? response) {
+ response = null;
+
+ if (IsEmpty) return false;
+ if (string.IsNullOrWhiteSpace(message)) return false;
+
+ // ReSharper disable once UseDeconstruction
+ InteropEnvelopeParseResult parseResult = InteropEnvelopeProtocol.ParseIncomingMessage(message);
+ if (parseResult.IsIgnored) return false;
+
+ if (!parseResult.Success) {
+ window.Logger.LogWarning("Rejected invalid web message: {Reason}", parseResult.Error ?? "Unknown error");
+ return false;
+ }
+
+ string messageId = parseResult.MessageId!;
+ string? payload = parseResult.Payload;
+
+ if (!string.Equals(parseResult.Command, InteropEnvelopeProtocol.GetCommand, StringComparison.Ordinal))
+ return false;
+
+ if (!GetDataHandlers.TryGetValue(messageId, out Func? handler)) return false;
+
+ try {
+ response = handler(window, payload);
+ return true;
+ }
+ catch (Exception ex) when (IsNonFatalException(ex)) {
+ window.Logger.LogError(ex, "Unhandled exception while processing web message '{MessageId}'", messageId);
+ return false;
+ }
+ }
+
+ internal InfiniFrameWindowMessageHandlersSnapshot ToSnapshot()
+ => new(PostDataHandlers.ToArray(), GetDataHandlers.ToArray());
+
+ internal static InfiniFrameWindowMessageHandler FromSnapshot(InfiniFrameWindowMessageHandlersSnapshot snapshot) {
+ var copy = new InfiniFrameWindowMessageHandler();
+
+ foreach ((string key, Action value) in snapshot.PostDataHandlers) {
+ copy.RegisterHandler(key, value);
+ }
+
+ foreach ((string key, Func value) in snapshot.GetDataHandlers) {
+ copy.RegisterHandler(key, value);
+ }
+
+ return copy;
+ }
+
+ private static bool IsNonFatalException(Exception exception)
+ => exception is not (OutOfMemoryException or AccessViolationException);
+
+ public static void HandleMessageRequest(IInfiniFrameWindow window, string? message) {
+ if (string.IsNullOrWhiteSpace(message)) {
+ window.Logger.LogDebug("Rejected empty web message.");
+ return;
+ }
+
+ InteropEnvelopeParseResult parseResult = InteropEnvelopeProtocol.ParseIncomingMessage(message);
+ if (parseResult.IsIgnored) return;
+
+ if (!parseResult.Success) {
+ window.Logger.LogWarning("Rejected invalid web message: {Reason}", parseResult.Error ?? "Unknown error");
+ return;
+ }
+
+ string messageId = parseResult.MessageId!;
+
+ if (string.Equals(parseResult.Command, InteropEnvelopeProtocol.PostCommand, StringComparison.Ordinal)) {
+ window.MessageHandlers.TryHandlePostDataRequest(window, message);
+ return;
+ }
+
+ if (!string.Equals(parseResult.Command, InteropEnvelopeProtocol.GetCommand, StringComparison.Ordinal)) {
+ return;
+ }
+
+ try {
+ if (window.MessageHandlers.TryHandleGetDataRequest(window, message, out string? responsePayload)) {
+ SendSuccess(window, parseResult.RequestId, responsePayload);
+ return;
+ }
+
+ SendError(window, parseResult.RequestId, $"No getMessage handler is registered for message ID '{messageId}'.");
+
+ }
+ catch (Exception ex) when (IsNonFatalException(ex)) {
+ window.Logger.LogError(ex, "Unhandled exception while processing getMessage request '{MessageId}'.", messageId);
+ SendError(window, parseResult.RequestId, $"Unhandled exception while processing '{messageId}'.");
+ }
+ }
+
+ private static void SendSuccess(IInfiniFrameWindow window, string? requestId, string? data) {
+ string responsePayloadJson = JsonSerializer.Serialize(
+ new GetMessageSuccessResponse {
+ RequestId = requestId,
+ Success = true,
+ Data = data
+ },
+ InfiniFrameWindowMessagesJsonContext.Default.GetMessageSuccessResponse
+ );
+ string responseEnvelope = InteropEnvelopeProtocol.CreateEnvelopeMessage(
+ HandlerNames.GetResponse,
+ responsePayloadJson,
+ InteropEnvelopeProtocol.GetCommand,
+ requestId
+ );
+ window.SendWebMessage(responseEnvelope);
+ }
+
+ private static void SendError(IInfiniFrameWindow window, string? requestId, string error) {
+ string responsePayloadJson = JsonSerializer.Serialize(
+ new GetMessageErrorResponse {
+ RequestId = requestId,
+ Success = false,
+ Error = error
+ },
+ InfiniFrameWindowMessagesJsonContext.Default.GetMessageErrorResponse
+ );
+ string responseEnvelope = InteropEnvelopeProtocol.CreateEnvelopeMessage(
+ HandlerNames.GetResponse,
+ responsePayloadJson,
+ InteropEnvelopeProtocol.GetCommand,
+ requestId
+ );
+ window.SendWebMessage(responseEnvelope);
+ }
+}
diff --git a/src/InfiniFrame/HostMessaging/InfiniFrameWindowMessagesJsonContext.cs b/src/InfiniFrame/HostMessaging/InfiniFrameWindowMessagesJsonContext.cs
new file mode 100644
index 000000000..fcb027871
--- /dev/null
+++ b/src/InfiniFrame/HostMessaging/InfiniFrameWindowMessagesJsonContext.cs
@@ -0,0 +1,14 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+using System.Text.Json.Serialization;
+
+namespace InfiniFrame.HostMessaging;
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
+[JsonSerializable(typeof(GetMessageSuccessResponse))]
+[JsonSerializable(typeof(GetMessageErrorResponse))]
+internal partial class InfiniFrameWindowMessagesJsonContext : JsonSerializerContext;
\ No newline at end of file
diff --git a/src/InfiniFrame/InfiniFrameWindow.cs b/src/InfiniFrame/InfiniFrameWindow.cs
index c28b8fd63..e35d7b5d8 100644
--- a/src/InfiniFrame/InfiniFrameWindow.cs
+++ b/src/InfiniFrame/InfiniFrameWindow.cs
@@ -27,7 +27,7 @@ public sealed class InfiniFrameWindow : IInfiniFrameWindow {
public required IServiceProvider? ServiceProvider { get; init; }
public required IInfiniFrameWindow? Parent { get; init; }
public required IInfiniFrameWindowEvents Events { get; init; }
- public required IInfiniFrameWindowMessageHandlers MessageHandlers { get; init; }
+ public required IInfiniFrameWindowMessageHandler MessageHandlers { get; init; }
public IntPtr NativeType => WindowType.Value;
public IntPtr InstanceHandle { get; private set; }
@@ -114,7 +114,7 @@ public void Close() {
/// Send a message to the native window's native browser control's JavaScript context.
///
///
- /// In JavaScript, messages can be received via window.infiniframe.host.receiveMessage(callback).
+ /// In JavaScript, messages can be received via window.__infiniframe.host.receiveCallback(callback).
///
///
/// Thrown when the window is not initialized.
diff --git a/src/InfiniFrame/InfiniFrameWindowBuilder.cs b/src/InfiniFrame/InfiniFrameWindowBuilder.cs
index 31d2f2b6a..3cc519088 100644
--- a/src/InfiniFrame/InfiniFrameWindowBuilder.cs
+++ b/src/InfiniFrame/InfiniFrameWindowBuilder.cs
@@ -3,6 +3,7 @@
// ---------------------------------------------------------------------------------------------------------------------
using InfiniFrame.BuilderSnapshots;
using InfiniFrame.Configuration;
+using InfiniFrame.HostMessaging;
using InfiniFrame.Native;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -18,14 +19,14 @@ public class InfiniFrameWindowBuilder : IInfiniFrameWindowBuilder {
private readonly InfiniFrameWindowNativeParameterBuilder _configuration = new();
private readonly InfiniFrameWindowCustomSchemeHandlers _customSchemeHandlers = new();
- private readonly InfiniFrameWindowMessageHandlers _messageHandlers = new();
+ private readonly InfiniFrameWindowMessageHandler _messageHandlers = new();
private InfiniFrameWindowEvents _events = new();
private InfiniFrameWindowBuilder() {}
public IInfiniFrameWindowNativeParameterBuilder Configuration => _configuration;
public IInfiniFrameWindowEvents Events => _events;
- public IInfiniFrameWindowMessageHandlers MessageHandlers => _messageHandlers;
+ public IInfiniFrameWindowMessageHandler MessageHandlers => _messageHandlers;
public StaticAssetSettings? StaticAssets { get; set; }
public IInfiniFrameWindowCustomSchemeHandlers CustomSchemeHandlers => _customSchemeHandlers;
@@ -33,9 +34,13 @@ private InfiniFrameWindowBuilder() {}
// -----------------------------------------------------------------------------------------------------------------
// Constructors
// -----------------------------------------------------------------------------------------------------------------
- public static InfiniFrameWindowBuilder Create(InfiniFrameWindowEvents? events = null) => new() {
- _events = events ?? new InfiniFrameWindowEvents()
- };
+ public static InfiniFrameWindowBuilder Create(InfiniFrameWindowEvents? events = null) {
+ var builder = new InfiniFrameWindowBuilder {
+ _events = events ?? new InfiniFrameWindowEvents()
+ };
+
+ return builder;
+ }
// -----------------------------------------------------------------------------------------------------------------
// Methods
@@ -43,7 +48,7 @@ private InfiniFrameWindowBuilder() {}
public IInfiniFrameWindow Build(IServiceProvider? provider = null) {
InfiniFrameWindowBuildSnapshot snapshot = CreateSnapshot(provider);
InfiniFrameWindowEvents events = InfiniFrameWindowEvents.FromSnapshot(snapshot.Events);
- InfiniFrameWindowMessageHandlers messageHandlers = InfiniFrameWindowMessageHandlers.FromSnapshot(snapshot.MessageHandlers);
+ InfiniFrameWindowMessageHandler messageHandlers = InfiniFrameWindowMessageHandler.FromSnapshot(snapshot.MessageHandlers);
InfiniFrameWindowCustomSchemeHandlers customSchemes = InfiniFrameWindowCustomSchemeHandlers.FromSnapshot(snapshot.CustomSchemes);
var window = new InfiniFrameWindow {
@@ -103,9 +108,7 @@ internal InfiniFrameWindowBuildSnapshot CreateSnapshot(IServiceProvider? provide
throw new InvalidOperationException("Maximum number of custom scheme handlers is 16.");
InfiniFrameWindowMessageHandlersSnapshot messageHandlersSnapshot = _messageHandlers.ToSnapshot();
- InfiniFrameWindowMessageHandlers messageHandlers = InfiniFrameWindowMessageHandlers.FromSnapshot(messageHandlersSnapshot);
-
- InfiniFrameWindowEventsSnapshot eventsSnapshot = AddWebMessageHandler(_events.ToSnapshot(), messageHandlers.Handle);
+ InfiniFrameWindowEventsSnapshot eventsSnapshot = AddWebMessageHandler(_events.ToSnapshot());
InfiniFrameWindowCustomSchemeHandlersSnapshot customSchemesSnapshot = _customSchemeHandlers.ToSnapshot();
InfiniFrameWindowEvents events = InfiniFrameWindowEvents.FromSnapshot(eventsSnapshot);
@@ -133,10 +136,12 @@ internal InfiniFrameWindowBuildSnapshot CreateSnapshot(IServiceProvider? provide
}
private static InfiniFrameWindowEventsSnapshot AddWebMessageHandler(
- InfiniFrameWindowEventsSnapshot snapshot,
- Action handler
+ InfiniFrameWindowEventsSnapshot snapshot
) {
- Action[] handlersWithBridge = [..snapshot.WebMessageReceived, handler];
+ Action[] handlersWithBridge = [
+ ..snapshot.WebMessageReceived,
+ InfiniFrameWindowMessageHandler.HandleMessageRequest
+ ];
return snapshot with {
WebMessageReceived = handlersWithBridge
diff --git a/src/InfiniFrame/InfiniFrameWindowMessageHandlers.cs b/src/InfiniFrame/InfiniFrameWindowMessageHandlers.cs
deleted file mode 100644
index 2f09865b6..000000000
--- a/src/InfiniFrame/InfiniFrameWindowMessageHandlers.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-// ---------------------------------------------------------------------------------------------------------------------
-// Imports
-// ---------------------------------------------------------------------------------------------------------------------
-using InfiniFrame.BuilderSnapshots;
-using InfiniFrame.Interop;
-using InfiniFrame.Js.Interop;
-using Microsoft.Extensions.Logging;
-using System.Collections.Concurrent;
-
-namespace InfiniFrame;
-// ---------------------------------------------------------------------------------------------------------------------
-// Code
-// ---------------------------------------------------------------------------------------------------------------------
-public class InfiniFrameWindowMessageHandlers : IInfiniFrameWindowMessageHandlers {
-
- private ConcurrentDictionary> Handlers { get; } = new();
- public bool IsEmpty => Handlers.IsEmpty;
-
- // -----------------------------------------------------------------------------------------------------------------
- // Methods
- // -----------------------------------------------------------------------------------------------------------------
- public void RegisterMessageHandler(string messageId, Action handler) {
- Handlers.AddOrUpdate(messageId, handler, updateValueFactory: (_, _) => handler);
- }
-
- public void Handle(IInfiniFrameWindow window, string message) {
- if (IsEmpty) return;
- if (string.IsNullOrWhiteSpace(message)) return;
-
- InteropEnvelopeParseResult parseResult = InteropEnvelopeProtocol.ParseIncomingMessage(message);
- if (!parseResult.Success) {
- window.Logger.LogWarning("Rejected invalid web message: {Reason}", parseResult.Error ?? "Unknown error");
- return;
- }
-
- string messageId = parseResult.MessageId!;
- string? payload = parseResult.Payload;
-
- if (!Handlers.TryGetValue(messageId, out Action? handler)) return;
-
- try {
- handler(window, payload);
- }
- catch (Exception ex) when (IsNonFatalException(ex)) {
- window.Logger.LogError(ex, "Unhandled exception while processing web message '{MessageId}'", messageId);
- }
- }
-
- internal InfiniFrameWindowMessageHandlersSnapshot ToSnapshot()
- => new(Handlers.ToArray());
-
- internal static InfiniFrameWindowMessageHandlers FromSnapshot(InfiniFrameWindowMessageHandlersSnapshot snapshot) {
- var copy = new InfiniFrameWindowMessageHandlers();
-
- foreach ((string key, Action value) in snapshot.Handlers) {
- copy.RegisterMessageHandler(key, value);
- }
-
- return copy;
- }
-
- private static bool IsNonFatalException(Exception exception)
- => exception is not (OutOfMemoryException or AccessViolationException);
-}
diff --git a/tests/InfiniFrameTests.BlazorWebView/InfiniFrameBlazorAppBuilderTests.cs b/tests/InfiniFrameTests.BlazorWebView/InfiniFrameBlazorAppBuilderTests.cs
index db216cf48..131e07c36 100644
--- a/tests/InfiniFrameTests.BlazorWebView/InfiniFrameBlazorAppBuilderTests.cs
+++ b/tests/InfiniFrameTests.BlazorWebView/InfiniFrameBlazorAppBuilderTests.cs
@@ -59,56 +59,11 @@ public void Dispose() {
}
}
- private sealed class TriggerableUnhandledExceptionSource : IInfiniFrameUnhandledExceptionSource {
- private readonly object _gate = new();
- private readonly List _handlers = [];
-
- public IDisposable Register(UnhandledExceptionEventHandler handler) {
- ArgumentNullException.ThrowIfNull(handler);
-
- lock (_gate) {
- _handlers.Add(handler);
- }
-
- return new Subscription(this, handler);
- }
-
- public void Raise(Exception exception) {
- UnhandledExceptionEventHandler[] handlers;
- lock (_gate) {
- handlers = [.._handlers];
- }
-
- var args = new UnhandledExceptionEventArgs(exception, isTerminating: false);
- foreach (UnhandledExceptionEventHandler handler in handlers) {
- handler(this, args);
- }
- }
-
- private void Unregister(UnhandledExceptionEventHandler handler) {
- lock (_gate) {
- _handlers.Remove(handler);
- }
- }
-
- private sealed class Subscription(TriggerableUnhandledExceptionSource owner, UnhandledExceptionEventHandler handler) : IDisposable {
- private TriggerableUnhandledExceptionSource? _owner = owner;
- private UnhandledExceptionEventHandler? _handler = handler;
-
- public void Dispose() {
- TriggerableUnhandledExceptionSource? owner = Interlocked.Exchange(ref _owner, null);
- UnhandledExceptionEventHandler? handler = Interlocked.Exchange(ref _handler, null);
- if (owner is null || handler is null) return;
- owner.Unregister(handler);
- }
- }
- }
-
[Test]
public async Task Build_WithExternalProvider_ShouldUseProvidedServiceProvider() {
// Arrange
var builder = InfiniFrameBlazorAppBuilder.CreateDefault();
- await using ServiceProvider serviceProvider = builder.Services.BuildServiceProvider();
+ ServiceProvider serviceProvider = builder.Services.BuildServiceProvider();
// Act
InfiniFrameBlazorApp app = builder.Build(serviceProvider);
@@ -152,7 +107,7 @@ public async Task CreateDefault_RootComponents_RegisterForJavaScript_WritesToSha
builder.RootComponents.RegisterForJavaScript("test-js-component");
// Assert
- await using ServiceProvider provider = builder.Services.BuildServiceProvider();
+ ServiceProvider provider = builder.Services.BuildServiceProvider();
var store = provider.GetRequiredService();
var config = provider.GetRequiredService();
@@ -223,7 +178,7 @@ public async Task GlobalUnhandledExceptionHandler_CanBeDisabled() {
public async Task CreateDefault_RegistersUnhandledExceptionSourceByDefault() {
// Arrange
var builder = InfiniFrameBlazorAppBuilder.CreateDefault();
- await using ServiceProvider serviceProvider = builder.Services.BuildServiceProvider();
+ ServiceProvider serviceProvider = builder.Services.BuildServiceProvider();
// Act
var source = serviceProvider.GetService();
@@ -236,7 +191,7 @@ public async Task CreateDefault_RegistersUnhandledExceptionSourceByDefault() {
public async Task CreateDefault_ExceptionSourceRejectsNullHandler() {
// Arrange
var builder = InfiniFrameBlazorAppBuilder.CreateDefault();
- await using ServiceProvider serviceProvider = builder.Services.BuildServiceProvider();
+ ServiceProvider serviceProvider = builder.Services.BuildServiceProvider();
var source = serviceProvider.GetRequiredService();
// Act
@@ -250,31 +205,9 @@ public async Task CreateDefault_ExceptionSourceRejectsNullHandler() {
}
[Test]
- public async Task GlobalUnhandledExceptionHandler_RoutesToWindowAndStopsAfterDispose() {
- // Arrange
- var exceptionSource = new TriggerableUnhandledExceptionSource();
- var window = Substitute.For();
- var builder = InfiniFrameBlazorAppBuilder.CreateDefault();
- builder.Services.RemoveAll();
- builder.Services.RemoveAll();
- builder.Services.AddSingleton(exceptionSource);
- builder.Services.AddSingleton(window);
- InfiniFrameBlazorApp app = builder.Build();
-
- // Act
- exceptionSource.Raise(new InvalidOperationException("boom-before-dispose"));
- await app.DisposeAsync();
- exceptionSource.Raise(new InvalidOperationException("boom-after-dispose"));
-
- // Assert
- window.Received(1).ShowMessage(
- "Fatal exception",
- Arg.Is(text => text.Contains("boom-before-dispose", StringComparison.Ordinal)),
- Arg.Any(),
- Arg.Any());
- }
-
- [Test]
+ [NotInParallel(ParallelControl.InfiniFrame)]
+ [SkipUtility.SkipOnMacOs]
+ [SkipUtility.SkipOnLinux]
public async Task Run_WindowAlreadyClosed_DoesNotInvokeWindowAndDisposesServices() {
// Arrange
var window = Substitute.For();
diff --git a/tests/InfiniFrameTests.Js/GetMessageWebMessageHandlerTests.cs b/tests/InfiniFrameTests.Js/GetMessageWebMessageHandlerTests.cs
new file mode 100644
index 000000000..36398bdec
--- /dev/null
+++ b/tests/InfiniFrameTests.Js/GetMessageWebMessageHandlerTests.cs
@@ -0,0 +1,166 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+using InfiniFrame;
+using InfiniFrame.HostMessaging;
+using InfiniFrame.Interop;
+using InfiniFrame.Js;
+using InfiniFrame.Js.Interop;
+using InfiniFrame.Js.Interop.MessageHandlers;
+using InfiniFrameTests.Shared.TestDoubles;
+using NSubstitute;
+using System.Text.Json;
+
+namespace InfiniFrameTests.Js;
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+public class GetMessageWebMessageHandlerTests {
+ [Test]
+ public async Task GetMessage_StandardGetRequest_Title_ReturnsWindowTitle() {
+ // Arrange
+ (InfiniFrameWindowBuilder builder, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window, InfiniFrameWindowMessageHandler _)
+ = CreateWindowHarness();
+
+ builder.RegisterStandardGetWebMessageHandler();
+ window.Window.Title.Returns("Native Test Title");
+
+ string inboundMessage = InteropEnvelopeProtocol.CreateEnvelopeMessage(
+ HandlerNames.GetRequest,
+ "{\"command\":\"title\"}",
+ InteropEnvelopeProtocol.GetCommand,
+ "req-standard-get-1"
+ );
+
+ // Act
+ events.OnWebMessageReceived(inboundMessage);
+
+ // Assert
+ InteropEnvelopeParseResult response = GetLatestGetMessageResponse(window);
+
+ using JsonDocument doc = JsonDocument.Parse(response.Payload!);
+ JsonElement payload = doc.RootElement;
+
+ await Assert.That(payload.GetProperty("requestId").GetString()).IsEqualTo("req-standard-get-1");
+ await Assert.That(payload.GetProperty("success").GetBoolean()).IsTrue();
+ await Assert.That(payload.GetProperty("data").GetString()).IsEqualTo("Native Test Title");
+ }
+
+ [Test]
+ public async Task GetMessage_ResolvesRegisteredHandlerAndReturnsData() {
+ // Arrange
+ (InfiniFrameWindowBuilder _, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window, InfiniFrameWindowMessageHandler messageHandler)
+ = CreateWindowHarness();
+
+ messageHandler.RegisterHandler("app:echo", (_, payload) => $"echo:{payload}");
+
+ string inboundMessage = InteropEnvelopeProtocol.CreateEnvelopeMessage(
+ "app:echo",
+ "hello",
+ InteropEnvelopeProtocol.GetCommand,
+ "req-1"
+ );
+
+ // Act
+ events.OnWebMessageReceived(inboundMessage);
+
+ // Assert
+ InteropEnvelopeParseResult response = GetLatestGetMessageResponse(window);
+
+ using JsonDocument doc = JsonDocument.Parse(response.Payload!);
+ JsonElement payload = doc.RootElement;
+
+ await Assert.That(payload.GetProperty("requestId").GetString()).IsEqualTo("req-1");
+ await Assert.That(payload.GetProperty("success").GetBoolean()).IsTrue();
+ await Assert.That(payload.GetProperty("data").GetString()).IsEqualTo("echo:hello");
+ }
+
+ [Test]
+ public async Task GetMessage_WithoutRegisteredHandler_ReturnsErrorResponse() {
+ // Arrange
+ (InfiniFrameWindowBuilder _, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window, InfiniFrameWindowMessageHandler _)
+ = CreateWindowHarness();
+
+ string inboundMessage = InteropEnvelopeProtocol.CreateEnvelopeMessage(
+ "app:missing",
+ "hello",
+ InteropEnvelopeProtocol.GetCommand,
+ "req-2"
+ );
+
+ // Act
+ events.OnWebMessageReceived(inboundMessage);
+
+ // Assert
+ InteropEnvelopeParseResult response = GetLatestGetMessageResponse(window);
+
+ using JsonDocument doc = JsonDocument.Parse(response.Payload!);
+ JsonElement payload = doc.RootElement;
+
+ await Assert.That(payload.GetProperty("requestId").GetString()).IsEqualTo("req-2");
+ await Assert.That(payload.GetProperty("success").GetBoolean()).IsFalse();
+ await Assert.That(payload.GetProperty("error").GetString())
+ .Contains("No getMessage handler is registered");
+ }
+
+ [Test]
+ public async Task GetMessage_WithoutRegisteredService_ReturnsErrorResponse() {
+ // Arrange
+ (InfiniFrameWindowBuilder _, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window, InfiniFrameWindowMessageHandler _)
+ = CreateWindowHarness();
+
+ string inboundMessage = InteropEnvelopeProtocol.CreateEnvelopeMessage(
+ "app:echo",
+ "hello",
+ InteropEnvelopeProtocol.GetCommand,
+ "req-3"
+ );
+
+ // Act
+ events.OnWebMessageReceived(inboundMessage);
+
+ // Assert
+ InteropEnvelopeParseResult response = GetLatestGetMessageResponse(window);
+
+ using JsonDocument doc = JsonDocument.Parse(response.Payload!);
+ JsonElement payload = doc.RootElement;
+
+ await Assert.That(payload.GetProperty("requestId").GetString())
+ .IsEqualTo("req-3");
+
+ await Assert.That(payload.GetProperty("success").GetBoolean())
+ .IsFalse();
+
+ await Assert.That(payload.GetProperty("error").GetString())
+ .Contains("No getMessage handler is registered");
+ }
+
+ private static (InfiniFrameWindowBuilder Builder, InfiniFrameWindowEvents Events, RecordingInfiniFrameWindowSubstitute Window, InfiniFrameWindowMessageHandler MessageHandler) CreateWindowHarness() {
+ var builder = InfiniFrameWindowBuilder.Create();
+ var events = (InfiniFrameWindowEvents)builder.Events;
+ var messageHandler = (InfiniFrameWindowMessageHandler)builder.MessageHandlers;
+
+ RecordingInfiniFrameWindowSubstitute window = new RecordingInfiniFrameWindowSubstitute()
+ .BindToBuilder(builder);
+
+ events.WebMessageReceived.Add(InfiniFrameWindowMessageHandler.HandleMessageRequest);
+ events.CompleteSetup(window.Window);
+
+ return (builder, events, window, messageHandler);
+ }
+
+ private static InteropEnvelopeParseResult GetLatestGetMessageResponse(
+ RecordingInfiniFrameWindowSubstitute window
+ ) {
+ InteropEnvelopeParseResult responseEnvelope = window.GetSentMessagesSnapshot()
+ .Select(InteropEnvelopeProtocol.ParseIncomingMessage)
+ .LastOrDefault(r =>
+ r.Success &&
+ r.MessageId == HandlerNames.GetResponse
+ );
+
+ Fail.When(!responseEnvelope.Success, "Expected a valid getMessage response envelope.");
+
+ return responseEnvelope;
+ }
+}
diff --git a/tests/InfiniFrameTests.Js/InfiniFrameJsTests.cs b/tests/InfiniFrameTests.Js/InfiniFrameJsTests.cs
index 3a3850087..05092bab9 100644
--- a/tests/InfiniFrameTests.Js/InfiniFrameJsTests.cs
+++ b/tests/InfiniFrameTests.Js/InfiniFrameJsTests.cs
@@ -13,7 +13,6 @@ namespace InfiniFrameTests.Js;
// ---------------------------------------------------------------------------------------------------------------------
public class InfiniFrameJsTests {
[Test]
- [DisplayName($"{nameof(InfiniFrameJsTests)}.{nameof(SetPointerCaptureAsync_InvokesExpectedJsFunction)}")]
public async Task SetPointerCaptureAsync_InvokesExpectedJsFunction() {
// Arrange
var jsRuntime = new RecordingJsRuntime();
@@ -25,17 +24,15 @@ public async Task SetPointerCaptureAsync_InvokesExpectedJsFunction() {
await sut.SetPointerCaptureAsync(element, 42);
// Assert
- RecordingJsRuntime.Invocation invocation = jsRuntime.Invocations.Single();
- await Assert.That(invocation.Identifier).IsEqualTo("infiniFrame.setPointerCapture");
- await Assert.That(invocation.CancellationToken).IsEqualTo(CancellationToken.None);
- object?[] jsArguments = invocation.Arguments;
+ (string identifier, CancellationToken cancellationToken, object?[] jsArguments) = jsRuntime.Invocations.Single();
+ await Assert.That(identifier).IsEqualTo("infiniframe.utils.setPointerCapture");
+ await Assert.That(cancellationToken).IsEqualTo(CancellationToken.None);
await Assert.That(jsArguments.Length).IsEqualTo(2);
await Assert.That(jsArguments[0]).IsEqualTo(element);
await Assert.That(jsArguments[1]).IsEqualTo(42L);
}
[Test]
- [DisplayName($"{nameof(InfiniFrameJsTests)}.{nameof(ReleasePointerCaptureAsync_InvokesExpectedJsFunction)}")]
public async Task ReleasePointerCaptureAsync_InvokesExpectedJsFunction() {
// Arrange
var jsRuntime = new RecordingJsRuntime();
@@ -47,17 +44,15 @@ public async Task ReleasePointerCaptureAsync_InvokesExpectedJsFunction() {
await sut.ReleasePointerCaptureAsync(element, 7);
// Assert
- RecordingJsRuntime.Invocation invocation = jsRuntime.Invocations.Single();
- await Assert.That(invocation.Identifier).IsEqualTo("infiniFrame.releasePointerCapture");
- await Assert.That(invocation.CancellationToken).IsEqualTo(CancellationToken.None);
- object?[] jsArguments = invocation.Arguments;
+ (string identifier, CancellationToken cancellationToken, object?[] jsArguments) = jsRuntime.Invocations.Single();
+ await Assert.That(identifier).IsEqualTo("infiniframe.utils.releasePointerCapture");
+ await Assert.That(cancellationToken).IsEqualTo(CancellationToken.None);
await Assert.That(jsArguments.Length).IsEqualTo(2);
await Assert.That(jsArguments[0]).IsEqualTo(element);
await Assert.That(jsArguments[1]).IsEqualTo(7L);
}
[Test]
- [DisplayName($"{nameof(InfiniFrameJsTests)}.{nameof(SetPointerCaptureAsync_SwallowsOperationCanceled_WhenCancellationRequested)}")]
public async Task SetPointerCaptureAsync_SwallowsOperationCanceled_WhenCancellationRequested() {
// Arrange
var jsRuntime = new RecordingJsRuntime();
diff --git a/tests/InfiniFrameTests.Js/InfiniFrameTests.Js.csproj b/tests/InfiniFrameTests.Js/InfiniFrameTests.Js.csproj
index c534885b5..6a3b54342 100644
--- a/tests/InfiniFrameTests.Js/InfiniFrameTests.Js.csproj
+++ b/tests/InfiniFrameTests.Js/InfiniFrameTests.Js.csproj
@@ -15,7 +15,7 @@
-
+
TypeScript\Interop\interop-envelope-golden-vectors.json
Always
diff --git a/tests/InfiniFrameTests.Js/InteropEnvelopeProtocolTests.cs b/tests/InfiniFrameTests.Js/InteropEnvelopeProtocolTests.cs
index 8efe0234b..88bc5cce5 100644
--- a/tests/InfiniFrameTests.Js/InteropEnvelopeProtocolTests.cs
+++ b/tests/InfiniFrameTests.Js/InteropEnvelopeProtocolTests.cs
@@ -20,7 +20,6 @@ public class InteropEnvelopeProtocolTests {
);
[Test]
- [DisplayName($"{nameof(InteropEnvelopeProtocolTests)}.{nameof(CreateEnvelope_GoldenVectors)}")]
public async Task CreateEnvelope_GoldenVectors() {
JsonElement vectors = GoldenVectors.RootElement.GetProperty("createVectors");
foreach (JsonElement vector in vectors.EnumerateArray()) {
@@ -36,7 +35,6 @@ public async Task CreateEnvelope_GoldenVectors() {
}
[Test]
- [DisplayName($"{nameof(InteropEnvelopeProtocolTests)}.{nameof(ParseEnvelope_GoldenVectors)}")]
public async Task ParseEnvelope_GoldenVectors() {
JsonElement vectors = GoldenVectors.RootElement.GetProperty("parseVectors");
foreach (JsonElement vector in vectors.EnumerateArray()) {
@@ -54,19 +52,16 @@ public async Task ParseEnvelope_GoldenVectors() {
}
string expectedMessageId = vector.GetProperty("messageId").GetString()!;
- bool expectedIsLegacy = vector.GetProperty("isLegacyProtocol").GetBoolean();
string? expectedPayload = vector.GetProperty("payload").ValueKind == JsonValueKind.Null
? null
: vector.GetProperty("payload").GetString();
await Assert.That(result.MessageId).IsEqualTo(expectedMessageId);
- await Assert.That(result.IsLegacyProtocol).IsEqualTo(expectedIsLegacy);
await Assert.That(result.Payload).IsEqualTo(expectedPayload);
}
}
[Test]
- [DisplayName($"{nameof(InteropEnvelopeProtocolTests)}.{nameof(Parse_TooLargeMessage_IsRejected)}")]
public async Task Parse_TooLargeMessage_IsRejected() {
string message = new('a', InteropEnvelopeProtocol.MaxMessageSizeBytes + 1);
InteropEnvelopeParseResult result = InteropEnvelopeProtocol.ParseIncomingMessage(message);
diff --git a/tests/InfiniFrameTests.Js/MessageHandlersTests.cs b/tests/InfiniFrameTests.Js/MessageHandlersTests.cs
index c55e5348d..e72ef5a8d 100644
--- a/tests/InfiniFrameTests.Js/MessageHandlersTests.cs
+++ b/tests/InfiniFrameTests.Js/MessageHandlersTests.cs
@@ -15,7 +15,6 @@ namespace InfiniFrameTests.Js;
// ---------------------------------------------------------------------------------------------------------------------
public class MessageHandlersTests {
[Test]
- [DisplayName($"{nameof(MessageHandlersTests)}.{nameof(WindowManagement_CloseMessage_ClosesWindow)}")]
public async Task WindowManagement_CloseMessage_ClosesWindow() {
// Arrange
(InfiniFrameWindowBuilder builder, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window) = CreateWindowHarness();
@@ -30,7 +29,6 @@ public async Task WindowManagement_CloseMessage_ClosesWindow() {
}
[Test]
- [DisplayName($"{nameof(MessageHandlersTests)}.{nameof(WindowManagement_RegistersWindowCloseSubscriptionAfterReadyHandshake)}")]
public async Task WindowManagement_RegistersWindowCloseSubscriptionAfterReadyHandshake() {
// Arrange
(InfiniFrameWindowBuilder builder, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window) = CreateWindowHarness();
@@ -47,7 +45,6 @@ public async Task WindowManagement_RegistersWindowCloseSubscriptionAfterReadyHan
}
[Test]
- [DisplayName($"{nameof(MessageHandlersTests)}.{nameof(FullscreenToggle_InvokesWindowMutation)}")]
public async Task FullscreenToggle_InvokesWindowMutation() {
// Arrange
(InfiniFrameWindowBuilder builder, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window) = CreateWindowHarness();
@@ -62,7 +59,6 @@ public async Task FullscreenToggle_InvokesWindowMutation() {
}
[Test]
- [DisplayName($"{nameof(MessageHandlersTests)}.{nameof(TitleChanged_WithPayload_InvokesWindowMutation)}")]
public async Task TitleChanged_WithPayload_InvokesWindowMutation() {
// Arrange
(InfiniFrameWindowBuilder builder, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window) = CreateWindowHarness();
@@ -77,7 +73,6 @@ public async Task TitleChanged_WithPayload_InvokesWindowMutation() {
}
[Test]
- [DisplayName($"{nameof(MessageHandlersTests)}.{nameof(TitleChanged_WithoutPayload_DoesNotInvokeWindowMutation)}")]
public async Task TitleChanged_WithoutPayload_DoesNotInvokeWindowMutation() {
// Arrange
(InfiniFrameWindowBuilder builder, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window) = CreateWindowHarness();
@@ -92,7 +87,6 @@ public async Task TitleChanged_WithoutPayload_DoesNotInvokeWindowMutation() {
}
[Test]
- [DisplayName($"{nameof(MessageHandlersTests)}.{nameof(OpenExternal_WithInvalidUrl_LogsWarningWithoutThrowing)}")]
public async Task OpenExternal_WithInvalidUrl_LogsWarningWithoutThrowing() {
// Arrange
(InfiniFrameWindowBuilder builder, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window) = CreateWindowHarness();
@@ -114,7 +108,6 @@ public async Task OpenExternal_WithInvalidUrl_LogsWarningWithoutThrowing() {
}
[Test]
- [DisplayName($"{nameof(MessageHandlersTests)}.{nameof(OpenExternal_WithDisallowedScheme_LogsWarningWithoutThrowing)}")]
public async Task OpenExternal_WithDisallowedScheme_LogsWarningWithoutThrowing() {
// Arrange
(InfiniFrameWindowBuilder builder, InfiniFrameWindowEvents events, RecordingInfiniFrameWindowSubstitute window) = CreateWindowHarness();
@@ -146,7 +139,7 @@ private static (InfiniFrameWindowBuilder Builder, InfiniFrameWindowEvents Events
RecordingInfiniFrameWindowSubstitute window = new RecordingInfiniFrameWindowSubstitute()
.BindToBuilder(builder);
- events.WebMessageReceived.Add(builder.MessageHandlers.Handle);
+ events.WebMessageReceived.Add((sender, message) => builder.MessageHandlers.TryHandlePostDataRequest(sender, message));
events.CompleteSetup(window.Window);
return (builder, events, window);
diff --git a/tests/InfiniFrameTests.Js/RegisterWindowCreatedUtilityTests.cs b/tests/InfiniFrameTests.Js/RegisterWindowCreatedUtilityTests.cs
index 0c95a04b3..b0ba0ffd5 100644
--- a/tests/InfiniFrameTests.Js/RegisterWindowCreatedUtilityTests.cs
+++ b/tests/InfiniFrameTests.Js/RegisterWindowCreatedUtilityTests.cs
@@ -11,7 +11,6 @@ namespace InfiniFrameTests.Js;
// ---------------------------------------------------------------------------------------------------------------------
public class RegisterWindowCreatedUtilityTests {
[Test]
- [DisplayName($"{nameof(RegisterWindowCreatedUtilityTests)}.{nameof(Registration_IsGatedByWindowReadyHandshake)}")]
public async Task Registration_IsGatedByWindowReadyHandshake() {
// Arrange
const string registrationMessageId = "__infiniframe:register:test";
@@ -21,7 +20,7 @@ public async Task Registration_IsGatedByWindowReadyHandshake() {
RecordingInfiniFrameWindowSubstitute window = new RecordingInfiniFrameWindowSubstitute()
.BindToBuilder(builder);
- events.WebMessageReceived.Add(builder.MessageHandlers.Handle);
+ events.WebMessageReceived.Add((sender, message) => builder.MessageHandlers.TryHandlePostDataRequest(sender, message));
events.CompleteSetup(window.Window);
RegisterWindowCreatedUtility.RegisterWindowCreatedWebMessage(builder, registrationMessageId);
@@ -44,8 +43,6 @@ public async Task Registration_IsGatedByWindowReadyHandshake() {
}
[Test]
- [DisplayName(
- $"{nameof(RegisterWindowCreatedUtilityTests)}.{nameof(Registration_IsIdempotentAcrossRepeatedReadyMessages)}")]
public async Task Registration_IsIdempotentAcrossRepeatedReadyMessages() {
// Arrange
const string registrationMessageId = "__infiniframe:register:test";
@@ -55,7 +52,7 @@ public async Task Registration_IsIdempotentAcrossRepeatedReadyMessages() {
RecordingInfiniFrameWindowSubstitute window = new RecordingInfiniFrameWindowSubstitute()
.BindToBuilder(builder);
- events.WebMessageReceived.Add(builder.MessageHandlers.Handle);
+ events.WebMessageReceived.Add((sender, message) => builder.MessageHandlers.TryHandlePostDataRequest(sender, message));
events.CompleteSetup(window.Window);
RegisterWindowCreatedUtility.RegisterWindowCreatedWebMessage(builder, registrationMessageId);
@@ -71,8 +68,6 @@ public async Task Registration_IsIdempotentAcrossRepeatedReadyMessages() {
}
[Test]
- [DisplayName(
- $"{nameof(RegisterWindowCreatedUtilityTests)}.{nameof(Registration_FallbackSend_DoesNotBlockLaterReadyResend)}")]
public async Task Registration_FallbackSend_DoesNotBlockLaterReadyResend() {
// Arrange
const string registrationMessageId = "__infiniframe:register:test";
@@ -82,7 +77,7 @@ public async Task Registration_FallbackSend_DoesNotBlockLaterReadyResend() {
RecordingInfiniFrameWindowSubstitute window = new RecordingInfiniFrameWindowSubstitute()
.BindToBuilder(builder);
- events.WebMessageReceived.Add(builder.MessageHandlers.Handle);
+ events.WebMessageReceived.Add((sender, message) => builder.MessageHandlers.TryHandlePostDataRequest(sender, message));
events.CompleteSetup(window.Window);
RegisterWindowCreatedUtility.RegisterWindowCreatedWebMessage(builder, registrationMessageId);
diff --git a/tests/InfiniFrameTests.Js/ServiceCollectionExtensionsTests.cs b/tests/InfiniFrameTests.Js/ServiceCollectionExtensionsTests.cs
index 5b9cb0274..93b851116 100644
--- a/tests/InfiniFrameTests.Js/ServiceCollectionExtensionsTests.cs
+++ b/tests/InfiniFrameTests.Js/ServiceCollectionExtensionsTests.cs
@@ -10,7 +10,6 @@ namespace InfiniFrameTests.Js;
// ---------------------------------------------------------------------------------------------------------------------
public class ServiceCollectionExtensionsTests {
[Test]
- [DisplayName($"{nameof(ServiceCollectionExtensionsTests)}.{nameof(AddInfiniFrameJs_RegistersScopedService)}")]
public async Task AddInfiniFrameJs_RegistersScopedService() {
// Arrange
var services = new ServiceCollection();
diff --git a/tests/InfiniFrameTests.Js/WindowRegistrationStateMachineTests.cs b/tests/InfiniFrameTests.Js/WindowRegistrationStateMachineTests.cs
index b2e33fd1f..7b00d7500 100644
--- a/tests/InfiniFrameTests.Js/WindowRegistrationStateMachineTests.cs
+++ b/tests/InfiniFrameTests.Js/WindowRegistrationStateMachineTests.cs
@@ -9,7 +9,6 @@ namespace InfiniFrameTests.Js;
// ---------------------------------------------------------------------------------------------------------------------
public class WindowRegistrationStateMachineTests {
[Test]
- [DisplayName($"{nameof(WindowRegistrationStateMachineTests)}.{nameof(InitialState_IsReadyPending_AndTimeoutEligible)}")]
public async Task InitialState_IsReadyPending_AndTimeoutEligible() {
var sut = new WindowRegistrationStateMachine();
@@ -17,7 +16,6 @@ public async Task InitialState_IsReadyPending_AndTimeoutEligible() {
}
[Test]
- [DisplayName($"{nameof(WindowRegistrationStateMachineTests)}.{nameof(TryBeginRegistrationSendOnReady_ReturnsFalse_WhenSendAlreadyInProgress)}")]
public async Task TryBeginRegistrationSendOnReady_ReturnsFalse_WhenSendAlreadyInProgress() {
var sut = new WindowRegistrationStateMachine();
@@ -30,7 +28,6 @@ public async Task TryBeginRegistrationSendOnReady_ReturnsFalse_WhenSendAlreadyIn
}
[Test]
- [DisplayName($"{nameof(WindowRegistrationStateMachineTests)}.{nameof(CompleteRegistrationSend_Success_BlocksFurtherSends)}")]
public async Task CompleteRegistrationSend_Success_BlocksFurtherSends() {
var sut = new WindowRegistrationStateMachine();
sut.TryBeginRegistrationSendOnReady();
@@ -43,7 +40,6 @@ public async Task CompleteRegistrationSend_Success_BlocksFurtherSends() {
}
[Test]
- [DisplayName($"{nameof(WindowRegistrationStateMachineTests)}.{nameof(CompleteRegistrationSend_Failure_AllowsRetry)}")]
public async Task CompleteRegistrationSend_Failure_AllowsRetry() {
var sut = new WindowRegistrationStateMachine();
sut.TryBeginRegistrationSendOnReady();
@@ -56,7 +52,6 @@ public async Task CompleteRegistrationSend_Failure_AllowsRetry() {
}
[Test]
- [DisplayName($"{nameof(WindowRegistrationStateMachineTests)}.{nameof(CompleteRegistrationSend_Failure_WithoutReady_DoesNotEnableTimeoutLogging)}")]
public async Task CompleteRegistrationSend_Failure_WithoutReady_DoesNotEnableTimeoutLogging() {
var sut = new WindowRegistrationStateMachine();
diff --git a/tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/CustomElementsTests.cs b/tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/CustomElementsTests.cs
index e0f6c7402..a1b8caae4 100644
--- a/tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/CustomElementsTests.cs
+++ b/tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/CustomElementsTests.cs
@@ -19,14 +19,16 @@ public sealed class CustomElementsTests : InfiniFramePlaywrightTestBase {
public async Task CustomElement_Registers_Renders_AndUpdatesFromAttributes() {
IPage page = await GetRootPageAsync();
- bool isCustomElementDefined = await page.EvaluateAsync(
+ bool isCustomElementDefined = await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => window.customElements.get('infiniframe-custom-element') !== undefined"
);
if (!isCustomElementDefined) {
isCustomElementDefined = await WaitForStateChangeAsync(
false,
- stateProvider: () => page.EvaluateAsync(
+ stateProvider: () => EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => window.customElements.get('infiniframe-custom-element') !== undefined"
)
@@ -35,7 +37,8 @@ public async Task CustomElement_Registers_Renders_AndUpdatesFromAttributes() {
await Assert.That(isCustomElementDefined).IsTrue();
- await page.EvaluateAsync(
+ await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"""
() => {
@@ -50,9 +53,10 @@ await page.EvaluateAsync(
"""
);
- string? renderedAlpha = await WaitForStateChangeAsync(
- (string?)null,
- stateProvider: () => page.EvaluateAsync(
+ string? renderedAlpha = await WaitForStateChangeAsync(
+ null,
+ stateProvider: () => EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => document.querySelector('#custom-element-test-host .custom-element-probe-value')?.textContent?.trim() ?? null"
)
@@ -60,14 +64,16 @@ await page.EvaluateAsync(
await Assert.That(renderedAlpha).IsEqualTo("alpha");
- await page.EvaluateAsync(
+ await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => document.getElementById('custom-element-test-host')?.setAttribute('label', 'beta')"
);
string? renderedBeta = await WaitForStateChangeAsync(
renderedAlpha,
- stateProvider: () => page.EvaluateAsync(
+ stateProvider: () => EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => document.querySelector('#custom-element-test-host .custom-element-probe-value')?.textContent?.trim() ?? null"
)
@@ -81,14 +87,16 @@ await page.EvaluateAsync(
public async Task JsComponent_WithoutInitializer_AutoRegisters_AsCustomElement_ByDefault() {
IPage page = await GetRootPageAsync();
- bool isCustomElementDefined = await page.EvaluateAsync(
+ bool isCustomElementDefined = await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => window.customElements.get('infiniframe-no-init-component') !== undefined"
);
if (!isCustomElementDefined) {
isCustomElementDefined = await WaitForStateChangeAsync(
false,
- stateProvider: () => page.EvaluateAsync(
+ stateProvider: () => EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => window.customElements.get('infiniframe-no-init-component') !== undefined"
)
@@ -97,7 +105,8 @@ public async Task JsComponent_WithoutInitializer_AutoRegisters_AsCustomElement_B
await Assert.That(isCustomElementDefined).IsTrue();
- await page.EvaluateAsync(
+ await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"""
() => {
@@ -112,9 +121,10 @@ await page.EvaluateAsync(
"""
);
- string? renderedGamma = await WaitForStateChangeAsync(
- (string?)null,
- stateProvider: () => page.EvaluateAsync(
+ string? renderedGamma = await WaitForStateChangeAsync(
+ null,
+ stateProvider: () => EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => document.querySelector('#no-init-component-host .custom-element-probe-value')?.textContent?.trim() ?? null"
)
@@ -122,14 +132,16 @@ await page.EvaluateAsync(
await Assert.That(renderedGamma).IsEqualTo("gamma");
- await page.EvaluateAsync(
+ await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => document.getElementById('no-init-component-host')?.setAttribute('label', 'delta')"
);
string? renderedDelta = await WaitForStateChangeAsync(
renderedGamma,
- stateProvider: () => page.EvaluateAsync(
+ stateProvider: () => EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => document.querySelector('#no-init-component-host .custom-element-probe-value')?.textContent?.trim() ?? null"
)
diff --git a/tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/ScriptSrcImportTests.cs b/tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/ScriptSrcImportTests.cs
index 7623f6453..5656dea4a 100644
--- a/tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/ScriptSrcImportTests.cs
+++ b/tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/ScriptSrcImportTests.cs
@@ -20,7 +20,8 @@ public sealed class ScriptSrcImportTests : InfiniFramePlaywrightTestBase {
public async Task ClassicScriptSrc_IsLoaded_AndExecutesCode() {
IPage page = await GetRootPageAsync();
- var state = await page.EvaluateAsync(
+ var state = await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"""
() => ({
diff --git a/tests/InfiniFrameTests.Playwright.WebApp.React/InfiniFrameTests.Playwright.WebApp.React.csproj b/tests/InfiniFrameTests.Playwright.WebApp.React/InfiniFrameTests.Playwright.WebApp.React.csproj
index 3d32c8b11..0bb9001db 100644
--- a/tests/InfiniFrameTests.Playwright.WebApp.React/InfiniFrameTests.Playwright.WebApp.React.csproj
+++ b/tests/InfiniFrameTests.Playwright.WebApp.React/InfiniFrameTests.Playwright.WebApp.React.csproj
@@ -4,13 +4,11 @@
true
$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..\..'))\
-
-
- $([System.String]::Copy('$(TargetFrameworks)').Split(';')[0])
-
-
- $(TargetFramework)
-
+
+ true
+
+
+ $(ProjectDir)wwwroot\index.html
@@ -23,28 +21,30 @@
-
+
+
+
+
+
+
+ BeforeTargets="Build"
+ Condition="'$(DesignTimeBuild)'!='true' and '$(RunFrontendBuild)'=='true'">
-
+
-
+ Condition="'$(CI)' == 'true'"
+ WorkingDirectory="$(ProjectDir)Sources\infiniframe-playwright-react" />
-
-
-
+
+
+ false
+
+
diff --git a/tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react/package-lock.json b/tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react/package-lock.json
index 5cbbfac8d..0eb8a7fab 100644
--- a/tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react/package-lock.json
+++ b/tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react/package-lock.json
@@ -22,7 +22,7 @@
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.5.0",
"typescript": "^6.0.3",
- "typescript-eslint": "^8.59.0",
+ "typescript-eslint": "^8.59.1",
"vite": "^8.0.10"
}
},
@@ -904,17 +904,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz",
- "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz",
+ "integrity": "sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
- "@typescript-eslint/scope-manager": "8.59.0",
- "@typescript-eslint/type-utils": "8.59.0",
- "@typescript-eslint/utils": "8.59.0",
- "@typescript-eslint/visitor-keys": "8.59.0",
+ "@typescript-eslint/scope-manager": "8.59.1",
+ "@typescript-eslint/type-utils": "8.59.1",
+ "@typescript-eslint/utils": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
@@ -927,7 +927,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.59.0",
+ "@typescript-eslint/parser": "^8.59.1",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
@@ -943,16 +943,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz",
- "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.1.tgz",
+ "integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.59.0",
- "@typescript-eslint/types": "8.59.0",
- "@typescript-eslint/typescript-estree": "8.59.0",
- "@typescript-eslint/visitor-keys": "8.59.0",
+ "@typescript-eslint/scope-manager": "8.59.1",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1",
"debug": "^4.4.3"
},
"engines": {
@@ -968,14 +968,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz",
- "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz",
+ "integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.59.0",
- "@typescript-eslint/types": "^8.59.0",
+ "@typescript-eslint/tsconfig-utils": "^8.59.1",
+ "@typescript-eslint/types": "^8.59.1",
"debug": "^4.4.3"
},
"engines": {
@@ -990,14 +990,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz",
- "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz",
+ "integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.59.0",
- "@typescript-eslint/visitor-keys": "8.59.0"
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1008,9 +1008,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz",
- "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz",
+ "integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1025,15 +1025,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz",
- "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz",
+ "integrity": "sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.59.0",
- "@typescript-eslint/typescript-estree": "8.59.0",
- "@typescript-eslint/utils": "8.59.0",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1",
+ "@typescript-eslint/utils": "8.59.1",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
@@ -1050,9 +1050,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz",
- "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz",
+ "integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1064,16 +1064,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz",
- "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz",
+ "integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.59.0",
- "@typescript-eslint/tsconfig-utils": "8.59.0",
- "@typescript-eslint/types": "8.59.0",
- "@typescript-eslint/visitor-keys": "8.59.0",
+ "@typescript-eslint/project-service": "8.59.1",
+ "@typescript-eslint/tsconfig-utils": "8.59.1",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/visitor-keys": "8.59.1",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
@@ -1105,16 +1105,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz",
- "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz",
+ "integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
- "@typescript-eslint/scope-manager": "8.59.0",
- "@typescript-eslint/types": "8.59.0",
- "@typescript-eslint/typescript-estree": "8.59.0"
+ "@typescript-eslint/scope-manager": "8.59.1",
+ "@typescript-eslint/types": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1129,13 +1129,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz",
- "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz",
+ "integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.59.0",
+ "@typescript-eslint/types": "8.59.1",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -2536,16 +2536,16 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.59.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.0.tgz",
- "integrity": "sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw==",
+ "version": "8.59.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.1.tgz",
+ "integrity": "sha512-xqDcFVBmlrltH64lklOVp1wYxgJr6LVdg3NamBgH2OOQDLFdTKfIZXF5PfghrnXQKXZGTQs8tr1vL7fJvq8CTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.59.0",
- "@typescript-eslint/parser": "8.59.0",
- "@typescript-eslint/typescript-estree": "8.59.0",
- "@typescript-eslint/utils": "8.59.0"
+ "@typescript-eslint/eslint-plugin": "8.59.1",
+ "@typescript-eslint/parser": "8.59.1",
+ "@typescript-eslint/typescript-estree": "8.59.1",
+ "@typescript-eslint/utils": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
diff --git a/tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react/package.json b/tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react/package.json
index 4893c7525..d953fbf96 100644
--- a/tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react/package.json
+++ b/tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react/package.json
@@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
- "build": "tsc -b && vite build",
+ "build": "npx tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
@@ -23,7 +23,7 @@
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.5.0",
"typescript": "^6.0.3",
- "typescript-eslint": "^8.59.0",
+ "typescript-eslint": "^8.59.1",
"vite": "^8.0.10"
}
}
diff --git a/tests/InfiniFrameTests.Playwright.WebApp.Vue/InfiniFrameTests.Playwright.WebApp.Vue.csproj b/tests/InfiniFrameTests.Playwright.WebApp.Vue/InfiniFrameTests.Playwright.WebApp.Vue.csproj
index 3cc4a1e5b..3719630a0 100644
--- a/tests/InfiniFrameTests.Playwright.WebApp.Vue/InfiniFrameTests.Playwright.WebApp.Vue.csproj
+++ b/tests/InfiniFrameTests.Playwright.WebApp.Vue/InfiniFrameTests.Playwright.WebApp.Vue.csproj
@@ -4,13 +4,11 @@
true
$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..\..'))\
-
-
- $([System.String]::Copy('$(TargetFrameworks)').Split(';')[0])
-
-
- $(TargetFramework)
-
+
+ true
+
+
+ $(ProjectDir)wwwroot\index.html
@@ -23,28 +21,30 @@
-
-
+
+
+
+
-
+
+
-
+
+
+ Condition="'$(CI)' == 'true'"
+ WorkingDirectory="$(ProjectDir)Sources\infiniframe-playwright-vue" />
-
-
-
+
+
+ false
+
+
@@ -56,7 +56,7 @@
SkipUnchangedFiles="true" />
-
+
diff --git a/tests/InfiniFrameTests.Playwright.WebApp.Vue/Sources/infiniframe-playwright-vue/package.json b/tests/InfiniFrameTests.Playwright.WebApp.Vue/Sources/infiniframe-playwright-vue/package.json
index 795f87248..a38ff6033 100644
--- a/tests/InfiniFrameTests.Playwright.WebApp.Vue/Sources/infiniframe-playwright-vue/package.json
+++ b/tests/InfiniFrameTests.Playwright.WebApp.Vue/Sources/infiniframe-playwright-vue/package.json
@@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
- "build": "vue-tsc -b && vite build",
+ "build": "npx vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
diff --git a/tests/InfiniFrameTests.Playwright/TestUtility/BlazorPlaywrightContextBase.cs b/tests/InfiniFrameTests.Playwright/TestUtility/BlazorPlaywrightContextBase.cs
index 3616db399..f7eb0d339 100644
--- a/tests/InfiniFrameTests.Playwright/TestUtility/BlazorPlaywrightContextBase.cs
+++ b/tests/InfiniFrameTests.Playwright/TestUtility/BlazorPlaywrightContextBase.cs
@@ -21,6 +21,7 @@ public abstract class BlazorPlaywrightContextBase(string documen
private IInfiniFrameWindow? _window;
private Thread? _appThread;
private readonly int _playwrightDevtoolsPort = PlaywrightConnectionUtility.GetAvailablePort();
+ private readonly string _webViewUserDataPath = PlaywrightConnectionUtility.CreateUniqueWebViewUserDataPath(typeof(TRootComponent).FullName ?? typeof(TRootComponent).Name);
// -----------------------------------------------------------------------------------------------------------------
// Methods
@@ -44,6 +45,8 @@ protected void AfterAll() {
_app = null;
_window = null;
_appThread = null;
+
+ PlaywrightConnectionUtility.DeleteDirectorySafely(_webViewUserDataPath);
}
protected override Uri CreatePlaywrightConnectionUri(string relativeUrl)
@@ -56,7 +59,9 @@ protected virtual void ConfigureRootComponents(RootComponentList rootComponents)
protected virtual void ConfigureWindowBuilder(IInfiniFrameWindowBuilder windowBuilder, int playwrightDevtoolsPort) {
windowBuilder
.SetTitle(DefaultDocumentTitle)
+ .SetTemporaryFilesPath(_webViewUserDataPath)
.SetBrowserControlInitParameters($"--remote-debugging-port={playwrightDevtoolsPort}")
+ .RegisterStandardGetWebMessageHandler()
.RegisterWindowManagementWebMessageHandler()
.RegisterFullScreenWebMessageHandler()
.RegisterOpenExternalTargetWebMessageHandler()
diff --git a/tests/InfiniFrameTests.Playwright/TestUtility/PlaywrightConnectionUtility.cs b/tests/InfiniFrameTests.Playwright/TestUtility/PlaywrightConnectionUtility.cs
index 2b23411de..625b16f8d 100644
--- a/tests/InfiniFrameTests.Playwright/TestUtility/PlaywrightConnectionUtility.cs
+++ b/tests/InfiniFrameTests.Playwright/TestUtility/PlaywrightConnectionUtility.cs
@@ -19,11 +19,88 @@ public static class PlaywrightConnectionUtility {
// Methods
// -----------------------------------------------------------------------------------------------------------------
public static int GetAvailablePort() {
- using TcpListener listener = new(IPAddress.Loopback, 0);
- listener.Start();
- int port = ((IPEndPoint)listener.LocalEndpoint).Port;
- listener.Stop();
- return port;
+ using Mutex mutex = new(false, "InfiniFramePlaywrightPortReservation");
+
+ try {
+ mutex.WaitOne();
+ }
+ catch (AbandonedMutexException) {
+ // Previous test process exited while reserving a port. The reservation file is still usable.
+ }
+
+ try {
+ string reservationFile = Path.Join(Path.GetTempPath(), "infiniframe-playwright", "reserved-ports.txt");
+ Directory.CreateDirectory(Path.GetDirectoryName(reservationFile)!);
+ DateTimeOffset now = DateTimeOffset.UtcNow;
+ DateTimeOffset oldestValidReservation = now.AddHours(-6);
+
+ List<(int Port, DateTimeOffset ReservedAt)> activeReservations = File.Exists(reservationFile)
+ ? File.ReadLines(reservationFile)
+ .Select(ParsePortReservation)
+ .Where(reservation => reservation.Port > 0 && reservation.ReservedAt >= oldestValidReservation)
+ .ToList()
+ : [];
+ HashSet reservedPorts = activeReservations.Select(static reservation => reservation.Port).ToHashSet();
+
+ for (int attempt = 0; attempt < 100; attempt++) {
+ using TcpListener listener = new(IPAddress.Loopback, 0);
+ listener.Start();
+ int port = ((IPEndPoint)listener.LocalEndpoint).Port;
+
+ if (reservedPorts.Contains(port))
+ continue;
+
+ activeReservations.Add((port, now));
+ File.WriteAllLines(
+ reservationFile,
+ activeReservations.Select(static reservation => $"{reservation.Port}|{reservation.ReservedAt.UtcTicks}"));
+ return port;
+ }
+
+ throw new InvalidOperationException("Could not reserve an available Playwright port.");
+ }
+ finally {
+ mutex.ReleaseMutex();
+ }
+ }
+
+ private static (int Port, DateTimeOffset ReservedAt) ParsePortReservation(string line) {
+ string[] parts = line.Split('|', 2);
+
+ if (!int.TryParse(parts[0], out int port))
+ return (0, DateTimeOffset.MinValue);
+
+ if (parts.Length < 2 || !long.TryParse(parts[1], out long ticks))
+ return (port, DateTimeOffset.UtcNow);
+
+ return (port, new DateTimeOffset(ticks, TimeSpan.Zero));
+ }
+
+ public static string CreateUniqueWebViewUserDataPath(string name) {
+ string safeName = string.Concat(name.Select(static c => Path.GetInvalidFileNameChars().Contains(c) ? '-' : c));
+ string path = Path.Join(
+ Path.GetTempPath(),
+ "infiniframe-playwright",
+ $"{safeName}-{Environment.ProcessId}-{Guid.NewGuid():N}");
+
+ Directory.CreateDirectory(path);
+ return path;
+ }
+
+ public static void DeleteDirectorySafely(string? path) {
+ if (string.IsNullOrWhiteSpace(path))
+ return;
+
+ try {
+ if (Directory.Exists(path))
+ Directory.Delete(path, recursive: true);
+ }
+ catch (IOException) {
+ // WebView2 can release files shortly after the window closes.
+ }
+ catch (UnauthorizedAccessException) {
+ // Best-effort cleanup for test-only user data folders.
+ }
}
public static async Task CreatePlaywrightAsync(TimeSpan timeout = default) {
diff --git a/tests/InfiniFrameTests.Playwright/TestUtility/ServerPlaywrightContextBase.cs b/tests/InfiniFrameTests.Playwright/TestUtility/ServerPlaywrightContextBase.cs
index a47d814a7..a6263b706 100644
--- a/tests/InfiniFrameTests.Playwright/TestUtility/ServerPlaywrightContextBase.cs
+++ b/tests/InfiniFrameTests.Playwright/TestUtility/ServerPlaywrightContextBase.cs
@@ -19,6 +19,7 @@ public abstract class ServerPlaywrightContextBase(string documentTitle) : Playwr
private InfiniFrameServerTestUtility? _utility;
private int _serverPort;
private int _playwrightDevtoolsPort;
+ private string? _webViewUserDataPath;
private string ServerUrl => $"http://127.0.0.1:{_serverPort}";
private string PlaywrightConnectionString => $"http://127.0.0.1:{_playwrightDevtoolsPort}";
@@ -34,6 +35,9 @@ protected void AfterAll() {
_utility?.Dispose();
_utility = null;
+
+ PlaywrightConnectionUtility.DeleteDirectorySafely(_webViewUserDataPath);
+ _webViewUserDataPath = null;
}
protected override Uri CreatePlaywrightConnectionUri(string relativeUrl)
@@ -42,6 +46,7 @@ protected override Uri CreatePlaywrightConnectionUri(string relativeUrl)
private void StartUtilityWithFreshPorts() {
_serverPort = PlaywrightConnectionUtility.GetAvailablePort();
_playwrightDevtoolsPort = PlaywrightConnectionUtility.GetAvailablePort();
+ _webViewUserDataPath = PlaywrightConnectionUtility.CreateUniqueWebViewUserDataPath(GetType().FullName ?? GetType().Name);
using var startupCancellation = new CancellationTokenSource(TimeSpan.FromSeconds(90));
@@ -51,7 +56,9 @@ private void StartUtilityWithFreshPorts() {
windowBuilder: windowBuilder => windowBuilder
.SetStartUrl(ServerUrl)
.SetTitle(DefaultDocumentTitle)
+ .SetTemporaryFilesPath(_webViewUserDataPath)
.SetBrowserControlInitParameters($"--remote-debugging-port={_playwrightDevtoolsPort}")
+ .RegisterStandardGetWebMessageHandler()
.RegisterWindowManagementWebMessageHandler()
.RegisterFullScreenWebMessageHandler()
.RegisterOpenExternalTargetWebMessageHandler()
diff --git a/tests/InfiniFrameTests.Playwright/Tests/InfiniFramePlaywrightTestBase.cs b/tests/InfiniFrameTests.Playwright/Tests/InfiniFramePlaywrightTestBase.cs
index f6c4328f9..0fb9ca342 100644
--- a/tests/InfiniFrameTests.Playwright/Tests/InfiniFramePlaywrightTestBase.cs
+++ b/tests/InfiniFrameTests.Playwright/Tests/InfiniFramePlaywrightTestBase.cs
@@ -12,6 +12,7 @@ public abstract class InfiniFramePlaywrightTestBase {
protected abstract IPlaywrightRuntimeContext RuntimeContext { get; }
private const string RootRelativeUrl = "/";
+ private const int NavigationRetryCount = 5;
// -----------------------------------------------------------------------------------------------------------------
// Methods
@@ -19,10 +20,11 @@ public abstract class InfiniFramePlaywrightTestBase {
[Before(Test)]
public async Task ResetStateBeforeEachTest() {
RuntimeContext.ResetWindowCloseRequestCount();
- RuntimeContext.Window.SetTitle(RuntimeContext.DefaultDocumentTitle);
IPage page = await GetRootPageAsync();
- await page.EvaluateAsync(
+ RuntimeContext.Window.SetTitle(RuntimeContext.DefaultDocumentTitle);
+ await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
$"() => {{ document.title = '{RuntimeContext.DefaultDocumentTitle}'; }}"
);
@@ -93,4 +95,42 @@ protected static async Task WaitForStateChangeAsync(
Fail.Test("State change timeout exceeded");
return default!;
}
+
+ protected static async Task EvaluateWhenPageReadyAsync(IPage page, string script) {
+ for (int attempt = 1; attempt <= NavigationRetryCount; attempt++) {
+ try {
+ await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
+ await page.EvaluateAsync(script);
+ return;
+ }
+ catch (PlaywrightException exception) when (
+ attempt < NavigationRetryCount &&
+ IsExecutionContextDestroyedByNavigation(exception)
+ ) {
+ await page.WaitForTimeoutAsync(150);
+ }
+ }
+ Fail.Test($"Could not execute script: {script} within timeout");
+ }
+
+ protected static async Task EvaluateWhenPageReadyAsync(IPage page, string script) {
+ for (int attempt = 1; attempt <= NavigationRetryCount; attempt++) {
+ try {
+ await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
+ var result = await page.EvaluateAsync(script);
+ return result;
+ }
+ catch (PlaywrightException exception) when (
+ attempt < NavigationRetryCount &&
+ IsExecutionContextDestroyedByNavigation(exception)
+ ) {
+ await page.WaitForTimeoutAsync(150);
+ }
+ }
+ Fail.Test($"Could not execute script: {script} within timeout");
+ return default!;
+ }
+
+ private static bool IsExecutionContextDestroyedByNavigation(PlaywrightException exception)
+ => exception.Message.Contains("Execution context was destroyed", StringComparison.OrdinalIgnoreCase);
}
diff --git a/tests/InfiniFrameTests.Playwright/Tests/SharedJavascriptInteropTests.cs b/tests/InfiniFrameTests.Playwright/Tests/SharedJavascriptInteropTests.cs
index f4d214ed8..babf57dda 100644
--- a/tests/InfiniFrameTests.Playwright/Tests/SharedJavascriptInteropTests.cs
+++ b/tests/InfiniFrameTests.Playwright/Tests/SharedJavascriptInteropTests.cs
@@ -64,10 +64,61 @@ public async Task TitleHtmlButton_ShouldToggleInfiniFrameTitle() {
}
finally {
RuntimeContext.Window.SetTitle(RuntimeContext.DefaultDocumentTitle);
- await page.EvaluateAsync(
+ await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
$"() => {{ document.title = '{RuntimeContext.DefaultDocumentTitle}'; }}"
);
}
}
+
+ [Test]
+ [NotInParallel(ParallelControl.Playwright)]
+ public async Task GetTitleAsyncFromJs_ShouldReturnNativeWindowTitl() {
+ // Arrange
+ IPage page = await GetRootPageAsync();
+ string originalTitleState = RuntimeContext.Window.Title;
+
+ // Act
+ string titleFromJsInitially = await EvaluateWhenPageReadyAsync(
+ page,
+ // lang=javascript
+ "async () => await window.infiniframe.window.getTitleAsync()"
+ );
+
+ // Assert
+ await Assert.That(titleFromJsInitially).IsEqualTo(originalTitleState);
+ }
+
+ [Test]
+ [NotInParallel(ParallelControl.Playwright)]
+ public async Task GetTitleAsyncFromJs_ShouldReturnNativeWindowTitle_AndShouldReturnCorrectTitle() {
+ IPage page = await GetRootPageAsync();
+ string originalTitleState = RuntimeContext.Window.Title;
+
+ string titleFromJsInitially = await EvaluateWhenPageReadyAsync(
+ page,
+ // lang=javascript
+ "async () => await window.infiniframe.window.getTitleAsync()"
+ );
+
+ await Assert.That(titleFromJsInitially).IsEqualTo(originalTitleState);
+
+ await page.ClickAsync(TitleToggleButtonSelector);
+ string toggledTitle = await WaitForStateChangeAsync(
+ originalTitleState,
+ stateProvider: () => RuntimeContext.Window.Title
+ );
+
+ string titleFromJs = await EvaluateWhenPageReadyAsync(
+ page,
+ // lang=javascript
+ "async () => await window.infiniframe.window.getTitleAsync()"
+ );
+
+ await Assert.That(toggledTitle).IsEqualTo(ToggledTitle)
+ .And.IsNotEqualTo(originalTitleState);
+ await Assert.That(titleFromJs).IsEqualTo(ToggledTitle)
+ .And.IsNotEqualTo(originalTitleState);
+ }
}
diff --git a/tests/InfiniFrameTests.Playwright/Tests/SharedJavascriptTests.cs b/tests/InfiniFrameTests.Playwright/Tests/SharedJavascriptTests.cs
index 58b1bc87c..22eee4aac 100644
--- a/tests/InfiniFrameTests.Playwright/Tests/SharedJavascriptTests.cs
+++ b/tests/InfiniFrameTests.Playwright/Tests/SharedJavascriptTests.cs
@@ -20,23 +20,25 @@ public abstract class SharedJavascriptTests : InfiniFramePlaywrightTestBase {
[NotInParallel(ParallelControl.Playwright)]
public async Task InfiniWindowIsInitialized() {
IPage page = await GetRootPageAsync();
-
- var initState = await page.EvaluateAsync(
+ var initState = await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"""
() => ({
- hasHostBridge: window.infiniframe?.host !== undefined && window.infiniframe?.host !== null,
- hasInfiniFrameApi: window.infiniFrame !== undefined && window.infiniFrame !== null,
- hasHostMessaging: window.infiniFrame?.HostMessaging !== undefined && window.infiniFrame?.HostMessaging !== null,
- hasSendMessageToHost: typeof window.infiniFrame?.sendMessageToHost === 'function'
+ hasNativeHostBridge: window.__infiniframe?.host !== undefined && window.__infiniframe?.host !== null,
+ hasInfiniFrameApi: window.infiniframe !== undefined && window.infiniframe !== null,
+ hasMessaging: window.infiniframe?.messaging !== undefined && window.infiniframe?.messaging !== null,
+ hasWindow: window.infiniframe?.window !== undefined && window.infiniframe?.window !== null,
+ hasUtils: window.infiniframe?.utils !== undefined && window.infiniframe?.utils !== null,
})
"""
);
- await Assert.That(initState.GetProperty("hasHostBridge").GetBoolean()).IsTrue();
+ await Assert.That(initState.GetProperty("hasNativeHostBridge").GetBoolean()).IsTrue();
await Assert.That(initState.GetProperty("hasInfiniFrameApi").GetBoolean()).IsTrue();
- await Assert.That(initState.GetProperty("hasHostMessaging").GetBoolean()).IsTrue();
- await Assert.That(initState.GetProperty("hasSendMessageToHost").GetBoolean()).IsTrue();
+ await Assert.That(initState.GetProperty("hasMessaging").GetBoolean()).IsTrue();
+ await Assert.That(initState.GetProperty("hasWindow").GetBoolean()).IsTrue();
+ await Assert.That(initState.GetProperty("hasUtils").GetBoolean()).IsTrue();
}
[Test]
@@ -45,9 +47,10 @@ public async Task DynamicallyUpdateTitleFromJs() {
IPage page = await GetRootPageAsync();
string originalTitle = RuntimeContext.Window.Title;
- await page.EvaluateAsync(
+ await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
- $"() => window.infiniframe?.host?.postMessage({{ id: '__infiniframe:title:change', data: '{NewTitleFromHostMessage}', version: 1 }})"
+ $"() => window.__infiniframe?.host?.postData({{ id: '__infiniframe:title:change', command: 'Post', data: '{NewTitleFromHostMessage}', version: 2 }})"
);
string updatedTitle = await WaitForStateChangeAsync(
originalTitle,
@@ -67,7 +70,8 @@ public async Task WindowClose() {
RuntimeContext.SuppressWindowCloseRequests(true);
try {
- await page.EvaluateAsync(
+ await EvaluateWhenPageReadyAsync(
+ page,
// lang=javascript
"() => window.close()"
);
diff --git a/tests/InfiniFrameTests.Shared/InfiniFrameServerTestUtility.cs b/tests/InfiniFrameTests.Shared/InfiniFrameServerTestUtility.cs
index 33cd029da..cafecf37f 100644
--- a/tests/InfiniFrameTests.Shared/InfiniFrameServerTestUtility.cs
+++ b/tests/InfiniFrameTests.Shared/InfiniFrameServerTestUtility.cs
@@ -107,8 +107,13 @@ public void Dispose() {
// ignored
}
- if (!_thread.Join(TimeSpan.FromSeconds(5)))
+ bool stoppedInTime = _thread.Join(TimeSpan.FromSeconds(5));
+ if (!stoppedInTime) {
+ Console.WriteLine(
+ $"[InfiniFrameServerTestUtility] Warning: server thread did not stop within 5s. " +
+ $"ThreadId={_thread.ManagedThreadId}, State={_thread.ThreadState}. Interrupting thread.");
_thread.Interrupt();
+ }
}
private static bool IsNonFatalException(Exception exception)
diff --git a/tests/InfiniFrameTests.Shared/TestDoubles/RecordingInfiniFrameWindowSubstitute.cs b/tests/InfiniFrameTests.Shared/TestDoubles/RecordingInfiniFrameWindowSubstitute.cs
index a2666db8b..9f73286ff 100644
--- a/tests/InfiniFrameTests.Shared/TestDoubles/RecordingInfiniFrameWindowSubstitute.cs
+++ b/tests/InfiniFrameTests.Shared/TestDoubles/RecordingInfiniFrameWindowSubstitute.cs
@@ -2,6 +2,7 @@
// Imports
// ---------------------------------------------------------------------------------------------------------------------
using InfiniFrame;
+using InfiniFrame.HostMessaging;
using InfiniFrame.Js.Interop;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
@@ -43,7 +44,7 @@ public RecordingInfiniFrameWindowSubstitute() {
// Default wiring for simple tests that don't need explicit builder binding.
Window.Events.Returns(new InfiniFrameWindowEvents());
- Window.MessageHandlers.Returns(new InfiniFrameWindowMessageHandlers());
+ Window.MessageHandlers.Returns(new InfiniFrameWindowMessageHandler());
}
// -----------------------------------------------------------------------------------------------------------------
@@ -65,4 +66,10 @@ public int CountEnvelopeMessagesById(string messageId) {
.Select(InteropEnvelopeProtocol.ParseIncomingMessage)
.Count(result => result.Success && string.Equals(result.MessageId, messageId, StringComparison.Ordinal));
}
+
+ public IReadOnlyList GetSentMessagesSnapshot() {
+ lock (_sentWebMessagesLock) {
+ return [.._sentWebMessages];
+ }
+ }
}
diff --git a/tests/InfiniFrameTests.Tools.Pack/Services/PublishServiceTests.cs b/tests/InfiniFrameTests.Tools.Pack/Services/PublishServiceTests.cs
index c26bdd3f2..1fd9085ad 100644
--- a/tests/InfiniFrameTests.Tools.Pack/Services/PublishServiceTests.cs
+++ b/tests/InfiniFrameTests.Tools.Pack/Services/PublishServiceTests.cs
@@ -17,6 +17,13 @@ public class PublishServiceTests {
private static readonly SemaphoreSlim PublishTestLock = new(1, 1);
private TemporaryDirectory TemporaryDirectory { get; set; } = null!;
+
+ #if DEBUG
+ private const string Configuration = "Debug";
+ #else
+ private const string Configuration = "Release";
+ #endif
+
// -----------------------------------------------------------------------------------------------------------------
// Test Setup
// -----------------------------------------------------------------------------------------------------------------
@@ -40,7 +47,7 @@ public async Task PublishAsync_Throws_WhenProjectFileDoesNotExist() {
var options = new PublishOptions {
ProjectPath = Path.Join(Path.GetTempPath(), $"missing-project-{Guid.NewGuid():N}.csproj"),
Rid = "auto",
- Configuration = "Release",
+ Configuration = Configuration,
Framework = "net10.0",
SelfContained = true
};
@@ -77,7 +84,7 @@ await File.WriteAllTextAsync(appProjectPath, """
var options = new PublishOptions {
ProjectPath = appProjectPath,
Rid = rid,
- Configuration = "Release",
+ Configuration = Configuration,
Framework = "net10.0",
SelfContained = true,
Output = outputPath
@@ -136,7 +143,7 @@ await File.WriteAllTextAsync(Path.Join(appDirectory, "Program.cs"), """
var options = new PublishOptions {
ProjectPath = appProjectPath,
Rid = rid,
- Configuration = "Release",
+ Configuration = Configuration,
Framework = "net10.0",
SelfContained = true,
Output = outputPath
@@ -199,7 +206,7 @@ await File.WriteAllTextAsync(Path.Join(appDirectory, "Program.cs"), $$"""
var options = new PublishOptions {
ProjectPath = appProjectPath,
Rid = rid,
- Configuration = "Release",
+ Configuration = Configuration,
Framework = "net10.0",
SelfContained = true,
Output = outputPath
diff --git a/tests/InfiniFrameTests.WebServer/InfiniFrameWebApplicationTests.cs b/tests/InfiniFrameTests.WebServer/InfiniFrameWebApplicationTests.cs
index 2f110aaa2..3a0ec06a0 100644
--- a/tests/InfiniFrameTests.WebServer/InfiniFrameWebApplicationTests.cs
+++ b/tests/InfiniFrameTests.WebServer/InfiniFrameWebApplicationTests.cs
@@ -21,7 +21,6 @@ private static IInfiniFrameWindow CreateMockWindow() {
}
[Test]
- [DisplayName($"{nameof(InfiniFrameWebApplicationTests)}.{nameof(CreateBuilder_ShouldReturnValidBuilder)}")]
public async Task CreateBuilder_ShouldReturnValidBuilder() {
// Arrange & Act
InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder();
@@ -33,7 +32,6 @@ public async Task CreateBuilder_ShouldReturnValidBuilder() {
}
[Test]
- [DisplayName($"{nameof(InfiniFrameWebApplicationTests)}.{nameof(UseAutoServerClose_WhenWindowNotCreated_ShouldRegisterWithBuilder)}")]
public async Task UseAutoServerClose_WhenWindowNotCreated_ShouldRegisterWithBuilder() {
// Arrange
var mockWindowBuilder = Substitute.For();
@@ -60,7 +58,6 @@ public async Task UseAutoServerClose_WhenWindowNotCreated_ShouldRegisterWithBuil
}
[Test]
- [DisplayName($"{nameof(InfiniFrameWebApplicationTests)}.{nameof(UseAutoServerClose_WhenWindowCreated_ShouldRegisterWithWindow)}")]
public async Task UseAutoServerClose_WhenWindowCreated_ShouldRegisterWithWindow() {
// Arrange
IInfiniFrameWindow mockWindow = CreateMockWindow();
@@ -87,7 +84,6 @@ public async Task UseAutoServerClose_WhenWindowCreated_ShouldRegisterWithWindow(
}
[Test]
- [DisplayName($"{nameof(InfiniFrameWebApplicationTests)}.{nameof(UseAutoServerClose_ClosingHandler_ShouldReturnFalse)}")]
public async Task UseAutoServerClose_ClosingHandler_ShouldReturnFalse() {
// Arrange
IInfiniFrameWindow mockWindow = CreateMockWindow();
@@ -115,7 +111,6 @@ public async Task UseAutoServerClose_ClosingHandler_ShouldReturnFalse() {
}
[Test]
- [DisplayName($"{nameof(InfiniFrameWebApplicationTests)}.{nameof(UseAutoServerClose_ClosingHandler_ShouldInitiateStopAsync)}")]
public async Task UseAutoServerClose_ClosingHandler_ShouldInitiateStopAsync() {
// Arrange
IInfiniFrameWindow mockWindow = CreateMockWindow();
@@ -143,6 +138,7 @@ public async Task UseAutoServerClose_ClosingHandler_ShouldInitiateStopAsync() {
while (!appLifetime.ApplicationStopping.IsCancellationRequested && DateTime.UtcNow < deadline) {
await Task.Delay(50);
}
+
if (!appLifetime.ApplicationStopping.IsCancellationRequested) {
Console.WriteLine("Timed out waiting for ApplicationStopping after closing handler invocation.");
}
@@ -154,7 +150,6 @@ await Assert.That(appLifetime.ApplicationStopping.IsCancellationRequested)
}
[Test]
- [DisplayName($"{nameof(InfiniFrameWebApplicationTests)}.{nameof(Stop_ShouldCloseWindowAndStopWebApp)}")]
[Retry(5)]
public async Task Stop_ShouldCloseWindowAndStopWebApp() {
// Arrange
@@ -178,6 +173,7 @@ public async Task Stop_ShouldCloseWindowAndStopWebApp() {
while (!appLifetime.ApplicationStopping.IsCancellationRequested && DateTime.UtcNow < deadline) {
await Task.Delay(50);
}
+
if (!appLifetime.ApplicationStopping.IsCancellationRequested) {
Console.WriteLine("Timed out waiting for ApplicationStopping after Stop() call.");
}
@@ -189,7 +185,6 @@ await Assert.That(appLifetime.ApplicationStopping.IsCancellationRequested)
}
[Test]
- [DisplayName($"{nameof(InfiniFrameWebApplicationTests)}.{nameof(Window_Property_ShouldReturnLazyValue)}")]
public async Task Window_Property_ShouldReturnLazyValue() {
// Arrange
IInfiniFrameWindow mockWindow = CreateMockWindow();
diff --git a/tests/InfiniFrameTests/InfiniFrameNativeParameterTests.cs b/tests/InfiniFrameTests/InfiniFrameNativeParameterTests.cs
index ce1aabb50..789fd5ce2 100644
--- a/tests/InfiniFrameTests/InfiniFrameNativeParameterTests.cs
+++ b/tests/InfiniFrameTests/InfiniFrameNativeParameterTests.cs
@@ -13,7 +13,6 @@ public class InfiniFrameNativeParameterTests {
// This test should onl fails if the InfiniFrameNativeParameterTests C# struct is wrongly defined
// and has parameters in the wrong order, compared to the struct on the c++ side.
[Test]
- [DisplayName($"{nameof(InfiniFrameNativeParameterTests)}.{nameof(ReturnAsIsIsValid)}")]
public async Task ReturnAsIsIsValid() {
// Arrange
IntPtr[] customSchemeNames = new IntPtr[16];
diff --git a/tests/InfiniFrameTests/InfiniFrameWindowBuilderTests.cs b/tests/InfiniFrameTests/InfiniFrameWindowBuilderTests.cs
index 51706a8e4..badb06c1a 100644
--- a/tests/InfiniFrameTests/InfiniFrameWindowBuilderTests.cs
+++ b/tests/InfiniFrameTests/InfiniFrameWindowBuilderTests.cs
@@ -3,6 +3,7 @@
// ---------------------------------------------------------------------------------------------------------------------
using InfiniFrame;
using InfiniFrame.BuilderSnapshots;
+using InfiniFrame.HostMessaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -20,6 +21,8 @@ public class InfiniFrameWindowBuilderTests {
return null;
}
+ private const int DefaultIncludedMessageHandlers = 0;
+
[Test]
public async Task ResolveLogger_WithoutProvider_UsesSharedFallbackLogger() {
// Act
@@ -66,7 +69,7 @@ public async Task CreateSnapshot_CanBeCalledMoreThanOnce_WithUniqueMutableRefere
// Arrange
var builder = InfiniFrameWindowBuilder.Create();
builder.Events.WindowCreated.Add(_ => { });
- builder.MessageHandlers.RegisterMessageHandler("ping", (_, _) => { });
+ builder.MessageHandlers.RegisterHandler("ping", (_, _) => { });
builder.RegisterCustomSchemeHandler("app", EmptyHandler);
// Act
@@ -76,8 +79,8 @@ public async Task CreateSnapshot_CanBeCalledMoreThanOnce_WithUniqueMutableRefere
// Assert
await Assert.That(first.Events.WindowCreated.Length).IsEqualTo(1);
await Assert.That(second.Events.WindowCreated.Length).IsEqualTo(1);
- await Assert.That(first.MessageHandlers.Handlers.Length).IsEqualTo(1);
- await Assert.That(second.MessageHandlers.Handlers.Length).IsEqualTo(1);
+ await Assert.That(first.MessageHandlers.PostDataHandlers.Length).IsEqualTo(DefaultIncludedMessageHandlers + 1);
+ await Assert.That(second.MessageHandlers.PostDataHandlers.Length).IsEqualTo(DefaultIncludedMessageHandlers + 1);
await Assert.That(first.CustomSchemes.OrderedSchemeNames.Length).IsEqualTo(1);
await Assert.That(second.CustomSchemes.OrderedSchemeNames.Length).IsEqualTo(1);
await Assert.That(first.CustomSchemes.OrderedSchemeNames[0]).IsEqualTo("app");
@@ -94,18 +97,18 @@ public async Task CreateSnapshot_MutationsDoNotLeakBetweenSnapshots() {
InfiniFrameWindowEvents firstEvents = InfiniFrameWindowEvents.FromSnapshot(first.Events);
InfiniFrameWindowEvents secondEvents = InfiniFrameWindowEvents.FromSnapshot(second.Events);
- InfiniFrameWindowMessageHandlers firstMessageHandlers = InfiniFrameWindowMessageHandlers.FromSnapshot(first.MessageHandlers);
- InfiniFrameWindowMessageHandlers secondMessageHandlers = InfiniFrameWindowMessageHandlers.FromSnapshot(second.MessageHandlers);
+ InfiniFrameWindowMessageHandler firstMessageHandlers = InfiniFrameWindowMessageHandler.FromSnapshot(first.MessageHandlers);
+ InfiniFrameWindowMessageHandler secondMessageHandlers = InfiniFrameWindowMessageHandler.FromSnapshot(second.MessageHandlers);
InfiniFrameWindowCustomSchemeHandlers firstSchemes = InfiniFrameWindowCustomSchemeHandlers.FromSnapshot(first.CustomSchemes);
InfiniFrameWindowCustomSchemeHandlers secondSchemes = InfiniFrameWindowCustomSchemeHandlers.FromSnapshot(second.CustomSchemes);
- firstMessageHandlers.RegisterMessageHandler("ping", (_, _) => { });
+ firstMessageHandlers.RegisterHandler("ping", (_, _) => { });
firstEvents.WindowCreated.Add(_ => { });
firstSchemes.RegisterCustomSchemeHandler("only-first", EmptyHandler);
// Assert
- await Assert.That(firstMessageHandlers.IsEmpty).IsFalse();
- await Assert.That(secondMessageHandlers.IsEmpty).IsTrue();
+ await Assert.That(firstMessageHandlers.Count).IsEqualTo(DefaultIncludedMessageHandlers + 1);
+ await Assert.That(secondMessageHandlers.Count).IsEqualTo(DefaultIncludedMessageHandlers);
await Assert.That(firstEvents.WindowCreated.Snapshot.Length).IsEqualTo(secondEvents.WindowCreated.Snapshot.Length + 1);
await Assert.That(firstSchemes.ContainsCustomSchemeHandler("only-first")).IsTrue();
await Assert.That(secondSchemes.ContainsCustomSchemeHandler("only-first")).IsFalse();
diff --git a/tests/InfiniFrameTests/Utilities/OrderedEventTests.cs b/tests/InfiniFrameTests/Utilities/OrderedEventTests.cs
index 26d26002b..d157d60ce 100644
--- a/tests/InfiniFrameTests/Utilities/OrderedEventTests.cs
+++ b/tests/InfiniFrameTests/Utilities/OrderedEventTests.cs
@@ -2,6 +2,7 @@
// Imports
// ---------------------------------------------------------------------------------------------------------------------
using InfiniFrame;
+using InfiniFrame.HostMessaging;
using InfiniFrame.Utilities;
using Microsoft.Extensions.Logging.Abstractions;
using System.Collections.Immutable;
@@ -19,14 +20,13 @@ private static InfiniFrameWindow CreateWindow() {
CustomSchemes = new InfiniFrameWindowCustomSchemeHandlers(),
Parent = null,
Events = events,
- MessageHandlers = new InfiniFrameWindowMessageHandlers()
+ MessageHandlers = new InfiniFrameWindowMessageHandler()
};
events.CompleteSetup(window);
return window;
}
[Test]
- [DisplayName($"{nameof(OrderedEventTests)}.{nameof(OrderedEvent_InvokesInRegistrationOrder)}")]
public async Task OrderedEvent_InvokesInRegistrationOrder() {
// Arrange
var orderedEvent = new InfiniFrameOrderedEvent();
@@ -46,7 +46,6 @@ public async Task OrderedEvent_InvokesInRegistrationOrder() {
}
[Test]
- [DisplayName($"{nameof(OrderedEventTests)}.{nameof(OrderedEvent_RemoveStopsInvocation)}")]
public async Task OrderedEvent_RemoveStopsInvocation() {
// Arrange
var orderedEvent = new InfiniFrameOrderedEvent();
@@ -65,7 +64,6 @@ public async Task OrderedEvent_RemoveStopsInvocation() {
}
[Test]
- [DisplayName($"{nameof(OrderedEventTests)}.{nameof(OrderedEvent_SnapshotIsImmutable)}")]
public async Task OrderedEvent_SnapshotIsImmutable() {
// Arrange
var orderedEvent = new InfiniFrameOrderedEvent();
@@ -84,7 +82,6 @@ public async Task OrderedEvent_SnapshotIsImmutable() {
}
[Test]
- [DisplayName($"{nameof(OrderedEventTests)}.{nameof(OrderedEvent_OperatorsAddAndRemove)}")]
public async Task OrderedEvent_OperatorsAddAndRemove() {
// Arrange
var orderedEvent = new InfiniFrameOrderedEvent();
@@ -103,7 +100,6 @@ public async Task OrderedEvent_OperatorsAddAndRemove() {
}
[Test]
- [DisplayName($"{nameof(OrderedEventTests)}.{nameof(OrderedEventWithPayload_InvokesWithPayload)}")]
public async Task OrderedEventWithPayload_InvokesWithPayload() {
// Arrange
var orderedEvent = new InfiniFrameOrderedEvent();
@@ -123,7 +119,6 @@ public async Task OrderedEventWithPayload_InvokesWithPayload() {
}
[Test]
- [DisplayName($"{nameof(OrderedEventTests)}.{nameof(ClosingEvent_ReturnsLastResult)}")]
public async Task ClosingEvent_ReturnsLastResult() {
// Arrange
var closingEvent = new InfiniFrameOrderedClosingEvent();
@@ -140,7 +135,6 @@ public async Task ClosingEvent_ReturnsLastResult() {
}
[Test]
- [DisplayName($"{nameof(OrderedEventTests)}.{nameof(ClosingEvent_ReturnsNullWhenEmpty)}")]
public async Task ClosingEvent_ReturnsNullWhenEmpty() {
// Arrange
var closingEvent = new InfiniFrameOrderedClosingEvent();
diff --git a/tests/InfiniFrameTests/WebMessageReceivedHandlerTests.cs b/tests/InfiniFrameTests/WebMessageReceivedHandlerTests.cs
index 10a79ff1c..355043c78 100644
--- a/tests/InfiniFrameTests/WebMessageReceivedHandlerTests.cs
+++ b/tests/InfiniFrameTests/WebMessageReceivedHandlerTests.cs
@@ -2,6 +2,7 @@
// Imports
// ---------------------------------------------------------------------------------------------------------------------
using InfiniFrame;
+using InfiniFrame.HostMessaging;
using Microsoft.Extensions.Logging.Abstractions;
namespace InfiniFrameTests;
@@ -29,7 +30,6 @@ public TestServiceProvider(params object[] services) {
// Tests
// -----------------------------------------------------------------------------------------------------------------
[Test]
- [DisplayName($"{nameof(WebMessageReceivedHandlerTests)}.{nameof(Handler_ResolvesServiceFromProvider)}")]
public async Task Handler_ResolvesServiceFromProvider() {
// Arrange
var events = new InfiniFrameWindowEvents();
@@ -41,7 +41,7 @@ public async Task Handler_ResolvesServiceFromProvider() {
CustomSchemes = new InfiniFrameWindowCustomSchemeHandlers(),
Parent = null,
Events = events,
- MessageHandlers = new InfiniFrameWindowMessageHandlers()
+ MessageHandlers = new InfiniFrameWindowMessageHandler()
};
events.CompleteSetup(window);
diff --git a/tests/InfiniFrameTests/WindowTests.cs b/tests/InfiniFrameTests/WindowTests.cs
index 6d6d55a4b..b49198ffb 100644
--- a/tests/InfiniFrameTests/WindowTests.cs
+++ b/tests/InfiniFrameTests/WindowTests.cs
@@ -15,7 +15,6 @@ public class WindowTests {
// Tests
// -----------------------------------------------------------------------------------------------------------------
[Test]
- [DisplayName($"{nameof(WindowTests)}.{nameof(InstanceHandle_IsDefined)}")]
[SkipUtility.SkipOnMacOs]
[NotInParallel(ParallelControl.InfiniFrame)]
[Timeout(TimeoutUtility.DefaultTimeout)]
@@ -31,7 +30,6 @@ public async Task InstanceHandle_IsDefined(CancellationToken ct) {
}
[Test]
- [DisplayName($"{nameof(WindowTests)}.{nameof(WindowHandle_IsDefined)}")]
[SkipUtility.SkipOnMacOs]
[NotInParallel(ParallelControl.InfiniFrame)]
[Timeout(TimeoutUtility.DefaultTimeout)]
@@ -49,7 +47,6 @@ public async Task WindowHandle_IsDefined(CancellationToken ct) {
}
[Test]
- [DisplayName($"{nameof(WindowTests)}.{nameof(Monitors_IsNotEmpty)}")]
[SkipUtility.SkipOnMacOs]
[NotInParallel(ParallelControl.InfiniFrame)]
[Timeout(TimeoutUtility.DefaultTimeout)]
@@ -66,7 +63,6 @@ public async Task Monitors_IsNotEmpty(CancellationToken ct) {
}
[Test]
- [DisplayName($"{nameof(WindowTests)}.{nameof(NativeType_IsDefined)}")]
[SkipUtility.SkipOnMacOs]
[NotInParallel(ParallelControl.InfiniFrame)]
[Timeout(TimeoutUtility.DefaultTimeout)]
@@ -82,7 +78,6 @@ public async Task NativeType_IsDefined(CancellationToken ct) {
}
[Test]
- [DisplayName($"{nameof(WindowTests)}.{nameof(Close_IsDefined)}")]
[SkipUtility.SkipOnMacOs]
[NotInParallel(ParallelControl.InfiniFrame)]
[Timeout(TimeoutUtility.DefaultTimeout)]