diff --git a/.github/workflows/mobile-release-preflight.yml b/.github/workflows/mobile-release-preflight.yml new file mode 100644 index 00000000..4412a5e6 --- /dev/null +++ b/.github/workflows/mobile-release-preflight.yml @@ -0,0 +1,20 @@ +name: Mobile release preflight tools +on: + pull_request: + paths: ['v2/mobile/scripts/**', '.github/workflows/mobile-release-preflight.yml'] + push: + branches: [main] + paths: ['v2/mobile/scripts/**', '.github/workflows/mobile-release-preflight.yml'] + workflow_dispatch: +permissions: + contents: read +jobs: + artifact-verifier-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Test fail-closed APK validation + run: python -m unittest discover -s v2/mobile/scripts -p 'test_*.py' diff --git a/.github/workflows/v2-release.yml b/.github/workflows/v2-release.yml index d5bfc26a..f6c1aeb8 100644 --- a/.github/workflows/v2-release.yml +++ b/.github/workflows/v2-release.yml @@ -444,19 +444,35 @@ jobs: working-directory: v2 run: npm run screenshots + # The phone app lives on the default branch only once it has merged; + # until then there is nothing to capture. + - name: Regenerate phone gallery + working-directory: v2 + run: | + if [[ ! -f mobile/screenshots/capture.mjs ]]; then + echo "No mobile capture on this branch; skipping." + exit 0 + fi + cd mobile + npm ci + npx playwright install chromium + npm run screenshots + - name: Commit if the galleries changed working-directory: v2 env: TAG: ${{ needs.notes.outputs.tag }} run: | set -euo pipefail - if git diff --quiet -- screenshots/gallery.gif screenshots/gallery-light.gif; then + galleries=(screenshots/gallery.gif screenshots/gallery-light.gif) + [[ -f mobile/screenshots/gallery.gif ]] && galleries+=(mobile/screenshots/gallery.gif mobile/screenshots/contact-sheet.png) + if git diff --quiet -- "${galleries[@]}"; then echo "Galleries unchanged; nothing to commit." exit 0 fi git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add screenshots/gallery.gif screenshots/gallery-light.gif + git add -- "${galleries[@]}" git commit -m "Regenerate gallery GIFs for ${TAG}" # `merge-updater` and `refresh-screenshots` both commit to the # default branch and can finish at the same time, so the loser of diff --git a/CLAUDE.md b/CLAUDE.md index 447e03f6..7962df73 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -98,6 +98,8 @@ It runs offline against the demo fixture layer (`src/lib/demo-mock.ts`, gated be - **New screen/tab** → add a visit + capture step in `screenshots/capture.mjs`. - **New `invoke` command that loads on a screen** → add a fixture case in `src/lib/demo-mock.ts`, or that screen renders empty. +The phone app has its own capture: `cd v2/mobile && npm run screenshots` writes every mobile screen to `v2/mobile/screenshots/frames/` (gitignored), then rebuilds the committed `gallery.gif` and one-page `contact-sheet.png` shown in the root and mobile READMEs. The `refresh-screenshots` release job regenerates them too once the mobile app is on the default branch. It runs against `v2/mobile/src/lib/demo-mock.ts` (`VITE_DEMO=1`), which answers shared commands from the desktop fixtures and only adds the mobile-only ones. A new mobile screen needs an entry in `SCREENS` in `v2/mobile/screenshots/capture.mjs`. + The release workflow also regenerates the gallery on every `v2-*` tag (the `refresh-screenshots` job) and commits it to the default branch, so a release always ships current screenshots even if a manual regen was missed. Requires `ffmpeg` + `npx playwright install chromium` locally. Full pipeline docs: `v2/screenshots/README.md`. ## Cutting a v2 release diff --git a/README.md b/README.md index 9573bfdf..ec3450f3 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,15 @@ TVs on your network, and updates itself. You don't need a terminal or any script > cleanup over Wi-Fi, with no computer involved. It's in testing now. > [Watch the repo](https://github.com/bryanroscoe/shield_optimizer) to hear when it ships. +
+Preview the phone app + +ATV Optimizer for Android walkthrough + +Demo data, not a live device. See [`v2/mobile/`](v2/mobile/README.md). + +
+ ## Install ### macOS: Homebrew (easiest) diff --git a/docs/INTEGRATION-MOBILE-MAIN-2026-10-17.md b/docs/INTEGRATION-MOBILE-MAIN-2026-10-17.md new file mode 100644 index 00000000..0235bb20 --- /dev/null +++ b/docs/INTEGRATION-MOBILE-MAIN-2026-10-17.md @@ -0,0 +1,27 @@ +# Reviewed main integration into mobile + +The original integration draft combined mobile `60a1934c5e4c575a81bd124698b9a9f6203e5fe8` with reviewed main `ae0436e4e2809e4fa677542b41d0e09b29532f60`, including PR168 desktop quit behavior and PR200 installer admission hardening. Original contributor commits remain in the ancestry. No release, device installation, signing configuration change, or physical device operation occurred. + +The first normal merge is `13973d823c162815b46321442e9b1de636c7858d`, with parents mobile `60a1934` and main `40ba983`. Its only textual conflict was the Unreleased changelog: both branches' entries were retained, and the quit setting was explicitly labeled Desktop. The second normal merge of `ae0436e4` was automatic. Installer and quit production sources match reviewed main exactly. The mobile tracked tree remains unchanged from its parent. + +Shared driver compatibility was inspected: leased drivers retain mobile byte-stream forwarding; unsupported spawn remains the default; WirelessAdb inherits the no-server stop default. A core regression checks both state and snapshot stop paths skip dispatch for a driver without a server. Desktop owns the quit hook and setting; Android has no shared-server stop hook. + +## Historical local validation + +On the combined source, workspace formatting and clippy with warnings denied passed. Full serial workspace tests passed: core 489, desktop 109 (one preexisting live test ignored), mobile Rust 26, license 2, registry 9. Mobile Svelte check had zero errors/warnings, production build passed, and all 326 tests passed. Desktop Svelte check had zero errors/warnings, production build passed, and all 24 frontend test scripts passed. All 34 real-command-layer E2E scenarios passed against SimulatedAdb only; opener/install/update effects are stubbed. No physical devices were queried. + +The ARM64 Android debug APK compiled with existing JDK 17, SDK, NDK 28.2.13676358 and offline cached dependencies. APK SHA256: `9688e6a76902bca3bf0bef445693ba3396c341b72906c0c730e56da554c8d139`. The ignored local APK is not published. Upstream Kotlin/Gradle deprecation warnings remain. + +Rust used two build jobs, incremental compilation off, and dev/test debug info disabled after the initial parallel build exhausted disk. Only this checkout's generated target cache was removed. Two existing fake-process PID preconditions failed under initial parallel load; the final combined workspace passed unchanged serially, including all eight `adb::driver::tests::process_lifetimes` fixtures recorded in [`rust.txt`](evidence/mobile-main-integration-2026-10-17/rust.txt). No deadlines were relaxed. Vendor tests passed 54 with one ignored after allowing their loopback mock listeners; vendor code is unchanged by the final main merge. + +Desktop validation used an existing installed cache copied locally without installing dependencies: Kit 2.61.1 versus locked 2.70.3 and Vite 6.4.2 versus locked 6.4.4. Svelte 5.55.9 and TypeScript 5.6.3 match. Mobile cache versions match its lock. At the time of local validation, exact-lock CI and cross-platform checks remained required; these local results alone did not establish Windows/Linux success. The subsequent exact-head CI receipt below closes that gate without changing the local environment receipt. + +Both gallery GIFs were regenerated (16 frames per theme) for the desktop setting. Representative [dark Devices](evidence/mobile-main-integration-2026-10-17/devices-dark.png), [light Devices](evidence/mobile-main-integration-2026-10-17/devices-light.png), and [Logs](evidence/mobile-main-integration-2026-10-17/logs-dark.png) frames use simulated data. PR200 changes no frontend source, so these remain current. [Validation logs](evidence/mobile-main-integration-2026-10-17/) contain the combined checks; inherited PR200 raw evidence is preserved verbatim, including trailing blank lines. + +## Subsequent review and merge receipt + +The preceding local validation is a historical receipt for integration head `93e9d46bed9935cd60ac84f0f0535fae320f58ea`, not a fresh run on later mobile commits. [Final independent source review](https://github.com/bryanroscoe/shield_optimizer/pull/201#issuecomment-6233713669) approved that exact head; [all six exact-lock/platform CI jobs](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39030576353) passed. [PR201](https://github.com/bryanroscoe/shield_optimizer/pull/201) merged as `d14363bc76c05f2342289d9503e95175b2d44da2`. This closes its review/exact-lock gate while preserving the disclosed local-cache provenance. Documentation reconciliation does not rerun or replace those logs. + +## Remaining acceptance gates + +Independent review and exact-lock CI were completed for PR201 as linked above. Real phone/TV authentication, reconnect, remote and Android lifecycle checks remain physical-device gates. Desktop opt-in shutdown with real ADB, other clients, updater/restart, custom servers, and Windows/macOS/Linux remains a physical/platform acceptance gate. Simulated tests and a successful debug compile do not prove those behaviors. diff --git a/docs/evidence/mobile-main-integration-2026-10-17/android-build.txt b/docs/evidence/mobile-main-integration-2026-10-17/android-build.txt new file mode 100644 index 00000000..59018e1d --- /dev/null +++ b/docs/evidence/mobile-main-integration-2026-10-17/android-build.txt @@ -0,0 +1,51 @@ + Info Using installed NDK: /Users/bryanroscoe/Android/sdk/ndk/28.2.13676358 + Info Looking up installed tauri packages to check mismatched versions... + Running beforeBuildCommand `npm run build` +npm notice run atv-optimizer-mobile@0.1.0 build +npm notice run vite build +vite v6.4.3 building for production... +transforming... +✓ 196 modules transformed. +rendering chunks... +computing gzip size... +build/index.html 0.47 kB │ gzip: 0.30 kB +build/assets/Geist-Variable-Bj2R_7yk.woff2 69.65 kB +build/assets/GeistMono-Variable-Dispecij.woff2 71.37 kB +build/assets/material-symbols-rounded-BwUW-B6-.woff2 123.34 kB +build/assets/index-CESwICGG.css 115.17 kB │ gzip: 17.35 kB +build/assets/notices.generated-B-XobKCb.js 338.84 kB │ gzip: 33.32 kB +build/assets/index-D-BjJy4N.js 379.86 kB │ gzip: 121.03 kB +✓ built in 1.38s + Blocking waiting for file lock on build directory + Compiling shield-optimizer-core v2.3.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/crates/core) + Compiling atv-optimizer-mobile v0.1.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/src-tauri) + Finished `dev` profile [unoptimized] target(s) in 21.85s + Info symlinking lib "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" in jniLibs dir "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/src-tauri/gen/android/app/src/main/jniLibs/arm64-v8a" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "libandroid.so" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "libdl.so" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "liblog.so" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "libm.so" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "libc.so" + Info symlink at "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/src-tauri/gen/android/app/src/main/jniLibs/arm64-v8a/libatv_optimizer_mobile_lib.so" points to "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" +ATV Optimizer: release signing is not configured — the release APK/AAB will be UNSIGNED and Play will reject it. Create gen/android/keystore.properties (storeFile/storePassword/keyAlias/keyPassword) or set ATVOPT_KEYSTORE_PATH, ATVOPT_KEYSTORE_PASSWORD, ATVOPT_KEYSTORE_ALIAS and ATVOPT_KEYSTORE_KEY_PASSWORD. See mobile/RELEASE.md. +npm notice run atv-optimizer-mobile@0.1.0 tauri +npm notice run tauri android android-studio-script --target aarch64 + Info connection; remote_addr=127.0.0.1:62382 conn_id=0 + Info Using installed NDK: /Users/bryanroscoe/Android/sdk/ndk/28.2.13676358 + Finished `dev` profile [unoptimized] target(s) in 0.51s + Info symlinking lib "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" in jniLibs dir "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/src-tauri/gen/android/app/src/main/jniLibs/arm64-v8a" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "libandroid.so" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "libdl.so" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "liblog.so" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "libm.so" + Info "/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/target/aarch64-linux-android/debug/libatv_optimizer_mobile_lib.so" requires shared lib "libc.so" + +[Incubating] Problems report is available at: file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/src-tauri/gen/android/build/reports/problems/problems-report.html + +Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0. + +You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins. + +For more on this, please refer to https://docs.gradle.org/8.14.3/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation. + Finished 1 APK at: + /Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/src-tauri/gen/android/app/build/outputs/apk/universal/debug/app-universal-debug.apk diff --git a/docs/evidence/mobile-main-integration-2026-10-17/desktop-tests.json b/docs/evidence/mobile-main-integration-2026-10-17/desktop-tests.json new file mode 100644 index 00000000..eb2dd138 --- /dev/null +++ b/docs/evidence/mobile-main-integration-2026-10-17/desktop-tests.json @@ -0,0 +1,98 @@ +[ + { + "name": "test:app-files-catalog", + "code": 0 + }, + { + "name": "test:pairing-flow", + "code": 0 + }, + { + "name": "test:adb-errors", + "code": 0 + }, + { + "name": "test:sideload-scan", + "code": 0 + }, + { + "name": "test:device-list-rows", + "code": 0 + }, + { + "name": "test:stop-adb-on-quit", + "code": 0 + }, + { + "name": "test:release-notes", + "code": 0 + }, + { + "name": "test:version-compare", + "code": 0 + }, + { + "name": "test:update-notes-dialog", + "code": 0 + }, + { + "name": "test:notes-seen", + "code": 0 + }, + { + "name": "test:report-bug", + "code": 0 + }, + { + "name": "test:app-report", + "code": 0 + }, + { + "name": "test:remote-paste", + "code": 0 + }, + { + "name": "test:memory-safety", + "code": 0 + }, + { + "name": "test:recommendation-labels", + "code": 0 + }, + { + "name": "test:optimize-plan", + "code": 0 + }, + { + "name": "test:playback-shell", + "code": 0 + }, + { + "name": "test:optimize-safety-all-rows", + "code": 0 + }, + { + "name": "test:shell-acknowledge", + "code": 0 + }, + { + "name": "test:icon-alignment", + "code": 0 + }, + { + "name": "test:launcher-rows", + "code": 0 + }, + { + "name": "test:files-drop", + "code": 0 + }, + { + "name": "test:context-menu", + "code": 0 + }, + { + "name": "test:device-logs", + "code": 0 + } +] \ No newline at end of file diff --git a/docs/evidence/mobile-main-integration-2026-10-17/devices-dark.png b/docs/evidence/mobile-main-integration-2026-10-17/devices-dark.png new file mode 100644 index 00000000..2681d6d8 Binary files /dev/null and b/docs/evidence/mobile-main-integration-2026-10-17/devices-dark.png differ diff --git a/docs/evidence/mobile-main-integration-2026-10-17/devices-light.png b/docs/evidence/mobile-main-integration-2026-10-17/devices-light.png new file mode 100644 index 00000000..52fbd2a0 Binary files /dev/null and b/docs/evidence/mobile-main-integration-2026-10-17/devices-light.png differ diff --git a/docs/evidence/mobile-main-integration-2026-10-17/e2e.txt b/docs/evidence/mobile-main-integration-2026-10-17/e2e.txt new file mode 100644 index 00000000..453122c8 --- /dev/null +++ b/docs/evidence/mobile-main-integration-2026-10-17/e2e.txt @@ -0,0 +1,204 @@ +npm notice run atv-optimizer-v2@2.3.0 e2e +npm notice run node e2e/run.mjs +[e2e] building e2e_server (cargo, feature e2e)… + Compiling shield-optimizer-core v2.3.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/crates/core) + Compiling shield-optimizer-v2 v2.3.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/src-tauri) + Finished `dev` profile [unoptimized] target(s) in 12.67s +[e2e] server http://127.0.0.1:62086 ui http://127.0.0.1:62094 + +▶ devices-scan-lan + · 01 empty list + · 02 scan connected the Shield and found an unauthorized box + · 03 unauthorized row explains the on-TV prompt + ✓ devices-scan-lan (2.2s) + +▶ devices-two-transports-forget + · 01 one row for two transports + · 02 forget removed the row + ✓ devices-two-transports-forget (1.3s) + +▶ devices-alias-route + · 01 the mDNS alias opened the collapsed row under its ip:port key + ✓ devices-alias-route (1.4s) + +▶ devices-reattach-message + · 01 forget warns that adb will re-attach it + · 02 the row came back and the app says why + ✓ devices-reattach-message (4.0s) + +▶ devices-tv-evidence + · 01 unconfirmed TV opens, not-a-TV is labelled + · 02 open anyway asks first + · 03 tools opened on the phone + ✓ devices-tv-evidence (1.3s) + +▶ devices-pair-pin-auto-connect + · 01 pair form filled + · 02 paired, then connected on the advertised connect port + ✓ devices-pair-pin-auto-connect (1.5s) + +▶ devices-connect-errors + · 01 a typo'd IP is rejected before adb runs + · 02 an unreachable address gets an explanation + ✓ devices-connect-errors (1.1s) + +▶ health-cards-and-memory + · 01 health cards + · 02 memory rows: full names, pids, suggestions + · 03 a disabled app leaves the memory table + ✓ health-cards-and-memory (1.8s) + +▶ optimize-select-all-safe-run-restore + · 01 plan built from the device + · 02 select all safe armed 12 rows + · 03 plan ran + · 04 restore plan armed + · 05 restore ran + ✓ optimize-select-all-safe-run-restore (2.4s) + +▶ app-report-state-during-post-optimize-resync + · 01 report state is unknown during the post-run re-read + · 02 report state is the post-run state once the re-read lands + ✓ app-report-state-during-post-optimize-resync (7.4s) + +▶ app-report-state-after-direct-optimize + · 01 report state is unknown after a run with no inventory to re-read + ✓ app-report-state-after-direct-optimize (1.3s) + +▶ app-report-state-during-optimize-run + · 01 report state is unknown while the run is in progress + · 02 report state is the post-run state once the run finishes + ✓ app-report-state-during-optimize-run (9.3s) + +▶ app-list-keep-per-hardware-id + · 01 kept com.amazon.amazonvideo.livingroom + · 02 kept after reload + · 03 kept at another address of the same hardware id + · 04 a different hardware id is unaffected + · 05 optimize plan defaults the kept app to Keep + ✓ app-list-keep-per-hardware-id (5.0s) + +▶ launcher-lists-home-handlers-only + · 01 only catalog launchers and real HOME handlers + ✓ launcher-lists-home-handlers-only (0.9s) + +▶ launcher-switch-through-setup-wraith + · 01 switch landed after the transient holder + ✓ launcher-switch-through-setup-wraith (2.3s) + +▶ launcher-current-is-the-role-holder-not-setup-wraith + · 01 Setup Wraith is labelled a setup helper + · 02 Monet is the current launcher after refresh + · 03 snapshot records Monet + ✓ launcher-current-is-the-role-holder-not-setup-wraith (2.3s) + +▶ launcher-set-home-refused-setter + · 01 picked Projectivy + · 02 refusal reported, nothing disabled + ✓ launcher-set-home-refused-setter (6.7s) + +▶ launcher-disable-stock-hands-home-over + · 01 Android took the request but stock still holds Home + · 02 confirm + · 03 stock disabled, Projectivy is Home + ✓ launcher-disable-stock-hands-home-over (7.2s) + +▶ launcher-takeover-verification-failure-restores-stock + · 01 verification failed and stock was restored + ✓ launcher-takeover-verification-failure-restores-stock (11.8s) + +▶ launcher-settings-refused-and-home-kept + · 01 Settings is never the takeover target + ✓ launcher-settings-refused-and-home-kept (6.5s) + +▶ setup-wraith-takeover-disables-it-and-recovery-restores-both + · 01 takeover left Setup Wraith off, with its way back + · 02 Re-enable Setup Wraith works + ✓ setup-wraith-takeover-disables-it-and-recovery-restores-both (2.0s) + +▶ setup-wraith-re-enabling-stock-re-enables-the-helper + ✓ setup-wraith-re-enabling-stock-re-enables-the-helper (1.4s) + +▶ setup-wraith-takeover-failure-restores-stock-and-the-helper + ✓ setup-wraith-takeover-failure-restores-stock-and-the-helper (0.5s) + +▶ setup-wraith-callout-states + · 01 enabled while stock is on + · 02 enabled while stock is off + · 03 one-click fix turned it off + ✓ setup-wraith-callout-states (2.2s) + +▶ setup-wraith-one-click-fix-refuses-without-a-real-launcher + ✓ setup-wraith-one-click-fix-refuses-without-a-real-launcher (0.0s) + +▶ setup-wraith-confirm-names-both-effects + · 01 confirm names both effects + ✓ setup-wraith-confirm-names-both-effects (6.5s) + +▶ setup-wraith-shield-is-unchanged + ✓ setup-wraith-shield-is-unchanged (6.5s) + +▶ snapshot-save-preview-restore + · 01 snapshot saved + · 02 preview of an unchanged device: no launcher change, real Now values + · 03 preview after changes + · 04 restored + ✓ snapshot-save-preview-restore (3.4s) + +▶ tweaks-screensaver + · 01 current screensaver read from the device + · 02 Basic Daydream + · 03 None + · 04 Restore previous + · 05 refused flag write is reported + ✓ tweaks-screensaver (2.4s) + +▶ tweaks-background-limit + · 01 Android's limit and the leftover key + · 02 leftover key removed, limit unchanged + ✓ tweaks-background-limit (1.3s) + +▶ shell-acknowledgement-gate + · 01 run gated on the acknowledgement + · 02 acknowledged, the command ran on the device + ✓ shell-acknowledgement-gate (1.2s) + +▶ report-bug-diagnostics + · 01 bundle built from the backend + ✓ report-bug-diagnostics (1.1s) + +▶ visual-smoke-dark + · 01 devices + · 02 tab overview + · 03 tab health + · 04 tab optimize + · 05 tab apps + · 06 tab launcher + · 07 tab tweaks + · 08 tab snapshot + · 09 tab sideload + · 10 tab remote + · 11 tab files + · 12 tab media + · 13 tab shell + · 14 snapshots page + ✓ visual-smoke-dark (9.9s) + +▶ visual-smoke-light + · 01 devices + · 02 tab overview + · 03 tab health + · 04 tab optimize + · 05 tab apps + · 06 tab launcher + · 07 tab tweaks + · 08 tab snapshot + · 09 tab sideload + · 10 tab remote + · 11 tab files + · 12 tab media + · 13 tab shell + · 14 snapshots page + ✓ visual-smoke-light (9.9s) + +34/34 scenarios passed. Screenshots: /Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/e2e/artifacts diff --git a/docs/evidence/mobile-main-integration-2026-10-17/logs-dark.png b/docs/evidence/mobile-main-integration-2026-10-17/logs-dark.png new file mode 100644 index 00000000..6c003f71 Binary files /dev/null and b/docs/evidence/mobile-main-integration-2026-10-17/logs-dark.png differ diff --git a/docs/evidence/mobile-main-integration-2026-10-17/mobile-tests.txt b/docs/evidence/mobile-main-integration-2026-10-17/mobile-tests.txt new file mode 100644 index 00000000..fcea2fdd --- /dev/null +++ b/docs/evidence/mobile-main-integration-2026-10-17/mobile-tests.txt @@ -0,0 +1,336 @@ +npm notice run atv-optimizer-mobile@0.1.0 test +npm notice run node --test --test-concurrency=1 tests/*.test.mjs +✔ a disabled katniss is found by package and by name, and can be re-enabled (1881.772125ms) +✔ not-installed recognised apps hide by default and can be revealed (338.002625ms) +✔ long-press opens the action menu without opening the sheet; a tap still opens it (896.723375ms) +✔ menu Disable goes through the safety lookup and confirm, never straight to the TV (443.803458ms) +✔ report opens a prefilled GitHub form through the opener (799.350917ms) +✔ without an opener, report falls back to copy and a link to copy (804.784083ms) +✔ rows, chips and labels fit a 384px screen (135.27125ms) +✔ long report keyboard wrap reveals its focused actions without moving the locked page (775.124917ms) +✔ menu Report returns focus to its original app row after closing (468.383167ms) +✔ report includes the successful APK fallback visible in the detail sheet (774.402958ms) +✔ APK report keeps exact 0 bytes, unknown data/cache and identical copied JSON (788.464ms) +✔ APK report keeps exact 1234567 bytes, unknown data/cache and identical copied JSON (794.863833ms) +✔ diskstats remains the report source when a complete measured row exists (791.209917ms) +✔ APK report keeps storage unknown for a pending fallback (780.6515ms) +✔ APK report keeps storage unknown for a failed fallback (775.017583ms) +✔ APK report keeps storage unknown for a missing fallback (766.674375ms) +✔ APK report keeps storage unknown for a negative fallback (783.187208ms) +✔ remeasure invalidates a successful APK receipt and ignores an older fallback reply (1194.357875ms) +✔ APK fallback cannot cross a same-endpoint reconnect (1206.601875ms) +✔ APK fallback cannot cross a TV switch (1225.124667ms) +✔ an APK result for a closed package cannot replace the next package measurement (1169.198416ms) +✔ system visibility composes with enabled and disabled filters (1203.124875ms) +✔ search reports empty results and can reveal or clear them (570.797834ms) +✔ filtering never mutates packages and protected-app safety stays enforced (252.797125ms) +✔ katniss is in the shared catalogue (142.952208ms) +✔ katniss is found by package and by name when enabled (0.883417ms) +✔ katniss is found by package and by name when disabled (0.098958ms) +✔ a disabled katniss is reachable from the Disabled chip so it can be re-enabled (0.063791ms) +✔ not-installed catalogue rows hide by default and are counted as hidden (0.109625ms) +✔ an unread state is unknown: shown under All, never under Enabled (0.051334ms) +✔ system packages in Everything else follow the toggle and are counted when hidden (0.065542ms) +✔ a package in both lists is listed once, as recognised (0.077083ms) +✔ validatedStates drops malformed values instead of guessing (0.117291ms) +✔ onDeviceCount leaves out only rows known to be missing (0.091583ms) +✔ installed picker hides systems by default and composes reveal with search (1168.665291ms) +✔ one chosen app makes one backup call for the expected TV and package (175.552625ms) +✔ a delayed A picker read cannot replace the newer B result (215.917708ms) +✔ a backup started on A stays owned by A when it finishes after a switch to B (207.710792ms) +✔ a stale displayed A row cannot start a backup on B (150.130542ms) +✔ a restore confirmation opened on A cannot restore onto B (170.628792ms) +✔ picker and backup failures stay explicit and device-owned (198.701041ms) +✔ disconnected state and APK-only limits are stated plainly (147.163ms) +✔ Dashboard counts enabled among installed recommended apps (1127.541667ms) +✔ Dashboard treats incomplete package state replies as unavailable (136.434417ms) +✔ a stale same-serial count cannot overwrite a newer generation (146.264458ms) +✔ zero recommended candidates opens Optional apps with Keep selected (122.972917ms) +✔ Caution defaults are recommended while Unknown defaults move to Optional (145.94425ms) +✔ protected defaults stay visible and blocked in Optional (164.583833ms) +✔ a plan containing only Unknown defaults opens Optional after safety resolves (114.307375ms) +✔ a user's tab choice survives pending safety resolution (148.024291ms) +✔ optional choices survive tabs and confirmation names every actual action (527.64175ms) +✔ protected and unresolved optional actions remain blocked while Unknown is explicit (142.530333ms) +✔ Restore keeps Recommended and All curated tabs with its prior defaults (164.25625ms) +✔ differing Unknown reasons stay on their own rows (115.201916ms) +✔ Safe defaults are recommended, selected and actionable (115.943917ms) +✔ the confirm summary names the Safe verdict rather than calling it Caution (129.223042ms) +✔ Diagnostics reports the TV's Android version and hardware details (1068.152375ms) +✔ Dashboard shows the Android version beside the address (120.318541ms) +✔ A property the TV did not report reads as unknown, never as a neighbour's value (142.470125ms) +✔ A device with no profile says so instead of showing blank rows (121.958083ms) +✔ Missing health data leaves the totals blank rather than inventing them (119.985792ms) +✔ Diagnostics header fits 320px at 100% text with every control usable (1245.10925ms) +✔ Diagnostics header fits 384px at 100% text with every control usable (270.377833ms) +✔ Diagnostics header fits 320px at 200% text with every control usable (265.128375ms) +✔ Diagnostics header fits 384px at 200% text with every control usable (241.666584ms) +✔ non-Shield and disconnected headers keep the applicable controls without overflow (259.527167ms) +✔ collapses repeated advertisements and separates saved from new hosts (1.665875ms) +✔ the live endpoint is the only connected row (0.157417ms) +✔ a connected TV that does not advertise stays one connected saved row (0.099042ms) +✔ only the saved TV whose id is live claims a shared live endpoint (#115) (0.164208ms) +✔ two id-less saved TVs sharing the live endpoint never both claim connected (#146) (0.1405ms) +✔ a stale advert never names the live row after a different saved TV (#115) (0.078917ms) +✔ a stale advert at an old host never shows the live TV a second time (#115) (0.093458ms) +✔ a legacy advert sharing the live TV's serial prefix is not dropped as stale (#149) (0.061375ms) +✔ a live TV that reports no id is never named after a stale advert at its address (#115) (0.079541ms) +✔ dropping a stale advert keeps the TV now advertising from that address (#115) (0.103625ms) +✔ saved TVs sharing an endpoint that answers unverified each keep their row (#115) (0.096958ms) +✔ an address change reports a new host and a saved endpoint that is absent (0.064ms) +✔ a rotated port keeps the saved endpoint truthful instead of calling it offline (0.063959ms) +✔ a host advertising only a pairing service is not treated as answering (0.045875ms) +✔ two saved identities at one endpoint stay two real rows (#115) (0.054333ms) +✔ a saved TV that shared an address with one that moved stays in the scan (#115) (0.050834ms) +✔ a stored placeholder id is not a wildcard for serial-less adbd (#116) (0.060417ms) +✔ a saved id only matches its own advertised serial, not a longer one (#117) (6.796709ms) +✔ distinct saved endpoints on one host stay distinct rows (0.050708ms) +✔ malformed advertisements are dropped (0.055875ms) +✔ instance names are only used when they are unambiguous and not adb ids (0.035541ms) +✔ legacy connect ports are the only ones flagged as needing no pairing code (0.052583ms) +✔ an advertised serial verifies a saved TV and names it (0.036417ms) +✔ a verified serial recognizes a saved TV that moved to a new address (0.03525ms) +✔ a serial that matches no saved TV leaves the row unclaimed (0.035ms) +✔ two TVs advertising only adb ids are each named from their own serial (0.046333ms) +✔ rows are ordered by usability then recency, not by mDNS answer order (0.060167ms) +✔ matrix covers every registered mobile command and opener exactly once (222.703875ms) +✔ store observations never unlock; stale, duplicate and ack events are isolated (0.349292ms) +✔ errors/cancel preserve access; revocation preserves independent legacy source (0.083208ms) +✔ cancel and errors never create access; acknowledgment cannot precede verification (0.084666ms) +✔ simulated UI keeps recovery usable through pending/error/revocation (1036.3025ms) +✔ screenshot destination, bytes and Close are truthful at 384px / 100% text (1122.135708ms) +✔ screenshot destination, bytes and Close are truthful at 320px / 200% text (188.736667ms) +✔ screenshot failure creates no preview or false saved result and preserves earlier saved bytes (266.189125ms) +✔ file-copy success identifies the actual private destination and failure adds no copied row (166.17975ms) +✔ report copy and oversized GitHub fallback use preview text and never claim a saved file (826.902083ms) +✔ report clipboard failure exposes the selectable preview without false copy success (765.489292ms) +✔ diagnostic JSON and debug log Copy actions state clipboard-only output and report failures (265.446292ms) +✔ file results belong to the live connection that started them (145.442375ms) +✔ reconnecting to the same endpoint invalidates an old file action (0.117458ms) +✔ a lost connection cannot accept a file result or delete confirmation (0.046542ms) +✔ the catalog is desktop's file, not a copy (221.36125ms) +✔ a search that could not run never summarizes as no matches (0.309583ms) +✔ delete is only offered strictly inside /sdcard (0.174833ms) +✔ Find SmartTube backups searches every catalog dir and copies a hit to the phone (927.882625ms) +✔ an unsearched directory reads as a failed search, never as no matches (#86) (192.210958ms) +✔ a rejected search drops the older answer instead of showing it as current (180.339083ms) +✔ delete asks first, sends the exact /sdcard path, and reloads the folder (533.918041ms) +✔ no delete control outside /sdcard (134.817125ms) +✔ a delete confirmed after a TV switch is not sent to the new TV (187.442125ms) +✔ reconnecting to the same endpoint clears an old delete confirmation (183.337375ms) +✔ a copy completing after a TV switch does not mark the new TV's row copied (219.116ms) +✔ RAM uses explicit used/total or available complement; zero/full are real values (216.85775ms) +✔ missing and invalid memory is not painted as zero/full (0.149125ms) +✔ storage retains reported percentages and distinguishes missing/invalid from zero (0.085916ms) +✔ temperature comparison uses only a matching sensor own threshold (0.434834ms) +✔ reported device status is independent of hottest sensor status; no temperature-derived grade (0.182666ms) +✔ both visible bars mean used, share neutral styling and show 0/50/100 honestly (869.08625ms) +✔ legacy100C stays unknown; a device-reported critical status is shown even at30C (165.809875ms) +✔ below-threshold light/moderate readings do not describe throttling onset or infer HAL severity (0.173333ms) +✔ threshold provenance stays unspecified for hardware, HIDL-like and unknown sources (0.111625ms) +✔ visible thresholds retain reported status and never claim onset or a severity without provenance (144.5805ms) +✔ checked-in Shield meminfo keeps explicit Used despite non-complementary Free accounting (0.8775ms) +✔ Shield accounting renders explicit used/free without changing the source report (123.336167ms) +✔ Setup Wraith reads off / on / risk from the launcher rows (236.332042ms) +✔ a takeover turns Setup Wraith off only when an enabled stock names it (0.366792ms) +✔ Disable stock launcher stays off until the picked app is Home or stock is in the way (0.2835ms) +✔ the stock-disable message never claims what wasn't verified or re-read (0.138208ms) +✔ picker rows hide system apps, search name and package, dedupe and sort (10.6205ms) +✔ source site links are offered only for http(s) URLs (0.188125ms) +✔ Setup Wraith on with stock off warns and turns it off, then re-reads the TV (868.930625ms) +✔ Setup Wraith off offers Re-enable through enable_package (147.951083ms) +✔ a not-ok default switch re-reads the TV and offers its diagnostics (206.305167ms) +✔ set any app as Home reports Android's answer, then unlocks the separate stock step (242.461042ms) +✔ Save snapshot first leaves stock alone when the snapshot fails (219.003375ms) +✔ a locked Set as Home opens the paywall and calls nothing else (232.175ms) +✔ source site hands the launcher's page to the phone's browser, not the app (137.282208ms) +✔ source site copies the link when the browser can't be opened (127.852666ms) +✔ after opening the Play Store the list re-reads until the launcher is installed (159.16375ms) +✔ same-endpoint reconnect cancels confirmations and late launcher results (429.77275ms) +✔ lost connection removes actionable rows and reconnect discards an old list reply (247.868083ms) +✔ old snapshot completion cannot unlock a newer Home action after reconnect (378.703542ms) +✔ stock takeover dialog locks scrolling, traps focus and Escape cancels without a mutation (310.804209ms) +✔ Setup Wraith row uses the guarded helper command and refreshes confirmed success (167.609459ms) +✔ helper row preserves guarded failure: rolled back (167.573209ms) +✔ helper row preserves guarded failure: rollback unconfirmed (165.107333ms) +✔ late helper-row result cannot overwrite a fresh same-endpoint reconnect (582.837959ms) +✔ late helper-row result cannot overwrite a fresh TV switch (538.707792ms) +✔ ordinary stock row keeps its launcher disable command (160.227ms) +✔ same-update replacement preserves document lock and original opener across an empty stack (1065.202708ms) +✔ async resolve settles genuine close before any later replacement (116.904792ms) +✔ async cancel settles genuine close before any later replacement (105.793125ms) +✔ async reject settles genuine close before any later replacement (101.735417ms) +✔ genuine empty close clears provenance before an unrelated modal opens (105.576792ms) +✔ nested out-of-order teardown and replacement retain the document opener (106.745459ms) +✔ superseded close callbacks cannot steal focus from a new top modal (117.887833ms) +✔ removed opener and duplicate teardown do not resurrect focus or leak a lock (105.042166ms) +✔ alertdialog without enabled controls receives focus and Escape releases the genuine close (115.039375ms) +✔ closing the focused child then a non-top ancestor restores the surviving middle modal (130.078666ms) +✔ Optimize recommends exactly what desktop pre-selects (172.171875ms) +✔ an uninstall the store can't give back runs as Disable (0.135ms) +✔ review pill only for resolved, unprotected review rows (0.069875ms) +✔ plan search matches name or package, case-insensitively (0.067125ms) +✔ row safety never reads unresolved as a verdict (0.692625ms) +✔ the sheet sees the plan's state and catalog entry (0.066291ms) +✔ Health: a confirmed catalog app gets the App List's recommendation (0.143833ms) +✔ Health: a name no installed package carries is a process, not an app (0.085583ms) +✔ Health: an unreadable installed list confirms nothing and says so (0.096208ms) +✔ Health: an installed package outside the catalog shows its verdict (0.095333ms) +✔ snapshot preview: Now is what the TV reported, and matching settings read Already set (0.173041ms) +✔ snapshot preview: launcher change, skipped launcher, and an unset Now (0.074792ms) +✔ snapshot preview summary matches desktop's wording (0.062625ms) +✔ Optimize Back returns to the screen that opened it (1143.347166ms) +✔ Optimize search filters by name and package id (143.416083ms) +✔ tapping an Optimize row opens the app sheet, not a toggle (184.41725ms) +✔ Safe to remove and review chips stay on one line at 384px (151.979083ms) +✔ Health suggests per row: app recommendation, process, and package verdicts (190.165917ms) +✔ Health never calls a package-shaped name Not an app when installed apps can't be read (127.483ms) +✔ Snapshots: Beta, Preview restore, and a real Now column (175.058584ms) +✔ modal sheet blocks background wheel/edge scroll and restores position, focus and existing styles repeatedly (938.348791ms) +✔ modal nested confirmation keeps the page locked and Escape/Tab affect only the top layer (509.787584ms) +✔ modal backdrop, screen teardown and disconnect release the document without leaving a lock (270.031792ms) +✔ modal paywall dismissal retains the underlying app sheet lock (503.943625ms) +✔ modal lock survives out-of-order owner teardown and restores the original opener after its child (148.45775ms) +✔ modal long-sheet keyboard wrap reveals last action and Close without moving the background (457.652292ms) +✔ Free diagnostics reports are explicit, honest and per-interface (1169.331375ms) +✔ resource_sample ignores stale resolve after reconnect to the same serial (166.183667ms) +✔ resource_sample ignores stale reject after reconnect to the same serial (182.672667ms) +✔ media_report ignores stale resolve after reconnect to the same serial (188.526625ms) +✔ media_report ignores stale reject after reconnect to the same serial (171.696708ms) +✔ resource sampling admits one request and no live-refresh polling (182.426792ms) +✔ manual audio toggle preserves unknown and newer encodings through readback (184.196416ms) +✔ playback errors do not replace existing health metrics (155.593583ms) +✔ Free audio writes use the existing paywall and do not fabricate readback (148.511125ms) +✔ late audio mutation failure cannot overwrite another TV's state (158.477958ms) +✔ report fidelity and wrapping at 320px with 100% text (189.936042ms) +✔ report fidelity and wrapping at 384px with 100% text (199.5105ms) +✔ report fidelity and wrapping at 320px with 200% text (201.648917ms) +✔ empty reports and failed rereads retain honest unavailable states and keyboard access (184.606958ms) +✔ the shared sideload catalog is served to the webview (178.511333ms) +✔ uninstall is only recommended when the store can give the app back (0.86625ms) +✔ no recommendation label ever says Remove (0.183ms) +✔ unknown and unresolved safety never read as a default action (0.059541ms) +✔ safety labels match the desktop vocabulary (0.069084ms) +✔ an app report carries no device identifiers and is labelled mobile (0.963458ms) +✔ keeps mixed saved and new TVs and deduplicates repeated advertisements (8.573791ms) +✔ moves one matching hardware identity across an address change (0.195625ms) +✔ moves one matching hardware identity across a port change (0.122ms) +✔ keeps different hardware identities that claim the same address (0.12475ms) +✔ does not merge a saved id-less row into a live identified TV (0.1875ms) +✔ does not merge an id-less live TV into a saved identified row (0.196958ms) +✔ matches id-less rows only at the exact endpoint (0.186208ms) +✔ forgets identified TVs by id and id-less TVs by exact endpoint (0.210458ms) +✔ returns cached names only for an unambiguous or matching identity (0.145ms) +✔ a reused address never lends an identified TV's name to a TV that reports no id (#117) (0.107459ms) +✔ a stored placeholder id becomes an id-less row with its own stable key, never merged with another (#116, #146) (0.166916ms) +✔ reconnecting a TV that reports a placeholder id refreshes its row instead of appending (#116) (0.086083ms) +✔ never guesses which of two id-less TVs at one endpoint just reconnected (#146) (0.203792ms) +✔ sorts by lastUsed and truncates the oldest saved rows (0.241084ms) +✔ ignores corrupt records and normalizes recoverable stored values (0.082792ms) +✔ builds stable identity keys for host collisions and identical endpoints (0.079667ms) +✔ a selected reconnect token activates only its saved row (0.0495ms) +✔ matches the current TV by verified id or an exact id-less endpoint (0.059334ms) +✔ only an unambiguous match counts as the live connection (#146) (0.07525ms) +✔ forgets the exact selected identity at an identical endpoint (0.11725ms) +✔ auto-dials only when the saved list truly has one entry (0.052166ms) +✔ a lone id-less match stores a fingerprint from the live device's properties (#154) (0.75575ms) +✔ a different model or manufacturer at a saved id-less row's address is not silently inherited (#154) (0.174209ms) +✔ a manufacturer-only disagreement also counts as a mismatch (#154) (0.080542ms) +✔ an empty saved fingerprint (an older row, or a device that reported nothing) never blocks a match (#154) (0.079417ms) +✔ a matching fingerprint still refreshes the row normally, and is never treated as proof (#154) (0.065083ms) +✔ a hardware-identified reconnect is never second-guessed by fingerprint (#154) (0.075125ms) +✔ a stored fingerprint survives a read/normalize round trip and sanitizes stray fields (0.060417ms) +✔ hardware Back and direct navigation keep Optimize cancellable (1528.532333ms) +✔ Tweaks is a bottom tab and Optimize is a pushed screen; Back unwinds both to Home (194.33525ms) +✔ remote queue is discarded after navigation and a TV switch (216.301125ms) +✔ ordinary remote taps cannot accumulate an unlimited backlog (175.557583ms) +✔ failed health refresh preserves the last report and exposes the error (114.728125ms) +✔ one failed recovery stays lost until an explicit retry (117.713291ms) +✔ a silent recovery onto a different device does not inherit the old saved row or its name (#154 follow-up) (116.810583ms) +✔ a silent recovery's different-device note shows immediately on whatever screen is already open, and clears itself (#154 follow-up) (4128.47175ms) +✔ the Devices screen hands its different-device note on to Dashboard instead of losing it on navigate (#154 follow-up) (4146.409667ms) +✔ canceled connect cannot update the session when its old reply arrives (123.127ms) +✔ stale reboot completion cannot disconnect a newer session (111.118083ms) +✔ cold single saved TV waits for its profile, then opens a clean dashboard stack (111.984416ms) +✔ cold multi-TV launch waits for a choice and opens the chosen TV (198.795166ms) +✔ cold saved TV missing profile stays on onboarding (113.802292ms) +✔ cold saved TV wrong profile stays on onboarding (113.239084ms) +✔ cold saved TV rejected request stays on onboarding (112.228708ms) +✔ authorization failure stays honest and a deliberate retry can open Dashboard (167.501625ms) +✔ explicit disconnect suppresses automatic saved-TV redial (169.978667ms) +✔ first-time connection keeps the connected interstitial (172.703166ms) +✔ add-TV connection keeps the connected interstitial (169.582959ms) +✔ saved TV success opens its dashboard once without canceling the connection (133.711083ms) +✔ leaving onboarding cancels a saved TV connection that is still pending (172.877959ms) +✔ cancel and retry of the same saved TV ignores the first completion (245.309333ms) +✔ a late transport error from A cannot start recovery on B (172.175583ms) +✔ a health load that lands after its profile vanished cannot wedge later loads (#118) (105.885042ms) +✔ cancelling a saved-TV reconnect never reports a connection failure (#118) (195.613042ms) +✔ Diagnostics keeps its safety badges when two top apps swap rank (#118) (172.787875ms) +✔ diagnostics: disconnect requires a new sheet selection before any action (1814.541625ms) +✔ diagnostics: reconnect requires a new sheet selection before any action (822.376917ms) +✔ diagnostics: switch requires a new sheet selection before any action (832.698ms) +✔ diagnostics: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (921.24675ms) +✔ diagnostics: Disable nested confirmation is cleared by same-endpoint reconnect (525.377292ms) +✔ diagnostics: Uninstall nested confirmation is cleared by same-endpoint reconnect (492.299666ms) +✔ diagnostics: paywall and pending measurements cannot follow the opening identity (580.448458ms) +✔ optimize: disconnect requires a new sheet selection before any action (807.960417ms) +✔ optimize: reconnect requires a new sheet selection before any action (806.30225ms) +✔ optimize: switch requires a new sheet selection before any action (832.561958ms) +✔ optimize: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (862.0285ms) +✔ optimize: Disable nested confirmation is cleared by same-endpoint reconnect (475.375083ms) +✔ optimize: Uninstall nested confirmation is cleared by same-endpoint reconnect (484.449959ms) +✔ optimize: paywall and pending measurements cannot follow the opening identity (547.374834ms) +✔ diagnostics: a failed reconnect or transient liveness loss still invalidates the opening (262.956083ms) +✔ optimize: a failed reconnect or transient liveness loss still invalidates the opening (246.496667ms) +✔ Free users see every current value, including hooks, mic and screensaver (1272.285ms) +✔ Turning the hooks back on is free; turning them off opens the paywall (240.207292ms) +✔ hooks row is hidden when the package is missing and on non-Shield read failures (331.705834ms) +✔ Assistant button revokes the mic through set_app_permission and re-reads (154.340083ms) +✔ screensaver: Basic Daydream, Off, then Restore previous brings the vendor one back (228.950541ms) +✔ a refused screensaver flag stops before the component is written (144.457709ms) +✔ Dashboard has one Optimize call to action and Tune tiles for Tweaks and Launcher (189.523ms) +✔ Pro dashboard drops the PRO tags (125.992167ms) +✔ More is titled More, lists Optimize, and no longer duplicates the Tweaks tab (166.016333ms) +✔ endpoint reuse never restores another TV's screensaver and interrupted writes stop (405.921667ms) +✔ verified TV retains originals across a rotated port, replacement hardware does not (227.296041ms) +✔ a reconnect between screensaver writes prevents the second write (281.811167ms) +✔ current gap: Pro loss blocks restoring an app-changed screensaver even with the original known (199.370125ms) +✔ Free users retain emergency recovery without an unlock or payment (183.553542ms) +✔ scaling readback matches both resolution and density and clears stale selection on failure (243.934667ms) +✔ Custom DNS editing does not claim the TV has applied it; failed read clears selection (218.571917ms) +✔ equivalent Android animation number formats select the same speed (176.876166ms) +✔ DNS confirmed changes survive navigation and reconnect, while unset stays explicit (212.364125ms) +✔ Daydream component refuse never enables an off screensaver with previous component (176.07325ms) +✔ Daydream component reject never enables an off screensaver with previous component (186.056458ms) +✔ Daydream component refuse never enables an off screensaver with unset component (157.43125ms) +✔ Daydream component reject never enables an off screensaver with unset component (173.897667ms) +✔ Basic Daydream does not enable after a refused component; failed enable retains off with configured component (199.582417ms) +✔ Restore previous does not enable after a refused component; failed enable retains off with configured component (230.177542ms) +✔ Off keeps the switch disabled if component clearing fails (162.5855ms) +✔ a reconnect after the Daydream component write prevents enabling (188.089584ms) +✔ screensaver names preserve raw known, custom, disabled and missing component states (1261.616375ms) +✔ catalog failure stays custom and late old catalog names cannot cross connections (247.659875ms) +✔ density-only readback shows the override instead of physical density or device default (144.480583ms) +✔ density-only matching preset, absent size and reset readbacks retain their distinct states (273.806542ms) +✔ scaling preset row fits 320px at 100% text and preserves controls (1426.439958ms) +✔ scaling preset row fits 384px at 100% text and preserves controls (1398.531583ms) +✔ scaling preset row fits 320px at 200% text and preserves controls (1391.284167ms) +✔ scaling preset row fits 384px at 200% text and preserves controls (1409.683167ms) +✔ records uncatalogued packages and unresolved native processes distinctly (2.17475ms) +✔ deduplicates repeat polls, saturates counts, and separates device context (0.195791ms) +✔ drops stale scheduled observations after a device changes (0.096583ms) +✔ rejects paths, addresses, control strings, and malformed package tokens (0.117625ms) +✔ bounds record count and serialized byte size while marking truncation (0.272125ms) +✔ fails harmlessly for corrupt or full storage (0.138417ms) +✔ exports an immutable exact snapshot with allowlisted fields only (0.190875ms) +✔ clear prevents a queued old save from resurrecting records (0.244417ms) +✔ clear failures remain visible and do not claim deletion (0.146708ms) +ℹ tests 326 +ℹ suites 0 +ℹ pass 326 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 101016.817708 diff --git a/docs/evidence/mobile-main-integration-2026-10-17/rust.txt b/docs/evidence/mobile-main-integration-2026-10-17/rust.txt new file mode 100644 index 00000000..858615a4 --- /dev/null +++ b/docs/evidence/mobile-main-integration-2026-10-17/rust.txt @@ -0,0 +1,719 @@ + Checking shield-optimizer-core v2.3.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/crates/core) + Checking atv-optimizer-mobile v0.1.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/src-tauri) + Checking shield-optimizer-v2 v2.3.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/src-tauri) + Checking atvopt-license v0.1.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/tools/atvopt-license) + Checking registry-triage v0.1.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/tools/registry-triage) + Finished `dev` profile [unoptimized] target(s) in 6.67s + Compiling shield-optimizer-core v2.3.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/crates/core) + Compiling atv-optimizer-mobile v0.1.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/src-tauri) + Compiling shield-optimizer-v2 v2.3.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/src-tauri) + Compiling registry-triage v0.1.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/tools/registry-triage) + Compiling atvopt-license v0.1.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/tools/atvopt-license) + Finished `test` profile [unoptimized] target(s) in 14.81s + Running unittests src/lib.rs (target/debug/deps/atv_optimizer_mobile_lib-354c841cea3930c4) + +running 26 tests +test file_commands::tests::complete_bundle_round_trips ... ok +test file_commands::tests::concurrent_downloads_publish_distinct_complete_files ... ok +test file_commands::tests::delete_target_must_be_a_top_level_backup ... ok +test file_commands::tests::download_names_dedupe_instead_of_clobbering ... ok +test file_commands::tests::failed_download_removes_partial_output_and_preserves_existing_files ... ok +test file_commands::tests::parses_every_split_apk_path ... ok +test file_commands::tests::rejects_unsafe_reported_apk_path ... ok +test file_commands::tests::restore_path_must_stay_inside_backup_root ... ok +test license_tests::persisted_valid_license_round_trips ... ok +test license_tests::persistence_failure_is_reported ... ok +test limited_writer::tests::preserves_destination_errors ... ok +test limited_writer::tests::rejects_growth_without_writing_past_the_limit ... ok +test remote_lifecycle::tests::current_background_epoch_can_claim_cleanup_once ... ok +test remote_lifecycle::tests::newer_suspend_invalidates_an_older_timer ... ok +test remote_lifecycle::tests::resume_invalidates_pending_cleanup ... ok +test scrcpy_resource::tests::embedded_server_matches_audited_release ... ok +test scrcpy_resource::tests::materialize_creates_and_repairs_server ... ok +test wireless_adb::tests::canceled_handshakes_cannot_publish_or_supersede_a_retry ... ok +test wireless_adb::tests::connect_timeout_explains_the_tv_approval_prompt ... ok +test wireless_adb::tests::installs_get_the_slow_shell_timeout ... ok +test wireless_adb::tests::only_socket_level_errors_are_fatal ... ok +test wireless_adb::tests::pairing_codes_are_normalized_before_the_one_allowed_guess ... ok +test wireless_adb::tests::pairing_errors_name_the_next_step ... ok +test wireless_adb::tests::refused_connect_explains_network_debugging ... ok +test wireless_adb::tests::requested_serial_must_match_active_connection ... ok +test wireless_commands::tests::find_remote_requires_a_successful_shell_result ... ok + +test result: ok. 26 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s + + Running unittests src/main.rs (target/debug/deps/atv_optimizer_mobile-f9318574c2dbaf8e) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running unittests src/main.rs (target/debug/deps/atvopt_license-ae39b7a189fbf8a8) + +running 2 tests +test tests::private_key_file_round_trips ... ok +test tests::rejects_a_key_file_that_is_not_hex ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running unittests src/main.rs (target/debug/deps/registry_triage-8414bee1b54060de) + +running 9 tests +test tests::a_batch_aggregates_by_package_and_reports_today_without_promoting ... ok +test tests::a_desktop_app_report_is_read_and_never_promotes_the_app ... ok +test tests::a_truncated_export_is_named_and_its_counts_are_lower_bounds ... ok +test tests::desktop_bundles_yield_home_handlers_and_never_the_serial ... ok +test tests::mobile_records_that_break_the_contract_are_rejected ... ok +test tests::overlapping_folders_count_each_report_once ... ok +test tests::the_verdict_names_the_source_that_decided_it ... ok +test tests::tokens_follow_the_mobile_collector_rules ... ok +test tests::triage_leaves_the_report_folder_untouched ... ok + +test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running unittests src/lib.rs (target/debug/deps/shield_optimizer_core-29c4a2a32901dc1d) + +running 489 tests +test adb::batch::tests::checked_batch_records_each_real_shell_exit_even_if_the_last_succeeds ... ok +test adb::batch::tests::checked_reads_reject_failed_or_missing_required_sections ... ok +test adb::batch::tests::empty_batch_is_empty ... ok +test adb::batch::tests::empty_sections_stay_empty_and_keep_position ... ok +test adb::batch::tests::extra_sections_beyond_n_are_dropped ... ok +test adb::batch::tests::joins_with_semicolons_and_discards_stderr ... ok +test adb::batch::tests::preserves_interior_indentation_and_blank_lines ... ok +test adb::batch::tests::round_trips_a_realistic_batch ... ok +test adb::batch::tests::single_command_gets_no_separator ... ok +test adb::batch::tests::splits_into_exactly_n_sections ... ok +test adb::batch::tests::tolerates_sentinel_at_the_very_end ... ok +test adb::batch::tests::truncated_output_pads_with_empty_sections ... ok +test adb::driver::bounded_shell_tests::drivers_without_bounded_execution_are_unsupported ... ok +test adb::lifetimes::tests::a_closed_counter_refuses_new_leases_but_keeps_counting_old_ones ... ok +test adb::lifetimes::tests::a_closed_snapshot_refuses_every_spawning_method ... ok +test adb::lifetimes::tests::a_leased_snapshot_forwards_every_method_to_its_own_counterpart ... ok +test adb::lifetimes::tests::processes::a_child_dropped_with_no_runtime_to_reap_it_stays_counted ... ok +test adb::lifetimes::tests::processes::a_dropped_child_is_reaped_before_its_lease_goes ... ok +test adb::lifetimes::tests::processes::kill_and_reap_releases_once_the_process_is_gone ... ok +test adb::lifetimes::tests::processes::waiting_out_the_process_releases_it ... ok +test adb::lifetimes::tests::waiting_ends_when_the_last_lease_goes ... ok +test adb::lifetimes::tests::waiting_gives_up_at_the_deadline ... ok +test adb::parse::tests::a_bracketed_ipv6_transport_is_a_network_connection ... ok +test adb::parse::tests::audio_device_missing_returns_none ... ok +test adb::parse::tests::display_mode_sdr_only_when_hdr_list_empty ... ok +test adb::parse::tests::display_modes_degrade_to_empty_rather_than_guessing ... ok +test adb::parse::tests::display_reports_scope_modes_and_hdr_to_the_same_default_record ... ok +test adb::parse::tests::duplicate_modes_collapse_but_keep_the_active_flag ... ok +test adb::parse::tests::empty_mdns_output_is_not_an_error ... ok +test adb::parse::tests::hardware_properties_temp_none_when_empty ... ok +test adb::parse::tests::hardware_reading_pairs_limits_by_family_index_and_exact_length ... ok +test adb::parse::tests::ignores_header_and_blank_lines ... ok +test adb::parse::tests::instance_serial_rejects_malformed_or_unknown_names ... ok +test adb::parse::tests::ipv6_endpoint_is_bracketed_like_the_adb_transport_key ... ok +test adb::parse::tests::ls_parse_returns_empty_on_error_output ... ok +test adb::parse::tests::mdns_endpoint_keeps_an_ipv6_host_intact ... ok +test adb::parse::tests::mdns_wireless_debugging_serial_is_network_not_usb ... ok +test adb::parse::tests::meminfo_returns_empty_when_section_missing ... ok +test adb::parse::tests::multiple_displays_without_a_unique_default_are_unavailable ... ok +test adb::parse::tests::net_dev_preserves_interfaces_and_skips_loopback ... ok +test adb::parse::tests::net_dev_with_no_usable_interfaces_is_empty ... ok +test adb::parse::tests::one_eligible_display_does_not_prove_the_current_default ... ok +test adb::parse::tests::package_for_process_strips_only_a_subprocess_suffix ... ok +test adb::parse::tests::pairing_service_is_not_offered_as_a_connect_endpoint ... ok +test adb::parse::tests::parses_active_audio_device ... ok +test adb::parse::tests::parses_device_list_with_mixed_states ... ok +test adb::parse::tests::parses_df_data_storage ... ok +test adb::parse::tests::parses_df_data_storage_when_filesystem_name_wraps ... ok +test adb::parse::tests::parses_display_mode_shield_4k60_hdr10 ... ok +test adb::parse::tests::parses_every_display_mode_and_flags_the_active_one ... ok +test adb::parse::tests::parses_hardware_properties_temp_from_cpu_and_gpu ... ok +test adb::parse::tests::parses_ls_rows_including_spaced_names_and_symlinks ... ok +test adb::parse::tests::parses_meminfo_summary_kb ... ok +test adb::parse::tests::parses_multiple_hdr_types ... ok +test adb::parse::tests::parses_permission_grant_state ... ok +test adb::parse::tests::parses_pm_list_packages_output ... ok +test adb::parse::tests::parses_real_mdns_services_output ... ok +test adb::parse::tests::parses_real_shield_df_output ... ok +test adb::parse::tests::parses_thermal_max_temp ... ok +test adb::parse::tests::parses_thermal_rejects_garbage_values ... ok +test adb::parse::tests::parses_total_pss_section_and_sums_per_package ... ok +test adb::parse::tests::parses_usage_stats_last_used_and_never_opened ... ok +test adb::parse::tests::path_named_processes_are_kept_not_dropped ... ok +test adb::parse::tests::pm_list_packages_skips_garbage_lines ... ok +test adb::parse::tests::proc_stat_counts_iowait_as_idle ... ok +test adb::parse::tests::proc_stat_ignores_per_core_lines_and_missing_input ... ok +test adb::parse::tests::proc_stat_rejects_invalid_or_overflowing_counters ... ok +test adb::parse::tests::real_meminfo_keeps_every_row_with_its_full_name_and_pid ... ok +test adb::parse::tests::real_meminfo_package_map_never_trims_a_hal_into_a_package ... ok +test adb::parse::tests::real_top_names_only_yield_candidates_of_package_shape ... ok +test adb::parse::tests::repeated_single_display_records_do_not_create_ambiguity ... ok +test adb::parse::tests::thermal_current_hal_wins_equal_temperature_with_new_status ... ok +test adb::parse::tests::thermal_current_hal_wins_even_when_cache_is_hotter ... ok +test adb::parse::tests::thermal_empty_current_section_does_not_resurrect_cache_but_legacy_falls_back ... ok +test adb::parse::tests::thermal_reading_preserves_sensor_context_and_excludes_non_celsius_objects ... ok +test adb::parse::tests::thermal_unknown_context_and_invalid_status_never_become_cpu_or_normal ... ok +test adb::parse::tests::unparseable_mdns_rows_are_dropped_not_guessed ... ok +test adb::remote_input::tests::android_resident_server_command_is_exact ... ok +test adb::remote_input::tests::format_scid_is_8_hex_digits_with_high_bit_cleared ... ok +test adb::remote_input::tests::forward_and_push_args_are_well_formed ... ok +test adb::remote_input::tests::inject_keycode_down_select_is_exact_14_bytes ... ok +test adb::remote_input::tests::inject_keycode_up_dpad_with_repeat_and_meta ... ok +test adb::remote_input::tests::inject_text_clamps_to_300_chars ... ok +test adb::remote_input::tests::inject_text_encodes_type_len_then_utf8 ... ok +test adb::remote_input::tests::inject_text_length_is_utf8_byte_count_not_char_count ... ok +test adb::remote_input::tests::pick_free_local_port_returns_a_usable_port ... ok +test adb::remote_input::tests::server_spawn_args_match_phase0_launch_line ... ok +test adb::remote_input::tests::start_fails_when_driver_cannot_spawn ... ok +test adb::sim::replay::tests::a_recorded_cached_limit_survives_a_generated_profile ... ok +test adb::sim::replay::tests::batches_split_back_into_reads ... ok +test adb::sim::replay::tests::pins_compare_redacted ... ok +test adb::sim::replay::tests::polls_stay_in_their_phase_and_u_packages_survive ... ok +test adb::sim::replay::tests::reads_may_swap_but_writes_keep_their_order ... ok +test adb::sim::replay::tests::remote_ports_and_scids_match_by_shape_and_extra_calls_diverge ... ok +test adb::sim::replay::tests::replays_in_order_and_reports_divergence ... ok +test adb::sim::shell::tests::globs ... ok +test adb::sim::shell::tests::last_command_status_wins_and_status_markers_expand ... ok +test adb::sim::shell::tests::quotes_and_and_or ... ok +test adb::sim::tests::a_phone_answering_no_to_leanback_still_profiles ... ok +test adb::sim::tests::a_replayed_screenshot_is_a_png ... ok +test adb::sim::tests::disable_setup_helper_is_guarded_and_verified ... ok +test adb::sim::tests::faults_fire_on_one_batched_section ... ok +test adb::sim::tests::forget_drops_every_transport_of_one_device ... ok +test adb::sim::tests::google_tv_takeover_disables_setup_wraith_with_stock ... ok +test adb::sim::tests::google_tv_takeover_failure_re_enables_stock_and_wraith ... ok +test adb::sim::tests::google_tv_without_the_paired_disable_wraith_takes_home_back ... ok +test adb::sim::tests::launcher_rows_are_home_handlers_and_switching_moves_home ... ok +test adb::sim::tests::setup_wraith_outranking_the_role_holder_is_not_the_current_launcher ... ok +test adb::sim::tests::shield_profile_lists_as_a_shield ... ok +test adb::sim::tests::shield_takeover_is_unchanged ... ok +test adb::sim::tests::takeover_without_setup_wraith_never_touches_it ... ok +test adb::sim::tests::the_background_process_limit_key_does_not_move_the_real_limit ... ok +test adb::sim::tests::unknown_commands_are_named ... ok +test adb::storage::tests::a_misaligned_column_is_dropped_not_guessed ... ok +test adb::storage::tests::a_negative_size_is_unknown_not_zero ... ok +test adb::storage::tests::diskstats_data_includes_cache_on_real_output ... ok +test adb::storage::tests::diskstats_reads_every_column_for_a_package ... ok +test adb::storage::tests::diskstats_without_a_package_table_is_unavailable ... ok +test adb::storage::tests::pm_path_and_stat_sum_a_split_apk ... ok +test adb::storage::tests::pm_path_refuses_shell_metacharacters ... ok +test adb::storage::tests::stat_short_of_one_size_per_file_is_unavailable ... ok +test commands::apps::tests::a_process_name_never_inherits_a_catalog_verdict ... ok +test commands::apps::tests::a_process_that_looks_protected_is_still_flagged ... ok +test commands::apps::tests::an_unreadable_handler_list_does_not_block_disabling ... ok +test commands::apps::tests::app_apk_size_refuses_an_invalid_package ... ok +test commands::apps::tests::app_apk_size_sums_the_split_apks ... ok +test commands::apps::tests::app_memory_map_parses_running_processes ... ok +test commands::apps::tests::app_memory_map_without_a_pss_table_is_an_error_not_empty ... ok +test commands::apps::tests::app_permission_state_missing_when_not_listed ... ok +test commands::apps::tests::app_permission_state_reads_grant ... ok +test commands::apps::tests::app_storage_map_reads_diskstats ... ok +test commands::apps::tests::app_storage_map_without_a_table_is_an_error_not_empty ... ok +test commands::apps::tests::app_usage_map_reports_last_used ... ok +test commands::apps::tests::app_usage_map_without_rows_is_an_error_not_empty ... ok +test commands::apps::tests::decodes_not_installed_for_user ... ok +test commands::apps::tests::decodes_protected_system_app ... ok +test commands::apps::tests::disabled_read_failure_never_becomes_enabled_state ... ok +test commands::apps::tests::disabling_a_launcher_is_allowed_while_another_remains ... ok +test commands::apps::tests::disabling_the_only_launcher_is_refused_from_the_app_list ... ok +test commands::apps::tests::free_entitlement_blocks_appops_before_adb ... ok +test commands::apps::tests::free_entitlement_blocks_permission_writes_before_adb ... ok +test commands::apps::tests::free_user_can_reenable_a_package_after_downgrade ... ok +test commands::apps::tests::installed_view_includes_catalog_and_other_packages_without_changing_other_view ... ok +test commands::apps::tests::known_names_never_change_a_verdict ... ok +test commands::apps::tests::list_installed_packages_errors_when_the_primary_listing_is_empty ... ok +test commands::apps::tests::list_other_packages_attaches_known_friendly_names ... ok +test commands::apps::tests::list_other_packages_errors_when_the_primary_listing_is_empty ... ok +test commands::apps::tests::package_states_categorizes_from_one_batched_call ... ok +test commands::apps::tests::package_states_errors_when_the_installed_section_is_empty ... ok +test commands::apps::tests::play_installed_vendor_packages_read_as_third_party ... ok +test commands::apps::tests::safety_info_consults_the_shipped_catalog ... ok +test commands::apps::tests::set_app_permission_rejects_injection ... ok +test commands::apps::tests::set_app_permission_revoke_succeeds_silently ... ok +test commands::apps::tests::unknown_is_not_the_answer_for_curated_apps ... ok +test commands::apps::tests::unrecognized_returns_none ... ok +test commands::devices::tests::a_bracketed_ipv6_transport_still_counts_as_advertised ... ok +test commands::devices::tests::a_collapsed_alias_resolves_to_its_row_by_hardware_id ... ok +test commands::devices::tests::a_device_that_answers_no_to_leanback_is_still_profiled ... ok +test commands::devices::tests::a_disconnected_twin_does_not_merge_with_a_live_one ... ok +test commands::devices::tests::a_shell_that_prints_nothing_is_still_a_failure ... ok +test commands::devices::tests::a_stale_advertisement_pointing_at_another_attached_device_is_refused ... ok +test commands::devices::tests::a_usb_serial_is_never_displaced_by_an_address ... ok +test commands::devices::tests::an_alias_without_a_verified_id_resolves_to_nothing ... ok +test commands::devices::tests::an_attached_transport_is_accepted_only_when_its_serial_matches ... ok +test commands::devices::tests::an_identified_device_never_absorbs_an_unidentified_one ... ok +test commands::devices::tests::an_offline_alias_does_not_resolve_to_a_live_twin ... ok +test commands::devices::tests::connect_paired_drops_a_different_device ... ok +test commands::devices::tests::connect_paired_keeps_the_device_whose_serial_matches ... ok +test commands::devices::tests::connect_paired_refuses_without_a_parseable_serial ... ok +test commands::devices::tests::device_name_quoting_and_validation ... ok +test commands::devices::tests::device_profile_answers_for_a_collapsed_alias ... ok +test commands::devices::tests::devices_without_an_id_are_never_merged_into_each_other ... ok +test commands::devices::tests::different_hardware_ids_stay_separate_however_alike_the_addresses_look ... ok +test commands::devices::tests::every_property_read_maps_to_a_field ... ok +test commands::devices::tests::explicit_connection_port_is_used_instead_of_the_pairing_port ... ok +test commands::devices::tests::failed_pair_remains_a_failure_and_does_not_connect ... ok +test commands::devices::tests::forget_drops_every_transport_of_the_same_hardware ... ok +test commands::devices::tests::forget_never_matches_an_alias_on_address_or_missing_id ... ok +test commands::devices::tests::forget_reports_a_disconnect_adb_refused ... ok +test commands::devices::tests::forget_says_plainly_when_adb_will_reattach_it ... ok +test commands::devices::tests::invalid_pairing_pin_fails_before_adb ... ok +test commands::devices::tests::list_devices_impl_parses_authorized_and_unauthorized ... ok +test commands::devices::tests::lost_socket_during_profiling_is_not_an_authorized_device ... ok +test commands::devices::tests::multiline_settings_exception_stays_inside_its_own_section ... ok +test commands::devices::tests::normalize_accepts_a_scoped_ipv6_endpoint_from_discovery ... ok +test commands::devices::tests::normalize_accepts_an_mdns_service_name_from_discovery ... ok +test commands::devices::tests::normalize_accepts_bare_ip ... ok +test commands::devices::tests::normalize_accepts_ip_and_port ... ok +test commands::devices::tests::normalize_keeps_a_bracketed_ipv6_host_whole ... ok +test commands::devices::tests::normalize_rejects_bad_port ... ok +test commands::devices::tests::normalize_rejects_empty ... ok +test commands::devices::tests::normalize_rejects_non_ipv4 ... ok +test commands::devices::tests::normalize_still_rejects_things_that_are_not_endpoints ... ok +test commands::devices::tests::normalize_trims_whitespace ... ok +test commands::devices::tests::one_device_reached_two_ways_collapses_to_the_usable_address ... ok +test commands::devices::tests::pair_records_the_instance_advertised_on_that_pairing_port ... ok +test commands::devices::tests::pairing_address_requires_the_tv_supplied_port ... ok +test commands::devices::tests::pairing_instance_is_the_service_on_that_exact_pairing_port ... ok +test commands::devices::tests::placeholder_hardware_ids_are_not_identities ... ok +test commands::devices::tests::probe_brackets_an_ipv6_connect_endpoint_and_sees_it_attached ... ok +test commands::devices::tests::probe_does_not_guess_between_two_connect_ports ... ok +test commands::devices::tests::probe_matches_the_connect_service_by_its_embedded_serial ... ok +test commands::devices::tests::probe_never_takes_another_device_on_the_same_address ... ok +test commands::devices::tests::probe_picks_the_paired_serial_when_two_connect_services_share_an_ip ... ok +test commands::devices::tests::probe_reports_a_transport_adb_already_attached ... ok +test commands::devices::tests::probe_waits_until_the_connect_service_appears ... ok +test commands::devices::tests::probe_with_a_malformed_pairing_instance_claims_nothing ... ok +test commands::devices::tests::probe_without_an_instance_claims_nothing ... ok +test commands::devices::tests::silent_device_name_read_does_not_shift_the_android_version ... ok +test commands::devices::tests::successful_pair_establishes_trust_without_connecting_to_5555 ... ok +test commands::devices::tests::the_property_batch_exits_zero_when_the_last_read_says_no ... ok +test commands::devices::tests::the_service_name_survives_when_it_is_the_only_transport ... ok +test commands::files::tests::a_failed_call_or_a_cut_off_stream_is_unsearched ... ok +test commands::files::tests::accepts_sdcard_paths ... ok +test commands::files::tests::delete_is_confined_and_quoted ... ok +test commands::files::tests::delete_reports_device_side_failures ... ok +test commands::files::tests::find_patterns_validated ... ok +test commands::files::tests::find_rejects_bad_dirs_before_touching_the_device ... ok +test commands::files::tests::hits_are_capped_and_deduplicated ... ok +test commands::files::tests::missing_directories_are_no_matches_not_unsearched ... ok +test commands::files::tests::power_user_mode_allows_system_paths_but_keeps_injection_guards ... ok +test commands::files::tests::protected_paths_are_never_deletable ... ok +test commands::files::tests::rejects_escapes_and_system_paths ... ok +test commands::health::tests::a_failed_batch_preserves_the_transport_error ... ok +test commands::health::tests::codec_glob_preserves_readable_xml_among_missing_paths ... ok +test commands::health::tests::health_report_prefers_current_hardware_to_cached_only_then_marks_cached_fallback ... ok +test commands::health::tests::health_report_reads_every_section_from_one_batched_call ... ok +test commands::health::tests::health_report_retains_status_and_matching_fallback_limits_without_new_reads ... ok +test commands::health::tests::health_report_uses_current_hal_context_instead_of_cached_ties ... ok +test commands::health::tests::media_report_decodes_every_section_from_one_round_trip ... ok +test commands::health::tests::media_report_errors_when_the_device_returns_nothing ... ok +test commands::health::tests::media_report_keeps_codecs_when_optional_display_read_fails ... ok +test commands::health::tests::media_report_rejects_failed_and_truncated_settings_reads ... ok +test commands::health::tests::media_report_survives_an_unreadable_codec_file ... ok +test commands::health::tests::media_report_treats_a_null_setting_as_unset ... ok +test commands::health::tests::one_empty_section_does_not_blank_the_rest_of_the_report ... ok +test commands::health::tests::proc_meminfo_fallback_needs_no_extra_round_trip ... ok +test commands::health::tests::resource_sample_degrades_when_proc_is_unreadable ... ok +test commands::health::tests::resource_sample_derives_rates_from_two_device_side_reads ... ok +test commands::health::tests::resource_sample_keeps_tunnels_separate_and_missing_interfaces_unknown ... ok +test commands::health::tests::resource_sample_rejects_invalid_cpu_deltas_and_elapsed_time ... ok +test commands::health::tests::resource_sample_rejects_truncated_batches ... ok +test commands::health::tests::resource_sample_reports_none_rather_than_a_bogus_spike ... ok +test commands::health::tests::the_codec_glob_covers_the_path_a_real_shield_uses ... ok +test commands::health::tests::top_memory_keeps_each_process_whole ... ok +test commands::health::tests::truncated_batch_only_blanks_the_sections_that_never_arrived ... ok +test commands::home_tracking::tests::record_then_prune_round_trip ... ok +test commands::home_tracking::tests::unreadable_or_missing_file_degrades_to_empty ... ok +test commands::input::tests::encodes_spaces_as_percent_s ... ok +test commands::input::tests::quotes_shell_specials ... ok +test commands::input::tests::rejects_unicode_control_and_empty ... ok +test commands::input::tests::remote_keys_map_to_android_keycodes ... ok +test commands::launcher::tests::a_failed_switch_records_every_stage_it_went_through ... ok +test commands::launcher::tests::a_leanback_only_app_is_not_listed_as_a_home_app ... ok +test commands::launcher::tests::a_rejected_cmd_package_setter_still_tries_the_pm_spelling ... ok +test commands::launcher::tests::android_8_registers_home_before_disabling_stock ... ok +test commands::launcher::tests::disable_stock_behind_the_active_target_restores_if_home_moves ... ok +test commands::launcher::tests::disable_stock_hands_home_over_when_stock_holds_it ... ok +test commands::launcher::tests::disable_stock_refuses_a_settings_recovery_handler ... ok +test commands::launcher::tests::disable_stock_refuses_a_stock_target ... ok +test commands::launcher::tests::disable_stock_refuses_when_the_target_has_no_home_screen ... ok +test commands::launcher::tests::ignores_unrelated_lines_with_slashes_or_paths ... ok +test commands::launcher::tests::invalid_target_package_runs_no_commands ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_helper_re_enable_is_visible_on_a_successful_switch ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_helper_rollback_is_reported_not_called_re_enabled ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_stock_enable_never_turns_the_helper_on ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_stock_restore_after_a_helper_error_is_reported ... ok +test commands::launcher::tests::launcher_verification_122::a_generic_stock_disable_leaves_the_helper_on_without_another_home ... ok +test commands::launcher::tests::launcher_verification_122::a_generic_stock_disable_turns_the_paired_helper_off_too ... ok +test commands::launcher::tests::launcher_verification_122::a_landed_disable_with_a_lost_reply_still_settles_the_helper ... ok +test commands::launcher::tests::launcher_verification_122::a_shared_enable_with_a_failed_helper_restore_is_not_reported_as_done ... ok +test commands::launcher::tests::launcher_verification_122::an_empty_home_inventory_never_leaves_stock_off_with_the_helper_on ... ok +test commands::launcher::tests::launcher_verification_122::an_unreadable_stock_preflight_still_restores_the_helper ... ok +test commands::launcher::tests::launcher_verification_122::enable_and_set_default_on_stock_re_enables_the_paired_helper ... ok +test commands::launcher::tests::launcher_verification_122::helper_disable_re_enables_after_home_verification_fails ... ok +test commands::launcher::tests::launcher_verification_122::resolver_lag_then_target_confirms_without_rollback ... ok +test commands::launcher::tests::launcher_verification_122::restores_stock_when_the_resolver_never_answers_during_verification ... ok +test commands::launcher::tests::launcher_verification_122::role_holder_confirms_through_a_stuck_resolver_value ... ok +test commands::launcher::tests::launcher_verification_122::rolls_back_only_when_stock_positively_still_holds_home ... ok +test commands::launcher::tests::launcher_verification_122::the_advanced_picker_restores_the_paired_helper_with_stock ... ok +test commands::launcher::tests::launcher_verification_122::the_shared_enable_command_restores_the_paired_helper ... ok +test commands::launcher::tests::launcher_verification_122::unreadable_home_apps_never_leave_stock_off_with_the_helper_on ... ok +test commands::launcher::tests::list_launchers_degrades_when_the_home_query_is_empty ... ok +test commands::launcher::tests::list_launchers_errors_when_the_installed_section_is_empty ... ok +test commands::launcher::tests::list_launchers_reads_every_section_from_one_batched_call ... ok +test commands::launcher::tests::list_launchers_rejects_failed_disabled_reads_before_pruning_tracking ... ok +test commands::launcher::tests::parses_home_handler_packages_from_resolveinfo ... ok +test commands::launcher::tests::restore_failure_variants_keep_primary_failure_and_fresh_home_observation ... ok +test commands::launcher::tests::set_home_any_refuses_honestly_for_an_app_without_a_home_screen ... ok +test commands::launcher::tests::set_home_any_rejects_injected_activity_names ... ok +test commands::launcher::tests::set_home_any_reports_stock_holding_home_without_taking_it_over ... ok +test commands::launcher::tests::set_home_any_succeeds_when_home_verifies_and_tries_the_given_activity_first ... ok +test commands::launcher::tests::set_launcher_falls_back_to_set_home_activity_when_role_unsupported ... ok +test commands::launcher::tests::set_launcher_first_strategy_wins_and_skips_the_rest ... ok +test commands::launcher::tests::set_launcher_from_stock_offers_takeover_without_grinding_the_ladder ... ok +test commands::launcher::tests::set_launcher_reports_failure_with_reason_when_all_strategies_fail ... ok +test commands::launcher::tests::set_launcher_stock_takeover_does_not_revert_when_it_verifies ... ok +test commands::launcher::tests::set_launcher_stock_takeover_reverts_when_it_does_not_verify ... ok +test commands::launcher::tests::stock_disable_uncertainty_restores_once_and_reports_unavailable_home ... ok +test commands::launcher::tests::target_enable_failures_stop_before_any_launcher_mutation ... ok +test commands::loader::tests::channel_provider_entry_has_high_risk ... ok +test commands::loader::tests::device_scope_uses_known_families_and_agrees_with_its_list ... ok +test commands::loader::tests::embedded_data_includes_known_defunct_apps ... ok +test commands::loader::tests::embedded_json_loads_and_parses ... ok +test commands::loader::tests::embedded_launchers_parse_with_every_shipped_entry ... ok +test commands::loader::tests::every_custom_launcher_has_a_source_url_and_no_package_repeats ... ok +test commands::loader::tests::every_shipped_catalog_entry_gets_a_reviewed_verdict ... ok +test commands::loader::tests::every_shipped_entry_has_a_usable_description ... ok +test commands::loader::tests::find_resolves_every_package_exactly_once ... ok +test commands::loader::tests::known_names_accept_a_bare_name_or_a_described_entry ... ok +test commands::loader::tests::known_names_do_not_duplicate_catalog_entries ... ok +test commands::loader::tests::known_names_map_parses_and_has_expected_entries ... ok +test commands::loader::tests::no_duplicate_packages_within_any_bundled_list ... ok +test commands::loader::tests::no_shipped_entry_overrides_the_protected_list ... ok +test commands::loader::tests::non_reinstallable_uninstall_entries_are_gated_to_disable ... ok +test commands::loader::tests::review_apps_are_never_auto_selected ... ok +test commands::loader::tests::review_dates_parse_and_are_not_in_the_future ... ok +test commands::loader::tests::safe_entries_carry_a_review_date_or_are_listed_as_gaps ... ok +test commands::loader::tests::shipped_known_names_are_display_only ... ok +test commands::logcat::tests::app_capture_resolves_the_main_pid_each_time ... ok +test commands::logcat::tests::device_failure_is_not_an_empty_success ... ok +test commands::logcat::tests::failed_or_ambiguous_pid_never_reads_all_logs ... ok +test commands::logcat::tests::invalid_filter_never_touches_the_driver ... ok +test commands::logcat::tests::unsuccessful_pid_command_never_uses_its_stdout ... ok +test commands::optimize::tests::prepare_optimize_builds_plan_from_device_state ... ok +test commands::optimize::tests::prepare_optimize_errors_when_the_installed_section_is_empty ... ok +test commands::optimize::tests::restore_refuses_a_failed_disabled_read_even_when_memory_succeeds ... ok +test commands::recovery::tests::free_user_can_recover_disabled_packages_after_downgrade ... ok +test commands::screenshot::tests::accepts_png_bytes ... ok +test commands::screenshot::tests::concurrent_same_timestamp_captures_never_replace_each_other ... ok +test commands::screenshot::tests::exhausted_names_fail_without_overwriting_or_leaving_staging_files ... ok +test commands::screenshot::tests::existing_symlink_target_is_not_followed_or_overwritten ... ok +test commands::screenshot::tests::failed_partial_write_publishes_nothing_and_preserves_prior_capture ... ok +test commands::screenshot::tests::flattens_serial_for_filenames ... ok +test commands::screenshot::tests::rejects_empty_output ... ok +test commands::screenshot::tests::rejects_error_text_with_message ... ok +test commands::screenshot::tests::same_timestamp_retains_each_capture ... ok +test commands::screenshot::tests::unavailable_directory_returns_an_error_without_a_saved_path ... ok +test commands::shell::tests::a_blocked_command_never_reaches_the_device ... ok +test commands::shell::tests::a_chained_command_hiding_a_disable_is_still_refused_whole ... ok +test commands::shell::tests::a_transport_failure_surfaces_as_an_error_not_an_empty_result ... ok +test commands::shell::tests::an_empty_command_is_rejected_before_the_driver ... ok +test commands::shell::tests::disabling_an_unprotected_package_is_allowed_through ... ok +test commands::shell::tests::runs_a_plain_command_and_returns_both_streams ... ok +test commands::snapshot::tests::current_settings_map_pairs_keys_to_values_in_order ... ok +test commands::snapshot::tests::disable_from_plan_records_invalid_name_without_aborting ... ok +test commands::snapshot::tests::disable_from_plan_records_pm_failure_without_aborting ... ok +test commands::snapshot::tests::installed_and_disabled_errors_when_the_installed_section_is_empty ... ok +test commands::snapshot::tests::installed_and_disabled_reads_both_lists_in_one_round_trip ... ok +test commands::snapshot::tests::installed_and_disabled_surfaces_a_transport_failure ... ok +test commands::snapshot::tests::setting_apply_distinguishes_empty_put_delete_and_failure ... ok +test commands::snapshot::tests::settings_capture_rejects_failed_and_truncated_reads ... ok +test commands::snapshot::tests::snapshot_plan_requires_a_successful_disabled_read ... ok +test commands::state::tests::a_driver_without_a_server_skips_stop_without_dispatching_commands ... ok +test commands::state::tests::closing_admission_refuses_new_snapshots_but_not_running_ones ... ok +test commands::state::tests::every_snapshot_counts_until_its_last_clone_goes ... ok +test commands::state::tests::snapshots_of_a_swapped_driver_share_one_count ... ok +test commands::tuning::tests::accepts_real_dot_hostnames ... ok +test commands::tuning::tests::an_unreadable_cached_limit_is_unknown_not_fatal ... ok +test commands::tuning::tests::basic_daydream_is_the_aosp_component_name ... ok +test commands::tuning::tests::builds_put_with_quoted_value ... ok +test commands::tuning::tests::cached_limit_parses_only_the_current_value ... ok +test commands::tuning::tests::empty_value_deletes ... ok +test commands::tuning::tests::rejects_bad_namespace_and_bad_key ... ok +test commands::tuning::tests::rejects_bad_or_unsafe_hostnames ... ok +test commands::tuning::tests::tweaks_preserve_empty_values_without_shifting_audio_settings ... ok +test commands::tuning::tests::tweaks_reject_failed_or_truncated_readback ... ok +test commands::tuning::tests::value_metacharacters_are_quoted_not_rejected ... ok +test commands::tuning::tests::value_quoting_handles_spaces_and_single_quotes ... ok +test engine::app_lists::tests::device_specific_overrides_common ... ok +test engine::app_lists::tests::duplicate_packages_within_a_list_are_collapsed ... ok +test engine::app_lists::tests::googletv_excludes_shield_entries ... ok +test engine::app_lists::tests::shield_merges_common_and_shield ... ok +test engine::app_lists::tests::unknown_returns_common_only ... ok +test engine::detection::tests::a_device_that_said_nothing_either_way_is_unknown ... ok +test engine::detection::tests::a_leanback_no_outweighs_nothing_else ... ok +test engine::detection::tests::a_phone_says_so_and_we_believe_it ... ok +test engine::detection::tests::detects_googletv_by_onn_brand ... ok +test engine::detection::tests::detects_googletv_chromecast ... ok +test engine::detection::tests::detects_googletv_streamer ... ok +test engine::detection::tests::detects_shield_by_brand ... ok +test engine::detection::tests::detects_shield_by_codename_only ... ok +test engine::detection::tests::generic_box_reporting_tv_characteristic_is_google_tv ... ok +test engine::detection::tests::google_branded_phone_is_not_google_tv ... ok +test engine::detection::tests::google_branded_tv_with_tv_characteristic_is_google_tv ... ok +test engine::detection::tests::leanback_alone_is_enough_to_call_it_a_tv ... ok +test engine::detection::tests::non_tv_tokens_are_matched_per_token_and_case_insensitively ... ok +test engine::detection::tests::shield_friendly_model_known ... ok +test engine::detection::tests::shield_friendly_model_unknown_passes_through ... ok +test engine::detection::tests::tv_characteristic_still_wins_over_a_leanback_no ... ok +test engine::detection::tests::unknown_when_no_signals ... ok +test engine::diagnostics::tests::a_host_only_bundle_says_no_device_rather_than_inventing_one ... ok +test engine::diagnostics::tests::a_package_inventory_is_never_part_of_the_bundle ... ok +test engine::diagnostics::tests::an_unreadable_device_claims_nothing_about_itself ... ok +test engine::diagnostics::tests::setup_helper_state_says_not_installed_or_unknown_without_guessing ... ok +test engine::diagnostics::tests::the_device_section_carries_both_tv_signals_and_the_verdict ... ok +test engine::launcher::tests::a_pairing_must_name_a_catalogued_transient_holder ... ok +test engine::launcher::tests::current_home_agreeing_sources_keep_the_activity ... ok +test engine::launcher::tests::current_home_disagreement_shows_the_role_holder_with_a_note ... ok +test engine::launcher::tests::current_home_falls_back_to_the_resolver_without_a_role ... ok +test engine::launcher::tests::current_home_keeps_a_stock_launcher_that_overrides_the_role ... ok +test engine::launcher::tests::current_home_prefers_the_role_holder_over_a_transient_resolver_answer ... ok +test engine::launcher::tests::current_home_role_without_a_resolver_answer_is_the_role ... ok +test engine::launcher::tests::disabled_setup_wraith_keeps_its_row_without_being_tracked ... ok +test engine::launcher::tests::last_enabled_home_handler_guard ... ok +test engine::launcher::tests::launcher_rows_carry_the_catalog_source_url ... ok +test engine::launcher::tests::launcher_rows_include_other_home_handlers_but_never_safe_fallbacks ... ok +test engine::launcher::tests::launcher_rows_keep_tracked_disabled_handlers_visible ... ok +test engine::launcher::tests::launcher_rows_mark_disabled_stock ... ok +test engine::launcher::tests::launcher_rows_mark_setup_wraith_as_a_setup_helper ... ok +test engine::launcher::tests::launcher_rows_omit_stock_not_on_device ... ok +test engine::launcher::tests::launcher_rows_put_installed_stock_first ... ok +test engine::launcher::tests::package_name_validation_accepts_valid ... ok +test engine::launcher::tests::package_name_validation_rejects_invalid ... ok +test engine::launcher::tests::paired_holders_come_only_from_the_stock_being_disabled ... ok +test engine::launcher::tests::wraith_alone_is_not_a_home_so_it_is_never_the_survivor ... ok +test engine::logcat::tests::a_tag_is_an_allowlist_not_an_additional_global_filter ... ok +test engine::logcat::tests::filters_cannot_inject_shell_words_or_logcat_flags ... ok +test engine::logcat::tests::process_resolution_is_strict ... ok +test engine::logcat::tests::snapshots_are_finite_and_never_clear_or_change_logging ... ok +test engine::logcat::tests::unresolved_app_filter_never_falls_back_to_all_apps ... ok +test engine::media::tests::absent_frame_setting_and_display_data_stay_unknown ... ok +test engine::media::tests::audio_codes_preserve_unknowns_and_do_not_confuse_mpegh_with_dts ... ok +test engine::media::tests::duplicate_documents_do_not_duplicate_decoders ... ok +test engine::media::tests::empty_and_malformed_xml_do_not_panic_or_invent_support ... ok +test engine::media::tests::reads_multiline_nested_types_comments_and_concatenated_documents ... ok +test engine::media::tests::software_metadata_and_mixed_decoders_preserve_presence ... ok +test engine::media::tests::vendor_name_does_not_prove_hardware_acceleration ... ok +test engine::optimize::tests::memory_map_propagated ... ok +test engine::optimize::tests::optimize_skips_uninstalled_and_disabled ... ok +test engine::optimize::tests::restore_skips_enabled_and_uninstalled ... ok +test engine::optimize::tests::uninstall_downgraded_to_disable_when_not_reinstallable ... ok +test engine::optimize::tests::uninstall_method_chosen_when_optimize_and_installed ... ok +test engine::safety::tests::a_catalog_entry_with_no_description_still_reads_cleanly ... ok +test engine::safety::tests::a_hand_written_caution_outranks_a_generic_risk_tier ... ok +test engine::safety::tests::a_reviewed_safe_app_is_safe_not_unknown ... ok +test engine::safety::tests::a_risky_reviewed_app_is_caution_and_says_why ... ok +test engine::safety::tests::all_verdicts_serialize_with_tag_and_reason ... ok +test engine::safety::tests::an_uncatalogued_package_is_still_unknown ... ok +test engine::safety::tests::caution_rules_keep_their_reasons_unless_protected_precedence_wins ... ok +test engine::safety::tests::channel_provider_is_caution_not_never ... ok +test engine::safety::tests::every_protected_rule_keeps_its_reason_and_membership ... ok +test engine::safety::tests::framework_is_never_disable ... ok +test engine::safety::tests::gms_is_never_disable ... ok +test engine::safety::tests::representative_protected_packages_remain_protected ... ok +test engine::safety::tests::safe_fallback_settings_is_never_disable ... ok +test engine::safety::tests::safe_is_never_reached_without_a_catalog_entry ... ok +test engine::safety::tests::shell_is_never_disable_for_obvious_reasons ... ok +test engine::safety::tests::shell_runner_allows_disabling_an_unprotected_package ... ok +test engine::safety::tests::shell_runner_allows_reading_a_protected_package ... ok +test engine::safety::tests::shell_runner_blocks_chained_and_quoted_attempts ... ok +test engine::safety::tests::shell_runner_blocks_disabling_a_never_disable_package ... ok +test engine::safety::tests::shell_runner_blocks_every_destructive_spelling ... ok +test engine::safety::tests::shell_runner_blocks_literal_clear_updates_and_component_targets ... ok +test engine::safety::tests::shell_runner_gate_agrees_with_the_never_disable_list ... ok +test engine::safety::tests::shell_runner_gate_does_not_pretend_to_stop_deliberate_evasion ... ok +test engine::safety::tests::system_keyboards_are_never_disable ... ok +test engine::safety::tests::the_protected_list_outranks_any_catalog_entry ... ok +test engine::safety::tests::unmatched_packages_are_unknown_with_the_exact_reason ... ok +test engine::snapshot::tests::absent_and_empty_settings_roundtrip_and_plan_separately ... ok +test engine::snapshot::tests::apply_plan_categorizes_packages ... ok +test engine::snapshot::tests::apply_plan_skips_settings_already_at_target ... ok +test engine::snapshot::tests::apply_plan_warns_on_cross_device ... ok +test engine::snapshot::tests::current_values_cover_every_setting_the_snapshot_mentions ... ok +test engine::snapshot::tests::label_roundtrips ... ok +test engine::snapshot::tests::launcher_already_home_is_not_a_change ... ok +test engine::snapshot::tests::launcher_not_installed_is_skipped_and_named ... ok +test engine::snapshot::tests::launcher_that_differs_or_is_unknown_is_set ... ok +test engine::snapshot::tests::legacy_omissions_never_become_deletions ... ok +test engine::snapshot::tests::rejects_conflicting_present_and_absent_values ... ok +test engine::snapshot::tests::rejects_future_schema ... ok +test engine::snapshot::tests::rejects_malformed_json ... ok +test engine::snapshot::tests::rejects_missing_schema_version ... ok +test engine::snapshot::tests::rejects_zero_schema_version ... ok +test engine::snapshot::tests::roundtrip_json ... ok +test engine::snapshot::tests::v1_snapshot_loads_with_no_label ... ok +test license::tests::base32_round_trips_arbitrary_bytes ... ok +test license::tests::dev_test_key_is_accepted_in_debug_builds ... ok +test license::tests::expired_license_is_rejected ... ok +test license::tests::issue_then_verify_round_trips ... ok +test license::tests::issued_license_length_stays_typeable ... ok +test license::tests::issuing_rejects_bad_licensee ... ok +test license::tests::license_survives_lowercase_and_confusable_characters ... ok +test license::tests::malformed_strings_are_rejected ... ok +test license::tests::perpetual_license_has_no_expiry ... ok +test license::tests::production_key_id_is_trusted ... ok +test license::tests::signature_from_another_key_is_rejected ... ok +test license::tests::tampered_payload_is_rejected ... ok +test license::tests::unknown_key_id_is_rejected ... ok + +test result: ok. 489 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.06s + + Running unittests src/lib.rs (target/debug/deps/shield_optimizer_v2_lib-42621b022cfda0c9) + +running 110 tests +test adb::driver::tests::a_pairing_pin_never_reaches_the_log ... ok +test adb::driver::tests::a_server_target_override_is_left_alone ... ok +test adb::driver::tests::a_session_line_carries_the_schema_the_replay_parser_reads ... ok +test adb::driver::tests::a_session_line_never_carries_the_pin_or_typed_text ... ok +test adb::driver::tests::a_timeout_is_recorded_as_an_error_without_an_exit_code ... ok +test adb::driver::tests::binary_output_is_recorded_as_its_size ... ok +test adb::driver::tests::bounded_shell_deadline_keeps_partial_output ... ok +test adb::driver::tests::bounded_shell_preserves_nonzero_and_both_streams ... ok +test adb::driver::tests::bounded_shell_stops_continuous_output ... ok +test adb::driver::tests::bounded_utf8_handles_incomplete_and_invalid_characters ... ok +test adb::driver::tests::discovery_does_not_search_path_after_valid_managed_install ... ok +test adb::driver::tests::discovery_does_not_search_path_after_valid_override ... ok +test adb::driver::tests::discovery_never_touches_the_launch_directory_when_adb_is_installed ... ok +test adb::driver::tests::discovery_uses_path_after_higher_priority_candidates_fail ... ok +test adb::driver::tests::logged_stdout_is_capped_without_splitting_a_character ... ok +test adb::driver::tests::missing_binary_returns_typed_error ... ok +test adb::driver::tests::ordinary_subprocess_nonzero_contract_is_unchanged ... ok +test adb::driver::tests::output_success_check ... ok +test adb::driver::tests::package_listing_stdout_is_redacted_not_truncated ... ok +test adb::driver::tests::process_lifetimes::a_cancelled_call_stays_counted_until_its_process_is_reaped ... ok +test adb::driver::tests::process_lifetimes::a_resident_child_counts_until_it_is_reaped ... ok +test adb::driver::tests::process_lifetimes::a_stop_that_times_out_is_killed_and_reported_unknown ... ok +test adb::driver::tests::process_lifetimes::a_timed_out_call_is_reaped_before_it_returns ... ok +test adb::driver::tests::process_lifetimes::closed_process_admission_refuses_to_spawn ... ok +test adb::driver::tests::process_lifetimes::inherited_output_pipes_respect_timeout_after_direct_child_exit ... ok +test adb::driver::tests::process_lifetimes::replacement_and_old_driver_clones_share_closed_process_admission ... ok +test adb::driver::tests::process_lifetimes::the_stop_reports_what_kill_server_exited_with ... ok +test adb::driver::tests::repeated_discovery_resolves_once_until_explicitly_forgotten ... ok +test adb::driver::tests::the_expert_shell_is_recorded_without_its_command_or_output ... ok +test adb::driver::tests::typed_remote_text_never_reaches_the_log ... ok +test adb::install::tests::extract_zip_rejects_zip_slip ... ok +test adb::install::tests::install_root_is_under_data_local_dir ... ok +test adb::install::tests::platform_tools_url_matches_current_os ... ok +test adb::scan::tests::parses_dotted_prefix ... ok +test adb::scan::tests::parses_linux_le_hex_ip ... ok +test adb::scan::tests::rejects_bad_hex ... ok +test adb::scan::tests::rejects_malformed_prefix ... ok +test commands::app_report::tests::writes_a_report_and_nothing_else ... ok +test commands::backup::tests::ignores_noise_lines ... ok +test commands::backup::tests::names_single_and_split_backups ... ok +test commands::backup::tests::parses_single_apk_path ... ok +test commands::backup::tests::parses_split_apk_paths ... ok +test commands::backup::tests::parses_version_name_from_dumpsys ... ok +test commands::diagnostics::tests::an_empty_installed_list_is_unknown_not_absent ... ok +test commands::diagnostics::tests::home_handlers_are_read_from_the_real_resolveinfo_shape ... ok +test commands::diagnostics::tests::session_recordings_never_reach_the_bug_report_tail ... ok +test commands::diagnostics::tests::the_tail_is_the_last_n_lines_and_an_absent_log_is_empty ... ok +test commands::diagnostics::tests::ui_breadcrumbs_are_one_clean_bounded_line ... ok +test commands::files::tests::quotes_single_quotes_in_paths ... ok +test commands::install::tests::restart_adb_reports_failure_when_network_reconnect_fails ... ok +test commands::install::tests::restart_adb_reports_failure_when_start_server_errors ... ok +test commands::install::tests::restart_adb_retries_a_reconnect_that_fails_immediately ... ok +test commands::quit::tests::a_change_from_an_outdated_view_cannot_turn_the_stop_on ... ok +test commands::quit::tests::a_restart_for_an_update_leaves_the_server_running ... ok +test commands::quit::tests::after_admission_closes_new_work_is_refused_before_it_reaches_adb ... ok +test commands::quit::tests::an_adb_process_still_alive_leaves_the_server_running ... ok +test commands::quit::tests::an_operation_that_outlasts_the_drain_leaves_the_server_running ... ok +test commands::quit::tests::canceled_install_waiter_keeps_blocking_extraction_counted ... ok +test commands::quit::tests::canceled_pending_install_releases_admission_without_replacing_driver ... ok +test commands::quit::tests::install_after_closed_admission_has_no_installer_effects ... ok +test commands::quit::tests::once_quit_has_begun_the_setting_no_longer_changes ... ok +test commands::quit::tests::pending_install_prevents_stop_until_driver_replacement_finishes ... ok +test commands::quit::tests::quit_waits_for_an_operation_already_running_then_stops ... ok +test commands::quit::tests::quit_waits_for_old_snapshot_after_replacement_then_stops_live_driver ... ok +test commands::quit::tests::quitting_twice_stops_at_most_once ... ok +test commands::quit::tests::the_setting_starts_off ... ok +test commands::quit::tests::with_a_real_process::spoofed_start_output_and_restarts_do_not_stop_the_server ... ok +test commands::quit::tests::with_a_real_process::with_the_setting_on_quit_runs_kill_server_once ... ok +test commands::quit::tests::with_the_setting_off_quit_never_stops_the_server ... ok +test commands::scan::live::live_forget_then_auto_connect ... ignored +test commands::scan::tests::a_device_adb_already_holds_is_not_connected_a_second_time ... ok +test commands::scan::tests::a_device_adb_does_not_hold_is_still_connected ... ok +test commands::scan::tests::a_device_attached_at_the_very_endpoint_we_would_dial_is_still_dialled ... ok +test commands::scan::tests::a_pairing_only_device_is_reported_rather_than_connected_to ... ok +test commands::scan::tests::a_redial_drops_the_offline_alias_of_the_same_service_only ... ok +test commands::scan::tests::a_similar_instance_name_is_not_treated_as_the_same_device ... ok +test commands::scan::tests::an_advertised_host_adb_already_holds_is_still_not_probed_on_5555 ... ok +test commands::scan::tests::an_advertised_host_is_not_also_probed_on_5555 ... ok +test commands::scan::tests::an_advertised_port_is_used_instead_of_guessing_5555 ... ok +test commands::scan::tests::an_already_paired_device_is_not_asked_to_pair_again ... ok +test commands::scan::tests::an_offline_transport_does_not_stop_the_scan_redialling ... ok +test commands::scan::tests::an_unauthorized_transport_is_not_dialled_again_and_reports_unauthorized ... ok +test commands::scan::tests::classifies_connected_despite_daemon_startup_noise ... ok +test commands::scan::tests::classifies_failures ... ok +test commands::scan::tests::classifies_fresh_and_already_connected ... ok +test commands::scan::tests::classifies_unauthorized ... ok +test commands::scan::tests::legacy_devices_still_reach_5555_when_nothing_is_advertised ... ok +test commands::scan::tests::legacy_sweep_targets_are_unaffected_by_the_attached_list ... ok +test commands::scan::tests::real_world_scan_output_connects_the_shield_and_leaves_the_phone_alone ... ok +test commands::scan::tests::repeated_advertisements_produce_one_target_each ... ok +test commands::scan::tests::summary_mentions_unauthorized_devices ... ok +test commands::scan::tests::summary_plain_when_all_connected ... ok +test commands::scan::tests::summary_says_nothing_about_pairing_when_nothing_is_waiting ... ok +test commands::scan::tests::summary_tells_the_user_how_to_pair_a_waiting_device ... ok +test commands::sideload::tests::a_manifest_package_id_with_shell_syntax_is_treated_as_unreadable ... ok +test commands::sideload::tests::decodes_common_install_failures ... ok +test commands::update::tests::a_build_with_no_published_release_has_no_notes ... ok +test commands::update::tests::an_empty_release_body_is_not_notes ... ok +test commands::update::tests::drafts_are_ignored_for_both_answers ... ok +test commands::update::tests::finds_the_notes_for_the_version_actually_running ... ok +test commands::update::tests::higher_core_beats_any_prerelease ... ok +test commands::update::tests::newer_patch_and_minor ... ok +test commands::update::tests::non_v2_tags_are_skipped ... ok +test commands::update::tests::prerelease_numeric_segments_compare_numerically ... ok +test commands::update::tests::stable_beats_prerelease_same_core ... ok +test session::tests::an_unwritable_dir_is_swallowed ... ok +test session::tests::file_names_are_filesystem_safe_utc_timestamps ... ok +test session::tests::nothing_is_created_until_the_first_line_then_a_header_leads ... ok +test session::tests::rotation_keeps_the_newest_ten_files ... ok +test session::tests::the_size_cap_writes_one_notice_and_stops ... ok + +test result: ok. 109 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 4.24s + + Running unittests src/main.rs (target/debug/deps/shield_optimizer_v2-7e20e0fcff340859) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running unittests src/lib.rs (target/debug/deps/tauri_plugin_atv_adb-1564aed60f182287) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests atv_optimizer_mobile_lib + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests shield_optimizer_core + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests shield_optimizer_v2_lib + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests tauri_plugin_atv_adb + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s diff --git a/v2/ATV-OPTIMIZER-ANDROID-PLAN.md b/v2/ATV-OPTIMIZER-ANDROID-PLAN.md index 5d5826cc..8c9df897 100644 --- a/v2/ATV-OPTIMIZER-ANDROID-PLAN.md +++ b/v2/ATV-OPTIMIZER-ANDROID-PLAN.md @@ -61,4 +61,8 @@ From M2 onward, add hardware validation for pairing, reconnect, screencap, shell - Desktop rebrand is out of scope. - Mobile MVP excludes APK sideload, files, backup/clone, screen mirror, and mouse mode. + Status 2026-10-06: Files (browse, pull to the phone, delete under `/sdcard`, Find app backups) + and APK backup/restore have since shipped on mobile, unverified on a device. Phone-to-TV upload + (needs an Android document picker), APK sideload, cloning, screen mirror and mouse mode are + still not built. See `mobile/HANDOFF.md`. - Existing untracked root files (`atv-optimizer-android-strategy.html`, root `package.json`, root `package-lock.json`, root `node_modules/`) are not part of this implementation unless explicitly added later. diff --git a/v2/CHANGELOG.md b/v2/CHANGELOG.md index 1d698751..84f70ff0 100644 --- a/v2/CHANGELOG.md +++ b/v2/CHANGELOG.md @@ -25,12 +25,18 @@ this file and shows the newest few sections, dated, from its version button. ### Added - Add a read-only desktop Logs tab with severity, tag and main-process filters, bounded recent snapshots, local text search and optional auto-refresh that stops when leaving the view. Raw logs stay out of automatic debug reports. -- Stop ADB when quitting, beside Restart ADB on Devices, off by default. The adb server otherwise keeps running after the app closes and holds every network device's connection open, so the TV goes on receiving adb traffic. Turning it on stops that server for every tool on the computer (Android Studio, scrcpy, a terminal), which the setting says. The server is still left running while the app is using adb (an open Remote, a command in progress), when the app restarts for an update, and when adb is pointed at a non-default server. +- Desktop: Stop ADB when quitting, beside Restart ADB on Devices, off by default. The adb server otherwise keeps running after the app closes and holds every network device's connection open, so the TV goes on receiving adb traffic. Turning it on stops that server for every tool on the computer (Android Studio, scrcpy, a terminal), which the setting says. The server is still left running while the app is using adb (an open Remote, a command in progress), when the app restarts for an update, and when adb is pointed at a non-default server. ### Fixed +- **Find app backups said a folder "couldn't be searched" when it simply didn't exist** + ([#86](https://github.com/bryanroscoe/shield_optimizer/issues/86)). SmartTube can keep + backups in any of three folders, and most TVs only have one of them. A missing folder now + reads as no matches; only a search that actually failed is reported as not searched. + - Launcher source pages remain available for installed and current-default launchers. Projectivy links to its official GitHub releases; opening the page never uninstalls or changes the current launcher (#161). + ## v2-2.3.1 — 2026-10-04 Fixes for Google TV launcher switching (#122, #157, #158): Setup Wraith, Google TV's setup diff --git a/v2/Cargo.lock b/v2/Cargo.lock index dfb0ae32..5af565bf 100644 --- a/v2/Cargo.lock +++ b/v2/Cargo.lock @@ -384,6 +384,7 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-atv-adb", + "tauri-plugin-opener", "tempfile", "tokio", "tracing", diff --git a/v2/crates/core/Cargo.toml b/v2/crates/core/Cargo.toml index 4f7f1f07..a42106f7 100644 --- a/v2/crates/core/Cargo.toml +++ b/v2/crates/core/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Bryan Roscoe"] edition = "2021" rust-version = "1.81" license = "LicenseRef-Proprietary" +publish = false [features] test-support = [] @@ -23,8 +24,8 @@ chrono = { version = "0.4", features = ["serde"] } base64 = "0.22" ed25519-dalek = "2" tracing = "0.1" +tempfile = "3" async-trait = "0.1" [dev-dependencies] pretty_assertions = "1" -tempfile = "3" diff --git a/v2/crates/core/src/adb/mod.rs b/v2/crates/core/src/adb/mod.rs index 25253c7d..f7c9b4aa 100644 --- a/v2/crates/core/src/adb/mod.rs +++ b/v2/crates/core/src/adb/mod.rs @@ -19,11 +19,12 @@ pub use lifetimes::{Lease, Leases, TrackedChild}; pub use parse::{ instance_serial, is_network_endpoint, kb_to_mb, package_for_process, parse_active_audio_device, parse_device_list, parse_disabled_packages_output, parse_display_mode, parse_display_modes, - parse_dumpsys_meminfo, parse_hardware_properties_temp, parse_installed_packages_output, - parse_ls_output, parse_mdns_services, parse_meminfo_summary, parse_net_dev, - parse_permission_granted, parse_proc_stat, parse_pss_by_process, parse_storage_info, - parse_thermal_max_celsius, parse_total_pss_by_process, parse_usage_stats, AppUsage, CpuSample, - DisplayMode, FileEntry, MdnsService, NetSample, ProcessMemory, RamInfo, StorageInfo, + parse_dumpsys_meminfo, parse_hardware_properties_reading, parse_hardware_properties_temp, + parse_installed_packages_output, parse_ls_output, parse_mdns_services, parse_meminfo_summary, + parse_net_dev, parse_permission_granted, parse_proc_stat, parse_pss_by_process, + parse_storage_info, parse_thermal_max_celsius, parse_thermal_reading, parse_thermal_status, + parse_total_pss_by_process, parse_usage_stats, AppUsage, CpuSample, DisplayMode, FileEntry, + MdnsService, NetSample, ProcessMemory, RamInfo, StorageInfo, TemperatureReading, MDNS_SERVICE_CONNECT, MDNS_SERVICE_LEGACY, MDNS_SERVICE_PAIRING, }; pub use remote_input::RemoteInputSession; diff --git a/v2/crates/core/src/adb/parse.rs b/v2/crates/core/src/adb/parse.rs index 5d92b7ba..cbdecb3c 100644 --- a/v2/crates/core/src/adb/parse.rs +++ b/v2/crates/core/src/adb/parse.rs @@ -454,58 +454,198 @@ pub fn parse_meminfo_summary(meminfo: &str) -> RamInfo { info } -/// Parse the highest temperature reading from `dumpsys thermalservice`. The -/// service emits a list of HardwareThrottlingService temps per zone; we want -/// the hottest CPU-class zone since that's the one users care about for -/// throttling. Returns `None` if no readable temp is present. -pub fn parse_thermal_max_celsius(dumpsys_thermalservice: &str) -> Option { - static TEMP: LazyLock = LazyLock::new(|| { - // Common formats across Android 9-13: - // "Temperature{mValue=42.0, mType=0, mName=..." - // " CPU: temp=42.0 type=CPU" - Regex::new(r"mValue=([\d.]+)|temp=([\d.]+)").unwrap() +/// One selected reported temperature, with context from the same device report. +/// Limits are optional reported values, never universal CPU health thresholds. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct TemperatureReading { + pub celsius: f64, + pub source: String, + pub sensor_name: Option, + pub sensor_type: Option, + pub status: Option, + pub throttling_c: Option, + pub shutdown_c: Option, +} + +fn current_celsius(value: f64) -> bool { + // Preserve the previous parser's sanity range; this is not a health grade. + (10.0..120.0).contains(&value) +} + +/// AOSP dumps cached entries before current HAL entries. Never compare their +/// temperatures or statuses: the current section is authoritative even if empty. +/// Cached-only dumps and older unsectioned formats retain an explicit fallback. +fn thermal_temperature_records(text: &str) -> (&str, &'static str) { + static CURRENT: LazyLock = LazyLock::new(|| { + Regex::new(r"(?mi)^[ \t]*Current temperatures from HAL[ \t]*:[ \t]*").unwrap() }); + static CACHED: LazyLock = + LazyLock::new(|| Regex::new(r"(?mi)^[ \t]*Cached temperatures[ \t]*:[ \t]*").unwrap()); + static NEXT_SECTION: LazyLock = LazyLock::new(|| { + Regex::new(r"(?mi)^[ \t]*(?:Cached temperatures|HAL Ready|HAL connection|Thermal Status|Temperature thresholds|Current (?:temperatures|cooling devices|temperature thresholds) from HAL)[ \t]*:").unwrap() + }); + let section = CURRENT + .find(text) + .map(|heading| (heading, "thermalservice_current")) + .or_else(|| { + CACHED + .find(text) + .map(|heading| (heading, "thermalservice_cached")) + }); + let Some((heading, source)) = section else { + return (text, "thermalservice"); + }; + let records = &text[heading.end()..]; + let end = NEXT_SECTION + .find(records) + .map_or(records.len(), |next| next.start()); + (&records[..end], source) +} - let mut max: Option = None; - for caps in TEMP.captures_iter(dumpsys_thermalservice) { - let raw = caps.get(1).or(caps.get(2)).map(|m| m.as_str())?; - let Ok(t) = raw.parse::() else { continue }; - // Sanity check — drop obvious garbage like 0.0 or 999.0. - if !(10.0..120.0).contains(&t) { +/// The hottest readable reported Celsius sensor, not necessarily a CPU sensor. +/// Thermal HAL BCL types 6–8 report mV/mA/percent, and must not become °C. +pub fn parse_thermal_reading(text: &str) -> Option { + static RECORD: LazyLock = LazyLock::new(|| { + Regex::new(r"\bTemperature\{[^}]*\}|(?m)^[^{}\n]*(?:mValue=|temp=)[^{}\n]*$").unwrap() + }); + static VALUE: LazyLock = LazyLock::new(|| { + Regex::new(r"(?:mValue=|temp=)([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)(?:[,}\s]|$)") + .unwrap() + }); + static TYPE: LazyLock = + LazyLock::new(|| Regex::new(r"(?:mType|type)=([^,\s}]+)").unwrap()); + static NAME: LazyLock = + LazyLock::new(|| Regex::new(r#"(?:mName|name)=("[^"]*"|[^,}\n]+)"#).unwrap()); + static STATUS: LazyLock = + LazyLock::new(|| Regex::new(r"mStatus=(\d+)(?:[,}\s]|$)").unwrap()); + let mut hottest: Option = None; + let (records, source) = thermal_temperature_records(text); + for record in RECORD.find_iter(records) { + let record = record.as_str(); + let Some(value) = VALUE + .captures(record) + .and_then(|c| c[1].parse::().ok()) + else { + continue; + }; + if !current_celsius(value) { + continue; + } + let raw_type = TYPE.captures(record).map(|c| c[1].to_string()); + if matches!( + raw_type.as_deref(), + Some("6" | "7" | "8" | "BCL_VOLTAGE" | "BCL_CURRENT" | "BCL_PERCENTAGE") + ) { continue; } - max = Some(max.map_or(t, |m| m.max(t))); + let sensor_type = raw_type.map(|kind| match kind.as_str() { + "0" => "CPU".to_string(), + "1" => "GPU".to_string(), + "2" => "Battery".to_string(), + "3" => "Skin".to_string(), + "4" => "USB port".to_string(), + "5" => "Power amplifier".to_string(), + "9" => "NPU".to_string(), + _ => kind, + }); + if hottest + .as_ref() + .is_some_and(|current| current.celsius >= value) + { + continue; + } + hottest = Some(TemperatureReading { + celsius: value, + source: source.to_string(), + sensor_name: NAME + .captures(record) + .map(|c| c[1].trim().trim_matches('"').to_string()) + .filter(|s| !s.is_empty()), + sensor_type, + status: STATUS + .captures(record) + .and_then(|c| c[1].parse::().ok()) + .filter(|s| *s <= 6), + throttling_c: None, + shutdown_c: None, + }); } - max + hottest +} + +/// Legacy scalar retained for desktop/report compatibility. +pub fn parse_thermal_max_celsius(text: &str) -> Option { + parse_thermal_reading(text).map(|reading| reading.celsius) +} + +/// The framework-wide status is distinct from the selected sensor's status. +pub fn parse_thermal_status(text: &str) -> Option { + static STATUS: LazyLock = + LazyLock::new(|| Regex::new(r"(?mi)^\s*Thermal Status:\s*([0-6])\s*$").unwrap()); + STATUS.captures(text).and_then(|c| c[1].parse().ok()) } -/// Fallback temperature from `dumpsys hardware_properties`, used when -/// `thermalservice` reports nothing (older Shield firmware, e.g. 8.2.3, -/// formats it differently or restricts it). Reads the `CPU temperatures:` -/// and `GPU temperatures:` lines — `CPU temperatures: [32.0, 32.0, ...]` — -/// and returns the hottest in-range reading. Ignores the *throttling* / -/// *shutdown* / *vr* lines (those are limits, not the current temp). -pub fn parse_hardware_properties_temp(dumpsys_hardware_properties: &str) -> Option { - static FLOAT: LazyLock = LazyLock::new(|| Regex::new(r"-?\d+\.?\d*").unwrap()); - let mut max: Option = None; - for line in dumpsys_hardware_properties.lines() { - let l = line.trim(); - let is_current = (l.starts_with("CPU temperatures:") || l.starts_with("GPU temperatures:")) - && !l.contains("throttling") - && !l.contains("shutdown"); - if !is_current { +fn temperature_array(text: &str, label: &str) -> Option>> { + let line = text + .lines() + .find_map(|line| line.trim().strip_prefix(label))?; + let (values, _) = line.trim().strip_prefix('[')?.split_once(']')?; + if values.trim().is_empty() { + return Some(Vec::new()); + } + // Keep missing/nonfinite slots: dropping them would misalign sensor limits. + Some( + values + .split(',') + .map(|v| v.trim().parse::().ok().filter(|v| v.is_finite())) + .collect(), + ) +} + +/// Current CPU/GPU arrays only. Limits are paired by family and index only when +/// their array lengths match; a missing/NaN slot must never borrow a neighbour. +pub fn parse_hardware_properties_reading(text: &str) -> Option { + let mut hottest: Option = None; + for kind in ["CPU", "GPU"] { + let Some(values) = temperature_array(text, &format!("{kind} temperatures:")) else { continue; - } - for m in FLOAT.find_iter(l) { - let Ok(t) = m.as_str().parse::() else { + }; + let throttle = temperature_array(text, &format!("{kind} throttling temperatures:")); + let shutdown = temperature_array(text, &format!("{kind} shutdown temperatures:")); + let limit = |limits: &Option>>, index: usize| -> Option { + limits + .as_ref() + .filter(|limits| limits.len() == values.len()) + .and_then(|limits| limits[index]) + .filter(|value| *value > 0.0) + }; + for (index, value) in values.iter().enumerate() { + let Some(value) = value.filter(|v| current_celsius(*v)) else { continue; }; - if (10.0..120.0).contains(&t) { - max = Some(max.map_or(t, |cur: f64| cur.max(t))); + if hottest + .as_ref() + .is_some_and(|reading| reading.celsius >= value) + { + continue; } + hottest = Some(TemperatureReading { + celsius: value, + source: "hardware_properties".to_string(), + sensor_name: Some(format!("{kind} temperatures[{index}]")), + sensor_type: Some(kind.to_string()), + status: None, + throttling_c: limit(&throttle, index), + shutdown_c: limit(&shutdown, index), + }); } } - max + hottest +} + +/// Legacy scalar retained for desktop/report compatibility. +pub fn parse_hardware_properties_temp(text: &str) -> Option { + parse_hardware_properties_reading(text).map(|reading| reading.celsius) } /// Disk usage parsed from `df -h /data`. @@ -1407,6 +1547,105 @@ Inter-| Receive | Transmit\n\ assert_eq!(info.swap_mb, Some(512)); } + #[test] + fn thermal_current_hal_wins_equal_temperature_with_new_status() { + let text = "Cached temperatures:\nTemperature{mValue=80, mType=0, mName=cpu0, mStatus=0}\nHAL Ready: true\nCurrent temperatures from HAL:\nTemperature{mValue=80, mType=0, mName=cpu0, mStatus=4}\nCurrent cooling devices from HAL:"; + let reading = parse_thermal_reading(text).unwrap(); + assert_eq!(reading.celsius, 80.0); + assert_eq!(reading.status, Some(4)); + assert_eq!(reading.source, "thermalservice_current"); + } + + #[test] + fn thermal_current_hal_wins_even_when_cache_is_hotter() { + let text = "Cached temperatures:\nTemperature{mValue=100, mType=0, mName=cpu0, mStatus=5}\nHAL Ready: true\nCurrent temperatures from HAL:\nTemperature{mValue=70, mType=0, mName=cpu0, mStatus=1}\nCurrent cooling devices from HAL:"; + let reading = parse_thermal_reading(text).unwrap(); + assert_eq!(reading.celsius, 70.0); + assert_eq!(reading.status, Some(1)); + assert_eq!(parse_thermal_max_celsius(text), Some(70.0)); + } + + #[test] + fn thermal_empty_current_section_does_not_resurrect_cache_but_legacy_falls_back() { + let cached = "Cached temperatures:\nTemperature{mValue=80, mType=0, mName=cpu0, mStatus=0}\nHAL Ready: false"; + assert_eq!(parse_thermal_max_celsius(cached), Some(80.0)); + assert_eq!( + parse_thermal_reading(cached).unwrap().source, + "thermalservice_cached" + ); + let empty = format!("{cached}\nCurrent temperatures from HAL:\nCurrent cooling devices from HAL:\nTemperature{{mValue=99, mType=0, mName=not-a-temperature-section, mStatus=5}}"); + assert_eq!(parse_thermal_reading(&empty), None); + assert_eq!(parse_thermal_max_celsius("mValue=42.0"), Some(42.0)); + } + + #[test] + fn thermal_reading_preserves_sensor_context_and_excludes_non_celsius_objects() { + let text = "Thermal Status: 3\n\ + Temperature{mValue=30.0, mType=0, mName=cpu0, mStatus=0}\n\ + Temperature{mValue=100.0, mType=1, mName=gpu0, mStatus=4}\n\ + Temperature{mValue=119.0, mType=8, mName=battery-percent, mStatus=5}\n\ + CoolingDevice{mValue=110, mType=0, mName=fan}\n\ + TemperatureThreshold{mValue=118, mType=0, mName=cpu-limit}"; + let reading = parse_thermal_reading(text).unwrap(); + assert_eq!(reading.celsius, 100.0); + assert_eq!(reading.sensor_name.as_deref(), Some("gpu0")); + assert_eq!(reading.sensor_type.as_deref(), Some("GPU")); + assert_eq!(reading.status, Some(4)); + assert_eq!(parse_thermal_status(text), Some(3)); + assert_eq!(reading.throttling_c, None); + assert_eq!(parse_thermal_max_celsius(text), Some(100.0)); + } + + #[test] + fn thermal_unknown_context_and_invalid_status_never_become_cpu_or_normal() { + let reading = parse_thermal_reading("mValue=42.0").unwrap(); + assert_eq!(reading.sensor_name, None); + assert_eq!(reading.sensor_type, None); + assert_eq!(reading.status, None); + assert_eq!(parse_thermal_status("Thermal Status: 10"), None); + assert_eq!(parse_thermal_status("Thermal Status: -1"), None); + assert_eq!(parse_thermal_status("Thermal Status: 0"), Some(0)); + assert_eq!( + parse_thermal_reading("mValue=NaN\nmValue=Infinity\nmValue=1e3\nmValue=32junk"), + None + ); + assert_eq!( + parse_thermal_max_celsius("CPU: temp=3e1 type=CPU"), + Some(30.0) + ); + assert_eq!( + parse_thermal_max_celsius("mValue=10\nmValue=119.9\nmValue=120"), + Some(119.9) + ); + let unknown = + parse_thermal_reading("Temperature{mValue=40, mName=\"vendor,zone\", mStatus=99}") + .unwrap(); + assert_eq!(unknown.sensor_name.as_deref(), Some("vendor,zone")); + assert_eq!(unknown.status, None); + } + + #[test] + fn hardware_reading_pairs_limits_by_family_index_and_exact_length() { + let reading = parse_hardware_properties_reading("CPU temperatures: [NaN, 100]\nCPU throttling temperatures: [70, 89]\nCPU shutdown temperatures: [102]\nGPU temperatures: [30]\nGPU throttling temperatures: [50]").unwrap(); + assert_eq!(reading.celsius, 100.0); + assert_eq!(reading.sensor_name.as_deref(), Some("CPU temperatures[1]")); + assert_eq!(reading.throttling_c, Some(89.0)); + assert_eq!(reading.shutdown_c, None); + assert_eq!(reading.status, None); + let gpu = parse_hardware_properties_reading("CPU temperatures: [40]\nCPU throttling temperatures: [89]\nGPU temperatures: [100]\nGPU throttling temperatures: []").unwrap(); + assert_eq!(gpu.sensor_type.as_deref(), Some("GPU")); + assert_eq!(gpu.throttling_c, None); + assert_eq!( + parse_hardware_properties_reading( + "CPU throttling temperatures: [89]\nCPU shutdown temperatures: [102]" + ), + None + ); + let missing = parse_hardware_properties_reading("CPU temperatures: [30, 100]\nCPU throttling temperatures: [89, NaN]\nCPU shutdown temperatures: [102, -1]").unwrap(); + assert_eq!(missing.throttling_c, None); + assert_eq!(missing.shutdown_c, None); + } + #[test] fn parses_thermal_max_temp() { let input = "Temperature{mValue=38.0, mType=0, mName=\"SKIN\"}\ diff --git a/v2/crates/core/src/commands/apps.rs b/v2/crates/core/src/commands/apps.rs index a4b397c9..0f222071 100644 --- a/v2/crates/core/src/commands/apps.rs +++ b/v2/crates/core/src/commands/apps.rs @@ -1596,6 +1596,26 @@ mod tests { ); } + #[tokio::test] + async fn free_user_can_reenable_a_package_after_downgrade() { + use crate::commands::test_support::{state_with, MockAdb}; + use crate::license::Entitlement; + + let mock = MockAdb::default(); + let log = mock.shell_log(); + let state = state_with(mock).with_entitlement(Entitlement::Pro); + state.set_entitlement(Entitlement::Free); + let result = enable_package_impl(&state, "TV", "com.example.app") + .await + .unwrap(); + assert!(result.ok); + assert!(log + .lock() + .unwrap() + .iter() + .any(|x| x == "pm enable com.example.app")); + } + #[tokio::test] async fn free_entitlement_blocks_permission_writes_before_adb() { use crate::commands::test_support::{state_with, MockAdb}; diff --git a/v2/crates/core/src/commands/files.rs b/v2/crates/core/src/commands/files.rs new file mode 100644 index 00000000..a0bfc650 --- /dev/null +++ b/v2/crates/core/src/commands/files.rs @@ -0,0 +1,388 @@ +//! File-manager guards and device-side file operations shared by desktop and +//! mobile. Both apps wrap these in their own Tauri commands because the local +//! side differs (a host folder on desktop, app-private storage on a phone), +//! but the device-side rules — what may be touched and how a search failure is +//! reported — must not drift between them. + +use serde::Serialize; + +use crate::adb::AdbDriver; +use crate::commands::apps::ActionResult; +use crate::commands::quote_shell_arg; + +/// Validate a device path for file-manager use. Always absolute, no `..` +/// traversal, no control characters (they'd corrupt the shell line even +/// quoted). When `allow_system` is false the path must also live under +/// `/sdcard`; power-user mode lifts only that boundary — the injection guards +/// stay. Returns the trimmed path. +pub fn validate_device_path(path: &str, allow_system: bool) -> Result { + let p = path.trim(); + if !p.starts_with('/') { + return Err(format!("Path must be absolute: {p:?}")); + } + if p.split('/').any(|seg| seg == "..") { + return Err("Path traversal (`..`) is not allowed.".to_string()); + } + if p.chars().any(|c| c.is_control()) { + return Err("Path contains control characters.".to_string()); + } + if !allow_system && p != "/sdcard" && !p.starts_with("/sdcard/") { + return Err(format!("Path must be under /sdcard: {p:?}")); + } + Ok(p.to_string()) +} + +/// `/sdcard`-confined validation — for the paths that are user-storage only by +/// design (the device-to-device copy and the backup finder). +pub fn validate_sdcard_path(path: &str) -> Result { + validate_device_path(path, false) +} + +/// Refuse deleting the filesystem root, `/sdcard` itself, or a critical system +/// mount even in power-user mode — a recursive delete there could brick the +/// device. Subpaths are the user's call (and mostly permission-denied without +/// root). Returns the refusal message, or `None` if the path is deletable. +pub fn protected_delete_reason(path: &str) -> Option { + let trimmed = path.trim_end_matches('/'); + if trimmed == "/sdcard" { + return Some("Refusing to delete /sdcard itself.".to_string()); + } + const PROTECTED: &[&str] = &[ + "", "/system", "/data", "/vendor", "/proc", "/sys", "/dev", "/boot", "/init", "/sbin", + "/bin", "/etc", + ]; + if PROTECTED.contains(&trimmed) { + return Some(format!( + "Refusing to delete a protected system path: {path}" + )); + } + None +} + +/// Remove a file or directory (recursively). Confined to `/sdcard` unless +/// `allow_system` (desktop power-user mode) is set; callers confirm with the +/// user first, and `protected_delete_reason` still blocks catastrophic +/// targets. +pub async fn delete_path_with( + adb: &dyn AdbDriver, + serial: &str, + path: &str, + allow_system: bool, +) -> Result { + let path = validate_device_path(path, allow_system)?; + if let Some(reason) = protected_delete_reason(&path) { + return Err(reason); + } + let out = adb + .shell(serial, &format!("rm -rf {}", quote_shell_arg(&path))) + .await + .map_err(|e| format!("rm: {e}"))?; + let noise = out.combined().trim().to_string(); + // The desktop driver turns a nonzero exit into an error above; the mobile + // transport hands it back as data, so it is checked here too. + let exited_cleanly = matches!(out.exit_code, None | Some(0)); + if noise.is_empty() && exited_cleanly { + Ok(ActionResult { + ok: true, + message: format!("Deleted {path}."), + }) + } else if noise.is_empty() { + Ok(ActionResult { + ok: false, + message: format!("The TV did not confirm that {path} was deleted."), + }) + } else { + Ok(ActionResult { + ok: false, + message: noise, + }) + } +} + +/// Filename patterns for `find -name`: glob stars and dots only — no slashes, +/// quotes, or anything the shell could reinterpret. +pub fn validate_find_pattern(pattern: &str) -> Result<(), String> { + let ok = !pattern.is_empty() + && pattern.len() <= 64 + && pattern + .chars() + .all(|c| c.is_ascii_alphanumeric() || matches!(c, '*' | '.' | '_' | '-')); + if ok { + Ok(()) + } else { + Err(format!("Invalid search pattern: {pattern:?}")) + } +} + +/// Most hits one search returns. +pub const MAX_FIND_HITS: usize = 100; + +/// Printed after `find` finishes. Its presence is the only proof the search +/// ran to the end: a missing directory makes `find` exit nonzero (which the +/// desktop driver reports as an error, indistinguishable from a dropped +/// connection), while a cut-off stream produces partial output with no error +/// at all. +const FIND_DONE: &str = "__ATVOPT_FIND_DONE__"; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct FindResult { + /// Matching file paths, capped at [`MAX_FIND_HITS`]. + pub hits: Vec, + /// Directories the search could not be run against at all, because the ADB + /// call itself failed. A directory that simply does not exist is *not* + /// listed here — that is a real "no matches". Without this split, a + /// dropped connection rendered as "export from the app first", telling the + /// user to redo something that had worked (GitHub #86). + pub unsearched: Vec, +} + +/// Locate files matching a name pattern under one or more `/sdcard` +/// directories. Powers the app-backup finder (e.g. Projectivy's `*.plbackup` +/// exports land wherever the user's file picker put them). +pub async fn find_files_with( + adb: &dyn AdbDriver, + serial: &str, + dirs: &[String], + pattern: &str, +) -> Result { + validate_find_pattern(pattern)?; + let dirs = dirs + .iter() + .map(|dir| validate_sdcard_path(dir)) + .collect::, _>>()?; + let mut hits: Vec = Vec::new(); + let mut unsearched = Vec::new(); + for dir in dirs { + // A missing directory or a permission denial is expected for some + // candidates, and `find` reports those on stderr — suppress them, since + // an empty result is the honest answer. The trailing marker keeps the + // exit status 0 and proves the search completed. + let cmd = format!( + "find {} -maxdepth 4 -type f -name '{pattern}' 2>/dev/null; echo {FIND_DONE}", + quote_shell_arg(&dir) + ); + let out = match adb.shell(serial, &cmd).await { + Ok(out) if out.stdout.lines().any(|line| line.trim() == FIND_DONE) => out, + _ => { + unsearched.push(dir); + continue; + } + }; + for line in out.stdout.lines() { + if hits.len() >= MAX_FIND_HITS { + break; + } + let line = line.trim(); + if line.starts_with("/sdcard/") && !hits.iter().any(|h| h == line) { + hits.push(line.to_string()); + } + } + } + Ok(FindResult { hits, unsearched }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::commands::test_support::MockAdb; + use pretty_assertions::assert_eq; + + fn dirs(list: &[&str]) -> Vec { + list.iter().map(|s| s.to_string()).collect() + } + + #[test] + fn find_patterns_validated() { + assert!(validate_find_pattern("*.plbackup").is_ok()); + assert!(validate_find_pattern("backup_*.zip").is_ok()); + assert!(validate_find_pattern("a/b").is_err()); + assert!(validate_find_pattern("x'y").is_err()); + assert!(validate_find_pattern("").is_err()); + assert!(validate_find_pattern(&"x".repeat(65)).is_err()); + } + + #[test] + fn accepts_sdcard_paths() { + assert_eq!(validate_sdcard_path("/sdcard").unwrap(), "/sdcard"); + assert_eq!( + validate_sdcard_path("/sdcard/Download/file 1.mp4").unwrap(), + "/sdcard/Download/file 1.mp4" + ); + } + + #[test] + fn rejects_escapes_and_system_paths() { + assert!(validate_sdcard_path("/data/data/com.x").is_err()); + assert!(validate_sdcard_path("/sdcard/../data").is_err()); + assert!(validate_sdcard_path("/sdcardX/evil").is_err()); + assert!(validate_sdcard_path("/sdcard/a\nb").is_err()); + assert!(validate_sdcard_path("/storage/emulated/0/x").is_err()); + assert!(validate_sdcard_path("").is_err()); + } + + #[test] + fn power_user_mode_allows_system_paths_but_keeps_injection_guards() { + assert!(validate_device_path("/system/app", false).is_err()); + assert_eq!( + validate_device_path("/system/app", true).unwrap(), + "/system/app" + ); + assert_eq!(validate_device_path("/", true).unwrap(), "/"); + assert!(validate_device_path("/system/../x", true).is_err()); + assert!(validate_device_path("/system/a\nb", true).is_err()); + assert!(validate_device_path("relative/path", true).is_err()); + } + + #[test] + fn protected_paths_are_never_deletable() { + for p in [ + "/", "/system", "/data", "/vendor", "/sdcard", "/system/", "/sdcard/", + ] { + assert!(protected_delete_reason(p).is_some(), "{p} must be refused"); + } + assert!(protected_delete_reason("/sdcard/Download/old.zip").is_none()); + assert!(protected_delete_reason("/system/app/Bloat/Bloat.apk").is_none()); + } + + const SMARTTUBE_DIRS: &[&str] = &[ + "/sdcard/Documents/SmartTubeBackup", + "/sdcard/SmartTubeBackup", + "/sdcard/Android/data/com.teamsmart.videomanager.tv", + ]; + + #[tokio::test] + async fn missing_directories_are_no_matches_not_unsearched() { + let hit = "/sdcard/Documents/SmartTubeBackup/org.smarttube.stable_1.zip"; + let mock = MockAdb::default() + .on_shell( + "'/sdcard/Documents/SmartTubeBackup'", + &format!("{hit}\n{FIND_DONE}\n"), + ) + // `find` printed nothing for a directory that isn't there, but the + // marker still ran — that is an honest empty search. + .on_shell("'/sdcard/SmartTubeBackup'", &format!("{FIND_DONE}\n")) + .on_shell("'/sdcard/Android/data/", &format!("{FIND_DONE}\n")); + let log = mock.shell_log(); + let result = find_files_with(&mock, "tv", &dirs(SMARTTUBE_DIRS), "*.zip") + .await + .unwrap(); + assert_eq!( + result, + FindResult { + hits: vec![hit.to_string()], + unsearched: vec![], + } + ); + let log = log.lock().unwrap(); + assert_eq!(log.len(), 3); + assert!(log[0].contains("-name '*.zip'")); + } + + #[tokio::test] + async fn a_failed_call_or_a_cut_off_stream_is_unsearched() { + let mock = MockAdb::default() + .on_shell_err("'/sdcard/Documents/SmartTubeBackup'", "device offline") + // Output without the completion marker: the stream ended early. + .on_shell( + "'/sdcard/SmartTubeBackup'", + "/sdcard/SmartTubeBackup/partial.zip\n", + ) + .on_shell("'/sdcard/Android/data/", &format!("{FIND_DONE}\n")); + let result = find_files_with(&mock, "tv", &dirs(SMARTTUBE_DIRS), "*.zip") + .await + .unwrap(); + assert!(result.hits.is_empty()); + assert_eq!( + result.unsearched, + dirs(&SMARTTUBE_DIRS[..2]), + "a search that never finished must not read as no matches" + ); + } + + #[tokio::test] + async fn find_rejects_bad_dirs_before_touching_the_device() { + let mock = MockAdb::default(); + let log = mock.shell_log(); + assert!( + find_files_with(&mock, "tv", &dirs(&["/sdcard/ok", "/data"]), "*.zip") + .await + .is_err() + ); + assert!( + find_files_with(&mock, "tv", &dirs(&["/sdcard"]), "*.zip'; rm") + .await + .is_err() + ); + assert!(log.lock().unwrap().is_empty()); + } + + #[tokio::test] + async fn hits_are_capped_and_deduplicated() { + let mut many: String = (0..150) + .map(|i| format!("/sdcard/Download/f{i}.zip\n")) + .collect(); + many.push_str("/sdcard/Download/f0.zip\n"); + many.push_str(FIND_DONE); + let mock = MockAdb::default().on_shell("find", &many); + let result = find_files_with( + &mock, + "tv", + &dirs(&["/sdcard/Download", "/sdcard"]), + "*.zip", + ) + .await + .unwrap(); + assert_eq!(result.hits.len(), MAX_FIND_HITS); + assert!(result.unsearched.is_empty()); + } + + #[tokio::test] + async fn delete_is_confined_and_quoted() { + let mock = MockAdb::default(); + let log = mock.shell_log(); + for bad in [ + "/sdcard", + "/sdcard/", + "/sdcard/../data", + "/data/local/tmp/x", + "/storage/emulated/0/x", + "relative", + "/sdcard/a\nb", + ] { + assert!( + delete_path_with(&mock, "tv", bad, false).await.is_err(), + "{bad:?} must be refused" + ); + } + assert!(log.lock().unwrap().is_empty()); + + let r = delete_path_with(&mock, "tv", "/sdcard/Download/it's.zip", false) + .await + .unwrap(); + assert!(r.ok, "{}", r.message); + assert_eq!( + log.lock().unwrap().as_slice(), + [r"rm -rf '/sdcard/Download/it'\''s.zip'".to_string()] + ); + } + + #[tokio::test] + async fn delete_reports_device_side_failures() { + let mock = MockAdb::default() + .on_shell("noisy", "rm: /sdcard/noisy: Permission denied") + .on_shell_exit("silent", "", 1) + .on_shell_err("gone", "device offline"); + let noisy = delete_path_with(&mock, "tv", "/sdcard/noisy", false) + .await + .unwrap(); + assert!(!noisy.ok); + assert!(noisy.message.contains("Permission denied")); + let silent = delete_path_with(&mock, "tv", "/sdcard/silent", false) + .await + .unwrap(); + assert!(!silent.ok); + assert!(delete_path_with(&mock, "tv", "/sdcard/gone", false) + .await + .is_err()); + } +} diff --git a/v2/crates/core/src/commands/health.rs b/v2/crates/core/src/commands/health.rs index 406d3293..53cf17f0 100644 --- a/v2/crates/core/src/commands/health.rs +++ b/v2/crates/core/src/commands/health.rs @@ -5,9 +5,9 @@ use tauri::State; use crate::adb::{ batch_command, kb_to_mb, package_for_process, parse_active_audio_device, parse_display_mode, - parse_display_modes, parse_hardware_properties_temp, parse_meminfo_summary, parse_net_dev, - parse_proc_stat, parse_pss_by_process, parse_storage_info, parse_thermal_max_celsius, - split_batch, DisplayMode, RamInfo, StorageInfo, + parse_display_modes, parse_hardware_properties_reading, parse_meminfo_summary, parse_net_dev, + parse_proc_stat, parse_pss_by_process, parse_storage_info, parse_thermal_reading, + parse_thermal_status, split_batch, DisplayMode, RamInfo, StorageInfo, TemperatureReading, }; use crate::engine::media::{ build_capabilities, parse_media_codecs, surround_mode, MediaCapabilities, @@ -36,6 +36,10 @@ pub struct HealthReport { pub ram: RamInfo, pub storage: StorageInfo, pub temperature_c: Option, + /// Optional context from the same existing thermal reads. Old clients retain + /// their scalar temperature; new clients can identify the sensor and limits. + pub temperature_reading: Option, + pub thermal_status: Option, /// Current active audio output (e.g. "HDMI", "BUILTIN_SPEAKER"). Parsed /// from `dumpsys audio`; `None` when the section isn't present. pub audio_device: Option, @@ -346,8 +350,21 @@ async fn health_report_for(state: &AppState, serial: &str) -> Result Result, serial: String, ) -> Result { + panic_recovery_impl(state.inner(), &serial).await +} + +async fn panic_recovery_impl(state: &AppState, serial: &str) -> Result { let adb = state.adb_snapshot().await; let disabled_out = adb - .shell(&serial, "pm list packages -d") + .shell(serial, "pm list packages -d") .await .map_err(|e| format!("pm list packages -d: {e}"))?; let disabled = parse_disabled_packages_output(&disabled_out.stdout); @@ -55,7 +59,7 @@ pub async fn panic_recovery( }); continue; } - match adb.shell(&serial, &format!("pm enable {pkg}")).await { + match adb.shell(serial, &format!("pm enable {pkg}")).await { Ok(out) if !out.shell_reported_failure() => { restored.push(pkg.clone()); } @@ -87,3 +91,30 @@ pub async fn panic_recovery( message, }) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::commands::test_support::{state_with, MockAdb}; + use crate::license::Entitlement; + + #[tokio::test] + async fn free_user_can_recover_disabled_packages_after_downgrade() { + let mock = MockAdb::default().on_shell( + "pm list packages -d", + "package:com.example.one\npackage:com.example.two\n", + ); + let log = mock.shell_log(); + let state = state_with(mock).with_entitlement(Entitlement::Pro); + state.set_entitlement(Entitlement::Free); + let result = panic_recovery_impl(&state, "TV").await.unwrap(); + assert_eq!(result.restored, vec!["com.example.one", "com.example.two"]); + assert!(result.failed.is_empty()); + let calls = log.lock().unwrap(); + assert!(calls.iter().any(|x| x == "pm enable com.example.one")); + assert!(calls.iter().any(|x| x == "pm enable com.example.two")); + assert!(!calls + .iter() + .any(|x| x.contains("disable-user") || x.contains("uninstall"))); + } +} diff --git a/v2/crates/core/src/commands/screenshot.rs b/v2/crates/core/src/commands/screenshot.rs index 7617c5a0..85474099 100644 --- a/v2/crates/core/src/commands/screenshot.rs +++ b/v2/crates/core/src/commands/screenshot.rs @@ -1,6 +1,11 @@ //! Screenshot capture — `adb exec-out screencap -p`, saved under the app //! data dir with a base64 copy for an instant in-app preview. +use std::{ + io::Write, + path::{Path, PathBuf}, +}; + use serde::Serialize; use tauri::State; @@ -46,6 +51,37 @@ fn filename_safe(serial: &str) -> String { .collect() } +const MAX_CAPTURE_NAMES: u32 = 1000; + +fn publish_screenshot( + dir: &Path, + stem: &str, + write: impl FnOnce(&mut std::fs::File) -> std::io::Result<()>, +) -> Result { + let mut temporary = tempfile::NamedTempFile::new_in(dir) + .map_err(|e| format!("stage screenshot in {}: {e}", dir.display()))?; + write(temporary.as_file_mut()).map_err(|e| format!("write screenshot: {e}"))?; + temporary + .as_file_mut() + .flush() + .map_err(|e| format!("flush screenshot: {e}"))?; + // Publish complete bytes without replacing a destination; existence checks alone race. + for n in 1..=MAX_CAPTURE_NAMES { + let name = if n == 1 { + format!("{stem}.png") + } else { + format!("{stem} ({n}).png") + }; + let path = dir.join(name); + match temporary.persist_noclobber(&path) { + Ok(_) => return Ok(path), + Err(e) if e.error.kind() == std::io::ErrorKind::AlreadyExists => temporary = e.file, + Err(e) => return Err(format!("save screenshot {}: {}", path.display(), e.error)), + } + } + Err("Could not reserve a screenshot filename. Earlier captures were preserved; retry the capture.".into()) +} + #[tauri::command] pub async fn take_screenshot( state: State<'_, AppState>, @@ -69,10 +105,13 @@ pub async fn take_screenshot( .await .map_err(|e| format!("create {}: {e}", dir.display()))?; let stamp = chrono::Local::now().format("%Y-%m-%d_%H-%M-%S"); - let path = dir.join(format!("{}_{stamp}.png", filename_safe(&serial))); - tokio::fs::write(&path, &bytes) - .await - .map_err(|e| format!("write {}: {e}", path.display()))?; + let stem = format!("{}_{stamp}", filename_safe(&serial)); + let saved_bytes = bytes.clone(); + let path = tokio::task::spawn_blocking(move || { + publish_screenshot(&dir, &stem, |file| file.write_all(&saved_bytes)) + }) + .await + .map_err(|e| format!("save screenshot task: {e}"))??; Ok(ScreenshotResult { path: path.display().to_string(), @@ -107,4 +146,106 @@ mod tests { assert_eq!(filename_safe("192.168.42.71:5555"), "192-168-42-71-5555"); assert_eq!(filename_safe("0123456789ABCDEF"), "0123456789ABCDEF"); } + fn save(dir: &Path, stem: &str, bytes: &[u8]) -> Result { + publish_screenshot(dir, stem, |file| file.write_all(bytes)) + } + + #[test] + fn same_timestamp_retains_each_capture() { + let dir = tempfile::tempdir().unwrap(); + let stem = "TV_2026-10-17_12-00-00"; + let first = save(dir.path(), stem, b"first PNG").unwrap(); + let second = save(dir.path(), stem, b"second PNG").unwrap(); + assert_eq!(first, dir.path().join(format!("{stem}.png"))); + assert_eq!(second, dir.path().join(format!("{stem} (2).png"))); + assert_eq!(std::fs::read(first).unwrap(), b"first PNG"); + assert_eq!(std::fs::read(second).unwrap(), b"second PNG"); + assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), 2); + } + + #[test] + fn concurrent_same_timestamp_captures_never_replace_each_other() { + let dir = tempfile::tempdir().unwrap(); + let existing = dir.path().join("TV_fixed-time.png"); + std::fs::write(&existing, b"old capture").unwrap(); + let barrier = std::sync::Arc::new(std::sync::Barrier::new(12)); + let handles: Vec<_> = (0..12) + .map(|n| { + let dir = dir.path().to_owned(); + let barrier = barrier.clone(); + std::thread::spawn(move || { + let bytes = format!("PNG capture {n}"); + barrier.wait(); + let path = save(&dir, "TV_fixed-time", bytes.as_bytes()).unwrap(); + (path, bytes) + }) + }) + .collect(); + let mut paths = std::collections::HashSet::new(); + for handle in handles { + let (path, bytes) = handle.join().unwrap(); + assert_eq!(std::fs::read(&path).unwrap(), bytes.as_bytes()); + assert!(paths.insert(path)); + } + assert_eq!(std::fs::read(existing).unwrap(), b"old capture"); + assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), 13); + } + + #[test] + fn failed_partial_write_publishes_nothing_and_preserves_prior_capture() { + let dir = tempfile::tempdir().unwrap(); + let existing = save(dir.path(), "TV", b"old capture").unwrap(); + let error = publish_screenshot(dir.path(), "TV", |file| { + file.write_all(b"partial")?; + Err(std::io::Error::other("injected write failure")) + }) + .unwrap_err(); + assert!(error.contains("injected write failure")); + assert_eq!(std::fs::read(existing).unwrap(), b"old capture"); + assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), 1); + } + + #[test] + fn exhausted_names_fail_without_overwriting_or_leaving_staging_files() { + let dir = tempfile::tempdir().unwrap(); + for n in 1..=MAX_CAPTURE_NAMES { + let name = if n == 1 { + "TV.png".into() + } else { + format!("TV ({n}).png") + }; + std::fs::write(dir.path().join(name), b"retained").unwrap(); + } + let error = save(dir.path(), "TV", b"new").unwrap_err(); + assert!(error.contains("Earlier captures were preserved")); + let entries: Vec<_> = std::fs::read_dir(dir.path()).unwrap().collect(); + assert_eq!(entries.len(), MAX_CAPTURE_NAMES as usize); + for entry in entries { + assert_eq!(std::fs::read(entry.unwrap().path()).unwrap(), b"retained"); + } + } + + #[test] + fn unavailable_directory_returns_an_error_without_a_saved_path() { + let dir = tempfile::tempdir().unwrap(); + assert!(save(&dir.path().join("missing"), "TV", b"new") + .unwrap_err() + .contains("stage screenshot")); + assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), 0); + } + + #[cfg(unix)] + #[test] + fn existing_symlink_target_is_not_followed_or_overwritten() { + let dir = tempfile::tempdir().unwrap(); + let target = dir.path().join("retained.png"); + std::fs::write(&target, b"retained").unwrap(); + let link = dir.path().join("TV.png"); + std::os::unix::fs::symlink(&target, &link).unwrap(); + let saved = save(dir.path(), "TV", b"new").unwrap(); + assert_eq!(saved, dir.path().join("TV (2).png")); + assert!(link.is_symlink()); + assert_eq!(std::fs::read(target).unwrap(), b"retained"); + assert_eq!(std::fs::read(saved).unwrap(), b"new"); + } } diff --git a/v2/crates/core/src/commands/state.rs b/v2/crates/core/src/commands/state.rs index 010f5418..560fcab7 100644 --- a/v2/crates/core/src/commands/state.rs +++ b/v2/crates/core/src/commands/state.rs @@ -322,6 +322,32 @@ mod tests { ) } + #[tokio::test] + async fn a_driver_without_a_server_skips_stop_without_dispatching_commands() { + let mock = MockAdb::default(); + let raw = mock.raw_log(); + let shell = mock.shell_log(); + let state = AppState::new( + Arc::new(mock), + AppListBundle::default(), + std::env::temp_dir(), + ); + assert!(matches!( + state.stop_adb_server(std::time::Duration::ZERO).await, + ServerStop::Skipped(_) + )); + assert!(matches!( + state + .adb_snapshot() + .await + .stop_server(std::time::Duration::ZERO) + .await, + ServerStop::Skipped(_) + )); + assert!(raw.lock().unwrap().is_empty()); + assert!(shell.lock().unwrap().is_empty()); + } + #[tokio::test] async fn every_snapshot_counts_until_its_last_clone_goes() { let state = state(); diff --git a/v2/mobile/.gitignore b/v2/mobile/.gitignore index e36fbda8..3a717420 100644 --- a/v2/mobile/.gitignore +++ b/v2/mobile/.gitignore @@ -11,3 +11,8 @@ key.properties *.jks *.keystore *.p12 +__pycache__/ + +# Screenshot frames — regenerable via `npm run screenshots`. gallery.gif and +# contact-sheet.png are committed. +/screenshots/frames/ diff --git a/v2/mobile/ANDROID-DEPENDENCIES.md b/v2/mobile/ANDROID-DEPENDENCIES.md new file mode 100644 index 00000000..2445e29e --- /dev/null +++ b/v2/mobile/ANDROID-DEPENDENCIES.md @@ -0,0 +1,45 @@ +# Android dependency notice inventory — release preparation + +Sources: app and ADB-plugin `build.gradle.kts`, plus the Android build files from locked +Tauri 2.11.2 and opener 2.5.4 Cargo registry packages. This inventory adds the previously omitted +AndroidX Browser dependency. It is not a resolved Gradle dependency report. + +| Declaration site | Maven coordinates / declared version | +|---|---| +| App | androidx.webkit:webkit:1.14.0 | +| App | androidx.appcompat:appcompat:1.7.1 | +| App | androidx.activity:activity-ktx:1.10.1 | +| App | com.google.android.material:material:1.12.0 | +| App | androidx.lifecycle:lifecycle-process:2.10.0 | +| ADB plugin | androidx.core:core-ktx:1.13.1 | +| ADB plugin | org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2 | +| Tauri Android | androidx.core:core-ktx:1.7.0; androidx.appcompat:appcompat:1.6.0; Material:1.7.0 | +| Tauri Android | com.fasterxml.jackson.core:jackson-databind:2.15.3 | +| Opener | androidx.browser:browser:1.8.0; androidx.core:core-ktx:1.9.0; Jackson Databind:2.15.3 | + +All listed libraries declare Apache-2.0 licensing. Official project sources: +https://android.googlesource.com/platform/frameworks/support/, +https://github.com/material-components/material-components-android, +https://github.com/Kotlin/kotlinx.coroutines, +https://github.com/FasterXML/jackson-databind, +https://github.com/FasterXML/jackson-datatypes-misc. +Do not confuse build plugins or test-only JUnit/Espresso with packaged runtime libraries. + +Before distribution, initialize the Android project through the supported Tauri build flow, +then save Gradle `:app:dependencies --configuration universalReleaseRuntimeClasspath` (confirm +configuration name in that generated project). Inventory every resolved artifact, license and +upstream NOTICE, including transitive Kotlin/Jackson dependencies; preserve any required texts. +Reconcile selected versions with this declaration inventory and regenerate notices. This final +resolved-graph/license-text gate remains OPEN; it cannot be cleared by the declaration table. +No Gradle dependency or package version is changed by this preparation. + +## Locked published-crate evidence + +The cached `tauri-2.11.2.crate` archive SHA-256 is +`437404997acf375d85f1177afa7e11bb971f274ed6a7b83a2a3e339015f4cc28`, exactly matching +`v2/Cargo.lock`. Its archived `tauri-2.11.2/mobile/android/build.gradle.kts` SHA-256 is +`aab1b0ecb929ea70b33ad42d7df55c185a3485f9609ec998047232b45df76a4d`; line 43 declares +`com.fasterxml.jackson.core:jackson-databind:2.15.3`. The local extracted file is byte-identical. +It does NOT declare jackson-datatype-json-org. A declaration seen in a newer cached Tauri version +must not be attributed to this locked package. This receipt verifies declarations, not Gradle +transitive resolution or packaged contents. diff --git a/v2/mobile/BACKLOG.md b/v2/mobile/BACKLOG.md index 7906d46e..3e4eee2b 100644 --- a/v2/mobile/BACKLOG.md +++ b/v2/mobile/BACKLOG.md @@ -1,5 +1,7 @@ # ATV Optimizer mobile backlog +Current owner release-polish findings and acceptance checks: [RELEASE-POLISH.md](RELEASE-POLISH.md). This supplement preserves the historical backlog below. + Updated 2026-09-05 America/Chicago (lifecycle evidence/current authority). Bryan's feedback records a debug build from `main` at `44d2d66` installed on the Pixel; this is owner-reported use, not a completed physical stability matrix. @@ -13,6 +15,25 @@ nothing in `v2/mobile` has been verified on a phone or TV since `44d2d66`. Accep the September 10 policy in `AGENTS.md`; physical items below remain gated. The older installation instructions are not current permission. +## Desktop parity pass (2026-10-06) + +Shipped to mobile on `mobile-parity-pass` (details in `HANDOFF.md`): Optimize search and app +sheet, Diagnostics suggestions, snapshot preview Now column, Apps Recognised section with +storage details, Report this app (#100) and long-press menu (#129), Launcher Setup Wraith card, +set-any-app-as-Home and separate stock disable (#157/#158), the Tweaks tab at desktop parity, Files +app-backup finder and TV-side delete (shared core, desktop #86 fix), and links opening in the +phone's browser. **Nothing here has run on a phone or TV.** Browser suites pass (207/207). + +Remaining parity gaps, in rough order: + +1. Device pass of everything above on the Pixel against a Shield and a Google TV. +2. Phone→TV upload in Files — needs an Android document picker (folds into P1.3 SAF). +3. Install APK / sideload from the phone. +4. Bug-report bundle (`collect_diagnostics`) with an explicit share step. +5. Patch-notes viewer. +6. List `androidx.browser` (pulled in by the opener plugin) and the other Gradle dependencies in + `scripts/notices-header.md`. + ## Correctness checkpoint Implemented in the current checkpoint: @@ -263,8 +284,8 @@ complete the physical matrix. 2026-09-04, unverified end-to-end; follow the manual test in `PAIRING-PLAN.md`: Pixel pairing service from the host first, then a Google TV, including the wrong-code and silent-reconnect checks). -3. Add Android SAF import/export and user-selected push destinations, then consider Google Drive - sync for complete APK bundles. +3. Add Android SAF import/export and user-selected push destinations (this is also what unblocks + phone→TV upload in Files), then consider Google Drive sync for complete APK bundles. 4. Add panic recovery, advanced reboot, permission/app-op controls, reinstall-existing, and a deliberate performance/correctness pass across Launcher, Tweaks, Files, and Backups. 5. ~~Signed commercial licensing~~ — done 2026-09-04 (`crates/core/src/license.rs`, diff --git a/v2/mobile/BILLING-COMMAND-MATRIX.json b/v2/mobile/BILLING-COMMAND-MATRIX.json new file mode 100644 index 00000000..a7874206 --- /dev/null +++ b/v2/mobile/BILLING-COMMAND-MATRIX.json @@ -0,0 +1,717 @@ +{ + "base": "f7897c1292d702fdfad1b77c3193312a641475b2", + "scope": "all registered mobile commands plus scoped opener; current gates, not target policy", + "commands": [ + { + "command": "wireless_discover", + "api": "wirelessDiscover", + "gate": "Free", + "backend": "src-tauri/src/wireless_commands.rs", + "callers": [ + "src/lib/session.svelte.ts", + "src/screens/Onboarding.svelte" + ] + }, + { + "command": "wireless_pair", + "api": "wirelessPair", + "gate": "Free", + "backend": "src-tauri/src/wireless_commands.rs", + "callers": [ + "src/lib/session.svelte.ts" + ] + }, + { + "command": "wireless_connect", + "api": "wirelessConnect", + "gate": "Free", + "backend": "src-tauri/src/wireless_commands.rs", + "callers": [ + "src/lib/session.svelte.ts" + ] + }, + { + "command": "wireless_cancel_connect", + "api": "wirelessCancelConnect", + "gate": "Free", + "backend": "src-tauri/src/wireless_commands.rs", + "callers": [ + "src/lib/session.svelte.ts" + ] + }, + { + "command": "wireless_disconnect", + "api": "wirelessDisconnect", + "gate": "Free", + "backend": "src-tauri/src/wireless_commands.rs", + "callers": [ + "src/lib/session.svelte.ts" + ] + }, + { + "command": "wireless_status", + "api": "wirelessStatus", + "gate": "Free", + "backend": "src-tauri/src/wireless_commands.rs", + "callers": [ + "src/lib/session.svelte.ts" + ] + }, + { + "command": "find_remote", + "api": "findRemote", + "gate": "Free", + "backend": "src-tauri/src/wireless_commands.rs", + "callers": [ + "src/components/FindRemoteButton.svelte" + ] + }, + { + "command": "activate_license", + "api": "activateLicense", + "gate": "Free", + "backend": "src-tauri/src/lib.rs", + "callers": [ + "src/lib/session.svelte.ts" + ] + }, + { + "command": "get_entitlement", + "api": "getEntitlement", + "gate": "Free", + "backend": "src-tauri/src/lib.rs", + "callers": [ + "src/lib/session.svelte.ts" + ] + }, + { + "command": "license_info", + "api": "licenseInfo", + "gate": "Free", + "backend": "../crates/core/src/commands/license.rs", + "callers": [ + "src/screens/More.svelte" + ] + }, + { + "command": "read_debug_log", + "api": "readDebugLog", + "gate": "Free", + "backend": "src-tauri/src/lib.rs", + "callers": [ + "src/screens/More.svelte" + ] + }, + { + "command": "list_devices", + "api": "listDevices", + "gate": "Free", + "backend": "../crates/core/src/commands/devices.rs", + "callers": [ + "src/lib/session.svelte.ts" + ] + }, + { + "command": "device_profile", + "api": "deviceProfile", + "gate": "Free", + "backend": "../crates/core/src/commands/devices.rs", + "callers": [] + }, + { + "command": "rename_device", + "api": "renameDevice", + "gate": "Free", + "backend": "../crates/core/src/commands/devices.rs", + "callers": [ + "src/screens/Devices.svelte" + ] + }, + { + "command": "health_report", + "api": "healthReport", + "gate": "Free", + "backend": "../crates/core/src/commands/health.rs", + "callers": [ + "src/lib/session.svelte.ts" + ] + }, + { + "command": "media_report", + "api": "mediaReport", + "gate": "Free", + "backend": "../crates/core/src/commands/health.rs", + "callers": [ + "src/screens/Diagnostics.svelte" + ] + }, + { + "command": "resource_sample", + "api": "resourceSample", + "gate": "Free", + "backend": "../crates/core/src/commands/health.rs", + "callers": [ + "src/screens/Diagnostics.svelte" + ] + }, + { + "command": "app_list_for_device", + "api": "appListForDevice", + "gate": "Free", + "backend": "../crates/core/src/commands/health.rs", + "callers": [ + "src/lib/session.svelte.ts", + "src/screens/Apps.svelte" + ] + }, + { + "command": "report_all", + "api": "reportAll", + "gate": "Free", + "backend": "../crates/core/src/commands/health.rs", + "callers": [] + }, + { + "command": "list_launchers", + "api": "listLaunchers", + "gate": "Free", + "backend": "../crates/core/src/commands/launcher.rs", + "callers": [ + "src/screens/Launcher.svelte" + ] + }, + { + "command": "current_launcher", + "api": "currentLauncher", + "gate": "Free", + "backend": "../crates/core/src/commands/launcher.rs", + "callers": [ + "src/screens/Launcher.svelte" + ] + }, + { + "command": "channel_provider_disabled", + "api": "channelProviderDisabled", + "gate": "Free", + "backend": "../crates/core/src/commands/launcher.rs", + "callers": [ + "src/screens/Launcher.svelte" + ] + }, + { + "command": "set_default_launcher", + "api": "setDefaultLauncher", + "gate": "LauncherTakeover", + "backend": "../crates/core/src/commands/launcher.rs", + "callers": [ + "src/screens/Launcher.svelte" + ] + }, + { + "command": "disable_launcher", + "api": "disableLauncher", + "gate": "CuratedDebloat", + "backend": "../crates/core/src/commands/launcher.rs", + "callers": [ + "src/screens/Launcher.svelte" + ] + }, + { + "command": "set_home_any", + "api": "setHomeAny", + "gate": "LauncherTakeover", + "backend": "../crates/core/src/commands/launcher.rs", + "callers": [ + "src/screens/Launcher.svelte" + ] + }, + { + "command": "disable_stock_launcher", + "api": "disableStockLauncher", + "gate": "LauncherTakeover", + "backend": "../crates/core/src/commands/launcher.rs", + "callers": [ + "src/screens/Launcher.svelte" + ] + }, + { + "command": "disable_setup_helper", + "api": "disableSetupHelper", + "gate": "LauncherTakeover", + "backend": "../crates/core/src/commands/launcher.rs", + "callers": [ + "src/screens/Launcher.svelte" + ] + }, + { + "command": "force_stop", + "api": "forceStop", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte" + ] + }, + { + "command": "take_screenshot", + "api": "takeScreenshot", + "gate": "Free", + "backend": "../crates/core/src/commands/screenshot.rs", + "callers": [ + "src/screens/Dashboard.svelte" + ] + }, + { + "command": "package_states", + "api": "packageStates", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/lib/session.svelte.ts", + "src/screens/Apps.svelte", + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "list_other_packages", + "api": "listOtherPackages", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/screens/Apps.svelte" + ] + }, + { + "command": "list_installed_packages", + "api": "listInstalledPackages", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/screens/Backups.svelte", + "src/screens/Diagnostics.svelte", + "src/screens/Launcher.svelte" + ] + }, + { + "command": "app_memory_map", + "api": "appMemoryMap", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte" + ] + }, + { + "command": "app_storage_map", + "api": "appStorageMap", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte" + ] + }, + { + "command": "app_apk_size", + "api": "appApkSize", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppDetailSheet.svelte" + ] + }, + { + "command": "app_usage_map", + "api": "appUsageMap", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte" + ] + }, + { + "command": "safety_info", + "api": "safetyInfo", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte", + "src/screens/Diagnostics.svelte", + "src/screens/Optimize.svelte" + ] + }, + { + "command": "process_safety_info", + "api": "processSafetyInfo", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/screens/Diagnostics.svelte" + ] + }, + { + "command": "trim_caches", + "api": "trimCaches", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/screens/Dashboard.svelte" + ] + }, + { + "command": "disable_package", + "api": "disablePackage", + "gate": "CuratedDebloat", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte", + "src/screens/Optimize.svelte", + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "enable_package", + "api": "enablePackage", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte", + "src/screens/Launcher.svelte", + "src/screens/Optimize.svelte", + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "uninstall_package", + "api": "uninstallPackage", + "gate": "CuratedDebloat", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte", + "src/screens/Optimize.svelte" + ] + }, + { + "command": "reinstall_existing", + "api": "reinstallExisting", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte" + ] + }, + { + "command": "set_app_permission", + "api": "setAppPermission", + "gate": "AppPermissionWrite", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "app_permission_state", + "api": "appPermissionState", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "set_app_op", + "api": "setAppOp", + "gate": "AppPermissionWrite", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [] + }, + { + "command": "get_app_op", + "api": "getAppOp", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [] + }, + { + "command": "open_play_store", + "api": "openPlayStore", + "gate": "Free", + "backend": "../crates/core/src/commands/apps.rs", + "callers": [ + "src/components/AppSheetHost.svelte", + "src/screens/Apps.svelte", + "src/screens/Launcher.svelte" + ] + }, + { + "command": "send_text", + "api": "sendText", + "gate": "Free", + "backend": "../crates/core/src/commands/input.rs", + "callers": [ + "src/screens/Remote.svelte" + ] + }, + { + "command": "send_key", + "api": "sendKey", + "gate": "Free", + "backend": "../crates/core/src/commands/input.rs", + "callers": [ + "src/screens/Remote.svelte" + ] + }, + { + "command": "open_settings", + "api": "openSettings", + "gate": "Free", + "backend": "../crates/core/src/commands/input.rs", + "callers": [ + "src/screens/Remote.svelte" + ] + }, + { + "command": "remote_warm", + "api": "remoteWarm", + "gate": "Free", + "backend": "../crates/core/src/commands/input.rs", + "callers": [ + "src/screens/Remote.svelte" + ] + }, + { + "command": "panic_recovery", + "api": "panicRecovery", + "gate": "Free", + "backend": "../crates/core/src/commands/recovery.rs", + "callers": [ + "src/screens/More.svelte" + ] + }, + { + "command": "reboot_device", + "api": "rebootDevice", + "gate": "conditional: normal Free; recovery/bootloader AdvancedReboot", + "backend": "../crates/core/src/commands/reboot.rs", + "callers": [ + "src/screens/Dashboard.svelte", + "src/screens/More.svelte" + ] + }, + { + "command": "prepare_optimize", + "api": "prepareOptimize", + "gate": "OptimizeWizard", + "backend": "../crates/core/src/commands/optimize.rs", + "callers": [ + "src/screens/Optimize.svelte" + ] + }, + { + "command": "apply_performance_settings", + "api": "applyPerformanceSettings", + "gate": "TweaksWrite", + "backend": "../crates/core/src/commands/optimize.rs", + "callers": [ + "src/screens/Optimize.svelte" + ] + }, + { + "command": "get_tweaks", + "api": "getTweaks", + "gate": "Free", + "backend": "../crates/core/src/commands/tuning.rs", + "callers": [ + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "write_setting", + "api": "writeSetting", + "gate": "TweaksWrite", + "backend": "../crates/core/src/commands/tuning.rs", + "callers": [ + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "get_display_scaling", + "api": "getDisplayScaling", + "gate": "Free", + "backend": "../crates/core/src/commands/tuning.rs", + "callers": [ + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "set_display_scaling", + "api": "setDisplayScaling", + "gate": "TweaksWrite", + "backend": "../crates/core/src/commands/tuning.rs", + "callers": [ + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "get_private_dns", + "api": "getPrivateDns", + "gate": "Free", + "backend": "../crates/core/src/commands/tuning.rs", + "callers": [ + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "set_private_dns", + "api": "setPrivateDns", + "gate": "TweaksWrite", + "backend": "../crates/core/src/commands/tuning.rs", + "callers": [ + "src/screens/Tweaks.svelte" + ] + }, + { + "command": "delete_snapshot", + "api": "deleteSnapshot", + "gate": "Snapshot", + "backend": "../crates/core/src/commands/snapshot.rs", + "callers": [ + "src/screens/Snapshots.svelte" + ] + }, + { + "command": "snapshot_dir_path", + "api": "snapshotDirPath", + "gate": "Snapshot", + "backend": "../crates/core/src/commands/snapshot.rs", + "callers": [] + }, + { + "command": "list_snapshots", + "api": "listSnapshots", + "gate": "Snapshot", + "backend": "../crates/core/src/commands/snapshot.rs", + "callers": [ + "src/screens/Snapshots.svelte" + ] + }, + { + "command": "save_snapshot", + "api": "saveSnapshot", + "gate": "Snapshot", + "backend": "../crates/core/src/commands/snapshot.rs", + "callers": [ + "src/screens/Launcher.svelte", + "src/screens/Snapshots.svelte" + ] + }, + { + "command": "apply_snapshot", + "api": "applySnapshot", + "gate": "Snapshot", + "backend": "../crates/core/src/commands/snapshot.rs", + "callers": [ + "src/screens/Snapshots.svelte" + ] + }, + { + "command": "preview_apply", + "api": "previewApply", + "gate": "Snapshot", + "backend": "../crates/core/src/commands/snapshot.rs", + "callers": [ + "src/screens/Snapshots.svelte" + ] + }, + { + "command": "list_remote_dir", + "api": "listRemoteDir", + "gate": "Free", + "backend": "src-tauri/src/file_commands.rs", + "callers": [ + "src/screens/Files.svelte" + ] + }, + { + "command": "pull_file", + "api": "pullFile", + "gate": "Free", + "backend": "src-tauri/src/file_commands.rs", + "callers": [ + "src/screens/Files.svelte" + ] + }, + { + "command": "backup_apk", + "api": "backupApk", + "gate": "Free", + "backend": "src-tauri/src/file_commands.rs", + "callers": [ + "src/screens/Backups.svelte" + ] + }, + { + "command": "restore_apk_backup", + "api": "restoreApkBackup", + "gate": "Free", + "backend": "src-tauri/src/file_commands.rs", + "callers": [ + "src/screens/Backups.svelte" + ] + }, + { + "command": "list_backups", + "api": "listBackups", + "gate": "Free", + "backend": "src-tauri/src/file_commands.rs", + "callers": [ + "src/screens/Backups.svelte" + ] + }, + { + "command": "delete_backup", + "api": "deleteBackup", + "gate": "Free", + "backend": "src-tauri/src/file_commands.rs", + "callers": [ + "src/screens/Backups.svelte" + ] + }, + { + "command": "find_files", + "api": "findFiles", + "gate": "Free", + "backend": "src-tauri/src/file_commands.rs", + "callers": [ + "src/screens/Files.svelte" + ] + }, + { + "command": "delete_path", + "api": "deletePath", + "gate": "Free", + "backend": "src-tauri/src/file_commands.rs", + "callers": [ + "src/screens/Files.svelte" + ] + }, + { + "command": "plugin:opener|open_url", + "api": "openUrl", + "gate": "Free", + "backend": "src/lib/api.ts (scoped opener plugin)", + "callers": [ + "src/components/AppReportSheet.svelte", + "src/screens/Launcher.svelte" + ] + } + ] +} diff --git a/v2/mobile/BILLING-COMMAND-MATRIX.md b/v2/mobile/BILLING-COMMAND-MATRIX.md new file mode 100644 index 00000000..6f2e5d81 --- /dev/null +++ b/v2/mobile/BILLING-COMMAND-MATRIX.md @@ -0,0 +1,124 @@ +# Current mobile command and UI entitlement matrix + +Audited base: `f7897c1`. This is current behavior, not a pricing/gate migration. All 77 registered/invoked commands are listed. Free means no Pro entitlement prerequisite; connection, permission, safety and path restrictions still apply. Signed-key activation remains validated, not an unrestricted unlock. + +Callers name actual API call sites; `session.svelte.ts` mediates Onboarding/Devices connections and Dashboard/Diagnostics caches. Screens can show Pro badges without blocking navigation. An unused API wrapper is explicitly shown as having no direct caller. + +| Command | Current backend gate | Command source (relative to mobile) | API callers | +|---|---|---|---| +| `wireless_discover` | Free | `src-tauri/src/wireless_commands.rs` | `src/lib/session.svelte.ts`, `src/screens/Onboarding.svelte` | +| `wireless_pair` | Free | `src-tauri/src/wireless_commands.rs` | `src/lib/session.svelte.ts` | +| `wireless_connect` | Free | `src-tauri/src/wireless_commands.rs` | `src/lib/session.svelte.ts` | +| `wireless_cancel_connect` | Free | `src-tauri/src/wireless_commands.rs` | `src/lib/session.svelte.ts` | +| `wireless_disconnect` | Free | `src-tauri/src/wireless_commands.rs` | `src/lib/session.svelte.ts` | +| `wireless_status` | Free | `src-tauri/src/wireless_commands.rs` | `src/lib/session.svelte.ts` | +| `find_remote` | Free | `src-tauri/src/wireless_commands.rs` | `src/components/FindRemoteButton.svelte` | +| `activate_license` | Free | `src-tauri/src/lib.rs` | `src/lib/session.svelte.ts` | +| `get_entitlement` | Free | `src-tauri/src/lib.rs` | `src/lib/session.svelte.ts` | +| `license_info` | Free | `../crates/core/src/commands/license.rs` | `src/screens/More.svelte` | +| `read_debug_log` | Free | `src-tauri/src/lib.rs` | `src/screens/More.svelte` | +| `list_devices` | Free | `../crates/core/src/commands/devices.rs` | `src/lib/session.svelte.ts` | +| `device_profile` | Free | `../crates/core/src/commands/devices.rs` | No direct API caller | +| `rename_device` | Free | `../crates/core/src/commands/devices.rs` | `src/screens/Devices.svelte` | +| `health_report` | Free | `../crates/core/src/commands/health.rs` | `src/lib/session.svelte.ts` | +| `media_report` | Free | `../crates/core/src/commands/health.rs` | `src/screens/Diagnostics.svelte` | +| `resource_sample` | Free | `../crates/core/src/commands/health.rs` | `src/screens/Diagnostics.svelte` | +| `app_list_for_device` | Free | `../crates/core/src/commands/health.rs` | `src/lib/session.svelte.ts`, `src/screens/Apps.svelte` | +| `report_all` | Free | `../crates/core/src/commands/health.rs` | No direct API caller | +| `list_launchers` | Free | `../crates/core/src/commands/launcher.rs` | `src/screens/Launcher.svelte` | +| `current_launcher` | Free | `../crates/core/src/commands/launcher.rs` | `src/screens/Launcher.svelte` | +| `channel_provider_disabled` | Free | `../crates/core/src/commands/launcher.rs` | `src/screens/Launcher.svelte` | +| `set_default_launcher` | LauncherTakeover | `../crates/core/src/commands/launcher.rs` | `src/screens/Launcher.svelte` | +| `disable_launcher` | CuratedDebloat | `../crates/core/src/commands/launcher.rs` | `src/screens/Launcher.svelte` | +| `set_home_any` | LauncherTakeover | `../crates/core/src/commands/launcher.rs` | `src/screens/Launcher.svelte` | +| `disable_stock_launcher` | LauncherTakeover | `../crates/core/src/commands/launcher.rs` | `src/screens/Launcher.svelte` | +| `disable_setup_helper` | LauncherTakeover | `../crates/core/src/commands/launcher.rs` | `src/screens/Launcher.svelte` | +| `force_stop` | Free | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte` | +| `take_screenshot` | Free | `../crates/core/src/commands/screenshot.rs` | `src/screens/Dashboard.svelte` | +| `package_states` | Free | `../crates/core/src/commands/apps.rs` | `src/lib/session.svelte.ts`, `src/screens/Apps.svelte`, `src/screens/Tweaks.svelte` | +| `list_other_packages` | Free | `../crates/core/src/commands/apps.rs` | `src/screens/Apps.svelte` | +| `list_installed_packages` | Free | `../crates/core/src/commands/apps.rs` | `src/screens/Backups.svelte`, `src/screens/Diagnostics.svelte`, `src/screens/Launcher.svelte` | +| `app_memory_map` | Free | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte` | +| `app_storage_map` | Free | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte` | +| `app_apk_size` | Free | `../crates/core/src/commands/apps.rs` | `src/components/AppDetailSheet.svelte` | +| `app_usage_map` | Free | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte` | +| `safety_info` | Free | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte`, `src/screens/Diagnostics.svelte`, `src/screens/Optimize.svelte` | +| `process_safety_info` | Free | `../crates/core/src/commands/apps.rs` | `src/screens/Diagnostics.svelte` | +| `trim_caches` | Free | `../crates/core/src/commands/apps.rs` | `src/screens/Dashboard.svelte` | +| `disable_package` | CuratedDebloat | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte`, `src/screens/Optimize.svelte`, `src/screens/Tweaks.svelte` | +| `enable_package` | Free | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte`, `src/screens/Launcher.svelte`, `src/screens/Optimize.svelte`, `src/screens/Tweaks.svelte` | +| `uninstall_package` | CuratedDebloat | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte`, `src/screens/Optimize.svelte` | +| `reinstall_existing` | Free | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte` | +| `set_app_permission` | AppPermissionWrite | `../crates/core/src/commands/apps.rs` | `src/screens/Tweaks.svelte` | +| `app_permission_state` | Free | `../crates/core/src/commands/apps.rs` | `src/screens/Tweaks.svelte` | +| `set_app_op` | AppPermissionWrite | `../crates/core/src/commands/apps.rs` | No direct API caller | +| `get_app_op` | Free | `../crates/core/src/commands/apps.rs` | No direct API caller | +| `open_play_store` | Free | `../crates/core/src/commands/apps.rs` | `src/components/AppSheetHost.svelte`, `src/screens/Apps.svelte`, `src/screens/Launcher.svelte` | +| `send_text` | Free | `../crates/core/src/commands/input.rs` | `src/screens/Remote.svelte` | +| `send_key` | Free | `../crates/core/src/commands/input.rs` | `src/screens/Remote.svelte` | +| `open_settings` | Free | `../crates/core/src/commands/input.rs` | `src/screens/Remote.svelte` | +| `remote_warm` | Free | `../crates/core/src/commands/input.rs` | `src/screens/Remote.svelte` | +| `panic_recovery` | Free | `../crates/core/src/commands/recovery.rs` | `src/screens/More.svelte` | +| `reboot_device` | conditional: normal Free; recovery/bootloader AdvancedReboot | `../crates/core/src/commands/reboot.rs` | `src/screens/Dashboard.svelte`, `src/screens/More.svelte` | +| `prepare_optimize` | OptimizeWizard | `../crates/core/src/commands/optimize.rs` | `src/screens/Optimize.svelte` | +| `apply_performance_settings` | TweaksWrite | `../crates/core/src/commands/optimize.rs` | `src/screens/Optimize.svelte` | +| `get_tweaks` | Free | `../crates/core/src/commands/tuning.rs` | `src/screens/Tweaks.svelte` | +| `write_setting` | TweaksWrite | `../crates/core/src/commands/tuning.rs` | `src/screens/Tweaks.svelte` | +| `get_display_scaling` | Free | `../crates/core/src/commands/tuning.rs` | `src/screens/Tweaks.svelte` | +| `set_display_scaling` | TweaksWrite | `../crates/core/src/commands/tuning.rs` | `src/screens/Tweaks.svelte` | +| `get_private_dns` | Free | `../crates/core/src/commands/tuning.rs` | `src/screens/Tweaks.svelte` | +| `set_private_dns` | TweaksWrite | `../crates/core/src/commands/tuning.rs` | `src/screens/Tweaks.svelte` | +| `delete_snapshot` | Snapshot | `../crates/core/src/commands/snapshot.rs` | `src/screens/Snapshots.svelte` | +| `snapshot_dir_path` | Snapshot | `../crates/core/src/commands/snapshot.rs` | No direct API caller | +| `list_snapshots` | Snapshot | `../crates/core/src/commands/snapshot.rs` | `src/screens/Snapshots.svelte` | +| `save_snapshot` | Snapshot | `../crates/core/src/commands/snapshot.rs` | `src/screens/Launcher.svelte`, `src/screens/Snapshots.svelte` | +| `apply_snapshot` | Snapshot | `../crates/core/src/commands/snapshot.rs` | `src/screens/Snapshots.svelte` | +| `preview_apply` | Snapshot | `../crates/core/src/commands/snapshot.rs` | `src/screens/Snapshots.svelte` | +| `list_remote_dir` | Free | `src-tauri/src/file_commands.rs` | `src/screens/Files.svelte` | +| `pull_file` | Free | `src-tauri/src/file_commands.rs` | `src/screens/Files.svelte` | +| `backup_apk` | Free | `src-tauri/src/file_commands.rs` | `src/screens/Backups.svelte` | +| `restore_apk_backup` | Free | `src-tauri/src/file_commands.rs` | `src/screens/Backups.svelte` | +| `list_backups` | Free | `src-tauri/src/file_commands.rs` | `src/screens/Backups.svelte` | +| `delete_backup` | Free | `src-tauri/src/file_commands.rs` | `src/screens/Backups.svelte` | +| `find_files` | Free | `src-tauri/src/file_commands.rs` | `src/screens/Files.svelte` | +| `delete_path` | Free | `src-tauri/src/file_commands.rs` | `src/screens/Files.svelte` | +| `plugin:opener|open_url` | Free | `src/lib/api.ts (scoped opener plugin)` | `src/components/AppReportSheet.svelte`, `src/screens/Launcher.svelte` | + +## UI and recovery findings + +- Onboarding/Devices and compatibility reads are Free. Read errors/unknown evidence must stay honest. +- More marks Optimize, Launcher and Snapshots as Pro tools; navigation is still allowed, with per-action LOCKED handling. Tweaks current-value reads are Free; setters are Pro. +- Apps enable/reinstall and More emergency recovery are Free; the latter re-enables disabled packages rather than undoing every change. Existing UI/backend behavior is tested without widening gates. +- Optimize restore still requires Pro to prepare the plan; snapshots list/delete/apply are Pro-gated. Snapshot apply can disable packages and change settings, so it cannot be made Free as arbitrary undo. +- Settings, permission and Home before-value restore remain gated or incomplete. A bounded journal/undo mechanism needs separate review. This slice creates no persistent journal and does not make arbitrary writes free. +- FileManager/BackupClone enum labels do not imply enforcement: mobile file/backup handlers currently have no Pro prerequisite. No gate changes are proposed here. +- Remote basic input, file paths, package names, safety checks and connection-generation fencing remain independent safety boundaries. +- The new entitlement preview is isolated and unused by session/backend authorization; store purchase callbacks alone cannot grant real Pro. No real billing adapter or service exists in this slice. + +## Slice boundary and validation + +`entitlementPreview.ts` is a pure, ephemeral reducer. `EntitlementPreview.svelte` is an isolated +presentation component and is not imported into the running app. Only the test harness mounts +it; the simulated store lives under tests and cannot emit a verified verdict. Neither modifies +`session.entitlement`, AppState, license files, TV state or purchase history. Verified/revoked +reducer events are simulation inputs, not an implemented security boundary or provider verdict. +No persisted entitlement, purchase verification service, account system, product, price, payment +call, offline cache policy, tip URL or before-value journal is added. Production integration +requires the separately reviewed Play verification and free-undo design. + +Run `npm test`, `npm run check` and `npm run build` in mobile. Shared-core tests +`free_user_can_` exercise actual recovery implementations against mock ADB after entitlement +downgrade, not physical devices. The emergency recovery wrapper now delegates unchanged behavior +to a private helper so the Free-state path can be called in a unit test. No command gate changes. +The matrix coverage test detects added/removed registered commands; it is not a substitute for +reviewing future gate changes or auditing every indirect call chain. + +The approved product direction is documented separately in PR170: free compatibility checks, +$2.99 one-time Pro, no timed trial, optional benefit-free tips and normal Play promos for existing +supporters. This slice does not configure any of those products or change current gates. + +Local evidence: 450 shared-core tests and strict core Clippy pass; integrated mobile full suite 224/224 +(including cancellation and Free-recovery UI checks) passes. +Mobile check reports 0 errors / 0 warnings; build, Rust formatting and diff checks pass. These are +Mac host/mock/browser results, not Android transport or purchase-validation evidence. The branch +preserves merged release-preparation ancestry `7fe0e6a` through normal merge `a75605c`. diff --git a/v2/mobile/BILLING-IMPLEMENTATION-PLAN.md b/v2/mobile/BILLING-IMPLEMENTATION-PLAN.md new file mode 100644 index 00000000..30155f8f --- /dev/null +++ b/v2/mobile/BILLING-IMPLEMENTATION-PLAN.md @@ -0,0 +1,160 @@ +# Android commerce implementation plan — 2026-10-16 + +Status: draft plan only. No purchase code, product, account, credential, backend, payment or tip +link is configured by this change. Base: merged mobile safety work `f7897c1`. Release-preparation +PR169 remains a separate review. Nothing here authorizes a store submission or real charge. + +## Approved product direction + +- Free compatibility checks before purchase; one-time non-consumable Pro, intended US price $2.99. +- No timed trial, subscription or recurring charge. +- Complimentary Pro for existing Ko-fi supporters uses the normal Google Play one-time-product + promo redemption flow and the same verification/restore logic. No separate APK, client-only + unlock, hard-coded supporter key or alternate entitlement bypass. No codes issued or outreach + performed by this plan. +- Optional pure support tips confer no app functionality, entitlement, digital item, badge, + priority support or other digital benefit. External tips remain disabled with no URL until + the owner approves a destination and applicable Play policy compliance is confirmed. +- Preserve free recovery/undo even after payment failure, refund, revocation or loss of access. +- Display Google's localized ProductDetails price and terms, not a hard-coded universal price. + USD intent does not establish other currencies, taxes or regional price settings. + +## Current gate audit (no gates changed) + +| Existing path | Current evidence / implication | +|---|---| +| Discovery/connect/device status, health and package reads | Free API paths; keep enough real-device evidence visible to assess compatibility before purchase. Never claim compatibility from fabricated/unknown responses. | +| `apps::enable_package_impl`, `apps::reinstall_existing` | No require_pro gate in these recovery paths; retain free access and existing package/identity safeguards. | +| `recovery::panic_recovery`, More emergency recovery | Not Pro-gated. Keep reachable without billing service, entitlement or network verification. It re-enables packages; it is not a complete settings rollback. | +| `apps::disable_package_impl`, uninstall | CuratedDebloat Pro gate; preserve safety/last-home checks independently of entitlement. | +| `optimize::prepare_optimize_impl` | OptimizeWizard gate currently blocks plan preparation. Compatibility UI must not claim a free preview exists until separately implemented/tested. | +| Launcher set Home / disable stock | LauncherTakeover gates; re-enabling a package remains free. Audit UI reachability after downgrade and safe restoration of the prior Home choice. | +| `tuning::write_setting`, scaling and DNS setters | TweaksWrite gates also cover restoring old values. Thus current implementation does NOT prove all settings undo is free. Resolve this before paid release. | +| `apps::set_app_permission_impl`, app-op writes | AppPermissionWrite gates; distinguish new changes from narrowly authorized undo. | +| Snapshot list/delete/save/plan/apply | Snapshot gates apply even to list/delete. Existing snapshots re-apply a disabled/settings configuration; they are NOT complete undo and can perform new destructive changes. Do not simply make arbitrary apply free. | +| `Feature::FileManager` / BackupClone | Enum entries alone do not establish enforcement; backlog notes ungated paths. Inventory actual call sites before changing pricing gates. | +| `activate_license`, startup license.json | Offline signed-key flow, not Play purchase or restore. Keep legacy entitlement provenance separate; do not convert unverified local JSON into a Play purchase. | + +Phase 0 must finish the full command/UI matrix and add Free-state tests for every recovery path. +For reversible settings/permissions/Home changes, propose a bounded recorded-before-value undo +mechanism: exact verified device identity, operation provenance, expected-current-value guard, +finite allowed keys/packages, explicit confirmation, and no arbitrary shell/settings bypass. +Persist before mutation; preserve journal/snapshots through downgrade; never auto-replay TV writes +on billing callbacks. Details and migration need separate review before implementation. Do not +advertise universal undo until this gate is met. Scope does not promise to recover uninstalled +third-party APKs or missing data. + +## Supporter promo redemption and later roadmap + +Plan Play one-time-product promo codes for owner-selected existing Ko-fi supporters. Eligibility, +code availability, expiry, quotas and account requirements must be verified in the existing Play +account against the official promo documentation before issuance. Do not invent numeric limits. +Redeemed purchases must follow the same PURCHASED verification, idempotent delivery, +acknowledgment and owned-purchase restore path; promo purchases may lack a normal order ID, so +purchase tokens remain the processing key. Test redemption inside/outside the app, app installed +later, already-owned Pro, expired/invalid/used codes, repeated restore and refund/revocation +where applicable. Do not require supporters to buy again merely because a promo purchase lacks +an order ID. No supporter list, promo code or private purchase data belongs in source control. +Owner selection, code creation and distribution/outreach remain separate authorized actions. + +Keep the current repository for Android launch. Root `LICENSE` states all rights reserved and +publishes source for transparency/review; source availability is not a permissive reuse license. +No visibility, license or repository split is proposed or performed here. Native client code must +never contain publisher credentials or private verification/signing secrets regardless of visibility. + +Record iOS as **post-Android launch** roadmap work, not this implementation: separately assess +platform transport feasibility, App Store policy, StoreKit verification/restore and licensing, +privacy and physical-device validation. Do not assume Android Play entitlements automatically +transfer to Apple purchases or promise iOS feature parity/date before that assessment. + +## Planned architecture + +1. Android-native Google Play Billing adapter behind a narrow Tauri plugin; choose/pin a currently + supported Billing Library at implementation preflight and add its resolved notices. Connection, + product query, launch, updates and owned-purchase query are separate operations. Browser tests + use an injected fake; unsupported hosts return unavailable, never fabricated Pro. +2. Pure Rust entitlement state/reducer with explicit source (Play verified / legacy signed key), + verification status and retryable errors; retain one canonical backend enforcement layer. + A frontend boolean or successful billing-sheet launch never grants Pro. Coalesce duplicate + launches and fence asynchronous results by purchase/account/request identity, not TV endpoint. +3. A narrowly scoped verification service interface, mocked until hosting/authentication is + reviewed. Sensitive Google publisher credentials stay server-side. Verify purchase token, + package, exact approved product/quantity and PURCHASED state with Google before entitlement. + Store token processing idempotently: repeated delivery/restore of the same valid purchase must + not double-grant, double-acknowledge or be mistaken for fraud. An order ID is not the token key. +4. Authenticating the caller and proving token ownership across restore/reinstall needs an exact + design before exposing the service. Do not ship a public token-to-Pro oracle, assume a local + device ID authenticates a purchase, or invent accounts/credentials. Account requirements, + service hosting, retention and any signed offline-assertion key custody remain owner/security + gates; this plan creates none of them. +5. Verified entitlement must be durably committed before acknowledgment. Prefer server-side + acknowledgment and a durable idempotent retry queue, so app termination does not lose delivery. + Do not consume this non-consumable. Acknowledge promptly and monitor Google's deadline; pending + purchases are not acknowledged or granted until PURCHASED. Duplicate acknowledged deliveries + safely reconcile. No real refund API calls are authorized by this plan. +6. Proposed UX: compatibility result → localized Pro offer → Play purchase sheet → pending / + verifying / purchased. Provide Restore purchases, retry, and support paths without hiding free + tools. No provider or release product ID is guessed or created. Purchase unavailable remains + an honest unavailable state, not an invitation to an unapproved external checkout. + +## Required state transitions and recovery + +| Event | Required behavior | +|---|---| +| Product query unavailable / disconnected billing | Explain unavailability; keep compatibility and recovery usable; bounded reconnect/retry, no duplicate purchase launch. | +| User cancels | Return quietly to Free/current verified entitlement; cancellation is not a charge or a TV failure. | +| PENDING | Show pending; no Pro grant, no acknowledgment; reconcile purchase updates and foreground owned purchases. | +| PURCHASED but verification times out | Show verification pending, persist retry intent, no first-time Pro grant based only on client callback. | +| Verified PURCHASED | Persist entitlement and idempotent delivery, then acknowledge; expose failures/retries without double-charging. | +| Already owned | Reconcile owned purchases and restore rather than launching another charge. | +| Restore / reinstall / second eligible phone | Query owned non-consumable purchases, reverify through the same path, restore idempotently; no charge, no requirement to retain a copied license key. | +| Process death during purchase/ack | Recover from Play-owned queries and durable service state; never rely on one callback. | +| Known revoked / canceled entitlement | Remove Play-derived Pro for new premium actions; preserve independently valid legacy source if applicable, customer artifacts and free recovery. | +| Unknown network error | Do not equate temporary inability to query with confirmed revocation or claim refunds are observed instantly offline. | + +## Refunds, revocation and offline behavior + +Reconcile foreground owned-purchase queries with verified server state. Use authenticated, +idempotent RTDN handling and the appropriate Google API lookup; a notification is a wake-up, +not an independently trusted entitlement assertion. Cover voided/refunded/revoked one-time +purchases and delayed/reordered notifications; do not revoke on a notification type unrelated +to the approved product. Backfill gaps with bounded reconciliation/Voided Purchases handling. + +No design can discover a new refund while completely offline. Proposals for a cached verified +entitlement must explicitly define freshness, tamper/clock handling and the maximum offline +revocation delay. That policy and any key/service needed to enforce it remain unresolved; it is +not a customer trial and cannot silently become one. Never grant an initial purchase offline. +On fresh install offline, compatibility and free recovery work; purchase restoration cannot be +claimed complete until ownership is reverified. Do not promise lifetime offline irrevocability. + +## Bounded implementation sequence and tests + +1. Free recovery gate audit and failing-before/passing-after regression PR. Verify downgrade + cannot trap the user with app-written settings, lose snapshots, or trigger unsolicited undo. +2. Pure entitlement reducer, fake store adapter and UI state PR. No live payment calls; exhaustive + pending/cancel/error/duplicate/reorder/late response/account switch/process-death tests. +3. Native billing plugin and verifier contract PR, disabled without approved runtime configuration. + Mock package/product/token validation, replay resistance and legitimate repeated restores. +4. Verification/ack/revocation service implementation only after a separately approved exact + hosting/authentication/key/retention proposal. Test transactional delivery, acknowledgment + failure/retry, RTDN duplicates and refunds; never place publisher secrets in client bundles. +5. Store sandbox test only after product/account authorization: pending-to-paid/canceled, offline + restart, already-owned, restore, refund/revoke, same account across phones and account switch. + Capture simulated videos first; real test purchases and physical-device operations need their + own bounded execution scope. No real customer charges during verification. +6. Refresh privacy/Data safety, notices, localized offer/restore/support copy and release checklist. + Tips remain disabled until approved; prove no benefit/entitlement change when tipped. A tip + exception is not assumed merely because a button says "support". + +## Official references checked 2026-10-16 + +- Purchase integration: https://developer.android.com/google/play/billing/integrate +- Verification/ack/voided purchases: https://developer.android.com/google/play/billing/security +- One-time lifecycle: https://developer.android.com/google/play/billing/lifecycle/one-time +- Notifications: https://developer.android.com/google/play/billing/rtdn-reference +- Current product lookup: https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.productsv2/getproductpurchasev2 +- Promo reference (recheck in the account before issuance): https://support.google.com/googleplay/android-developer/answer/6321495 +- Payment/tip applicability: https://support.google.com/googleplay/android-developer/answer/9858738 + +Check API/library versions and policy again when implementing. These references are technical +inputs, not proof of account eligibility or a completed policy review. diff --git a/v2/mobile/DATA-INVENTORY.md b/v2/mobile/DATA-INVENTORY.md new file mode 100644 index 00000000..5a319579 --- /dev/null +++ b/v2/mobile/DATA-INVENTORY.md @@ -0,0 +1,34 @@ +# Mobile data inventory and privacy-policy preparation draft + +Status: internal factual inventory, not approved legal text or a published policy. No owner, +contact, retention promise, payment provider or jurisdiction is invented here. Review against the +final signed artifact, merged manifest and SDK graph before completing store disclosures. + +| Data / activity | Code evidence | Current behavior / disclosure topic | +|---|---|---| +| Saved TV endpoints, labels, hardware ID, fingerprint, auto-connect preference | `src/lib/savedDevices.ts`, `identity.ts`, `session.svelte.ts` | Local saved-TV storage; identity distinguishes TVs, never merely an IP. Explain removal and automatic reconnect controls. | +| ADB authorization / pairing material | `src-tauri/src/wireless_adb.rs`, vendored adb_client | Local authentication material enabling access to TVs the user authorizes; disclose safeguarding and how to revoke on the TV. | +| TV installed-app inventory, diagnostics, settings, screenshots and remote input | shared core commands, `src/lib/api.ts` | Sent between phone and selected TV. Legacy ADB and TLS pairing are distinct transports; do not promise all LAN traffic is encrypted. | +| Pulled files, APK bundles and snapshots | `src-tauri/src/file_commands.rs`, shared snapshot/files commands | App storage can contain user/device information. SAF export is not implemented. Verify deletion and OS backup behavior before promising retention/deletion. | +| License key and visible licensee | `src-tauri/src/lib.rs`, `LICENSING.md` | `license.json` in app data; offline signature verification at startup. Licensee may be a name/email embedded by issuer. No purchase/restore backend is implemented. | +| Debug/diagnostic logs | `src/lib/log.ts`, native tracing and `read_debug_log` | Explain logs and redaction limits. Review before sharing; logs are not automatically proof of anonymization. | +| External links | `api.openUrl`, source-site buttons, report-app flow | User-directed browser navigation leaves the app and follows destination practices. No general promise that the app never opens the internet. | + +Manifest declarations include INTERNET, ACCESS_NETWORK_STATE, ACCESS_WIFI_STATE, +CHANGE_WIFI_MULTICAST_STATE and NEARBY_WIFI_DEVICES (`neverForLocation`). No phone microphone +permission is declared here: the Assistant control changes a permission on the selected TV. +No billing SDK or automatic analytics upload was found in the inspected source. This is a scoped +source finding, not a guarantee about every transitive dependency or the final merged artifact. + +## Draft policy outline requiring owner completion + +- Identify app operator and privacy/support contact: **OWNER DECISION**. +- Explain local TV management, authorization, data categories above and user-initiated external + navigation/sharing. Validate any no-server/no-analytics statement against the final SDK graph. +- State actual retention, deletion, OS backup/transfer and key revocation behavior after testing: + **VERIFICATION REQUIRED**, not a promise that uninstall securely erases every copy. +- Select distribution, payment processing and any trial policy: **OWNER DECISION**. Revisit + disclosures when billing, verification servers or telemetry are added. +- Determine audience, jurisdictions, legal bases/rights and effective date: **OWNER/LEGAL REVIEW**. +- Publish an accessible privacy-policy URL and add an in-app entry only after owner approval; + complete Play Data safety from measured behavior, not the word "local" alone. diff --git a/v2/mobile/DEPENDENCY-REMEDIATION-20261016.md b/v2/mobile/DEPENDENCY-REMEDIATION-20261016.md new file mode 100644 index 00000000..f67af42e --- /dev/null +++ b/v2/mobile/DEPENDENCY-REMEDIATION-20261016.md @@ -0,0 +1,58 @@ +# Dependency remediation receipt — 2026-10-16 + +This changes dependencies/tooling only. No application feature, payment, account, permission, +font binary or license is changed. No force upgrade or dependency override is used. + +## Mobile + +- Verified no application import or build input uses `geist`; `src/app.css` directly loads the + checked-in WOFF2 files. Only package metadata, font provenance and a notices warning referred + to the npm package. Remove that unused dependency and its Next/Sharp peer chain (50 packages). +- Retain checked-in Geist 1.7.2 assets and complete OFL attribution/license. Update the provenance + wording and the optional installed-package notices check; regenerate both notices outputs. +- Target only compatible affected transitives: devalue 5.8.1 -> 5.9.4, nanoid 3.3.15 -> 3.3.20, + PostCSS 8.5.16 -> 8.5.30 and source-map-js 1.2.1 -> 1.2.2. Svelte/Vite major versions unchanged. +- Fresh audit and clean `npm ci --ignore-scripts`: **0 vulnerabilities**. Audit is time-dependent; + repeat at release. Install scripts were not enabled for this validation. + +## Desktop tooling + +The separate desktop lockfile had affected Vite, SvelteKit and shared build transitives. Its +compatible update keeps existing majors: Vite 6.4.2 -> 6.4.4, Kit 2.61.1 -> 2.70.3, devalue +5.8.1 -> 5.9.4, nanoid 3.3.12 -> 3.3.20, PostCSS 8.5.15 -> 8.5.30, source-map-js 1.2.1 -> 1.2.2. +Npm also synchronizes the stale root lockfile version metadata 2.1.0 -> the already existing +package.json version 2.3.0; this is NOT a version bump, release or tag. + +Fresh desktop audit: **0 critical/high/moderate; 3 low reports** for cookie 0.6.0 and its +SvelteKit/adapter-static dependency chain. These are one underlying advisory, not three distinct +vulnerabilities: https://github.com/advisories/GHSA-pxg6-pf52-xh8x. +Kit 2.70.3 still declares cookie ^0.6.0; fixed cookie >=0.7.0 is outside that range. Do not force +an override without a separate compatibility review. These are dev/build dependencies and this +app ships a static SPA (`ssr=false`), not a Node/SvelteKit cookie-serving backend. This limits the +observed exposure; it is not proof of safety for every build input or future server deployment. +The production-filtered desktop audit is zero. The affected Kit import is server-side. +Monitor the upstream range and re-audit before release. + +## Validation and typography + +Mobile/desktop clean installs, checks and ordinary builds pass. After preserving merged mobile +head `ff7d2e1` through normal merge `d120b8c`, the integrated mobile suite passes 224/224. +All 13 inherited release-verifier tests pass. All 26 esbuild platform lock entries are retained. +Desktop real-component launcher and Optimize safety tests pass against simulated devices. + +The before/after mobile screenshot is byte-identical at 384x812; Geist, Geist Mono and Material +Symbols all report loaded. All checked-in font bytes are identical: + +- Geist: a369fcf5628ea2aa4e1b9e2ec6a5b3624e365bda588e1f0f2f12b564f728fbb8 +- Geist Mono: fba8f577f38a2bbcbe818efa6348dd58f36303a10b8737c42fefad275be563ab +- Material Symbols: 920a58b8f7793d89dadacd5d14102d103a0059c8076c79bd32347751b3b4bd44 + + +Desktop demo comparisons covered 15 screens in both themes. 23 initial captures were identical; +three light frames initially captured a blank/wrong view. Targeted re-capture with explicit +rendered-heading assertions matched those three baselines byte-for-byte, yielding 26/30 exact. +The other four differ only in spinner phase or dynamic App List read timestamps; typography and +layout were inspected. No modified screenshot harness or regenerated gallery is committed. +A fixture-only compiled-demo probe hit the existing top-level-await/target constraint; normal +shipping builds pass and the supported dev-demo path was used for the comparison. This is not +physical-device, Android signed-artifact or general navigation-regression evidence. diff --git a/v2/mobile/EXPORT-DISCOVERABILITY.md b/v2/mobile/EXPORT-DISCOVERABILITY.md new file mode 100644 index 00000000..8e86e5f6 --- /dev/null +++ b/v2/mobile/EXPORT-DISCOVERABILITY.md @@ -0,0 +1,98 @@ +# Screenshot and report export diagnosis + +Read-only source investigation, 2026-10-17. Source audit base: `56aa7c56450464b2060c27838373029b6b90c14f`. +Reconciled with approved modal integration `83f69b77e2b19b643e21ae456d0f597e90ef7d83`; its +release-checklist completion is retained and it changes none of the export destinations below. +Approved PR186 is inherited from the current base; product changes in PR187/188 are not included. Open PRs were inspected; none implements Android export. +SAF export remains explicitly open in `BACKLOG.md`, `HANDOFF.md` and `RELEASE.md`. This document +proposes scope; it does not implement storage, permission, native or UI changes. + +## Current destinations and actions + +`src-tauri/src/lib.rs` resolves `app.path().app_data_dir()` and passes it to `AppState`. +The installed Tauri 2.11.2 source was checked: `src/path/android.rs::app_data_dir` invokes +`getDataDir`; `mobile/android/.../PathPlugin.kt::getDataDir` returns `activity.dataDir.absolutePath` +(API 24+) or `applicationInfo.dataDir`. This is the private app data root, **not** its `files/` +subdirectory. Common Android layouts resemble `/data/user//com.atvoptimizer.mobile` +(with `/data/data/...` aliases), but no physical runtime path was observed. The returned runtime +path is authoritative, including for another Android user/profile. Resolver failure uses the +existing host-development `ATVOptimizer` fallback; do not hard-code an Android absolute path. + +| Flow | Exact source destination | Current user feedback and actions | +|---|---|---| +| TV screenshot | `/screenshots/_.png` | Dashboard displays returned absolute path and base64 preview; explicitly says private phone storage and sharing/export unavailable. Close dismisses the preview, without deleting the PNG. Capture is automatic; there is no Save chooser/cancel path or screenshot history browser. Errors show a toast. | +| Files / found app backups copied to phone | `/downloads/`; collision suffixes preserve existing files | Toast says copied into this app's storage. “Copied this session” lists actual returned name, path and size. Callout says not exportable to Downloads/other apps. No local Open/Share action; session list is not a persistent phone-file browser. “Open folder” on a search hit navigates to the **TV** folder. | +| APK backup bundle | `/backups/-/`, with APK parts and manifest | Backups has a persistent in-app list, private-storage/no-export label and existing restore/delete actions. This is not portable app-data or full-device recovery. | +| Unknown-app diagnostic JSON | WebView `localStorage`, key `atv.unknownDiagnostics.v1`; explicit snapshot serialized in memory | More previews JSON, copies explicitly, shows copied/error feedback and allows manual selection after clipboard failure. No `.json` file is written by Copy. | +| “Report this app” | Report object/JSON in memory; clipboard after Copy or the long-report GitHub-opening fallback; optional prefilled GitHub web URL | Preview equals copy/issue text. Clipboard fallback and browser-failure messages exist. Opening a GitHub form does not submit it. No saved report-file destination or native Sharesheet action. | +| Debug log | Native `/debug.log` plus in-memory frontend call log | More reads the last approximately 500 native lines, combines with frontend log, and copies text. No exported file or native Share action. | +| Diagnostics playback/resources and health reports | Component/session memory; `report_all` returns structured values and its API wrapper has no screen consumer | Read/sample actions render results; there is no file-export/copy/share control for these reports. Do not tell users a “report file” has been saved. | +| Configuration snapshots | `/snapshots/_.json` | Separate in-app snapshot management. No Android SAF export flow. | + +Trace: shared `commands/state.rs`, `commands/screenshot.rs`, `commands/snapshot.rs`; +mobile `src-tauri/src/file_commands.rs`; `Dashboard.svelte`, `Files.svelte`, `Backups.svelte`, +`More.svelte`, `AppReportSheet.svelte`, `DiagnosticReports.svelte`, `lib/unknownDiagnostics.ts`. + +## Existing capability and test boundaries + +The opener plugin is installed, but `capabilities/default.json` grants only HTTP(S) `open-url`, +and `api.openUrl` rejects other schemes. That is browser-link capability, not permission to open +private files. The generated AndroidManifest already declares `androidx.core.content.FileProvider` at +`${applicationId}.fileprovider`, with `exported=false` and `grantUriPermissions=true`. +`gen/android/app/src/main/res/xml/file_paths.xml` maps `external-path` and `cache-path` to `.`. +A provider exists; screenshot Open/Share UI and the native export/share bridge remain unwired. +No app-level `ACTION_SEND` or SAF create-document flow was found in the inspected sources. No broad storage permission change is needed +for existing private saves, and none was made. + +Existing tests cover screenshot PNG/error validation and safe filename conversion in core; +file-copy feedback, stale-session suppression, collision preservation and partial-file cleanup; +unknown-diagnostic snapshot fidelity/redaction; and app-report browser/clipboard fallback. +No mobile browser test stub for native `take_screenshot` was found. Evidence-capture calls to +Playwright `page.screenshot` are not product screenshot tests. No SAF save/cancel or native Share +coverage exists because those product flows are not implemented. No tests were rerun for this +documentation-only investigation; previous unrelated green runs are not export acceptance. + +## Small existing-flow scope + +A safe first UI-only slice can clarify the screenshot preview's saved filename/private location, +state that it is absent from public Gallery/Downloads and that Close only dismisses the preview, +and keep Files' “Copied this session” destination language consistent. It must not add a misleading +Open/Share button or send users to Files as if that screen listed phone screenshots. Text reports +should continue saying **Copy**, with clipboard success/error states, rather than claiming file save. +Opening an oversized app report can also copy its text before browser launch; preserve that feedback. + +Add mocked screenshot success/error/close tests and clipboard-failure checks, with narrow/large-text +screenshots and a short interaction recording. Keep existing warning text, report redaction and +copy/export fidelity. This slice needs no native bridge, dependency, manifest, Tauri capability or +storage-permission changes. No such implementation is included here. + +## Broader export scope to agree before implementation + +A useful first native scope is one explicitly selected screenshot, rather than an entire export +center or blanket filesystem access. Choose either Save a copy or Share, then add the other later: + +- **Save a copy:** Android `ACTION_CREATE_DOCUMENT`, MIME `image/png`, user-selected destination, + and a native ContentResolver copy. Return saved/canceled/error explicitly; only claim save after + stream completion. Report provider display name/URI honestly rather than inventing a filesystem + path. The system picker does not require broad storage permissions. See + [Android document creation guidance](https://developer.android.com/training/data-storage/shared/documents-files). +- **Share:** a small Android bridge, content URI, correct MIME type and temporary read grant. + First audit/narrow the generated provider's URI exposure, or use an appropriately scoped separate + provider for a staged export/cache directory; the generated `.` paths must not be assumed to be + a narrowly scoped screenshot provider. Do not expose the entire app + data root, which also contains sensitive app state. Suggest a non-identifying export filename; + current screenshot names embed a sanitized ADB serial/endpoint. Launching a chooser is not proof + that a receiver completed sharing. See + [Android secure file sharing](https://developer.android.com/training/secure-file-sharing/share-file). + +These require new native command/capability plumbing and, for Share, review of the existing manifest provider/path +configuration (or an appropriately scoped separate provider) and a temporary URI permission grant. They are platform changes, despite not requiring +broad external-storage runtime permissions. Keep original private files intact on cancellation or +failure; scope source reads to validated exportable files; bound staging size/lifetime and handle +Activity recreation, missing provider/receiver, concurrent requests and stale UI continuations. +No persistent directory grants, Downloads-wide access, credential changes or security-setting +changes are proposed. Physical Android/provider validation would remain a separate acceptance gate. + +Do not extend screenshot export to APK backups or diagnostic bundles without separate format, +privacy and retention decisions. Existing app-report/unknown-diagnostic serialization should be +reused if a later explicit JSON-file export is approved. diff --git a/v2/mobile/FEATURES.md b/v2/mobile/FEATURES.md index 5c01057c..b5599975 100644 --- a/v2/mobile/FEATURES.md +++ b/v2/mobile/FEATURES.md @@ -47,7 +47,7 @@ registered on mobile yet. **Todo** = no mobile screen exists for this at all. | 6.2 | More / Settings | `wireless_disconnect`, `find_remote`; license/entitlement state (no desktop equivalent — desktop is always Pro) | Free (screen itself); gates Pro features | **Partial** | `More.svelte` fakes Pro status by string-matching the literal text `"pro"` in a license-key input (`licenseStatus = "Pro Unlocked"` if `licenseKey.trim().toLowerCase() === "pro"`) — a placeholder comment even says "Since license logic isn't fully compiled into Rust core yet". Entitlement is never actually read from the backend (`AppState::with_entitlement`). | | 6.3 | Pro paywall | Same entitlement concept as above — no desktop equivalent (desktop ships as Pro unconditionally) | Pro (marketing surface) | **Todo** | No dedicated paywall screen; the "Pro" upsell is inline copy inside `More.svelte`. The design's dedicated paywall (lifetime license, Ko-fi tip, feature bullets) is unbuilt. | | 7.1 | Devices hub | `list_devices`, `report_all`, `rename_device` — multi-device management, `Feature::MultiDevice` (enum exists, no call site wired yet on desktop or mobile) | Pro (per design) | **Todo** | Mobile has no multi-device concept at all — one connected device, no saved-device list, no `report_all` roll-up. Overlaps with 1.0's saved-TV persistence gap. | -| 7.2 | File transfer | `list_dir`, `pull_file`, `push_file`, `delete_path`, `find_files`, `copy_file_to_device` — desktop-only, live in `v2/src-tauri` (not shared core), `Feature::FileManager` (enum exists, unwired) | Pro (per design) | **Todo — needs Android SAF** | None of these commands exist in `v2/crates/core`; they're desktop-only Rust using local filesystem paths. Mobile has no filesystem access model — porting requires Android Storage Access Framework (SAF) document-tree APIs via the Kotlin plugin, a materially different implementation, not a straight port. | +| 7.2 | File transfer | Mobile: `list_remote_dir`, `pull_file` (into app storage), `find_files` + `delete_path` (shared `crates/core/src/commands/files.rs`, `/sdcard`-confined exactly as desktop); desktop-only: `push_file`, `copy_file_to_device`. `Feature::FileManager` (enum exists, unwired) | Pro (per design) | **Partial** | `Files.svelte` browses, copies to app storage, deletes under `/sdcard` with a confirmation, and runs desktop's app-files catalog (`v2/src/lib/app-files-catalog.json`, read in place) with failed searches kept distinct from "no matches" (#86). Upload from the phone and export out of app storage still need an Android SAF picker. | | 7.3 | Backups + Google Drive | Overlaps with 5.2 Snapshots (`save_snapshot`/`list_snapshots`) plus new ground: Google Drive upload/restore, cross-device "this phone" backup destination | Pro | **Todo** | Snapshot backend exists (see 5.2) but is unused on mobile; Google Drive integration doesn't exist anywhere in the codebase (desktop or mobile) — net-new scope, not a port. | | 8.1 | App detail sheet | `safety_info`, `app_memory_map`/`app_usage_map`, then the same disable/uninstall/keep actions as 4.1 | Mixed (view free, destructive actions Pro) | **Todo** | No bottom-sheet/detail view component exists on mobile; `Apps.svelte` only supports a flat list with inline actions. | | 8.2 | Risk & actions guide | `safety_info` (drives Safe/Blocked/Protected tiers); static "disable vs uninstall" explainer copy | Free | **Todo** | No standalone guide screen; `Diagnostics.svelte`'s inline `getSafetyTag` reimplementation (see 2.2/F5) is the closest thing, and it doesn't call `safety_info` at all. | diff --git a/v2/mobile/FREE-SETTINGS-UNDO-DESIGN.md b/v2/mobile/FREE-SETTINGS-UNDO-DESIGN.md new file mode 100644 index 00000000..2a9ae8cd --- /dev/null +++ b/v2/mobile/FREE-SETTINGS-UNDO-DESIGN.md @@ -0,0 +1,167 @@ +# Bounded Free settings undo — proposal, not implemented + +Base: merged mobile release-preparation `7fe0e6a`. PR170/171 remain unchanged. This branch adds +only a current-behavior characterization test and this design; it creates no journal, command, +payment wiring or security rule. Decisions below need review before implementation. + +## Demonstrated gap + +The new `current gap: Pro loss blocks restoring an app-changed screensaver even with the original +known` browser test changes the synthetic TV's screensaver as Pro, loses Pro, then selects the +existing Restore previous control. The current `write_setting` contract rejects the first restore +write with `LOCKED:tweaks_write`, leaving the app-written Off state in place. This test PASSES +when the gap is present; it is characterization, not evidence that Free undo is implemented. +The mocked boundary follows the real `tuning::write_setting` prerequisite. It does not exercise +Android or prove native security. Successful writes are synthetic test-fixture mutations only. + +## Smallest recommended first scope + +Cover the screensaver group only: `secure.screensaver_enabled` and +`secure.screensaver_components`, changed through a typed mobile screensaver action. No global, +system, arbitrary key, app permission/op, DNS, display override, Home, package, snapshot or file +mutation is authorized by a Free undo. Expand later only through reviewed typed operation types. +This first slice cannot claim Free undo for every existing Tweaks action. + +- Pro remains required for a new screensaver change. The before-state must be durably recorded + BEFORE the first TV write. A Free preview request identifies a record/revision; confirmation + accepts only its native preview handle, never caller-provided settings, values, namespace, + identity, path or shell expressions. Record ID/revision alone never authorizes a write. +- Every existing path that can touch either key must join the SAME native operation lock and + journal rules, or explicitly refuse those keys. This includes generic `write_setting`, snapshot + apply/settings-plan execution, performance/other wrappers and future driver call sites. Routing + only the new UI through a typed action is insufficient. An excluded snapshot/plan must be + rejected before ANY mutation, not halfway through applying unrelated settings. +- The native backend obtains a fresh normalized verified hardware serial using its existing + device-profile path. UI identity and host:port are insufficient. No hardware ID means covered + journaled writes and undo fail closed; connection-generation fallback is not persistent identity. +- Each request binds to live connection generation and hardware ID. Reconnect invalidates preview + and confirmation even for the same ID/endpoint; user must preview and confirm again. Fresh + verified ID permits later undo on the same TV's rotated port, never endpoint reuse by another TV. +- The UI explicitly says what two settings will be restored and what cannot be undone. No billing + connection, account, Pro entitlement or network purchase verification is needed to list/preview/ + confirm eligible undo. No automatic undo or replay on downgrade, restart, reconnect or refund. + +## Existing pieces to reuse, and limits + +- Reuse shared checked-shell framing/parsing, shell argument quoting and normalized identity rules. + Keep planning/validation pure in `crates/core/src/engine`; journal I/O stays in commands/native + storage. Extend the existing ADB driver if necessary; never introduce another ADB wrapper. +- Snapshot `current_settings_map` shows checked multi-key reads and explicit absent-settings + concepts. Its `settings get`/`"null"` interpretation alone is insufficient provenance: journal + capture must distinguish absent, present empty, literal `"null"` and unreadable. Use successful, + complete presence-aware reads with exact key matching; reject ambiguous/truncated/duplicate or + unparsable responses. Retain only the two allowlisted values, never the whole settings dump. +- Do not reuse snapshot apply as undo. It can disable packages/change unrelated settings; snapshot + files and the module-memory screensaver cache do not prove an app-issued before-state. +- `home_tracking` is best-effort, keyed by transport serial and can discard corrupt state. Those + semantics are unsuitable for authorizing a Free write. Its JSON storage style is illustrative, + not an adequate journal implementation. +- The current setting builder interprets empty string as delete. A new internal typed + `Absent | Present(String)` operation is needed to preserve presence/empty semantics exactly; + do not route typed empty restoration through the existing empty-means-delete wrapper. + +## Proposed record/storage contract + +Separate native-private `settings-undo-v1.json`, fixed path under app data, schema version 1. +No database or import/export endpoint; never load a caller-provided file. Do not change snapshot +schema or manufacture historical records from snapshots, cached UI values or old logs. + +A record contains: non-secret random record ID, monotonic revision, verified hardware ID, +operation type `screensaver`, presence-tagged before-values, immutable planned per-step/group post-values, +checked readback results (diagnostic until exact plan equality is proven), per-step results and status (`Prepared`, `AppliedVerified`, `UndoPrepared`, +`Undone`, `Unknown`). Timestamps are informational, never an entitlement/identity proof. + +Validate schema/version, identity, known operation, exact two-key set, value sizes/shapes, +revision and status before use. Reject unknown versions/corruption; do not silently start fresh +and then perform a covered mutation. Components must have bounded valid flattened-component +syntax; flags must be recognized presence-tagged values. Unsupported original vendor values +must block the new covered change before writing, rather than losing the restore target. + +Proposed limits: at most 64 active device records and 1 MiB total, individual values <=4 KiB. +Never auto-evict an unresolved/undoable record or expire recovery because Pro is lost. At capacity, +block new covered writes and offer explicit record review/forget (forget itself never touches TV). +One active group per TV retains the first before-state; repeated app changes may update its last +expected post-state only after actual current state matches the previous expected state. External +changes/conflicts require explicit resolution, not silent re-baselining. + +Persist by same-directory atomic replacement with file sync and required directory durability; +write failure, full disk or revision conflict before the TV write means NO TV write. Serialize +journal operations; file and revision reads must be confined, size-bounded and reject symlinks. +Do not report durable capture until the supported Android filesystem operation succeeds. + +## Native execution / crash contract + +A generation check before a call alone does not protect a multi-call operation. Implementation +needs a reviewed operation lease using the existing mobile connection/lifecycle lock: bind the +hardware identity check, before/readback, record preparation and each write to the same connection. +Do not hold a lock and re-enter a driver method that acquires it again. This is a material driver/ +concurrency design prerequisite, not a capability asserted to exist today. + +For a Pro change: verify identity/read exact before-state; persist Prepared; execute the finite +screen-saver transition plan; check command status AND read back. AppliedVerified requires EXACT equality to the pre-recorded +planned post-state for BOTH keys, including unchanged keys and every intended intermediate +state (such as temporary disable). Store the already planned values as expected provenance; +never adopt arbitrary observed values as app-owned post-state. Any mismatch becomes conflict/ +Unknown, with no new undo entitlement. Plan multi-key ordering explicitly and record it before +I/O. A successful shell return alone is not a verified change. + +For Free preview: load/validate record and revision; verify live hardware identity under the +lease; read both current values and require exact equality to verified planned post-values. +Native state retains a short-lived, single-use preview handle bound to record ID/revision, +verified hardware ID, live native connection generation, full expected current state and exact +restore/intermediate plan. The UI receives display information and that handle, not authority to +change the plan. No journal credential or persistent signing key is created. + +For confirmation: resolve the handle natively; revalidate liveness, generation, identity, revision +and BOTH expected values under the same lease. Expired, consumed, missing or reconnect-stale +handles fail before any write; consume/invalidate the preview so retries need a fresh preview. +A matching endpoint or record ID/revision does not save a stale confirmation. UI cleanup timing +is not a security boundary. After explicit fresh user confirmation, persist UndoPrepared and +restore only the native-preview-bound before-state in the reviewed safe order, +check/read back and durably mark Undone. Repeated undo is a no-op after verifying completion, not +another arbitrary write. Concurrent confirmations must lose the revision/operation lock race. + +Android settings has no general remote compare-and-swap. The connection lease prevents this +app's reconnect/concurrent actions, but cannot stop another TV app changing settings after a +read. Re-read immediately before each dependent step, abort on divergence, and disclose that +residual race; do not claim transactional Android rollback. + +Crash after a TV write but before durable verified result is inherently ambiguous. Prepared/ +UndoPrepared or partial-readback failures become Unknown; never replay automatically or guess +that a target value proves execution provenance. Block further covered mutation/Free undo until +reviewed reconciliation or manual device recovery. Some crash/partial failures therefore cannot +have guaranteed automatic Free undo. Preserve the record and explain the exact limitation. + +## Acceptance matrix for the later implementation + +- Pro write -> Pro loss/refund -> explicit Free undo on the same verified TV restores exact before + values, including proven absence/empty values. Same TV/new port works after fresh confirmation. +- Missing/unknown ID, replaced TV/same endpoint, generation change, stale confirmation, external + key change, malformed before-state, unknown schema and forged caller parameters produce no write. +- Disk-full/permission/sync failure before mutation, corrupt journal, capacity, competing revisions + and symlink/path escape all fail closed. No broad reset/cleanup can destroy retained evidence. +- Repeated changes retain original before-state; repeated undo is idempotent; retries never apply + a stale record. All grouped partial-step failures and crash windows are fault-injected. +- Native generic-setting and snapshot/performance paths cannot bypass the shared lock/journal; + excluded plans fail before any write. A concurrent alternate path cannot relabel an external + change as the planned app state. Exact full-state and intermediate readback mismatch tests + must prove that arbitrary observed values never become AppliedVerified. +- Pro setter remains gated; arbitrary setting/snapshot/app-permission operations remain gated. + Existing free package recovery still works without journal, billing or Pro. +- Installed before-component missing/unavailable is an explicit unsupported/manual case; do not + install APKs, invent replacement components or promise recovery of missing APK/data. + +## Decisions to resolve before code implementation + +1. Approve the deliberately narrow two-setting scope and the stricter requirement that covered + writes require verified identity and durable capture, including honest refusal on older TVs. +2. Approve separate schema-1 app-private storage, bounds, no auto-retention deletion and no legacy + migration. Determine Android backup/restore exclusion and update the factual data inventory. +3. Approve the native connection operation-lease/driver design and multi-key transition ordering; + the residual external-actor/crash limits are not solved by a JSON file. +4. Agree that app-private OS storage is the integrity boundary (no journal import, treat corruption + as denial). This is not cryptographic provenance against a rooted/tampered phone. If stronger + authenticity is required, a separately approved Android Keystore design is needed; this proposal + creates no key/credential and must not pretend checksums are authentication. +5. Accept Unknown/manual-recovery behavior and the inability to recover old unjournaled changes. + No existing saved data is rewritten and no universal Free undo claim is made. diff --git a/v2/mobile/HANDOFF.md b/v2/mobile/HANDOFF.md index 13c018bb..1a925942 100644 --- a/v2/mobile/HANDOFF.md +++ b/v2/mobile/HANDOFF.md @@ -68,8 +68,53 @@ different device is now at this address.") surfaces the mismatch once in whichev connect flow notices it. Covered by new tests in `savedDevices.test.mjs`, verified to fail before the fix; unverified on a device. +**Desktop parity pass (2026-10-06, branch `mobile-parity-pass`).** Mobile caught up with the +desktop features that shipped in v2-2.3.0/2.3.1. **None of it has run on a phone or TV yet**; +the evidence is the browser suites plus 384×812 screenshots in +`evidence/parity-2026-10-06/`. What changed: + +- *Navigation:* Tweaks is a bottom tab; Optimize is a screen pushed from the Dashboard's single + Optimize call to action, and Back returns to whichever screen opened it. The Dashboard gains + Tweaks and Launcher tiles; More is titled More and lists Optimize. +- *Optimize:* search by name or package id, tap a row for the app sheet, desktop's + `recommendation()` defaults and "Review: … if unused" pill. +- *Diagnostics:* desktop's Suggestion column. A process is only treated as an app once the TV + confirms the package is installed (`safety_info`); anything else goes to the catalog-free + `process_safety_info`, and an unreadable installed list reads "Not checked". +- *Snapshots:* marked Beta; "Preview restore" lists every row with a Now column from the plan's + `current_values`/`current_launcher`, and says why a missing launcher is skipped. +- *Apps:* a Recognised section lists catalogued apps (so `com.google.android.katniss` is findable + without Pro), one search over name and package, Hide not installed, the detail sheet's storage + figures with re-measure, Report this app (#100) and a long-press action menu (#129). +- *Launcher:* Setup Wraith card (off / on / risk, Turn it off, Re-enable), Advanced "set another + app as Home", a separate confirmed Disable stock launcher step, a re-read after every action + (#157/#158), Play Store install polling, and Source site links that open in the phone's browser. +- *Tweaks:* Nvidia System Hooks, Remote Assistant Button (mic permission), Screensaver (Basic + Daydream / Off / Restore previous), the 720p display preset, regrouped sections. +- *Files:* Find app backups from desktop's catalog, copy a hit to the phone, and TV-side delete + under `/sdcard`. The find/delete logic moved to core `commands::files`, shared with desktop, and + fixed desktop's #86 message for missing backup folders. +- *Plumbing:* `tauri-plugin-opener` (capability scoped to http/https; `api.openUrl` also rejects + other schemes), `types.ts` aligned with core, icon font re-subset, notices regenerated. + +Still not on mobile: **phone→TV upload** (needs an Android document picker / SAF), **install APK / +sideload**, the **bug-report bundle** (`collect_diagnostics`), and a **patch-notes viewer**. The +opener's Android library (`androidx.browser`) is not yet listed in the hand-maintained part of +the notices, which covers no Gradle dependencies at all. + --- +## Reconnect safety follow-up (2026-10-15) + +Launcher invalidates reads, picker/confirmation intent, polling and action continuations on +session generation or liveness changes, including reconnection to the same endpoint. The +session generation is reactive so screens observe a reconnect even if its liveness transitions +complete within one render. Tweaks keeps screensaver originals by normalized verified hardware +ID, falling back to connection generation when identity is unavailable. Multi-setting writes +stop before the next write if that connection changes. Already dispatched writes cannot be +recalled. These changes have simulated browser coverage only, not phone/TV validation. +See `evidence/reconnect-20261015/README.md` for reproducible tests and videos. + ## 1. What this is A phone/tablet app (**Tauri 2 + Rust + Svelte 5**, in `v2/mobile/`) that drives an **Android TV over wireless ADB** — no PC. Debloat / optimize / launcher / tweaks / snapshots / remote, etc. @@ -283,7 +328,8 @@ aarch64-Android; the clean APK has **zero GPL native libs** (only our `libatv_op `screens/*.svelte` (14), `components/*.svelte` (8), `app.css` (design tokens + offline @font-face). - `v2/mobile/src-tauri/src/` — mobile Tauri app: `lib.rs` (builder + `generate_handler!`), `wireless_adb.rs` (adb_client transport = `AdbDriver`), `wireless_commands.rs` (wireless_*), - `file_commands.rs` (list_remote_dir/pull_file/backup_apk/list_backups/restore_apk_backup/delete_backup). + `file_commands.rs` (list_remote_dir/pull_file/find_files/delete_path/backup_apk/list_backups/ + restore_apk_backup/delete_backup; find/delete wrap the shared `crates/core` `commands::files`). - `v2/mobile/tauri-plugin-atv-adb/` — mDNS discovery plugin (Kotlin NsdManager + thin Rust). - `v2/vendor/adb_client/` — vendored MIT transport; `SHIELD-OPTIMIZER-PATCH.md` lists every local change (raw service stream, finite timeouts, stray-stream tolerance, `tcp/pairing/`). @@ -418,6 +464,8 @@ reference these names. ## 9. Commit history (this effort, newest first) ``` +(2026-10-06 desktop parity pass on mobile-parity-pass: d2acf0b, bbd87d1, 77f8d3d, 4e054c1, + 65acf44, bbb7d21, then the integration and docs commits; see the checkpoint at the top) fae340c Mobile: document the licensing, release, and pairing backlog pass 5e76b24 Mobile: Android 11 wireless-debugging code pairing (SPAKE2) ← unverified on device aa4e589 Mobile: release signing config, version tooling, third-party notices diff --git a/v2/mobile/README.md b/v2/mobile/README.md index 6dc84151..96504653 100644 --- a/v2/mobile/README.md +++ b/v2/mobile/README.md @@ -3,6 +3,19 @@ An Android phone/tablet app that drives an Android TV over **wireless ADB** — no PC in the loop. Tauri 2 + Rust + Svelte 5, sharing the audited engine (`v2/crates/core`) with the desktop app. +ATV Optimizer phone app walkthrough + +
+Every screen at once + +All ATV Optimizer phone screens + +
+ +> Screenshots show demo data, not a live device. Regenerate with `npm run screenshots` from this +> directory (needs `ffmpeg` and `npx playwright install chromium`); see +> [`screenshots/capture.mjs`](screenshots/capture.mjs). + **[`HANDOFF.md`](HANDOFF.md) is the authoritative document.** Read it before doing any work here; this file is only an orientation. [`BACKLOG.md`](BACKLOG.md) is the ordered queue. diff --git a/v2/mobile/RELEASE-POLISH-RECEIPTS.md b/v2/mobile/RELEASE-POLISH-RECEIPTS.md new file mode 100644 index 00000000..366f1d1e --- /dev/null +++ b/v2/mobile/RELEASE-POLISH-RECEIPTS.md @@ -0,0 +1,40 @@ +# Mobile release-polish completion receipts + +Original reconciliation: 2026-10-17 through PR202. Updated on 2026-10-19 against `mobile-parity-pass` at `d388cad1e3da9c17e0a5c0bdeec52022fd8d9970` for PR204 and the reviewed PR206 main integration draft. GitHub merged-PR metadata, final-head check runs and linked review comments were read for each row. These are completed development-branch scopes, not release or physical-device acceptance. The [durable checklist](RELEASE-POLISH.md) retains the remaining gates. + +Each CI link reports six successful jobs for the listed final head: Rust on Ubuntu/macOS/Windows, desktop and mobile frontend checks, and simulated-device E2E. Review receipts describe independent source review; they do not imply the reviewer reran tests, inspected pixels or used a device. Where a PR was integrated again, the final integration receipt supersedes the earlier-head review/CI status. PR181's exact-head automated source-review completion is the available receipt. GitHub's automated summary can retain finding flags (including PR182/184/190/202); the linked independent scope approval does not rewrite those historical summaries or claim every review thread was resolved. + +| Merged scope | Final PR head | Merge commit | Final review / CI | +|---|---|---|---| +| [PR181](https://github.com/bryanroscoe/shield_optimizer/pull/181) — Fix mobile settings readback and system-bar spacing | `3c15c2147a0e4b2cf0768592d33b8f682811bcbc` | `6f3deefa5e46a03d292aa4ef1e84927440b03766` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/181#issuecomment-6228770166) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/38996153794) | +| [PR182](https://github.com/bryanroscoe/shield_optimizer/pull/182) — Lock mobile background scroll through nested app dialogs | `1dd9bef7f949f66e4a6b6e94ba11e5c319c9baa0` | `4d18b24ef5ce9b2d00e05c4f6f19d9a0a63f99a6` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/182#issuecomment-6229103597) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/38998647459) | +| [PR183](https://github.com/bryanroscoe/shield_optimizer/pull/183) — Prevent stale app sheets targeting a new TV connection | `fb30a5ce9c2cce04ce65ec4aa83da658f377b20f` | `cd2244b970e2d579627e89bc3fb59a44da41d75e` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/183#issuecomment-6229206394) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/38999174214) | +| [PR184](https://github.com/bryanroscoe/shield_optimizer/pull/184) — Clarify used capacity and sensor-aware thermal context | `ca7f4b118fd156d5299b2b5d7a2752e30111a3da` | `3a1881e9b73c768be3a417abbea14cf524cfbe40` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/184#issuecomment-6229462630) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39001182304) | +| [PR185](https://github.com/bryanroscoe/shield_optimizer/pull/185) — Improve mobile diagnostic report readability | `330275bd9858c9ea6ef8e22ab8df424350a498ef` | `56aa7c56450464b2060c27838373029b6b90c14f` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/185#issuecomment-6229684694) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39001843909) | +| [PR186](https://github.com/bryanroscoe/shield_optimizer/pull/186) — Fix mobile modal handoff focus and stock takeover ownership | `f3a69eca41100c727db3820c49f8a3d297be5a56` | `83f69b77e2b19b643e21ae456d0f597e90ef7d83` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/186#issuecomment-6230054781) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39004884597) | +| [PR187](https://github.com/bryanroscoe/shield_optimizer/pull/187) — Write the Daydream component before enabling it | `a7ec71516b80abb68837df23c491f589ca2750a1` | `0136c34b84e0ef30d48305841e7e1e7ca9b990e3` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/187#issuecomment-6230213083) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39005792456) | +| [PR188](https://github.com/bryanroscoe/shield_optimizer/pull/188) — Keep mobile thermal threshold wording conservative | `dd752bbd60efe8dbec9f5da6a827b80f266b8c2e` | `e05891dfbd33bb513eafb6794dcfd03c41d19ba4` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/188#issuecomment-6230305396) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39006778524) | +| [PR190](https://github.com/bryanroscoe/shield_optimizer/pull/190) — Clarify private saves and clipboard-only report actions | `1006f22bb24ae6a3871396f2f49d2023571ef4f2` | `dccfe99a805ffcb0c11f32a4fa9f227306ba1cf3` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/190#issuecomment-6230445672) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39006905652) | +| [PR191](https://github.com/bryanroscoe/shield_optimizer/pull/191) — Keep the mobile Diagnostics header usable at narrow widths | `0acba8a1bc5bce67f076e4e4227553ef40c97c89` | `5e80c0efd6620060bb03d5c354815990303628ab` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/191#issuecomment-6230606702) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39008777481) | +| [PR192](https://github.com/bryanroscoe/shield_optimizer/pull/192) — Preserve explicit Android RAM accounting in mobile diagnostics | `2bcd3084fc71874c3d5fc909e2388e7e25c54c3d` | `9265e5fdbf4c9c73f7cea5522e50c50dbcd90fe0` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/192#issuecomment-6230736433) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39009711282) | +| [PR193](https://github.com/bryanroscoe/shield_optimizer/pull/193) — Prevent screenshot captures from overwriting earlier saves | `c911d8be54b37a325922a927ce1d09eb45fb614b` | `b095f697b2a24c3d2fb8f56cef7056586b6355ef` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/193#issuecomment-6230822359) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39010618976) | +| [PR194](https://github.com/bryanroscoe/shield_optimizer/pull/194) — Show density-only overrides in the mobile display summary | `3bc3865afffbdc29022766f82640bccf277e1156` | `35b5deaa044bb2218c720eeaa64858486ffc195f` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/194#issuecomment-6230896763) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39010961139) | +| [PR195](https://github.com/bryanroscoe/shield_optimizer/pull/195) — Name mobile screensavers while preserving raw component details | `804c3ac70373239c97bcafe1ab97bc6d5fedf8c8` | `ba7c9d78d31b0a579f5ffd03d92cd403ebf47227` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/195#issuecomment-6231024167) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39011826136) | +| [PR196](https://github.com/bryanroscoe/shield_optimizer/pull/196) — Keep mobile scaling presets usable with enlarged text | `af537cc26204d60e13a00bd3256281243bb32a13` | `a73787a36bbf44311de2596e9a01c8232a049a02` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/196#issuecomment-6231130630) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39012607251) | +| [PR197](https://github.com/bryanroscoe/shield_optimizer/pull/197) — Use guarded Home verification for mobile setup-helper disables | `a909cec0f0368a6505e8be27f64205021f6f11da` | `4148f035c20450751d92d894456162cbc5ab837b` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/197#issuecomment-6231315788) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39013600547) | +| [PR198](https://github.com/bryanroscoe/shield_optimizer/pull/198) — Keep visible APK measurements in mobile app reports | `fd89c0fc9638d1df4b7af6313f949d6e69262dc8` | `60a1934c5e4c575a81bd124698b9a9f6203e5fe8` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/198#issuecomment-6231482040) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39014713923) | +| [PR201](https://github.com/bryanroscoe/shield_optimizer/pull/201) — Integrate reviewed desktop quit and installer hardening into mobile | `93e9d46bed9935cd60ac84f0f0535fae320f58ea` | `d14363bc76c05f2342289d9503e95175b2d44da2` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/201#issuecomment-6233713669) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39030576353) | +| [PR202](https://github.com/bryanroscoe/shield_optimizer/pull/202) — Fix mobile Restore detail action and stale removal selections | `63e5b85355633c3f5f7453a4551e34cd247e5eb0` | `b4546e8b810cfbb9c2729dafa9dbd7c2d51c9c2d` | [Review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/202#issuecomment-6234325588) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39034842743) | +| [PR204](https://github.com/bryanroscoe/shield_optimizer/pull/204) — Preserve unavailable safety in mobile app reports | `b7405034aaba11f911d9e0916a221148238c84cd` | `d388cad1e3da9c17e0a5c0bdeec52022fd8d9970` | [Final review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/204#issuecomment-6235587609) · [6/6 CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39043175482) | + +Original evidence READMEs and raw logs remain historical: their local counts, missing-dependency notes and then-pending review statements are not replaced by current results. PR185's later review/CI/merge closes the report-layout branch gate, PR198's final integration closes its conflict/review gate, and PR201's exact-lock CI closes its disclosed local-cache gate. PR202 records 334 local mobile tests and five planner tests; later PR204 records 343 mobile tests and nine focused cases. Those counts belong to their historical implementation receipts, not older PRs or this integration. PR204's linked final review/CI supersedes the pending wording in its original evidence without changing those logs. + +No new screenshots/video or gallery generation applied to the original documentation-only reconciliation. Existing evidence remains linked from the checklist. Native export wiring, icon preference, inventory, physical Android/vendor/lifecycle acceptance, desktop shared-client/updater acceptance, store/signing and release gates remain unverified as documented there. + +Historical PR203 documentation validation: all 31 local links/anchors resolve; 60 GitHub links match the fetched PR/comment/check-run metadata. All 19 listed merge commits are ancestors of the reconciled mobile base, and each final PR head has six successful checks. The final Rust log contains exactly eight passing desktop process-lifetime fixtures. Checklist table/stale-status checks and `git diff --check` pass. These are documentation/receipt checks; no product tests or visual captures were rerun. + +## Reviewed main scope in the mobile integration draft + +PR206 head `ef72305198af19d7457b97e02cd71cb00b00f7f5` passed independent source review according to the parent handoff and [all six exact-head CI jobs](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39185141432). [PR206](https://github.com/bryanroscoe/shield_optimizer/pull/206) was squash-merged into main as `c78176d5ea640a0e230633a3f0b0bc0720c01af4`. Its public automated summary still reported running when inspected; that is not presented as a completed automated review or a substitute for the parent's independent review. This mobile draft merges that main commit normally, preserving both mobile ancestry and the reviewed main commit. It does not claim the integration is already merged to the default mobile branch. + +The [integration receipt](evidence/launcher-protection-integration-2026-10-19/README.md) separates new host checks from historical PR206 evidence and fresh exact-head CI. Physical Android, native exports, shared-client/updater, store/signing and release gates remain open as listed in the checklist. diff --git a/v2/mobile/RELEASE-POLISH.md b/v2/mobile/RELEASE-POLISH.md new file mode 100644 index 00000000..41ebcb1a --- /dev/null +++ b/v2/mobile/RELEASE-POLISH.md @@ -0,0 +1,43 @@ +# Mobile release polish checklist + +Reconciled 2026-10-19 against mobile branch `d388cad1e3da9c17e0a5c0bdeec52022fd8d9970` (PR204); this integration includes reviewed main `c78176d5ea640a0e230633a3f0b0bc0720c01af4` as a draft. Release polish takes priority over new features. This supplements, and does not replace, [BACKLOG.md](BACKLOG.md). Owner observations below are reports, not completed device verification. Work sequentially in focused PRs. Do not infer permission to access a phone from it being powered on. + +[PR203](https://github.com/bryanroscoe/shield_optimizer/pull/203) merged the documentation reconciliation at `0c78c06823b4827aa3a4345b526f688b2813d739`. PR204 subsequently merged the report-safety follow-up at `d388cad1`; completed PR202/203 scopes and their historical receipts remain intact. + +The implementation status below means branch-complete within each reviewed scope, not released or physically accepted. [Final review, six-job CI and merge receipts](RELEASE-POLISH-RECEIPTS.md) identify the exact heads; original evidence retains its historical counts and limitations. The original documentation reconciliation did not rerun product checks. This integration's separate validation and remaining gates are recorded in [its receipt](evidence/launcher-protection-integration-2026-10-19/README.md). + +| Priority / finding | Current status / evidence | Scope checks / remaining acceptance | +|---|---|---| +| P1 App-sheet actions retain stale selection across reconnect / TV switch | Reproduced in Diagnostics and Optimize: Host rebound an old package to a new connection. Merged PR183 freezes the opening identity, clears parent/intent/paywall/read state and requires a new selection (frontend opening scope). Already-dispatched IPC/native generation enforcement is outside that fix. [Mock evidence](evidence/sheet-identity-2026-10-17/README.md). | Both screens: disconnect, same-endpoint reconnect, switch, failed reconnect/liveness loss, pending safety/measurements, nested Disable/Uninstall/paywall; no mutation from old opening. Fresh-selection mock write bound to current target; combine with PR182 lock/focus cleanup. Phone validation remains separate. | +| P1 Settings look unselected, including Private DNS | PR181 merged at `6f3deefa` after completed source review and six passing CI jobs: display presets now have active-state logic, animation values are normalized, and Custom DNS distinguishes editor state from applied mode. Explicit unset/custom/error states are covered. [Mock evidence](evidence/settings-polish-2026-10-17/README.md). | Mocked click/navigation/reconnect readback, unknown/custom/error and stale-reply guards are covered. Owner phone readback and interaction acceptance remains open. | +| P1 Content under phone status bar | The original overlap combined CSS safe-area env values with an edge-to-edge Android Activity lacking native inset handling. PR181 merged native container padding for bars/cutout at `6f3deefa`; source review, six-job CI and Android debug APK compilation passed. Physical verification remains open. | Phone portrait/landscape, cutout, gesture/3-button navigation, keyboard opening/closing; no overlap or doubled padding. Native compile is necessary but not physical proof. | +| P2 Density-only scaling override labeled as default | Reproduced at physical 3840x2160/640 with density override 300: old summary showed 640 and device default. Focused summary-only fix now shows 300 and density override; preset matching and writes are unchanged. [Simulated before/after evidence](evidence/density-only-label-2026-10-17/README.md). Independently reviewed with all six CI jobs passing; merged in PR194 at `35b5deaa`. PR181 remains intact. | Density-only fixture shows effective override density and Custom/default semantics; matching preset, missing size, defaults and read-error behavior retained. No setting writes from readback. | +| P2 Display scaling preset row at enlarged text | Reproduced 271px content inside 240px at 320px with card text doubled. Separate CSS-only slice wraps this row and retains 44×44px minimum targets; [before/after simulated evidence](evidence/scaling-preset-wrap-2026-10-17/README.md). Final source review and exact CI passed; PR196 merged at `a73787a`. Density-summary PR194 merged at `35b5deaa`; screensaver naming PR195 merged at `ba7c9d78`. | At 320/384px with 100/200% text, preserve every label, selected preset, Tab order, confirmations and returned focus without clipping or setting writes. Phone accessibility acceptance remains open. | +| P1 Background-process limit is confusing | Reported cap comes from `CUR_MAX_CACHED_PROCESSES`; old `global.background_process_limit=2` is separate. Merged PR181 wording distinguishes cap, process count and legacy deletion; source review and six-job CI passed, with no universal vendor claim. Physical vendor/readback acceptance remains open. | Cap 32/value 2, cap absent, legacy absent, deletion success/error; removal must not claim to change the cap. | +| P1 App-details sheet scrolls background | Merged PR182 provides shared lock/focus ownership and visible wrapped keyboard focus in long sheets; PR183 is validated with it. PR186 merged at `83f69b77` completes StockTakeoverDialog registration and sequential menu→report focus handoff, including out-of-order teardown. Final independent reviews and six-job CI passed for these scopes; physical verification remains open. | Lock background while open; sheet itself scrolls; restore prior position/focus on close, navigation and teardown; nested dialogs/reconnect cannot strand a lock. Test touch/wheel and keyboard with mocks, then phone. | +| P2 RAM/storage bars appear inconsistent | Confirmed both fill by used capacity; colors were static green/amber, not health thresholds. Independently reviewed accounting follow-up (PR192, all six CI jobs passed; merged at `9265e5f`) preserves explicit Used when Used + Free exceeds Total, covered by the checked-in Shield fixture and [simulated evidence](evidence/ram-accounting-2026-10-17/README.md). Metrics slice makes direction/percentages explicit, uses common neutral fill and separates missing data. [Mock evidence](evidence/metric-context-2026-10-17/README.md). PR184 merged at `3a1881e9` after independent source review and six passing CI jobs; PR192 is the later accounting follow-up. | 0/50/100%, missing/invalid values, free/available complement and consistent accessible labels/colors; cache use never becomes a pressure score. Phone/large-text check remains open. | +| P2 Temperature guidance too vague | Metrics slice retains sensor/status context, excludes known non-Celsius/limit/cooling objects, shows matched CPU/GPU reported points and removes generic20–100 hotness scale. No new device reads or universal thresholds. Separate wording follow-up uses a neutral reported sensor threshold and explicitly allows throttling below it because HAL/severity provenance is absent. [Mock wording evidence](evidence/thermal-threshold-wording-2026-10-17/README.md). PR184 merged sensor/parser context at `3a1881e9`; PR188 merged conservative wording at `e05891df`. Final independent source reviews and six-job CI passed; physical/vendor acceptance remains open. | Current/sensor/device severity separation; legacy unknowns;30/100°C fixtures; exact family/index/length matching and NaN slots; missing/invalid status never normal. No actual thermal/device validation claimed. | +| P2 Screensaver raw component / Restore previous | Separate display-only fix uses exact owner-package catalog names, Custom screensaver for unknowns and a keyboard-accessible raw Current/Previous disclosure. [Known/custom/Off/missing simulated evidence](evidence/screensaver-labels-2026-10-17/README.md); independently reviewed with all six CI jobs passing and merged in PR195 at `ba7c9d78`. Original-value identity rules and exact restore writes remain intact; ordered-write fix was merged in PR187. | Names never guessed; raw component values retained; catalog error/stale replies, malformed/list values, known/custom/Off/missing states covered. Phone acceptance remains separate. | +| P2 Playback and profiling report readability | Dedicated pass merged in PR185 at `56aa7c56`: grouped playback/resource sections, mode/interface rows, wrapping and scalable hierarchy preserve data, precision, units, warnings and exports. Independent source review and all six CI jobs passed. [Narrow/large-text screenshots, short video and fidelity fixtures](evidence/report-readability-2026-10-17/README.md) record historical local validation (263 mobile tests); physical Android confirmation remains open. | Mock fixtures cover narrow widths, enlarged browser text, long lists, missing values and overflow; screenshots/video are recorded. Physical Android font scaling, pinch/scroll and readability confirmation remain open. | +| P2 Diagnostics header overflows at 320px | Reproduced on merged base dccfe99a. A focused Diagnostics-only CSS fix wraps the header, keeps every control and uses scalable title/Live text with 44px minimum targets. [Mock narrow/large-text evidence](evidence/diagnostics-header-2026-10-17/README.md); independently approved with all six CI jobs passing and merged in PR191 at `5e80c0e`. Original measurement was 304px content inside 272px; phone verification remains open. | At 320px/384px and enlarged text, wrap title/actions without horizontal page overflow; preserve accessible action labels, tap targets and safe-area spacing. Focused browser regression and visual evidence are complete; physical scaling/touch/inset acceptance remains open. | +| P2 Screenshot/export destination and findability | [Merged read-only diagnosis](EXPORT-DISCOVERABILITY.md) traces private destinations, clipboard reports and generated provider scope. The clarification-only UI labels private screenshot/file destinations, explains preview Close retention, and identifies Copy as clipboard text. [Mock UI evidence](evidence/export-discoverability-2026-10-17/README.md). PR190 merged clarification-only UI at `dccfe99a` after completed independent source review and six passing CI jobs. Separate source-reviewed shared screenshot-retention fix (PR193, independently reviewed with all six CI jobs passing; merged at `b095f69`, Android acceptance open) stages complete PNGs and publishes without overwriting same-second or concurrent captures; [deterministic filesystem evidence](evidence/screenshot-retention-2026-10-17/README.md). Native Open/Share/SAF remain unwired; broader platform scope remains a decision. Do not promise Files visibility. | Trace exact app-private path read-only in code; success tells user what was saved and supported open/share/export choices. Save/cancel/error tested. No new broad storage permissions; scope Android picker/share implementation explicitly. | +| P2 Launcher icon appears top-heavy | [Current vs 1.8px downward optical comparison](evidence/settings-polish-2026-10-17/icon-optical-comparison.png) under simulated masks. Assets unchanged for preference review. | Agree subtle direction at launcher size; preserve safe zone/design; if adopted, SVG/vector/all density assets consistent. Actual launcher check separate from simulated masks. | +| P3 Whole-app pinch zoom | Owner is comfortable leaving it enabled. No viewport maximum-scale/user-scalable restriction added. Existing Remote pad has localized gesture handling. | Check zoom with scrolling, tap targets, safe areas, keyboard and modal lock; preserve Android accessibility text scaling. Browser emulation is not physical gesture proof. | +| P3 Many unreviewed apps | Inventory existing BACKLOG / issue history and shared `crates/core/data/app-lists/{common,shield,googletv,known-names}.json` before requesting new source/device reads. No app inventory obtained from a phone. | Classify existing review coverage and missing evidence per package/model/version; unknown stays Unknown; catalog changes individually sourced and safety-tested. Obtain explicit scope before any device interaction. | +| P2 Setup-helper row bypasses guarded Home verification | Source-verified row path used generic launcher disable. Focused routing fix sends setup-helper confirmations to the existing guarded helper command; [mock routing/rollback evidence](evidence/setup-helper-disable-2026-10-17/README.md). Independently reviewed with all six CI jobs passing; merged in PR197 at `4148f035`. | Preserve captured target, busy/refresh/error handling; verify mocked Home failure/re-enable ordering, rollback failure and stale reconnect/switch completion. No physical-device recovery claimed. | +| P2 App report omits visible APK-only fallback | Source-verified Apps report path used diskstats only. Focused fix passes a successful package/session/measurement-bound APK receipt from the detail sheet; [simulated value/preview evidence](evidence/apk-report-fallback-2026-10-17/README.md). Final integrated-head independent source review and all six CI jobs passed; PR198 merged at `60a1934c`. Its final local receipt records 326 mobile tests, check/build, and preserved source/evidence across integration; physical Android validation remains open. | Exact app bytes with apk_files source; data/cache remain unknown; opt-in sharing, diskstats precedence, failure/pending/zero, re-measure, package and connection invalidation covered. No device operations or new queries. | +| P2 Restore details offer Disable / inconclusive removal retains stale selection | PR202 merged at `b4546e8b` after exact-head independent source review and six passing CI jobs. Enable plans now map to disabled state; landed-but-false Disable/Uninstall responses clear child/parent selection in Optimize and Diagnostics while retaining the error and refresh. Confirmed uninstall retains Reinstall; target/session/fresh-safety guards remain. [Mock screenshots, two short videos and regressions](evidence/detail-state-2026-10-17/README.md) record 334 mobile and five core planner tests plus check/build. | Branch-complete in the mocked frontend scope; real Android mutations, transport/native cancellation and physical recovery are not established. | +| P2 Failed fresh sheet safety replaced by stale catalog verdict in reports | PR204 merged at `d388cad1` with exact-head independent source review and six passing CI jobs: explicit null from the sheet now supersedes an older catalog verdict, so report preview/copy remain unavailable unless the existing guarded retry returns a fresh verdict. Absent sheet state still permits the catalog fallback. [Mock regressions and simulated evidence](evidence/report-safety-2026-10-17/README.md). | [Final review receipt](https://github.com/bryanroscoe/shield_optimizer/pull/204#issuecomment-6235587609) and [exact-head CI](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39043175482) complete PR204's branch gate. Its historical local receipt records 343 mobile tests; physical safety/device acceptance remains open. Unknown and unavailable remain distinct, without a new safe/unsafe claim. | +| P2 Firmware rejects changing protected stock launcher (issue #205) | [PR206](https://github.com/bryanroscoe/shield_optimizer/pull/206) merged to main at `c78176d5` after independent source review reported by the parent and [six passing exact-head CI jobs](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39185141432). This draft integrates its shared guidance and qualified README promises into mobile with existing polish preserved. [Original simulated evidence](../evidence/firetv-protection-205-20261019/README.md) remains historical; [integration receipt](evidence/launcher-protection-integration-2026-10-19/README.md) records the combined checks. | Protection guidance requires explicit evidence for the exact package; exit 255 alone is inconclusive. Raw failures, precautionary restore outcome and actual Home readback remain separate. Reporter versions/build diagnostics and physical acceptance remain open; independent review/fresh CI are required for this integration. | +| Integration: desktop quit/installer hardening on mobile base | PR201 merged at `d14363bc` after final independent source review and six exact-lock/platform CI jobs passed. Original mobile tree is preserved; quit orchestration remains desktop-only. [Integration evidence](../../docs/INTEGRATION-MOBILE-MAIN-2026-10-17.md) retains local-cache limitations and historical logs. | Exact-lock CI gate is closed for PR201's head. Real shared-client/custom-server/updater/restart behavior across desktop platforms and physical Android acceptance remain open. | +| Store / signing / release readiness | This checklist and the reviewed development-branch merges do not approve publication. Existing [release-readiness backlog](BACKLOG.md#p2--release-readiness), [release procedure](RELEASE.md) and [licensing plan](LICENSING.md) remain authoritative. | Preserve outstanding store/checkout and entitlement acceptance, production signing/key custody, signed build/release measurements and owner release authorization. No store purchase, signed release or publication is verified here. | + +## Physical Android acceptance remains open + +Owner-driven phone/TV checks still cover system bars/cutout/rotation/navigation mode/keyboard, accessibility font scaling and pinch gestures, touch scrolling and modal focus, reconnect/liveness and native lifecycle, settings/vendor readback, measurements and real recovery. Browser screenshots and enlarged root text are simulated; debug compilation is not a device pass. Native Open/Share/SAF exports, optical icon preference and app-inventory work remain open at the scope stated above. See [LIFECYCLE-EVIDENCE.md](LIFECYCLE-EVIDENCE.md) and [BACKLOG.md](BACKLOG.md). + +## Historical first-branch validation + +The original `fix/mobile-settings-polish-20261017` receipt was based on `mobile-parity-pass` `5f4fc14`, restricted to settings readback/copy, native safe-area handling, tests/evidence and this checklist. That receipt describes its original scope; subsequent completion comes from the separately linked PRs, not from retroactively broadening that branch's validation. + +No install, real-device interaction, network/device setting change, credential operation or publication was performed for this documentation update. Screenshots/video and gallery regeneration are not applicable: no UI, assets or runtime behavior changed; existing simulated evidence remains linked rather than recaptured as a new acceptance claim. diff --git a/v2/mobile/RELEASE.md b/v2/mobile/RELEASE.md index d1f252d9..f2106501 100644 --- a/v2/mobile/RELEASE.md +++ b/v2/mobile/RELEASE.md @@ -18,8 +18,11 @@ building and uploading is still a local, manual step. ## 1. Create the upload keystore (one time, then keep it forever) -An Android package's signing identity is permanent: a key change means a new listing. Generate the -key once, back it up in the password manager **and** offline, and never commit it. +Coordinate signing identity and backup with the owner before any creation. Distinguish the app +signing key from a Play upload key: Play App Signing supports upload-key reset and specific +app-signing-key upgrade procedures. Do not assume replacing a local key preserves sideload +updates. Keep credentials out of Git. The following is reference documentation, not authorization +to generate a key. ``` keytool -genkeypair -v \ @@ -152,20 +155,112 @@ $ANDROID_HOME/build-tools/36.0.0/apksigner verify --print-certs \ 1. Play Console → Internal testing → create release → upload the AAB. 2. Check the pre-launch report; `minSdk` is 24 and `targetSdk` 36. -3. Data safety form: the app talks only to devices on the user's own LAN over wireless ADB; there - is no backend and no analytics. Keep that answer accurate if telemetry is ever added. +3. Complete Data safety from the verified final artifact, actual data inventory and owner-approved + disclosures; see `DATA-INVENTORY.md`. Do not reuse preset LAN-only/no-backend/no-analytics + answers. Include user-initiated external navigation and review the resolved SDK graph. 4. Promote internal → closed → production once a real device has been exercised end to end. -## Not done yet — read before promising a release date - -- **SPAKE2 pairing is missing.** `adb_client` implements legacy network debugging (`:5555`) but not - the Android 11+ pairing-code handshake, so a Google TV device that has never been paired from a - PC cannot be onboarded from the phone alone. Clean-room SPAKE2 is queued; see - `HANDOFF.md` and `TRANSPORT-LICENSING-RESEARCH.md`. -- **SAF import/export is missing.** Backups and snapshots stay in app-private storage; there is no - Storage Access Framework picker, so a user cannot move a bundle off the phone. -- **Licensing / Pro entitlement** is not release-ready — the current unlock path is a development - key, not signed validation. See `LICENSING.md` for the current status. -- **No release automation.** There is no mobile tag namespace and no signed-build job; step 5 is - manual. an APK/AAB CI job is not set up yet; it needs - (SDK + NDK setup, the four Rust targets, keystore secrets). +## Testing-candidate gates (feature freeze, 2026-10-16) + +This is a preparation checklist, not approval to create credentials, upload or install a build. +No purchase, trial or pricing policy is selected. Keep the existing feature set frozen. + +- [ ] Integrate the independently reviewed reconnect fix (PR167) into the chosen candidate. +- [ ] Record exact commit, lockfiles, SDK/NDK/JDK versions and selected ABIs. +- [ ] Run host Rust fmt/clippy/tests, mobile `npm test`, check and build. Host CI is not an Android build. +- [ ] Resolve and review the Android release runtime dependency graph and its notices (see + `ANDROID-DEPENDENCIES.md`); notices for declaration sites are not a complete resolved graph. +- [ ] Confirm signing-key ownership and durable custody through the owner. Do not generate keys + merely to make a build pass. Existing Gradle config can emit an unsigned release when absent. +- [ ] Choose a new, never-uploaded `versionCode` across all tracks and build the candidate locally. +- [ ] Verify the APK with `python3 scripts/verify-release-apk.py candidate.apk`, using SDK tool + paths via `--apksigner`, `--zipalign`, `--apkanalyzer` as needed. All missing tools, signature, + debug-certificate/debuggable, package-id, target/version, ZIP and architecture-aware native ELF alignment failures + reject the artifact. ELF load segments require 16 KB alignment for arm64-v8a/x86_64; + valid 4 KB armeabi-v7a/x86 libraries are accepted, with ABI/header consistency checked. + The receipt certifies ELF LOAD alignment only, not full ELF/16 KB compatibility. It does + not validate GNU_RELRO layout or overlap; retain those checks (including NDK r27-and-earlier + hazards) and actual 16 KB runtime validation as separate gates. It never signs, installs or uploads. A successful check does not prove + the signing fingerprint is the owner's, or that the versionCode exceeds Play's latest value. +- [ ] For Play, separately verify the AAB signature, bundletool output and generated APKs, then + Play's pre-launch report. The APK checker deliberately does not accept AABs. +- [ ] Run on an Android 16 KB page-size environment; static alignment checks are not runtime tests. +- [ ] Perform the physical matrix in `PAIRING-PLAN.md` and `LIFECYCLE-EVIDENCE.md`: Shield legacy + debugging; fresh Google TV code pairing; wrong code; rotated port; replaced endpoint; + denied/unavailable discovery; reconnect; Home/background/Back/Recents; recreation/process + death; fresh install and upgrade; restore/undo; free and legitimately licensed Pro behavior. + Preserve #93 and #105 until their actual acceptance evidence exists. +- [ ] Review release disclosures, risk copy, accessibility, support contact and listing assets. + `DATA-INVENTORY.md` is a code-grounded draft, not a published privacy policy. +- [ ] Owner selects distribution and monetization. No Play purchase/restore or self-service trial + exists. Do not claim that offline key entry is store purchase restoration or that expiring + manually issued licenses constitute a self-service trial. The paywall uses neutral feature copy; approve actual purchase terms before public sale. +- [ ] Obtain the account-specific testing/verification requirements from the actual store account; + no account status or production-access eligibility has been verified here. + +### Implemented, not fully physically validated + +Android 11+ SPAKE2 code pairing is implemented in `src-tauri/src/wireless_adb.rs` and the vendored +transport. Signed Ed25519 offline licensing, persistence and debug-only development keys are +implemented; see `LICENSING.md`. The old claims that these implementations were missing were stale. +Neither implementation establishes current phone/TV end-to-end readiness. + +### Still missing or unresolved + +SAF import/export, mobile release automation, verified signed APK/AAB evidence, complete resolved +Android dependency notices, privacy/store disclosures, and a selected commerce channel remain open. +The preflight-tools workflow tests the APK checker with synthetic artifacts/mocked SDK outcomes; +it does not build/sign an APK, validate the native Android transport, or publish anything. + +### Current external requirements to verify at submission + +The configuration targets API36 (the mobile requirement effective August 31, 2026), with minSdk24. +Check current rules again before submission: + +- https://support.google.com/googleplay/android-developer/answer/11926878 +- https://developer.android.com/guide/practices/page-sizes +- https://developer.android.com/google/play/billing/integrate +- https://support.google.com/googleplay/android-developer/answer/9858738 +- https://support.google.com/googleplay/android-developer/answer/10144311 + +For local-network access, SDK36 and SDK37 have different requirements; do not add/request a +future permission speculatively: https://developer.android.com/privacy-and-security/local-network-permission + +## Open Shield issues at the 2026-10-16 release audit + +This is a dated triage snapshot, not a claim that an open issue is resolved. Re-read the tracker +before cutting a candidate; close only after its actual acceptance evidence is reviewed. + +| Issue | Release disposition / exact remaining evidence | +|---|---| +| [#93 Saved-TV discovery/reconnect](https://github.com/bryanroscoe/shield_optimizer/issues/93) | Physical gate: Shield without mDNS, rotated Google TV port, changed address, cached identity/name isolation and honest reconnect results. Browser coverage does not close this. | +| [#105 Restart/lifecycle report](https://github.com/bryanroscoe/shield_optimizer/issues/105) | Physical gate: distinguish Home/background, Back, Recents, recreation and process death. Do not claim restored live state or replay mutations from browser reconstruction. | +| [#123 Glance/Daydream](https://github.com/bryanroscoe/shield_optimizer/issues/123) | Current screensaver controls are not proof that the reporter's server-delivered Glance variant is covered. Obtain exact package/device behavior and reporter confirmation; retain vendor restore/reconnect tests. | +| [#161 Launcher sources/default](https://github.com/bryanroscoe/shield_optimizer/issues/161) | Desktop source-site work merged in PR165; mobile parity includes source links. Confirm the requested installed/current-default case and release availability; do not reimplement or auto-close. | +| [#162 Logcat](https://github.com/bryanroscoe/shield_optimizer/issues/162) | Desktop Logs merged in PR166. Confirm requested filtering/live-display acceptance and release availability; it is not a reason to add mobile features during this freeze. | + +PR167 reconnect safety was merged into `mobile-parity-pass` at +`f7897c1292d702fdfad1b77c3193312a641475b2` after independent review and six passing PR checks. +Its integration does not clear the physical gates above. Release-preparation PR169 includes that +ancestry by a normal merge. The existing test workflow triggers pushes to `main`, not feature +branches; a missing postmerge feature-branch run must not be reported as a passing run. + +### Preparation validation receipt (2026-10-16) + +On the release-preparation branch after merging `f7897c1`: 218 mobile browser/unit tests passed, +13 APK-verifier tests passed, mobile check reported 0 errors / 0 warnings and build succeeded. +The neutral paywall was inspected at 384×812. Notices regeneration inventoried 313 Cargo crates; +Android/JVM declarations are listed separately without claiming full transitive resolution. +The installed SDK apksigner rejected an unsigned synthetic archive through the CLI (exit 1, +no success receipt). Positive signing/SDK outcomes in unit tests are mocked: no genuine signed +release artifact or Android runtime was validated. This receipt is not release authorization. + +### Modal polish follow-up (2026-10-17, merged PR186) + +The separate modal follow-up registers `StockTakeoverDialog` with shared scroll/focus ownership +and preserves original-row focus through sequential `AppActionMenu` → Report transitions. +Regression coverage includes cancellation, async replacements, nested/out-of-order teardown, +short-screen keyboard access and restored scroll. See +[evidence and verification limits](evidence/modal-handoffs-2026-10-17/README.md). +These two P2s passed independent source review and all six CI jobs, and PR186 merged at +`83f69b77e2b19b643e21ae456d0f597e90ef7d83`. Host simulation does not clear physical-device gates. diff --git a/v2/mobile/THIRD-PARTY-NOTICES.md b/v2/mobile/THIRD-PARTY-NOTICES.md index a1f24ea9..4eff2180 100644 --- a/v2/mobile/THIRD-PARTY-NOTICES.md +++ b/v2/mobile/THIRD-PARTY-NOTICES.md @@ -8,6 +8,23 @@ Regenerate this document with `mobile/scripts/gen-notices.sh` after any dependen ## Components not managed by Cargo +### Android/JVM runtime dependencies + +The application declares AndroidX WebKit 1.14.0, AppCompat 1.7.1, Activity KTX 1.10.1, +Lifecycle Process 2.10.0 and Material Components 1.12.0. The ADB plugin declares AndroidX +Core KTX 1.13.1 and kotlinx-coroutines-android 1.10.2. Tauri Android declares older Core KTX, +AppCompat and Material versions plus Jackson Databind 2.15.3. +The opener plugin 2.5.4 declares **AndroidX Browser 1.8.0**, Core KTX 1.9.0 and Jackson Databind +2.15.3. These components use Apache License 2.0 (AndroidX, Material, Jackson) or Apache License +2.0 (kotlinx.coroutines); retain upstream notices and applicable license texts with the resolved +release inventory. The Apache License 2.0 text appears below. + +These are actual dependency declarations, not a claim of final Gradle resolution. Transitive +JVM dependencies, conflict-selected versions and their per-artifact NOTICE files must be captured +from the final release runtime classpath before distribution. See `ANDROID-DEPENDENCIES.md`. + + + ### Geist and Geist Mono — SIL Open Font License 1.1 `src/fonts/Geist-Variable.woff2` and `src/fonts/GeistMono-Variable.woff2` are the variable-weight @@ -145,16 +162,17 @@ appears in the Cargo inventory below with its upstream license text. Source: -### npm runtime dependencies +### Bundled npm-origin code and assets -Only these npm packages contribute code or assets to the shipped app; everything else in -`package.json` is build-time tooling. +These packages are the sources of bundled code/assets. Geist 1.7.2 is retained as checked-in +WOFF2 assets with its license; the unused npm package is not needed to build the app. +Other entries in `package.json` provide tooling, which can also contribute compiled runtime code. | Package | Version | License | | --- | --- | --- | | `svelte` | 5.56.4 | MIT | | `@tauri-apps/api` | 2.11.1 | Apache-2.0 OR MIT | -| `geist` | 1.7.2 | SIL OFL 1.1 (see above) | +| `geist` (checked-in WOFF2 assets only) | 1.7.2 | SIL OFL 1.1 (see above) | | `material-symbols` | 0.45.5 | Apache-2.0 (see above) | Svelte compiles into the app bundle; `@tauri-apps/api` ships as JavaScript. Both are used under @@ -162,12 +180,12 @@ the MIT License, whose text is reproduced in the MIT section below. ## Rust crate dependencies -308 crates are linked into the Android build (build-time and dev-only crates excluded). +313 crates are linked into the Android build (build-time and dev-only crates excluded). | License | Crates | | --- | --- | -| MIT | 271 | -| Apache-2.0 | 211 | +| MIT | 276 | +| Apache-2.0 | 213 | | Unicode-3.0 | 19 | | BSD-3-Clause | 11 | | Unlicense | 6 | @@ -303,6 +321,8 @@ A crate under `A OR B` is counted under both; we distribute under whichever term | [infer](https://github.com/bojand/infer) | 0.19.0 | MIT | | [inout](https://github.com/RustCrypto/utils) | 0.1.4 | MIT OR Apache-2.0 | | [ipnet](https://github.com/krisprice/ipnet) | 2.12.0 | MIT OR Apache-2.0 | +| [is-docker](https://github.com/TheLarkInn/is-docker) | 0.2.0 | MIT | +| [is-wsl](https://github.com/TheLarkInn/is-wsl) | 0.4.0 | MIT | | [itoa](https://github.com/dtolnay/itoa) | 1.0.18 | MIT OR Apache-2.0 | | [jni](https://github.com/jni-rs/jni-rs) | 0.21.1 | MIT OR Apache-2.0 | | [jni-sys](https://github.com/jni-rs/jni-sys) | 0.3.1 | MIT OR Apache-2.0 | @@ -337,9 +357,11 @@ A crate under `A OR B` is counted under both; we distribute under whichever term | [num_enum_derive](https://github.com/illicitonion/num_enum) | 0.7.6 | BSD-3-Clause OR MIT OR Apache-2.0 | | [once_cell](https://github.com/matklad/once_cell) | 1.21.4 | MIT OR Apache-2.0 | | [opaque-debug](https://github.com/RustCrypto/utils) | 0.3.1 | MIT OR Apache-2.0 | +| [open](https://github.com/Byron/open-rs) | 5.3.5 | MIT | | [option-ext](https://github.com/soc/option-ext.git) | 0.2.0 | MPL-2.0 | | [parking_lot](https://github.com/Amanieu/parking_lot) | 0.12.5 | MIT OR Apache-2.0 | | [parking_lot_core](https://github.com/Amanieu/parking_lot) | 0.9.12 | MIT OR Apache-2.0 | +| [pathdiff](https://github.com/Manishearth/pathdiff) | 0.2.3 | MIT OR Apache-2.0 | | [pem](https://github.com/jcreekmore/pem-rs.git) | 3.0.6 | MIT | | [pem-rfc7468](https://github.com/RustCrypto/formats/tree/master/pem-rfc7468) | 0.7.0 | Apache-2.0 OR MIT | | [percent-encoding](https://github.com/servo/rust-url/) | 2.3.2 | MIT OR Apache-2.0 | @@ -427,6 +449,7 @@ A crate under `A OR B` is counted under both; we distribute under whichever term | [tauri](https://github.com/tauri-apps/tauri) | 2.11.2 | Apache-2.0 OR MIT | | [tauri-codegen](https://github.com/tauri-apps/tauri) | 2.6.2 | Apache-2.0 OR MIT | | [tauri-macros](https://github.com/tauri-apps/tauri) | 2.6.2 | Apache-2.0 OR MIT | +| [tauri-plugin-opener](https://github.com/tauri-apps/plugins-workspace) | 2.5.4 | Apache-2.0 OR MIT | | [tauri-runtime](https://github.com/tauri-apps/tauri) | 2.11.2 | Apache-2.0 OR MIT | | [tauri-runtime-wry](https://github.com/tauri-apps/tauri) | 2.11.2 | Apache-2.0 OR MIT | | [tauri-utils](https://github.com/tauri-apps/tauri) | 2.9.2 | Apache-2.0 OR MIT | @@ -2694,6 +2717,36 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` +Used by: open 5.3.5 + +``` +The MIT License (MIT) +===================== + +Copyright © `2015` `Sebastian Thiel` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +``` + Used by: generic-array 0.14.7 ``` @@ -3673,7 +3726,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -Used by: brotli-decompressor 5.0.0, cesu8 1.1.0, chrono 0.4.44, dpi 0.1.2, jni-sys-macros 0.4.1, libm 0.2.16, ndk 0.9.0, ndk-sys 0.6.0+11769913, quick-protobuf 0.8.1, rcgen 0.14.8, siphasher 1.0.3, tao-macros 0.1.3, tauri 2.11.2, tauri-codegen 2.6.2, tauri-macros 2.6.2, tauri-runtime 2.11.2, tauri-runtime-wry 2.11.2, tauri-utils 2.9.2, unic-char-property 0.9.0, unic-char-range 0.9.0, unic-common 0.9.0, unic-ucd-ident 0.9.0, unic-ucd-version 0.9.0 +Used by: brotli-decompressor 5.0.0, cesu8 1.1.0, chrono 0.4.44, dpi 0.1.2, jni-sys-macros 0.4.1, libm 0.2.16, ndk 0.9.0, ndk-sys 0.6.0+11769913, quick-protobuf 0.8.1, rcgen 0.14.8, siphasher 1.0.3, tao-macros 0.1.3, tauri 2.11.2, tauri-codegen 2.6.2, tauri-macros 2.6.2, tauri-plugin-opener 2.5.4, tauri-runtime 2.11.2, tauri-runtime-wry 2.11.2, tauri-utils 2.9.2, unic-char-property 0.9.0, unic-char-range 0.9.0, unic-common 0.9.0, unic-ucd-ident 0.9.0, unic-ucd-version 0.9.0 ``` MIT License @@ -4570,6 +4623,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` +Used by: is-docker 0.2.0, is-wsl 0.4.0 + +``` +MIT License + +Copyright (c) 2023 Sean Larkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + Used by: string_cache 0.9.0 ``` @@ -5970,7 +6049,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -Used by: adler2 2.0.1, anyhow 1.0.102, async-trait 0.1.89, atomic-waker 1.1.2, camino 1.2.2, cargo-platform 0.1.9, cargo_metadata 0.19.2, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, dtoa 1.0.11, dyn-clone 1.0.20, erased-serde 0.4.10, fastrand 2.4.1, itoa 1.0.18, linux-raw-sys 0.12.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, pin-project-lite 0.2.17, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.45, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde-untagged 0.1.9, serde_core 1.0.228, serde_derive 1.0.228, serde_derive_internals 0.29.1, serde_json 1.0.150, serde_repr 0.1.20, servo_arc 0.4.3, syn 2.0.117, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, typeid 1.0.3, unicode-ident 1.0.24, utf-8 0.7.6, zmij 1.0.21 +Used by: adler2 2.0.1, anyhow 1.0.102, async-trait 0.1.89, atomic-waker 1.1.2, camino 1.2.2, cargo-platform 0.1.9, cargo_metadata 0.19.2, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, dtoa 1.0.11, dyn-clone 1.0.20, erased-serde 0.4.10, fastrand 2.4.1, itoa 1.0.18, linux-raw-sys 0.12.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, pathdiff 0.2.3, pin-project-lite 0.2.17, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.45, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde-untagged 0.1.9, serde_core 1.0.228, serde_derive 1.0.228, serde_derive_internals 0.29.1, serde_json 1.0.150, serde_repr 0.1.20, servo_arc 0.4.3, syn 2.0.117, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, typeid 1.0.3, unicode-ident 1.0.24, utf-8 0.7.6, zmij 1.0.21 ``` Permission is hereby granted, free of charge, to any diff --git a/v2/mobile/evidence/apk-report-fallback-2026-10-17/README.md b/v2/mobile/evidence/apk-report-fallback-2026-10-17/README.md new file mode 100644 index 00000000..90f3c433 --- /dev/null +++ b/v2/mobile/evidence/apk-report-fallback-2026-10-17/README.md @@ -0,0 +1,25 @@ +# Carry measured APK storage into an app report — 2026-10-17 + +Current integrated base: `4148f035c20450751d92d894456162cbc5ab837b`. + +Original base: mobile-parity-pass `ba7c9d78d31b0a579f5ffd03d92cd403ebf47227`. Separate from setup-helper routing PR197. Final integration checks use the actual current mobile base noted in the validation receipt. + +Local source verification confirms Apps built report storage only from diskstats, while AppDetailSheet held a successful APK fallback locally and passed only the app to onReport. The new pre-fix regression displayed APK 2.0 KB in the sheet but found `storage: null` in the report. See [recorded failure](reproduction-before.txt). The [external review finding](https://github.com/bryanroscoe/shield_optimizer/pull/163#discussion_r4301719603) was treated as untrusted evidence; no instructions in it were executed. + +The sheet now passes an internal successful-measurement receipt at Report click. It binds package, serial, connection generation and the Apps measurement-batch counter, plus the exact bytes and measurement time. Apps validates those bindings against the current list/session/batch whenever it builds report state. Re-measuring immediately invalidates the receipt, even while an existing preview is open; old async replies cannot replace a new batch, package or connection. Report close, list invalidation and reports opened from the menu clear the receipt. Identity/time fields remain internal and never enter report JSON. + +Only finite, nonnegative, successful APK sizes qualify. Fresh diskstats data keeps precedence. The fallback exports `source: apk_files`, exact app_bytes and null data_bytes/cache_bytes; even extra data/cache fields in a malformed fallback response cannot become a claimed measurement. Pending, failed, missing and negative values remain unknown. Zero is a valid reported size. State sharing stays opt-in; no new query, write, schema field or device operation is introduced. + +Simulated evidence shows [the measured APK value](visible-apk-measurement-simulated.png) and [its report preview](report-preview-simulated.png). The displayed 1.2 MB retains the exact 1234567 bytes in [preview/copied JSON](storage-report-simulated.json), with data/cache unknown. Tests verify copied text equals the preview and exclude serial/hardware id/internal binding fields. + +Regressions cover diskstats precedence, zero/exact bytes, pending/failed/missing/negative fallback, active-preview re-measure invalidation, older replies after a new measurement, package changes, same-endpoint reconnect and TV switch. All calls are browser mocks. No real package measurement, app action, device read, native storage/export permission, icon or catalog-data change occurred. No Rust production code changed; Android/physical acceptance remains open. + +Original-base validation: all 316 mobile tests passed (including all 21 apps/report tests), Svelte check reports 0 errors/0 warnings and the production build passed. This checkpoint precedes final integration with the newly merged preset-layout base. + +Current base integration: `a73787a36bbf44311de2596e9a01c8232a049a02` includes merged scaling-layout PR196. APK report source, its app/report tests and all captured evidence are unchanged byte-for-byte from the source-review checkpoint; only upstream files, completed checklist status and validation provenance are added. + +Current-base validation on `a73787a`: all 320 combined mobile tests passed, Svelte check reports 0 errors/0 warnings and the production build passed. The original 316-test result above is historical. No APK report source or evidence pixels changed during this integration. + +Latest integration: current mobile base `4148f035c20450751d92d894456162cbc5ab837b` includes merged helper-routing PR197. Approved APK-report source and app/report tests match `f1044a5` byte-for-byte; helper source/tests match the merged base exactly. The only conflict was the adjacent checklist rows, both of which are retained with accurate review/merge status. Original APK screenshots and simulated JSON remain unchanged and retain their original capture provenance. + +Latest validation on `4148f035`: all 326 combined mobile tests passed, Svelte check reports 0 errors/0 warnings and the production build passed. Previous 316/320-test receipts above are historical. No APK implementation, app/report fixture, captured PNG or simulated JSON changes were required. PR198 remains ready and unmerged. diff --git a/v2/mobile/evidence/apk-report-fallback-2026-10-17/report-preview-simulated.png b/v2/mobile/evidence/apk-report-fallback-2026-10-17/report-preview-simulated.png new file mode 100644 index 00000000..beff98e4 Binary files /dev/null and b/v2/mobile/evidence/apk-report-fallback-2026-10-17/report-preview-simulated.png differ diff --git a/v2/mobile/evidence/apk-report-fallback-2026-10-17/reproduction-before.txt b/v2/mobile/evidence/apk-report-fallback-2026-10-17/reproduction-before.txt new file mode 100644 index 00000000..1794609f --- /dev/null +++ b/v2/mobile/evidence/apk-report-fallback-2026-10-17/reproduction-before.txt @@ -0,0 +1,41 @@ +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ report includes the successful APK fallback visible in the detail sheet (1621.245084ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1903.480959 + +✖ failing tests: + +test at tests/apps-catalog.test.mjs:372:1 +✖ report includes the successful APK fallback visible in the detail sheet (1621.245084ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + null + - { + - app_bytes: 2048, + - cache_bytes: null, + - data_bytes: null, + - source: 'apk_files' + - } + + at TestContext. (file:///v2/mobile/tests/apps-catalog.test.mjs:375:10) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: null, + expected: { source: 'apk_files', app_bytes: 2048, data_bytes: null, cache_bytes: null }, + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/v2/mobile/evidence/apk-report-fallback-2026-10-17/storage-report-simulated.json b/v2/mobile/evidence/apk-report-fallback-2026-10-17/storage-report-simulated.json new file mode 100644 index 00000000..48544604 --- /dev/null +++ b/v2/mobile/evidence/apk-report-fallback-2026-10-17/storage-report-simulated.json @@ -0,0 +1,36 @@ +{ + "schema_version": 1, + "generated_at": "2026-10-17T07:18:59.011Z", + "truncated": false, + "source": "mobile_app_report", + "records": [ + { + "kind": "installed_package", + "token": "com.example.streambox", + "reason": "user_report_not_listed", + "app_version": "0.1.0", + "registry_version": null, + "device_family": "shield", + "device_os": "11", + "first_seen": "2026-10-17T07:18:59.011Z", + "last_seen": "2026-10-17T07:18:59.011Z", + "count": 1, + "app_name": "StreamBox", + "current_verdict": "unknown", + "verdict_source": "no_record", + "note": null, + "state": { + "installed": true, + "enabled": true, + "running": false, + "ram_mb": null, + "storage": { + "source": "apk_files", + "app_bytes": 1234567, + "data_bytes": null, + "cache_bytes": null + } + } + } + ] +} diff --git a/v2/mobile/evidence/apk-report-fallback-2026-10-17/validation.json b/v2/mobile/evidence/apk-report-fallback-2026-10-17/validation.json new file mode 100644 index 00000000..eaa658c7 --- /dev/null +++ b/v2/mobile/evidence/apk-report-fallback-2026-10-17/validation.json @@ -0,0 +1,33 @@ +{ + "original_base": "ba7c9d78d31b0a579f5ffd03d92cd403ebf47227", + "mobile_tests": 326, + "apps_report_tests": 21, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "build": "passed", + "measured_bytes_in_evidence": 1234567, + "export_storage": { + "source": "apk_files", + "app_bytes": 1234567, + "data_bytes": null, + "cache_bytes": null + }, + "identity_receipt": "Internal package, serial, connection generation and measurement-batch id; never exported.", + "limits": [ + "Browser mocks only; no device operations", + "No new queries, write actions or schema fields" + ], + "pending": null, + "current_base": "4148f035c20450751d92d894456162cbc5ab837b", + "original_mobile_tests": 316, + "integration": "Merge approved helper PR197; reconcile both checklist rows and validation provenance. APK source/app-report tests unchanged from f1044a5; helper source/tests exactly match merged base.", + "integration_history": [ + { + "base": "a73787a36bbf44311de2596e9a01c8232a049a02", + "mobile_tests": 320, + "head": "f1044a5b8af6ce01502223e4d569721e488dbe9e" + } + ] +} diff --git a/v2/mobile/evidence/apk-report-fallback-2026-10-17/visible-apk-measurement-simulated.png b/v2/mobile/evidence/apk-report-fallback-2026-10-17/visible-apk-measurement-simulated.png new file mode 100644 index 00000000..5a334d02 Binary files /dev/null and b/v2/mobile/evidence/apk-report-fallback-2026-10-17/visible-apk-measurement-simulated.png differ diff --git a/v2/mobile/evidence/cleartv-integration-2026-10-22/README.md b/v2/mobile/evidence/cleartv-integration-2026-10-22/README.md new file mode 100644 index 00000000..37c03eaa --- /dev/null +++ b/v2/mobile/evidence/cleartv-integration-2026-10-22/README.md @@ -0,0 +1,17 @@ +# Reviewed ClearTV catalog integration into mobile + +Mobile base: `b7bd2c0b50fc774c3ffad10ee67d95c9c7aa4a79`. Incoming main: `c227cc87782233b2a44ef1d6e27ca30335b88783`, the reviewed PR209 squash merge. A normal merge preserves both histories; no catalog change was reimplemented or duplicated. Main's postmerge [run39727206353](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39727206353) passed all six jobs on that exact main commit. This does not substitute for the integration's own checks. + +## Exact conflict and resolution + +The merge produced only two conflicts: `v2/screenshots/gallery.gif` and `gallery-light.gif`. These are generated desktop walkthroughs. The JSON catalog, loader regression, demo fixture, browser regression and CI capture support merged automatically. + +Both conflicts use reviewed main's regenerated bytes. After the merge, desktop `v2/src`, shared frontend code, capture script, both gallery GIFs, catalog, loader and launcher-row test match main exactly. The catalog still contains one `com.gathin.tvhome` identity; no engine, safety or takeover code changes were introduced. `gallery-resolution.json` records old/new blobs and resolved hashes. Mobile's `src`, `src-tauri` and `tauri-plugin-atv-adb` trees remain byte-identical to the mobile base. Existing mobile-only shared/backend work is retained through the normal merge. + +## Validation and visual rationale + +Combined workspace formatting and all-target Clippy with warnings denied pass. Full serial workspace tests pass in `workspace-tests.txt`; local checks use existing offline dependencies, two jobs, incremental off and dev/test debug info off, reusing this task's earlier target cache. Frontend dependencies remain offloaded, so no local browser/type/build execution or installation is claimed. The integration's fresh exact-head CI and independent review remain its merge gates; original PR209 CI is not relabeled as integration validation. + +There is no new UI rendering relative to reviewed main: matching desktop/capture source and unchanged mobile UI justify reusing [PR209's four labeled simulated states, 7.6-second video and gallery provenance](../../../evidence/cleartv-208-20261022/README.md). No new screenshots/video or another gallery rebuild is necessary for selecting the same reviewed generated bytes. Fresh CI still executes the imported state/deduplication regressions and simulated UI checks on the combined tree. + +Package-only detection cannot distinguish ClearTV Islamic from CleanYourTV using the same default application id. Shield2017 compatibility remains unverified. PR163 itself is not merged by this integration; its physical-device/release acceptance gates and PR180's separate approval gate remain open. No device/APK operations, installs, signatures/accessibility/security changes, model-cost changes, deployment or release is included. diff --git a/v2/mobile/evidence/cleartv-integration-2026-10-22/clippy.txt b/v2/mobile/evidence/cleartv-integration-2026-10-22/clippy.txt new file mode 100644 index 00000000..5bd9b932 --- /dev/null +++ b/v2/mobile/evidence/cleartv-integration-2026-10-22/clippy.txt @@ -0,0 +1,8 @@ + Checking shield-optimizer-core v2.3.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/crates/core) + Compiling tauri-plugin-atv-adb v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/mobile/tauri-plugin-atv-adb) + Compiling shield-optimizer-v2 v2.3.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/src-tauri) + Compiling atv-optimizer-mobile v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/mobile/src-tauri) + Checking adb_client v3.2.2 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/vendor/adb_client) + Checking atvopt-license v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/tools/atvopt-license) + Checking registry-triage v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/tools/registry-triage) + Finished `dev` profile [unoptimized] target(s) in 8.52s diff --git a/v2/mobile/evidence/cleartv-integration-2026-10-22/gallery-resolution.json b/v2/mobile/evidence/cleartv-integration-2026-10-22/gallery-resolution.json new file mode 100644 index 00000000..651bed1f --- /dev/null +++ b/v2/mobile/evidence/cleartv-integration-2026-10-22/gallery-resolution.json @@ -0,0 +1,19 @@ +{ + "mobile_base": "b7bd2c0b50fc774c3ffad10ee67d95c9c7aa4a79", + "incoming_main": "c227cc87782233b2a44ef1d6e27ca30335b88783", + "resolution": "reviewed main gallery bytes; combined desktop UI/capture source matches main", + "files": [ + { + "path": "v2/screenshots/gallery.gif", + "mobile_blob": "181eaec74b64b8571b125e3300ed297bd23470c2", + "main_blob": "ed7e81b555a9bd0f67c6101e0ccf6237d1599505", + "resolved_sha256": "9e82950174324ceba202666f8fec1a629da022b7f25634523c7878d3fe132b0c" + }, + { + "path": "v2/screenshots/gallery-light.gif", + "mobile_blob": "678a78f471612c5b1c2ef8abbd4a0541f3c2eccd", + "main_blob": "49a35d674d5ebe0c5003abf8a27feb98b399b4a2", + "resolved_sha256": "07a79c226f57bf2a685d99e45f4efa3bcdefe3baaaab77023f6f190711a32c23" + } + ] +} diff --git a/v2/mobile/evidence/cleartv-integration-2026-10-22/workspace-tests.txt b/v2/mobile/evidence/cleartv-integration-2026-10-22/workspace-tests.txt new file mode 100644 index 00000000..11dc206b --- /dev/null +++ b/v2/mobile/evidence/cleartv-integration-2026-10-22/workspace-tests.txt @@ -0,0 +1,720 @@ + Compiling shield-optimizer-core v2.3.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/crates/core) + Compiling tauri-plugin-atv-adb v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/mobile/tauri-plugin-atv-adb) + Compiling shield-optimizer-v2 v2.3.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/src-tauri) + Compiling atv-optimizer-mobile v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/mobile/src-tauri) + Compiling adb_client v3.2.2 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/vendor/adb_client) + Compiling registry-triage v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/tools/registry-triage) + Compiling atvopt-license v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-209-20261022/v2/tools/atvopt-license) + Finished `test` profile [unoptimized] target(s) in 14.74s + Running unittests src/lib.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/atv_optimizer_mobile_lib-354c841cea3930c4) + +running 26 tests +test file_commands::tests::complete_bundle_round_trips ... ok +test file_commands::tests::concurrent_downloads_publish_distinct_complete_files ... ok +test file_commands::tests::delete_target_must_be_a_top_level_backup ... ok +test file_commands::tests::download_names_dedupe_instead_of_clobbering ... ok +test file_commands::tests::failed_download_removes_partial_output_and_preserves_existing_files ... ok +test file_commands::tests::parses_every_split_apk_path ... ok +test file_commands::tests::rejects_unsafe_reported_apk_path ... ok +test file_commands::tests::restore_path_must_stay_inside_backup_root ... ok +test license_tests::persisted_valid_license_round_trips ... ok +test license_tests::persistence_failure_is_reported ... ok +test limited_writer::tests::preserves_destination_errors ... ok +test limited_writer::tests::rejects_growth_without_writing_past_the_limit ... ok +test remote_lifecycle::tests::current_background_epoch_can_claim_cleanup_once ... ok +test remote_lifecycle::tests::newer_suspend_invalidates_an_older_timer ... ok +test remote_lifecycle::tests::resume_invalidates_pending_cleanup ... ok +test scrcpy_resource::tests::embedded_server_matches_audited_release ... ok +test scrcpy_resource::tests::materialize_creates_and_repairs_server ... ok +test wireless_adb::tests::canceled_handshakes_cannot_publish_or_supersede_a_retry ... ok +test wireless_adb::tests::connect_timeout_explains_the_tv_approval_prompt ... ok +test wireless_adb::tests::installs_get_the_slow_shell_timeout ... ok +test wireless_adb::tests::only_socket_level_errors_are_fatal ... ok +test wireless_adb::tests::pairing_codes_are_normalized_before_the_one_allowed_guess ... ok +test wireless_adb::tests::pairing_errors_name_the_next_step ... ok +test wireless_adb::tests::refused_connect_explains_network_debugging ... ok +test wireless_adb::tests::requested_serial_must_match_active_connection ... ok +test wireless_commands::tests::find_remote_requires_a_successful_shell_result ... ok + +test result: ok. 26 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s + + Running unittests src/main.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/atv_optimizer_mobile-f9318574c2dbaf8e) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running unittests src/main.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/atvopt_license-ae39b7a189fbf8a8) + +running 2 tests +test tests::private_key_file_round_trips ... ok +test tests::rejects_a_key_file_that_is_not_hex ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running unittests src/main.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/registry_triage-8414bee1b54060de) + +running 9 tests +test tests::a_batch_aggregates_by_package_and_reports_today_without_promoting ... ok +test tests::a_desktop_app_report_is_read_and_never_promotes_the_app ... ok +test tests::a_truncated_export_is_named_and_its_counts_are_lower_bounds ... ok +test tests::desktop_bundles_yield_home_handlers_and_never_the_serial ... ok +test tests::mobile_records_that_break_the_contract_are_rejected ... ok +test tests::overlapping_folders_count_each_report_once ... ok +test tests::the_verdict_names_the_source_that_decided_it ... ok +test tests::tokens_follow_the_mobile_collector_rules ... ok +test tests::triage_leaves_the_report_folder_untouched ... ok + +test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running unittests src/lib.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/shield_optimizer_core-29c4a2a32901dc1d) + +running 494 tests +test adb::batch::tests::checked_batch_records_each_real_shell_exit_even_if_the_last_succeeds ... ok +test adb::batch::tests::checked_reads_reject_failed_or_missing_required_sections ... ok +test adb::batch::tests::empty_batch_is_empty ... ok +test adb::batch::tests::empty_sections_stay_empty_and_keep_position ... ok +test adb::batch::tests::extra_sections_beyond_n_are_dropped ... ok +test adb::batch::tests::joins_with_semicolons_and_discards_stderr ... ok +test adb::batch::tests::preserves_interior_indentation_and_blank_lines ... ok +test adb::batch::tests::round_trips_a_realistic_batch ... ok +test adb::batch::tests::single_command_gets_no_separator ... ok +test adb::batch::tests::splits_into_exactly_n_sections ... ok +test adb::batch::tests::tolerates_sentinel_at_the_very_end ... ok +test adb::batch::tests::truncated_output_pads_with_empty_sections ... ok +test adb::driver::bounded_shell_tests::drivers_without_bounded_execution_are_unsupported ... ok +test adb::lifetimes::tests::a_closed_counter_refuses_new_leases_but_keeps_counting_old_ones ... ok +test adb::lifetimes::tests::a_closed_snapshot_refuses_every_spawning_method ... ok +test adb::lifetimes::tests::a_leased_snapshot_forwards_every_method_to_its_own_counterpart ... ok +test adb::lifetimes::tests::processes::a_child_dropped_with_no_runtime_to_reap_it_stays_counted ... ok +test adb::lifetimes::tests::processes::a_dropped_child_is_reaped_before_its_lease_goes ... ok +test adb::lifetimes::tests::processes::kill_and_reap_releases_once_the_process_is_gone ... ok +test adb::lifetimes::tests::processes::waiting_out_the_process_releases_it ... ok +test adb::lifetimes::tests::waiting_ends_when_the_last_lease_goes ... ok +test adb::lifetimes::tests::waiting_gives_up_at_the_deadline ... ok +test adb::parse::tests::a_bracketed_ipv6_transport_is_a_network_connection ... ok +test adb::parse::tests::audio_device_missing_returns_none ... ok +test adb::parse::tests::display_mode_sdr_only_when_hdr_list_empty ... ok +test adb::parse::tests::display_modes_degrade_to_empty_rather_than_guessing ... ok +test adb::parse::tests::display_reports_scope_modes_and_hdr_to_the_same_default_record ... ok +test adb::parse::tests::duplicate_modes_collapse_but_keep_the_active_flag ... ok +test adb::parse::tests::empty_mdns_output_is_not_an_error ... ok +test adb::parse::tests::hardware_properties_temp_none_when_empty ... ok +test adb::parse::tests::hardware_reading_pairs_limits_by_family_index_and_exact_length ... ok +test adb::parse::tests::ignores_header_and_blank_lines ... ok +test adb::parse::tests::instance_serial_rejects_malformed_or_unknown_names ... ok +test adb::parse::tests::ipv6_endpoint_is_bracketed_like_the_adb_transport_key ... ok +test adb::parse::tests::ls_parse_returns_empty_on_error_output ... ok +test adb::parse::tests::mdns_endpoint_keeps_an_ipv6_host_intact ... ok +test adb::parse::tests::mdns_wireless_debugging_serial_is_network_not_usb ... ok +test adb::parse::tests::meminfo_returns_empty_when_section_missing ... ok +test adb::parse::tests::multiple_displays_without_a_unique_default_are_unavailable ... ok +test adb::parse::tests::net_dev_preserves_interfaces_and_skips_loopback ... ok +test adb::parse::tests::net_dev_with_no_usable_interfaces_is_empty ... ok +test adb::parse::tests::one_eligible_display_does_not_prove_the_current_default ... ok +test adb::parse::tests::package_for_process_strips_only_a_subprocess_suffix ... ok +test adb::parse::tests::pairing_service_is_not_offered_as_a_connect_endpoint ... ok +test adb::parse::tests::parses_active_audio_device ... ok +test adb::parse::tests::parses_device_list_with_mixed_states ... ok +test adb::parse::tests::parses_df_data_storage ... ok +test adb::parse::tests::parses_df_data_storage_when_filesystem_name_wraps ... ok +test adb::parse::tests::parses_display_mode_shield_4k60_hdr10 ... ok +test adb::parse::tests::parses_every_display_mode_and_flags_the_active_one ... ok +test adb::parse::tests::parses_hardware_properties_temp_from_cpu_and_gpu ... ok +test adb::parse::tests::parses_ls_rows_including_spaced_names_and_symlinks ... ok +test adb::parse::tests::parses_meminfo_summary_kb ... ok +test adb::parse::tests::parses_multiple_hdr_types ... ok +test adb::parse::tests::parses_permission_grant_state ... ok +test adb::parse::tests::parses_pm_list_packages_output ... ok +test adb::parse::tests::parses_real_mdns_services_output ... ok +test adb::parse::tests::parses_real_shield_df_output ... ok +test adb::parse::tests::parses_thermal_max_temp ... ok +test adb::parse::tests::parses_thermal_rejects_garbage_values ... ok +test adb::parse::tests::parses_total_pss_section_and_sums_per_package ... ok +test adb::parse::tests::parses_usage_stats_last_used_and_never_opened ... ok +test adb::parse::tests::path_named_processes_are_kept_not_dropped ... ok +test adb::parse::tests::pm_list_packages_skips_garbage_lines ... ok +test adb::parse::tests::proc_stat_counts_iowait_as_idle ... ok +test adb::parse::tests::proc_stat_ignores_per_core_lines_and_missing_input ... ok +test adb::parse::tests::proc_stat_rejects_invalid_or_overflowing_counters ... ok +test adb::parse::tests::real_meminfo_keeps_every_row_with_its_full_name_and_pid ... ok +test adb::parse::tests::real_meminfo_package_map_never_trims_a_hal_into_a_package ... ok +test adb::parse::tests::real_top_names_only_yield_candidates_of_package_shape ... ok +test adb::parse::tests::repeated_single_display_records_do_not_create_ambiguity ... ok +test adb::parse::tests::thermal_current_hal_wins_equal_temperature_with_new_status ... ok +test adb::parse::tests::thermal_current_hal_wins_even_when_cache_is_hotter ... ok +test adb::parse::tests::thermal_empty_current_section_does_not_resurrect_cache_but_legacy_falls_back ... ok +test adb::parse::tests::thermal_reading_preserves_sensor_context_and_excludes_non_celsius_objects ... ok +test adb::parse::tests::thermal_unknown_context_and_invalid_status_never_become_cpu_or_normal ... ok +test adb::parse::tests::unparseable_mdns_rows_are_dropped_not_guessed ... ok +test adb::remote_input::tests::android_resident_server_command_is_exact ... ok +test adb::remote_input::tests::format_scid_is_8_hex_digits_with_high_bit_cleared ... ok +test adb::remote_input::tests::forward_and_push_args_are_well_formed ... ok +test adb::remote_input::tests::inject_keycode_down_select_is_exact_14_bytes ... ok +test adb::remote_input::tests::inject_keycode_up_dpad_with_repeat_and_meta ... ok +test adb::remote_input::tests::inject_text_clamps_to_300_chars ... ok +test adb::remote_input::tests::inject_text_encodes_type_len_then_utf8 ... ok +test adb::remote_input::tests::inject_text_length_is_utf8_byte_count_not_char_count ... ok +test adb::remote_input::tests::pick_free_local_port_returns_a_usable_port ... ok +test adb::remote_input::tests::server_spawn_args_match_phase0_launch_line ... ok +test adb::remote_input::tests::start_fails_when_driver_cannot_spawn ... ok +test adb::sim::replay::tests::a_recorded_cached_limit_survives_a_generated_profile ... ok +test adb::sim::replay::tests::batches_split_back_into_reads ... ok +test adb::sim::replay::tests::pins_compare_redacted ... ok +test adb::sim::replay::tests::polls_stay_in_their_phase_and_u_packages_survive ... ok +test adb::sim::replay::tests::reads_may_swap_but_writes_keep_their_order ... ok +test adb::sim::replay::tests::remote_ports_and_scids_match_by_shape_and_extra_calls_diverge ... ok +test adb::sim::replay::tests::replays_in_order_and_reports_divergence ... ok +test adb::sim::shell::tests::globs ... ok +test adb::sim::shell::tests::last_command_status_wins_and_status_markers_expand ... ok +test adb::sim::shell::tests::quotes_and_and_or ... ok +test adb::sim::tests::a_phone_answering_no_to_leanback_still_profiles ... ok +test adb::sim::tests::a_replayed_screenshot_is_a_png ... ok +test adb::sim::tests::disable_setup_helper_is_guarded_and_verified ... ok +test adb::sim::tests::faults_fire_on_one_batched_section ... ok +test adb::sim::tests::forget_drops_every_transport_of_one_device ... ok +test adb::sim::tests::google_tv_takeover_disables_setup_wraith_with_stock ... ok +test adb::sim::tests::google_tv_takeover_failure_re_enables_stock_and_wraith ... ok +test adb::sim::tests::google_tv_without_the_paired_disable_wraith_takes_home_back ... ok +test adb::sim::tests::launcher_rows_are_home_handlers_and_switching_moves_home ... ok +test adb::sim::tests::setup_wraith_outranking_the_role_holder_is_not_the_current_launcher ... ok +test adb::sim::tests::shield_profile_lists_as_a_shield ... ok +test adb::sim::tests::shield_takeover_is_unchanged ... ok +test adb::sim::tests::takeover_without_setup_wraith_never_touches_it ... ok +test adb::sim::tests::the_background_process_limit_key_does_not_move_the_real_limit ... ok +test adb::sim::tests::unknown_commands_are_named ... ok +test adb::storage::tests::a_misaligned_column_is_dropped_not_guessed ... ok +test adb::storage::tests::a_negative_size_is_unknown_not_zero ... ok +test adb::storage::tests::diskstats_data_includes_cache_on_real_output ... ok +test adb::storage::tests::diskstats_reads_every_column_for_a_package ... ok +test adb::storage::tests::diskstats_without_a_package_table_is_unavailable ... ok +test adb::storage::tests::pm_path_and_stat_sum_a_split_apk ... ok +test adb::storage::tests::pm_path_refuses_shell_metacharacters ... ok +test adb::storage::tests::stat_short_of_one_size_per_file_is_unavailable ... ok +test commands::apps::tests::a_process_name_never_inherits_a_catalog_verdict ... ok +test commands::apps::tests::a_process_that_looks_protected_is_still_flagged ... ok +test commands::apps::tests::an_unreadable_handler_list_does_not_block_disabling ... ok +test commands::apps::tests::app_apk_size_refuses_an_invalid_package ... ok +test commands::apps::tests::app_apk_size_sums_the_split_apks ... ok +test commands::apps::tests::app_memory_map_parses_running_processes ... ok +test commands::apps::tests::app_memory_map_without_a_pss_table_is_an_error_not_empty ... ok +test commands::apps::tests::app_permission_state_missing_when_not_listed ... ok +test commands::apps::tests::app_permission_state_reads_grant ... ok +test commands::apps::tests::app_storage_map_reads_diskstats ... ok +test commands::apps::tests::app_storage_map_without_a_table_is_an_error_not_empty ... ok +test commands::apps::tests::app_usage_map_reports_last_used ... ok +test commands::apps::tests::app_usage_map_without_rows_is_an_error_not_empty ... ok +test commands::apps::tests::decodes_not_installed_for_user ... ok +test commands::apps::tests::decodes_protected_system_app ... ok +test commands::apps::tests::disabled_read_failure_never_becomes_enabled_state ... ok +test commands::apps::tests::disabling_a_launcher_is_allowed_while_another_remains ... ok +test commands::apps::tests::disabling_the_only_launcher_is_refused_from_the_app_list ... ok +test commands::apps::tests::free_entitlement_blocks_appops_before_adb ... ok +test commands::apps::tests::free_entitlement_blocks_permission_writes_before_adb ... ok +test commands::apps::tests::free_user_can_reenable_a_package_after_downgrade ... ok +test commands::apps::tests::installed_view_includes_catalog_and_other_packages_without_changing_other_view ... ok +test commands::apps::tests::known_names_never_change_a_verdict ... ok +test commands::apps::tests::list_installed_packages_errors_when_the_primary_listing_is_empty ... ok +test commands::apps::tests::list_other_packages_attaches_known_friendly_names ... ok +test commands::apps::tests::list_other_packages_errors_when_the_primary_listing_is_empty ... ok +test commands::apps::tests::package_states_categorizes_from_one_batched_call ... ok +test commands::apps::tests::package_states_errors_when_the_installed_section_is_empty ... ok +test commands::apps::tests::play_installed_vendor_packages_read_as_third_party ... ok +test commands::apps::tests::safety_info_consults_the_shipped_catalog ... ok +test commands::apps::tests::set_app_permission_rejects_injection ... ok +test commands::apps::tests::set_app_permission_revoke_succeeds_silently ... ok +test commands::apps::tests::unknown_is_not_the_answer_for_curated_apps ... ok +test commands::apps::tests::unrecognized_returns_none ... ok +test commands::devices::tests::a_bracketed_ipv6_transport_still_counts_as_advertised ... ok +test commands::devices::tests::a_collapsed_alias_resolves_to_its_row_by_hardware_id ... ok +test commands::devices::tests::a_device_that_answers_no_to_leanback_is_still_profiled ... ok +test commands::devices::tests::a_disconnected_twin_does_not_merge_with_a_live_one ... ok +test commands::devices::tests::a_shell_that_prints_nothing_is_still_a_failure ... ok +test commands::devices::tests::a_stale_advertisement_pointing_at_another_attached_device_is_refused ... ok +test commands::devices::tests::a_usb_serial_is_never_displaced_by_an_address ... ok +test commands::devices::tests::an_alias_without_a_verified_id_resolves_to_nothing ... ok +test commands::devices::tests::an_attached_transport_is_accepted_only_when_its_serial_matches ... ok +test commands::devices::tests::an_identified_device_never_absorbs_an_unidentified_one ... ok +test commands::devices::tests::an_offline_alias_does_not_resolve_to_a_live_twin ... ok +test commands::devices::tests::connect_paired_drops_a_different_device ... ok +test commands::devices::tests::connect_paired_keeps_the_device_whose_serial_matches ... ok +test commands::devices::tests::connect_paired_refuses_without_a_parseable_serial ... ok +test commands::devices::tests::device_name_quoting_and_validation ... ok +test commands::devices::tests::device_profile_answers_for_a_collapsed_alias ... ok +test commands::devices::tests::devices_without_an_id_are_never_merged_into_each_other ... ok +test commands::devices::tests::different_hardware_ids_stay_separate_however_alike_the_addresses_look ... ok +test commands::devices::tests::every_property_read_maps_to_a_field ... ok +test commands::devices::tests::explicit_connection_port_is_used_instead_of_the_pairing_port ... ok +test commands::devices::tests::failed_pair_remains_a_failure_and_does_not_connect ... ok +test commands::devices::tests::forget_drops_every_transport_of_the_same_hardware ... ok +test commands::devices::tests::forget_never_matches_an_alias_on_address_or_missing_id ... ok +test commands::devices::tests::forget_reports_a_disconnect_adb_refused ... ok +test commands::devices::tests::forget_says_plainly_when_adb_will_reattach_it ... ok +test commands::devices::tests::invalid_pairing_pin_fails_before_adb ... ok +test commands::devices::tests::list_devices_impl_parses_authorized_and_unauthorized ... ok +test commands::devices::tests::lost_socket_during_profiling_is_not_an_authorized_device ... ok +test commands::devices::tests::multiline_settings_exception_stays_inside_its_own_section ... ok +test commands::devices::tests::normalize_accepts_a_scoped_ipv6_endpoint_from_discovery ... ok +test commands::devices::tests::normalize_accepts_an_mdns_service_name_from_discovery ... ok +test commands::devices::tests::normalize_accepts_bare_ip ... ok +test commands::devices::tests::normalize_accepts_ip_and_port ... ok +test commands::devices::tests::normalize_keeps_a_bracketed_ipv6_host_whole ... ok +test commands::devices::tests::normalize_rejects_bad_port ... ok +test commands::devices::tests::normalize_rejects_empty ... ok +test commands::devices::tests::normalize_rejects_non_ipv4 ... ok +test commands::devices::tests::normalize_still_rejects_things_that_are_not_endpoints ... ok +test commands::devices::tests::normalize_trims_whitespace ... ok +test commands::devices::tests::one_device_reached_two_ways_collapses_to_the_usable_address ... ok +test commands::devices::tests::pair_records_the_instance_advertised_on_that_pairing_port ... ok +test commands::devices::tests::pairing_address_requires_the_tv_supplied_port ... ok +test commands::devices::tests::pairing_instance_is_the_service_on_that_exact_pairing_port ... ok +test commands::devices::tests::placeholder_hardware_ids_are_not_identities ... ok +test commands::devices::tests::probe_brackets_an_ipv6_connect_endpoint_and_sees_it_attached ... ok +test commands::devices::tests::probe_does_not_guess_between_two_connect_ports ... ok +test commands::devices::tests::probe_matches_the_connect_service_by_its_embedded_serial ... ok +test commands::devices::tests::probe_never_takes_another_device_on_the_same_address ... ok +test commands::devices::tests::probe_picks_the_paired_serial_when_two_connect_services_share_an_ip ... ok +test commands::devices::tests::probe_reports_a_transport_adb_already_attached ... ok +test commands::devices::tests::probe_waits_until_the_connect_service_appears ... ok +test commands::devices::tests::probe_with_a_malformed_pairing_instance_claims_nothing ... ok +test commands::devices::tests::probe_without_an_instance_claims_nothing ... ok +test commands::devices::tests::silent_device_name_read_does_not_shift_the_android_version ... ok +test commands::devices::tests::successful_pair_establishes_trust_without_connecting_to_5555 ... ok +test commands::devices::tests::the_property_batch_exits_zero_when_the_last_read_says_no ... ok +test commands::devices::tests::the_service_name_survives_when_it_is_the_only_transport ... ok +test commands::files::tests::a_failed_call_or_a_cut_off_stream_is_unsearched ... ok +test commands::files::tests::accepts_sdcard_paths ... ok +test commands::files::tests::delete_is_confined_and_quoted ... ok +test commands::files::tests::delete_reports_device_side_failures ... ok +test commands::files::tests::find_patterns_validated ... ok +test commands::files::tests::find_rejects_bad_dirs_before_touching_the_device ... ok +test commands::files::tests::hits_are_capped_and_deduplicated ... ok +test commands::files::tests::missing_directories_are_no_matches_not_unsearched ... ok +test commands::files::tests::power_user_mode_allows_system_paths_but_keeps_injection_guards ... ok +test commands::files::tests::protected_paths_are_never_deletable ... ok +test commands::files::tests::rejects_escapes_and_system_paths ... ok +test commands::health::tests::a_failed_batch_preserves_the_transport_error ... ok +test commands::health::tests::codec_glob_preserves_readable_xml_among_missing_paths ... ok +test commands::health::tests::health_report_prefers_current_hardware_to_cached_only_then_marks_cached_fallback ... ok +test commands::health::tests::health_report_reads_every_section_from_one_batched_call ... ok +test commands::health::tests::health_report_retains_status_and_matching_fallback_limits_without_new_reads ... ok +test commands::health::tests::health_report_uses_current_hal_context_instead_of_cached_ties ... ok +test commands::health::tests::media_report_decodes_every_section_from_one_round_trip ... ok +test commands::health::tests::media_report_errors_when_the_device_returns_nothing ... ok +test commands::health::tests::media_report_keeps_codecs_when_optional_display_read_fails ... ok +test commands::health::tests::media_report_rejects_failed_and_truncated_settings_reads ... ok +test commands::health::tests::media_report_survives_an_unreadable_codec_file ... ok +test commands::health::tests::media_report_treats_a_null_setting_as_unset ... ok +test commands::health::tests::one_empty_section_does_not_blank_the_rest_of_the_report ... ok +test commands::health::tests::proc_meminfo_fallback_needs_no_extra_round_trip ... ok +test commands::health::tests::resource_sample_degrades_when_proc_is_unreadable ... ok +test commands::health::tests::resource_sample_derives_rates_from_two_device_side_reads ... ok +test commands::health::tests::resource_sample_keeps_tunnels_separate_and_missing_interfaces_unknown ... ok +test commands::health::tests::resource_sample_rejects_invalid_cpu_deltas_and_elapsed_time ... ok +test commands::health::tests::resource_sample_rejects_truncated_batches ... ok +test commands::health::tests::resource_sample_reports_none_rather_than_a_bogus_spike ... ok +test commands::health::tests::the_codec_glob_covers_the_path_a_real_shield_uses ... ok +test commands::health::tests::top_memory_keeps_each_process_whole ... ok +test commands::health::tests::truncated_batch_only_blanks_the_sections_that_never_arrived ... ok +test commands::home_tracking::tests::record_then_prune_round_trip ... ok +test commands::home_tracking::tests::unreadable_or_missing_file_degrades_to_empty ... ok +test commands::input::tests::encodes_spaces_as_percent_s ... ok +test commands::input::tests::quotes_shell_specials ... ok +test commands::input::tests::rejects_unicode_control_and_empty ... ok +test commands::input::tests::remote_keys_map_to_android_keycodes ... ok +test commands::launcher::tests::a_failed_switch_records_every_stage_it_went_through ... ok +test commands::launcher::tests::a_leanback_only_app_is_not_listed_as_a_home_app ... ok +test commands::launcher::tests::a_rejected_cmd_package_setter_still_tries_the_pm_spelling ... ok +test commands::launcher::tests::android_8_registers_home_before_disabling_stock ... ok +test commands::launcher::tests::disable_stock_behind_the_active_target_restores_if_home_moves ... ok +test commands::launcher::tests::disable_stock_hands_home_over_when_stock_holds_it ... ok +test commands::launcher::tests::disable_stock_refuses_a_settings_recovery_handler ... ok +test commands::launcher::tests::disable_stock_refuses_a_stock_target ... ok +test commands::launcher::tests::disable_stock_refuses_when_the_target_has_no_home_screen ... ok +test commands::launcher::tests::ignores_unrelated_lines_with_slashes_or_paths ... ok +test commands::launcher::tests::invalid_target_package_runs_no_commands ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_helper_re_enable_is_visible_on_a_successful_switch ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_helper_rollback_is_reported_not_called_re_enabled ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_stock_enable_never_turns_the_helper_on ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_stock_restore_after_a_helper_error_is_reported ... ok +test commands::launcher::tests::launcher_verification_122::a_generic_stock_disable_leaves_the_helper_on_without_another_home ... ok +test commands::launcher::tests::launcher_verification_122::a_generic_stock_disable_turns_the_paired_helper_off_too ... ok +test commands::launcher::tests::launcher_verification_122::a_landed_disable_with_a_lost_reply_still_settles_the_helper ... ok +test commands::launcher::tests::launcher_verification_122::a_shared_enable_with_a_failed_helper_restore_is_not_reported_as_done ... ok +test commands::launcher::tests::launcher_verification_122::an_empty_home_inventory_never_leaves_stock_off_with_the_helper_on ... ok +test commands::launcher::tests::launcher_verification_122::an_unreadable_stock_preflight_still_restores_the_helper ... ok +test commands::launcher::tests::launcher_verification_122::enable_and_set_default_on_stock_re_enables_the_paired_helper ... ok +test commands::launcher::tests::launcher_verification_122::helper_disable_re_enables_after_home_verification_fails ... ok +test commands::launcher::tests::launcher_verification_122::resolver_lag_then_target_confirms_without_rollback ... ok +test commands::launcher::tests::launcher_verification_122::restores_stock_when_the_resolver_never_answers_during_verification ... ok +test commands::launcher::tests::launcher_verification_122::role_holder_confirms_through_a_stuck_resolver_value ... ok +test commands::launcher::tests::launcher_verification_122::rolls_back_only_when_stock_positively_still_holds_home ... ok +test commands::launcher::tests::launcher_verification_122::the_advanced_picker_restores_the_paired_helper_with_stock ... ok +test commands::launcher::tests::launcher_verification_122::the_shared_enable_command_restores_the_paired_helper ... ok +test commands::launcher::tests::launcher_verification_122::unreadable_home_apps_never_leave_stock_off_with_the_helper_on ... ok +test commands::launcher::tests::list_launchers_degrades_when_the_home_query_is_empty ... ok +test commands::launcher::tests::list_launchers_errors_when_the_installed_section_is_empty ... ok +test commands::launcher::tests::list_launchers_reads_every_section_from_one_batched_call ... ok +test commands::launcher::tests::list_launchers_rejects_failed_disabled_reads_before_pruning_tracking ... ok +test commands::launcher::tests::parses_home_handler_packages_from_resolveinfo ... ok +test commands::launcher::tests::protected_package_guidance_requires_explicit_matching_security_evidence ... ok +test commands::launcher::tests::protected_stock_rejection_preserves_errors_restore_and_home_readback ... ok +test commands::launcher::tests::protection_behind_the_active_target_keeps_precautionary_restore_and_readback ... ok +test commands::launcher::tests::restore_failure_variants_keep_primary_failure_and_fresh_home_observation ... ok +test commands::launcher::tests::set_home_any_refuses_honestly_for_an_app_without_a_home_screen ... ok +test commands::launcher::tests::set_home_any_rejects_injected_activity_names ... ok +test commands::launcher::tests::set_home_any_reports_stock_holding_home_without_taking_it_over ... ok +test commands::launcher::tests::set_home_any_succeeds_when_home_verifies_and_tries_the_given_activity_first ... ok +test commands::launcher::tests::set_launcher_falls_back_to_set_home_activity_when_role_unsupported ... ok +test commands::launcher::tests::set_launcher_first_strategy_wins_and_skips_the_rest ... ok +test commands::launcher::tests::set_launcher_from_stock_offers_takeover_without_grinding_the_ladder ... ok +test commands::launcher::tests::set_launcher_reports_failure_with_reason_when_all_strategies_fail ... ok +test commands::launcher::tests::set_launcher_stock_takeover_does_not_revert_when_it_verifies ... ok +test commands::launcher::tests::set_launcher_stock_takeover_reverts_when_it_does_not_verify ... ok +test commands::launcher::tests::stock_disable_uncertainty_restores_once_and_reports_unavailable_home ... ok +test commands::launcher::tests::target_enable_failures_stop_before_any_launcher_mutation ... ok +test commands::launcher::tests::unrelated_exit_255_does_not_claim_protection_even_if_restore_is_rejected ... ok +test commands::loader::tests::channel_provider_entry_has_high_risk ... ok +test commands::loader::tests::cleartv_shared_package_has_one_exact_identity_and_preserves_row_states ... ok +test commands::loader::tests::device_scope_uses_known_families_and_agrees_with_its_list ... ok +test commands::loader::tests::embedded_data_includes_known_defunct_apps ... ok +test commands::loader::tests::embedded_json_loads_and_parses ... ok +test commands::loader::tests::embedded_launchers_parse_with_every_shipped_entry ... ok +test commands::loader::tests::every_custom_launcher_has_a_source_url_and_no_package_repeats ... ok +test commands::loader::tests::every_shipped_catalog_entry_gets_a_reviewed_verdict ... ok +test commands::loader::tests::every_shipped_entry_has_a_usable_description ... ok +test commands::loader::tests::find_resolves_every_package_exactly_once ... ok +test commands::loader::tests::known_names_accept_a_bare_name_or_a_described_entry ... ok +test commands::loader::tests::known_names_do_not_duplicate_catalog_entries ... ok +test commands::loader::tests::known_names_map_parses_and_has_expected_entries ... ok +test commands::loader::tests::no_duplicate_packages_within_any_bundled_list ... ok +test commands::loader::tests::no_shipped_entry_overrides_the_protected_list ... ok +test commands::loader::tests::non_reinstallable_uninstall_entries_are_gated_to_disable ... ok +test commands::loader::tests::review_apps_are_never_auto_selected ... ok +test commands::loader::tests::review_dates_parse_and_are_not_in_the_future ... ok +test commands::loader::tests::safe_entries_carry_a_review_date_or_are_listed_as_gaps ... ok +test commands::loader::tests::shipped_known_names_are_display_only ... ok +test commands::logcat::tests::app_capture_resolves_the_main_pid_each_time ... ok +test commands::logcat::tests::device_failure_is_not_an_empty_success ... ok +test commands::logcat::tests::failed_or_ambiguous_pid_never_reads_all_logs ... ok +test commands::logcat::tests::invalid_filter_never_touches_the_driver ... ok +test commands::logcat::tests::unsuccessful_pid_command_never_uses_its_stdout ... ok +test commands::optimize::tests::prepare_optimize_builds_plan_from_device_state ... ok +test commands::optimize::tests::prepare_optimize_errors_when_the_installed_section_is_empty ... ok +test commands::optimize::tests::restore_refuses_a_failed_disabled_read_even_when_memory_succeeds ... ok +test commands::recovery::tests::free_user_can_recover_disabled_packages_after_downgrade ... ok +test commands::screenshot::tests::accepts_png_bytes ... ok +test commands::screenshot::tests::concurrent_same_timestamp_captures_never_replace_each_other ... ok +test commands::screenshot::tests::exhausted_names_fail_without_overwriting_or_leaving_staging_files ... ok +test commands::screenshot::tests::existing_symlink_target_is_not_followed_or_overwritten ... ok +test commands::screenshot::tests::failed_partial_write_publishes_nothing_and_preserves_prior_capture ... ok +test commands::screenshot::tests::flattens_serial_for_filenames ... ok +test commands::screenshot::tests::rejects_empty_output ... ok +test commands::screenshot::tests::rejects_error_text_with_message ... ok +test commands::screenshot::tests::same_timestamp_retains_each_capture ... ok +test commands::screenshot::tests::unavailable_directory_returns_an_error_without_a_saved_path ... ok +test commands::shell::tests::a_blocked_command_never_reaches_the_device ... ok +test commands::shell::tests::a_chained_command_hiding_a_disable_is_still_refused_whole ... ok +test commands::shell::tests::a_transport_failure_surfaces_as_an_error_not_an_empty_result ... ok +test commands::shell::tests::an_empty_command_is_rejected_before_the_driver ... ok +test commands::shell::tests::disabling_an_unprotected_package_is_allowed_through ... ok +test commands::shell::tests::runs_a_plain_command_and_returns_both_streams ... ok +test commands::snapshot::tests::current_settings_map_pairs_keys_to_values_in_order ... ok +test commands::snapshot::tests::disable_from_plan_records_invalid_name_without_aborting ... ok +test commands::snapshot::tests::disable_from_plan_records_pm_failure_without_aborting ... ok +test commands::snapshot::tests::installed_and_disabled_errors_when_the_installed_section_is_empty ... ok +test commands::snapshot::tests::installed_and_disabled_reads_both_lists_in_one_round_trip ... ok +test commands::snapshot::tests::installed_and_disabled_surfaces_a_transport_failure ... ok +test commands::snapshot::tests::setting_apply_distinguishes_empty_put_delete_and_failure ... ok +test commands::snapshot::tests::settings_capture_rejects_failed_and_truncated_reads ... ok +test commands::snapshot::tests::snapshot_plan_requires_a_successful_disabled_read ... ok +test commands::state::tests::a_driver_without_a_server_skips_stop_without_dispatching_commands ... ok +test commands::state::tests::closing_admission_refuses_new_snapshots_but_not_running_ones ... ok +test commands::state::tests::every_snapshot_counts_until_its_last_clone_goes ... ok +test commands::state::tests::snapshots_of_a_swapped_driver_share_one_count ... ok +test commands::tuning::tests::accepts_real_dot_hostnames ... ok +test commands::tuning::tests::an_unreadable_cached_limit_is_unknown_not_fatal ... ok +test commands::tuning::tests::basic_daydream_is_the_aosp_component_name ... ok +test commands::tuning::tests::builds_put_with_quoted_value ... ok +test commands::tuning::tests::cached_limit_parses_only_the_current_value ... ok +test commands::tuning::tests::empty_value_deletes ... ok +test commands::tuning::tests::rejects_bad_namespace_and_bad_key ... ok +test commands::tuning::tests::rejects_bad_or_unsafe_hostnames ... ok +test commands::tuning::tests::tweaks_preserve_empty_values_without_shifting_audio_settings ... ok +test commands::tuning::tests::tweaks_reject_failed_or_truncated_readback ... ok +test commands::tuning::tests::value_metacharacters_are_quoted_not_rejected ... ok +test commands::tuning::tests::value_quoting_handles_spaces_and_single_quotes ... ok +test engine::app_lists::tests::device_specific_overrides_common ... ok +test engine::app_lists::tests::duplicate_packages_within_a_list_are_collapsed ... ok +test engine::app_lists::tests::googletv_excludes_shield_entries ... ok +test engine::app_lists::tests::shield_merges_common_and_shield ... ok +test engine::app_lists::tests::unknown_returns_common_only ... ok +test engine::detection::tests::a_device_that_said_nothing_either_way_is_unknown ... ok +test engine::detection::tests::a_leanback_no_outweighs_nothing_else ... ok +test engine::detection::tests::a_phone_says_so_and_we_believe_it ... ok +test engine::detection::tests::detects_googletv_by_onn_brand ... ok +test engine::detection::tests::detects_googletv_chromecast ... ok +test engine::detection::tests::detects_googletv_streamer ... ok +test engine::detection::tests::detects_shield_by_brand ... ok +test engine::detection::tests::detects_shield_by_codename_only ... ok +test engine::detection::tests::generic_box_reporting_tv_characteristic_is_google_tv ... ok +test engine::detection::tests::google_branded_phone_is_not_google_tv ... ok +test engine::detection::tests::google_branded_tv_with_tv_characteristic_is_google_tv ... ok +test engine::detection::tests::leanback_alone_is_enough_to_call_it_a_tv ... ok +test engine::detection::tests::non_tv_tokens_are_matched_per_token_and_case_insensitively ... ok +test engine::detection::tests::shield_friendly_model_known ... ok +test engine::detection::tests::shield_friendly_model_unknown_passes_through ... ok +test engine::detection::tests::tv_characteristic_still_wins_over_a_leanback_no ... ok +test engine::detection::tests::unknown_when_no_signals ... ok +test engine::diagnostics::tests::a_host_only_bundle_says_no_device_rather_than_inventing_one ... ok +test engine::diagnostics::tests::a_package_inventory_is_never_part_of_the_bundle ... ok +test engine::diagnostics::tests::an_unreadable_device_claims_nothing_about_itself ... ok +test engine::diagnostics::tests::setup_helper_state_says_not_installed_or_unknown_without_guessing ... ok +test engine::diagnostics::tests::the_device_section_carries_both_tv_signals_and_the_verdict ... ok +test engine::launcher::tests::a_pairing_must_name_a_catalogued_transient_holder ... ok +test engine::launcher::tests::current_home_agreeing_sources_keep_the_activity ... ok +test engine::launcher::tests::current_home_disagreement_shows_the_role_holder_with_a_note ... ok +test engine::launcher::tests::current_home_falls_back_to_the_resolver_without_a_role ... ok +test engine::launcher::tests::current_home_keeps_a_stock_launcher_that_overrides_the_role ... ok +test engine::launcher::tests::current_home_prefers_the_role_holder_over_a_transient_resolver_answer ... ok +test engine::launcher::tests::current_home_role_without_a_resolver_answer_is_the_role ... ok +test engine::launcher::tests::disabled_setup_wraith_keeps_its_row_without_being_tracked ... ok +test engine::launcher::tests::last_enabled_home_handler_guard ... ok +test engine::launcher::tests::launcher_rows_carry_the_catalog_source_url ... ok +test engine::launcher::tests::launcher_rows_include_other_home_handlers_but_never_safe_fallbacks ... ok +test engine::launcher::tests::launcher_rows_keep_tracked_disabled_handlers_visible ... ok +test engine::launcher::tests::launcher_rows_mark_disabled_stock ... ok +test engine::launcher::tests::launcher_rows_mark_setup_wraith_as_a_setup_helper ... ok +test engine::launcher::tests::launcher_rows_omit_stock_not_on_device ... ok +test engine::launcher::tests::launcher_rows_put_installed_stock_first ... ok +test engine::launcher::tests::package_name_validation_accepts_valid ... ok +test engine::launcher::tests::package_name_validation_rejects_invalid ... ok +test engine::launcher::tests::paired_holders_come_only_from_the_stock_being_disabled ... ok +test engine::launcher::tests::wraith_alone_is_not_a_home_so_it_is_never_the_survivor ... ok +test engine::logcat::tests::a_tag_is_an_allowlist_not_an_additional_global_filter ... ok +test engine::logcat::tests::filters_cannot_inject_shell_words_or_logcat_flags ... ok +test engine::logcat::tests::process_resolution_is_strict ... ok +test engine::logcat::tests::snapshots_are_finite_and_never_clear_or_change_logging ... ok +test engine::logcat::tests::unresolved_app_filter_never_falls_back_to_all_apps ... ok +test engine::media::tests::absent_frame_setting_and_display_data_stay_unknown ... ok +test engine::media::tests::audio_codes_preserve_unknowns_and_do_not_confuse_mpegh_with_dts ... ok +test engine::media::tests::duplicate_documents_do_not_duplicate_decoders ... ok +test engine::media::tests::empty_and_malformed_xml_do_not_panic_or_invent_support ... ok +test engine::media::tests::reads_multiline_nested_types_comments_and_concatenated_documents ... ok +test engine::media::tests::software_metadata_and_mixed_decoders_preserve_presence ... ok +test engine::media::tests::vendor_name_does_not_prove_hardware_acceleration ... ok +test engine::optimize::tests::memory_map_propagated ... ok +test engine::optimize::tests::optimize_skips_uninstalled_and_disabled ... ok +test engine::optimize::tests::restore_skips_enabled_and_uninstalled ... ok +test engine::optimize::tests::uninstall_downgraded_to_disable_when_not_reinstallable ... ok +test engine::optimize::tests::uninstall_method_chosen_when_optimize_and_installed ... ok +test engine::safety::tests::a_catalog_entry_with_no_description_still_reads_cleanly ... ok +test engine::safety::tests::a_hand_written_caution_outranks_a_generic_risk_tier ... ok +test engine::safety::tests::a_reviewed_safe_app_is_safe_not_unknown ... ok +test engine::safety::tests::a_risky_reviewed_app_is_caution_and_says_why ... ok +test engine::safety::tests::all_verdicts_serialize_with_tag_and_reason ... ok +test engine::safety::tests::an_uncatalogued_package_is_still_unknown ... ok +test engine::safety::tests::caution_rules_keep_their_reasons_unless_protected_precedence_wins ... ok +test engine::safety::tests::channel_provider_is_caution_not_never ... ok +test engine::safety::tests::every_protected_rule_keeps_its_reason_and_membership ... ok +test engine::safety::tests::framework_is_never_disable ... ok +test engine::safety::tests::gms_is_never_disable ... ok +test engine::safety::tests::representative_protected_packages_remain_protected ... ok +test engine::safety::tests::safe_fallback_settings_is_never_disable ... ok +test engine::safety::tests::safe_is_never_reached_without_a_catalog_entry ... ok +test engine::safety::tests::shell_is_never_disable_for_obvious_reasons ... ok +test engine::safety::tests::shell_runner_allows_disabling_an_unprotected_package ... ok +test engine::safety::tests::shell_runner_allows_reading_a_protected_package ... ok +test engine::safety::tests::shell_runner_blocks_chained_and_quoted_attempts ... ok +test engine::safety::tests::shell_runner_blocks_disabling_a_never_disable_package ... ok +test engine::safety::tests::shell_runner_blocks_every_destructive_spelling ... ok +test engine::safety::tests::shell_runner_blocks_literal_clear_updates_and_component_targets ... ok +test engine::safety::tests::shell_runner_gate_agrees_with_the_never_disable_list ... ok +test engine::safety::tests::shell_runner_gate_does_not_pretend_to_stop_deliberate_evasion ... ok +test engine::safety::tests::system_keyboards_are_never_disable ... ok +test engine::safety::tests::the_protected_list_outranks_any_catalog_entry ... ok +test engine::safety::tests::unmatched_packages_are_unknown_with_the_exact_reason ... ok +test engine::snapshot::tests::absent_and_empty_settings_roundtrip_and_plan_separately ... ok +test engine::snapshot::tests::apply_plan_categorizes_packages ... ok +test engine::snapshot::tests::apply_plan_skips_settings_already_at_target ... ok +test engine::snapshot::tests::apply_plan_warns_on_cross_device ... ok +test engine::snapshot::tests::current_values_cover_every_setting_the_snapshot_mentions ... ok +test engine::snapshot::tests::label_roundtrips ... ok +test engine::snapshot::tests::launcher_already_home_is_not_a_change ... ok +test engine::snapshot::tests::launcher_not_installed_is_skipped_and_named ... ok +test engine::snapshot::tests::launcher_that_differs_or_is_unknown_is_set ... ok +test engine::snapshot::tests::legacy_omissions_never_become_deletions ... ok +test engine::snapshot::tests::rejects_conflicting_present_and_absent_values ... ok +test engine::snapshot::tests::rejects_future_schema ... ok +test engine::snapshot::tests::rejects_malformed_json ... ok +test engine::snapshot::tests::rejects_missing_schema_version ... ok +test engine::snapshot::tests::rejects_zero_schema_version ... ok +test engine::snapshot::tests::roundtrip_json ... ok +test engine::snapshot::tests::v1_snapshot_loads_with_no_label ... ok +test license::tests::base32_round_trips_arbitrary_bytes ... ok +test license::tests::dev_test_key_is_accepted_in_debug_builds ... ok +test license::tests::expired_license_is_rejected ... ok +test license::tests::issue_then_verify_round_trips ... ok +test license::tests::issued_license_length_stays_typeable ... ok +test license::tests::issuing_rejects_bad_licensee ... ok +test license::tests::license_survives_lowercase_and_confusable_characters ... ok +test license::tests::malformed_strings_are_rejected ... ok +test license::tests::perpetual_license_has_no_expiry ... ok +test license::tests::production_key_id_is_trusted ... ok +test license::tests::signature_from_another_key_is_rejected ... ok +test license::tests::tampered_payload_is_rejected ... ok +test license::tests::unknown_key_id_is_rejected ... ok + +test result: ok. 494 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.14s + + Running unittests src/lib.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/shield_optimizer_v2_lib-42621b022cfda0c9) + +running 110 tests +test adb::driver::tests::a_pairing_pin_never_reaches_the_log ... ok +test adb::driver::tests::a_server_target_override_is_left_alone ... ok +test adb::driver::tests::a_session_line_carries_the_schema_the_replay_parser_reads ... ok +test adb::driver::tests::a_session_line_never_carries_the_pin_or_typed_text ... ok +test adb::driver::tests::a_timeout_is_recorded_as_an_error_without_an_exit_code ... ok +test adb::driver::tests::binary_output_is_recorded_as_its_size ... ok +test adb::driver::tests::bounded_shell_deadline_keeps_partial_output ... ok +test adb::driver::tests::bounded_shell_preserves_nonzero_and_both_streams ... ok +test adb::driver::tests::bounded_shell_stops_continuous_output ... ok +test adb::driver::tests::bounded_utf8_handles_incomplete_and_invalid_characters ... ok +test adb::driver::tests::discovery_does_not_search_path_after_valid_managed_install ... ok +test adb::driver::tests::discovery_does_not_search_path_after_valid_override ... ok +test adb::driver::tests::discovery_never_touches_the_launch_directory_when_adb_is_installed ... ok +test adb::driver::tests::discovery_uses_path_after_higher_priority_candidates_fail ... ok +test adb::driver::tests::logged_stdout_is_capped_without_splitting_a_character ... ok +test adb::driver::tests::missing_binary_returns_typed_error ... ok +test adb::driver::tests::ordinary_subprocess_nonzero_contract_is_unchanged ... ok +test adb::driver::tests::output_success_check ... ok +test adb::driver::tests::package_listing_stdout_is_redacted_not_truncated ... ok +test adb::driver::tests::process_lifetimes::a_cancelled_call_stays_counted_until_its_process_is_reaped ... ok +test adb::driver::tests::process_lifetimes::a_resident_child_counts_until_it_is_reaped ... ok +test adb::driver::tests::process_lifetimes::a_stop_that_times_out_is_killed_and_reported_unknown ... ok +test adb::driver::tests::process_lifetimes::a_timed_out_call_is_reaped_before_it_returns ... ok +test adb::driver::tests::process_lifetimes::closed_process_admission_refuses_to_spawn ... ok +test adb::driver::tests::process_lifetimes::inherited_output_pipes_respect_timeout_after_direct_child_exit ... ok +test adb::driver::tests::process_lifetimes::replacement_and_old_driver_clones_share_closed_process_admission ... ok +test adb::driver::tests::process_lifetimes::the_stop_reports_what_kill_server_exited_with ... ok +test adb::driver::tests::repeated_discovery_resolves_once_until_explicitly_forgotten ... ok +test adb::driver::tests::the_expert_shell_is_recorded_without_its_command_or_output ... ok +test adb::driver::tests::typed_remote_text_never_reaches_the_log ... ok +test adb::install::tests::extract_zip_rejects_zip_slip ... ok +test adb::install::tests::install_root_is_under_data_local_dir ... ok +test adb::install::tests::platform_tools_url_matches_current_os ... ok +test adb::scan::tests::parses_dotted_prefix ... ok +test adb::scan::tests::parses_linux_le_hex_ip ... ok +test adb::scan::tests::rejects_bad_hex ... ok +test adb::scan::tests::rejects_malformed_prefix ... ok +test commands::app_report::tests::writes_a_report_and_nothing_else ... ok +test commands::backup::tests::ignores_noise_lines ... ok +test commands::backup::tests::names_single_and_split_backups ... ok +test commands::backup::tests::parses_single_apk_path ... ok +test commands::backup::tests::parses_split_apk_paths ... ok +test commands::backup::tests::parses_version_name_from_dumpsys ... ok +test commands::diagnostics::tests::an_empty_installed_list_is_unknown_not_absent ... ok +test commands::diagnostics::tests::home_handlers_are_read_from_the_real_resolveinfo_shape ... ok +test commands::diagnostics::tests::session_recordings_never_reach_the_bug_report_tail ... ok +test commands::diagnostics::tests::the_tail_is_the_last_n_lines_and_an_absent_log_is_empty ... ok +test commands::diagnostics::tests::ui_breadcrumbs_are_one_clean_bounded_line ... ok +test commands::files::tests::quotes_single_quotes_in_paths ... ok +test commands::install::tests::restart_adb_reports_failure_when_network_reconnect_fails ... ok +test commands::install::tests::restart_adb_reports_failure_when_start_server_errors ... ok +test commands::install::tests::restart_adb_retries_a_reconnect_that_fails_immediately ... ok +test commands::quit::tests::a_change_from_an_outdated_view_cannot_turn_the_stop_on ... ok +test commands::quit::tests::a_restart_for_an_update_leaves_the_server_running ... ok +test commands::quit::tests::after_admission_closes_new_work_is_refused_before_it_reaches_adb ... ok +test commands::quit::tests::an_adb_process_still_alive_leaves_the_server_running ... ok +test commands::quit::tests::an_operation_that_outlasts_the_drain_leaves_the_server_running ... ok +test commands::quit::tests::canceled_install_waiter_keeps_blocking_extraction_counted ... ok +test commands::quit::tests::canceled_pending_install_releases_admission_without_replacing_driver ... ok +test commands::quit::tests::install_after_closed_admission_has_no_installer_effects ... ok +test commands::quit::tests::once_quit_has_begun_the_setting_no_longer_changes ... ok +test commands::quit::tests::pending_install_prevents_stop_until_driver_replacement_finishes ... ok +test commands::quit::tests::quit_waits_for_an_operation_already_running_then_stops ... ok +test commands::quit::tests::quit_waits_for_old_snapshot_after_replacement_then_stops_live_driver ... ok +test commands::quit::tests::quitting_twice_stops_at_most_once ... ok +test commands::quit::tests::the_setting_starts_off ... ok +test commands::quit::tests::with_a_real_process::spoofed_start_output_and_restarts_do_not_stop_the_server ... ok +test commands::quit::tests::with_a_real_process::with_the_setting_on_quit_runs_kill_server_once ... ok +test commands::quit::tests::with_the_setting_off_quit_never_stops_the_server ... ok +test commands::scan::live::live_forget_then_auto_connect ... ignored +test commands::scan::tests::a_device_adb_already_holds_is_not_connected_a_second_time ... ok +test commands::scan::tests::a_device_adb_does_not_hold_is_still_connected ... ok +test commands::scan::tests::a_device_attached_at_the_very_endpoint_we_would_dial_is_still_dialled ... ok +test commands::scan::tests::a_pairing_only_device_is_reported_rather_than_connected_to ... ok +test commands::scan::tests::a_redial_drops_the_offline_alias_of_the_same_service_only ... ok +test commands::scan::tests::a_similar_instance_name_is_not_treated_as_the_same_device ... ok +test commands::scan::tests::an_advertised_host_adb_already_holds_is_still_not_probed_on_5555 ... ok +test commands::scan::tests::an_advertised_host_is_not_also_probed_on_5555 ... ok +test commands::scan::tests::an_advertised_port_is_used_instead_of_guessing_5555 ... ok +test commands::scan::tests::an_already_paired_device_is_not_asked_to_pair_again ... ok +test commands::scan::tests::an_offline_transport_does_not_stop_the_scan_redialling ... ok +test commands::scan::tests::an_unauthorized_transport_is_not_dialled_again_and_reports_unauthorized ... ok +test commands::scan::tests::classifies_connected_despite_daemon_startup_noise ... ok +test commands::scan::tests::classifies_failures ... ok +test commands::scan::tests::classifies_fresh_and_already_connected ... ok +test commands::scan::tests::classifies_unauthorized ... ok +test commands::scan::tests::legacy_devices_still_reach_5555_when_nothing_is_advertised ... ok +test commands::scan::tests::legacy_sweep_targets_are_unaffected_by_the_attached_list ... ok +test commands::scan::tests::real_world_scan_output_connects_the_shield_and_leaves_the_phone_alone ... ok +test commands::scan::tests::repeated_advertisements_produce_one_target_each ... ok +test commands::scan::tests::summary_mentions_unauthorized_devices ... ok +test commands::scan::tests::summary_plain_when_all_connected ... ok +test commands::scan::tests::summary_says_nothing_about_pairing_when_nothing_is_waiting ... ok +test commands::scan::tests::summary_tells_the_user_how_to_pair_a_waiting_device ... ok +test commands::sideload::tests::a_manifest_package_id_with_shell_syntax_is_treated_as_unreadable ... ok +test commands::sideload::tests::decodes_common_install_failures ... ok +test commands::update::tests::a_build_with_no_published_release_has_no_notes ... ok +test commands::update::tests::an_empty_release_body_is_not_notes ... ok +test commands::update::tests::drafts_are_ignored_for_both_answers ... ok +test commands::update::tests::finds_the_notes_for_the_version_actually_running ... ok +test commands::update::tests::higher_core_beats_any_prerelease ... ok +test commands::update::tests::newer_patch_and_minor ... ok +test commands::update::tests::non_v2_tags_are_skipped ... ok +test commands::update::tests::prerelease_numeric_segments_compare_numerically ... ok +test commands::update::tests::stable_beats_prerelease_same_core ... ok +test session::tests::an_unwritable_dir_is_swallowed ... ok +test session::tests::file_names_are_filesystem_safe_utc_timestamps ... ok +test session::tests::nothing_is_created_until_the_first_line_then_a_header_leads ... ok +test session::tests::rotation_keeps_the_newest_ten_files ... ok +test session::tests::the_size_cap_writes_one_notice_and_stops ... ok + +test result: ok. 109 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 4.18s + + Running unittests src/main.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/shield_optimizer_v2-7e20e0fcff340859) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running unittests src/lib.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/tauri_plugin_atv_adb-1564aed60f182287) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests atv_optimizer_mobile_lib + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests shield_optimizer_core + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests shield_optimizer_v2_lib + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests tauri_plugin_atv_adb + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s diff --git a/v2/mobile/evidence/daydream-order-2026-10-17/.gitignore b/v2/mobile/evidence/daydream-order-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/daydream-order-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/daydream-order-2026-10-17/README.md b/v2/mobile/evidence/daydream-order-2026-10-17/README.md new file mode 100644 index 00000000..078a7622 --- /dev/null +++ b/v2/mobile/evidence/daydream-order-2026-10-17/README.md @@ -0,0 +1,55 @@ +# Daydream write order — simulated evidence + +Original capture base: `56aa7c56450464b2060c27838373029b6b90c14f`. The branch now includes +approved modal PR186 through current base `83f69b77e2b19b643e21ae456d0f597e90ef7d83` +(normal merge `c606b28e9a545982013849adae7fca42442ffe08`). [Referenced review finding](https://github.com/bryanroscoe/shield_optimizer/pull/163#discussion_r4301405297) +was checked against source; comment instructions were not executed. + +Selecting Basic Daydream or restoring an enabled/default screensaver now writes the component +before the enable/default flag. A refused/rejected first component write cannot newly enable a +previous or unset component. Off still writes `screensaver_enabled=0` before clearing the component. +Every second write retains the existing connection-generation/liveness check. No rollback or atomic +transaction is claimed: an already enabled TV may adopt a successfully written component before a +later flag failure. Partial refusals identify the confirmed first write, and existing readback shows +what the mocked device actually retained. Entitlement and identity guards are unchanged. + +Four new tests [failed on the original order](reproduction-before.txt), leaving the flag at1 after +component refusal/rejection from a previous/unset component. Coverage also includes Basic/Restore +first/second failures, Off partial failure, successful exact order and reconnect between component +and enable writes. Existing free-tier/current recovery limitation remains explicitly tested. + +## Visual evidence + +- [Component refusal leaves Off selected](component-refused-off-simulated.png) +- [Successful Basic Daydream readback](basic-daydream-readback-simulated.png) +- [Short failure/recovery video](daydream-failure-recovery-simulated.webm) + +PNG evidence and a sampled video frame were visually inspected. All replies and mutations are mocked; +no real TV settings changed. Raw recordings are ignored. No desktop gallery screens changed. + +From `v2/mobile`, using existing dependencies/Chromium: + +```sh +DAYDREAM_EVIDENCE=1 node --test tests/tweaks-tab.test.mjs +npm test +npm run check +npm run build +``` + +Focused suite:27 passing cases including the optional capture. Svelte check:0 errors/0 warnings; +Vite build passed. The existing parent missing `.svelte-kit/tsconfig.json` startup notice remains. +No permission, credential, storage-policy, backend or platform change. No device query/install, +physical acceptance, release or merge. Independent review and cross-OS CI remain pending. + +Final full-suite receipt: **271/271 passed**, with no failures or skips. The focused run passed27 +including capture. `git diff --check` passed. Source/artifact hashes are in [validation.json](validation.json). + +## Approved-modal-base integration + +The Daydream implementation and its tests are byte-for-byte unchanged from independently reviewed +`2133e8ec6428a3d960345ce3fc050aac327cbb00`. Existing screenshots/video were captured on the +original base and are retained: this integration changes no Daydream appearance or product logic. +The merged modal tests, including reverse C-then-A teardown, run in the full combined suite. + +Combined validation on the approved modal base: **283/283 mobile tests passed**, with no failures +or skips; Svelte check0/0, build and diff check passed. No new product-code changes were needed. diff --git a/v2/mobile/evidence/daydream-order-2026-10-17/basic-daydream-readback-simulated.png b/v2/mobile/evidence/daydream-order-2026-10-17/basic-daydream-readback-simulated.png new file mode 100644 index 00000000..bee28f78 Binary files /dev/null and b/v2/mobile/evidence/daydream-order-2026-10-17/basic-daydream-readback-simulated.png differ diff --git a/v2/mobile/evidence/daydream-order-2026-10-17/component-refused-off-simulated.png b/v2/mobile/evidence/daydream-order-2026-10-17/component-refused-off-simulated.png new file mode 100644 index 00000000..837b13cb Binary files /dev/null and b/v2/mobile/evidence/daydream-order-2026-10-17/component-refused-off-simulated.png differ diff --git a/v2/mobile/evidence/daydream-order-2026-10-17/daydream-failure-recovery-simulated.webm b/v2/mobile/evidence/daydream-order-2026-10-17/daydream-failure-recovery-simulated.webm new file mode 100644 index 00000000..9d2f8768 Binary files /dev/null and b/v2/mobile/evidence/daydream-order-2026-10-17/daydream-failure-recovery-simulated.webm differ diff --git a/v2/mobile/evidence/daydream-order-2026-10-17/reproduction-before.txt b/v2/mobile/evidence/daydream-order-2026-10-17/reproduction-before.txt new file mode 100644 index 00000000..2c31a380 --- /dev/null +++ b/v2/mobile/evidence/daydream-order-2026-10-17/reproduction-before.txt @@ -0,0 +1,88 @@ +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ Daydream component refuse never enables an off screensaver with previous component (1119.46025ms) +✖ Daydream component reject never enables an off screensaver with previous component (142.389791ms) +✖ Daydream component refuse never enables an off screensaver with unset component (140.313458ms) +✖ Daydream component reject never enables an off screensaver with unset component (142.801291ms) +ℹ tests 4 +ℹ suites 0 +ℹ pass 0 +ℹ fail 4 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1851.328125 + +✖ failing tests: + +test at tests/tweaks-tab.test.mjs:398:5 +✖ Daydream component refuse never enables an off screensaver with previous component (1119.46025ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + '1' !== '0' + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/tweaks-tab.test.mjs:413:14) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: '1', + expected: '0', + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/tweaks-tab.test.mjs:398:5 +✖ Daydream component reject never enables an off screensaver with previous component (142.389791ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + '1' !== '0' + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/tweaks-tab.test.mjs:413:14) + at async Test.run (node:internal/test_runner/test:1389:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:960:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: '1', + expected: '0', + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/tweaks-tab.test.mjs:398:5 +✖ Daydream component refuse never enables an off screensaver with unset component (140.313458ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + '1' !== '0' + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/tweaks-tab.test.mjs:413:14) + at async Test.run (node:internal/test_runner/test:1389:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:960:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: '1', + expected: '0', + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/tweaks-tab.test.mjs:398:5 +✖ Daydream component reject never enables an off screensaver with unset component (142.801291ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + '1' !== '0' + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/tweaks-tab.test.mjs:413:14) + at async Test.run (node:internal/test_runner/test:1389:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:960:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: '1', + expected: '0', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/v2/mobile/evidence/daydream-order-2026-10-17/validation.json b/v2/mobile/evidence/daydream-order-2026-10-17/validation.json new file mode 100644 index 00000000..9e438854 --- /dev/null +++ b/v2/mobile/evidence/daydream-order-2026-10-17/validation.json @@ -0,0 +1,39 @@ +{ + "base": "83f69b77e2b19b643e21ae456d0f597e90ef7d83", + "full_mobile": { + "passed": 283, + "failed": 0, + "skipped": 0 + }, + "focused_with_capture": 27, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "build": "passed", + "sha256": { + "v2/mobile/src/screens/Tweaks.svelte": "dc5cd5da0b17e64cac2e8ac2e3521ebb03b6d437889e79de8d655b77127558f8", + "v2/mobile/tests/tweaks-tab.test.mjs": "ae64d09213c981fde7a6c5d702763d4cc92800ad137ace9573abe845f56b02fd", + "v2/mobile/evidence/daydream-order-2026-10-17/basic-daydream-readback-simulated.png": "142c6d5e9d7a067faa673dedbb5fb6664ab198794c466752eac93331fb0fffc1", + "v2/mobile/evidence/daydream-order-2026-10-17/component-refused-off-simulated.png": "25b52d0a62a98b5e034c4a04fb0e7105c91717826a2f0154953297da9739a80d", + "v2/mobile/evidence/daydream-order-2026-10-17/daydream-failure-recovery-simulated.webm": "8332b7e0a2035d416a8708ec83c53c5b50ca302c8bfae34d3d708c7496ee794c" + }, + "limitations": [ + "Simulated Tauri replies only; no device operations.", + "Independent review and CI pending." + ], + "original_base": "56aa7c56450464b2060c27838373029b6b90c14f", + "original_full_mobile": { + "passed": 271, + "failed": 0, + "skipped": 0 + }, + "integration": { + "merge": "c606b28e9a545982013849adae7fca42442ffe08", + "product_code_and_tests_unchanged_from": "2133e8ec6428a3d960345ce3fc050aac327cbb00", + "captures_retained_from_original_base": true, + "check": "0 errors / 0 warnings", + "build": "passed", + "diff_check": "passed" + } +} diff --git a/v2/mobile/evidence/density-only-label-2026-10-17/README.md b/v2/mobile/evidence/density-only-label-2026-10-17/README.md new file mode 100644 index 00000000..c549a4f4 --- /dev/null +++ b/v2/mobile/evidence/density-only-label-2026-10-17/README.md @@ -0,0 +1,17 @@ +# Density-only display summary — 2026-10-17 + +Based on mobile-parity-pass `9265e5fdbf4c9c73f7cea5522e50c50dbcd90fe0`. Separate from screenshot-retention PR193. + +Current source and fixtures reproduce one definite label bug: physical size 3840x2160, physical density 640 and override density 300 display as `3840x2160 @ 640 · device default · Custom size / density`. The pre-fix regression failed with that exact string; see [recorded failure](reproduction-before.txt) and [before screenshot](before-simulated.png). + +A three-line summary-only branch now displays `3840x2160 @ 300 · density override · Custom size / density`. When size is missing it explicitly says `Size not reported`; no resolution is invented. Actual values, parsers, preset matching, API calls, confirmations, write ordering and restoration are unchanged. A matching density-only 1920x1080/320 readback still selects 1080p, not Reset; custom 3840x2160/300 selects neither preset nor Reset. Tests also retain physical defaults, size-only overrides, missing readback and existing failed-read behavior. New fixture flows assert no scaling or setting write occurred. + +[Normal summary](after-384-simulated.png) is captured at 384px. [Narrow enlarged summary](after-320-summary-text-200-simulated.png) doubles the summary’s computed text size at 320px; the card fits horizontally. This is browser simulation, not Android accessibility or physical-phone verification. + +When all card text is doubled at 320px, the unchanged preset row measures 270px of content inside 240px. [Measurements](full-card-text-200-observations.json) and [simulated card](known-full-card-text-200-simulated.png) record that separate layout limitation; the long card passes behind the fixed bottom tabs in this capture. This label fix does not claim whole-card large-text acceptance. + +Remaining screensaver diagnosis: `screensaverLabel` still returns raw custom components for Current and Restore previous. Existing tests deliberately use `com.google.android.backdrop/.Backdrop` and verify exact restoration; custom explanation, identity binding and ordered writes already exist. The common catalog calls that package Ambient Mode, but Tweaks currently has no component-name resolver. A separate display choice remains: use catalog package names with visible raw component details, or a generic Custom screensaver label with the raw component retained. Arbitrary vendor service identities must not be guessed. No screensaver code or write behavior changed here. + +No existing density-specific branch was found in local/remote branch names or relevant commit subjects. Earlier settings PR181 remains intact. No launcher asset, catalog, device query, native storage/export permission, credential or security change was made. Existing launcher optical comparison remains available in the settings evidence directory; no icon change was attempted. + +Validation: 302/302 full mobile tests, three focused evidence tests, Svelte 0 errors/0 warnings and production build passed. See [validation receipt](validation.json). Open PR title searches for density/screensaver returned no duplicate focused PR. No Rust or desktop UI source changed. diff --git a/v2/mobile/evidence/density-only-label-2026-10-17/after-320-summary-text-200-simulated.png b/v2/mobile/evidence/density-only-label-2026-10-17/after-320-summary-text-200-simulated.png new file mode 100644 index 00000000..e51598c9 Binary files /dev/null and b/v2/mobile/evidence/density-only-label-2026-10-17/after-320-summary-text-200-simulated.png differ diff --git a/v2/mobile/evidence/density-only-label-2026-10-17/after-384-simulated.png b/v2/mobile/evidence/density-only-label-2026-10-17/after-384-simulated.png new file mode 100644 index 00000000..014d8e8d Binary files /dev/null and b/v2/mobile/evidence/density-only-label-2026-10-17/after-384-simulated.png differ diff --git a/v2/mobile/evidence/density-only-label-2026-10-17/before-simulated.png b/v2/mobile/evidence/density-only-label-2026-10-17/before-simulated.png new file mode 100644 index 00000000..dcbdf8dd Binary files /dev/null and b/v2/mobile/evidence/density-only-label-2026-10-17/before-simulated.png differ diff --git a/v2/mobile/evidence/density-only-label-2026-10-17/full-card-text-200-observations.json b/v2/mobile/evidence/density-only-label-2026-10-17/full-card-text-200-observations.json new file mode 100644 index 00000000..b8962747 --- /dev/null +++ b/v2/mobile/evidence/density-only-label-2026-10-17/full-card-text-200-observations.json @@ -0,0 +1,7 @@ +[ + { + "text": "4K1080p720pReset", + "width": 240, + "content": 270 + } +] diff --git a/v2/mobile/evidence/density-only-label-2026-10-17/known-full-card-text-200-simulated.png b/v2/mobile/evidence/density-only-label-2026-10-17/known-full-card-text-200-simulated.png new file mode 100644 index 00000000..eb16f792 Binary files /dev/null and b/v2/mobile/evidence/density-only-label-2026-10-17/known-full-card-text-200-simulated.png differ diff --git a/v2/mobile/evidence/density-only-label-2026-10-17/reproduction-before.txt b/v2/mobile/evidence/density-only-label-2026-10-17/reproduction-before.txt new file mode 100644 index 00000000..36d11261 --- /dev/null +++ b/v2/mobile/evidence/density-only-label-2026-10-17/reproduction-before.txt @@ -0,0 +1,36 @@ +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ density-only readback shows the override instead of physical density or device default (1129.3445ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1438.395583 + +✖ failing tests: + +test at tests/tweaks-tab.test.mjs:504:1 +✖ density-only readback shows the override instead of physical density or device default (1129.3445ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + '3840x2160 @ 640 · device default · Custom size / density' + - '3840x2160 @ 300 · density override · Custom size / density' + + at TestContext. (file:///v2/mobile/tests/tweaks-tab.test.mjs:511:10) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: '3840x2160 @ 640 · device default · Custom size / density', + expected: '3840x2160 @ 300 · density override · Custom size / density', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/v2/mobile/evidence/density-only-label-2026-10-17/validation.json b/v2/mobile/evidence/density-only-label-2026-10-17/validation.json new file mode 100644 index 00000000..4b9096ae --- /dev/null +++ b/v2/mobile/evidence/density-only-label-2026-10-17/validation.json @@ -0,0 +1,36 @@ +{ + "date": "2026-10-17", + "base": "9265e5fdbf4c9c73f7cea5522e50c50dbcd90fe0", + "scope": "Three-line summary branch; no setting-value, API, write or preset-selection changes.", + "before_regression": "Failed: physical 640 / device default shown instead of override 300.", + "focused_capture_tests": { + "passed": 3, + "failed": 0 + }, + "full_mobile_tests": { + "passed": 302, + "failed": 0 + }, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "mobile_build": "passed", + "screensaver": "Raw Current/Restore component reproduced by existing tests; naming policy remains separate.", + "open_pr_searches": { + "density_title": [], + "screensaver_title": [] + }, + "limitations": [ + "Simulated browser only; no phone/TV operations", + "Summary-only 200% text at 320px fits; whole-card 200% preset row overflows 270px inside 240px", + "No native accessibility or pinch gesture acceptance claimed" + ], + "unchanged": [ + "setting values and write behavior", + "unknown/custom/preset state calculation", + "launcher icons", + "catalog", + "native storage/export permissions" + ] +} diff --git a/v2/mobile/evidence/detail-state-2026-10-17/README.md b/v2/mobile/evidence/detail-state-2026-10-17/README.md new file mode 100644 index 00000000..52932bff --- /dev/null +++ b/v2/mobile/evidence/detail-state-2026-10-17/README.md @@ -0,0 +1,24 @@ +# Mobile app-detail state regressions — simulated + +Base: `d14363bc76c05f2342289d9503e95175b2d44da2` on mobile-parity-pass. This focused follow-up verifies two PR163 review findings against source contracts, independently of the external review text. + +The pure engine's Restore planner returns Enable only when an app is installed and disabled (`engine/optimize.rs`). `planItemApp` carries `planRowState` into AppSheetHost and AppDetailSheet, whose toggle uses that state. Mapping Enable to enabled therefore offered Disable. The corrected mapping retains disabled, missing and already-enabled skip semantics. + +Optimize reloads its plan on `onChanged`; Diagnostics reloads health and installed/safety lookups. Neither callback clears its selected AppItem. AppSheetHost already treats false mutation results as potentially landed and refreshes, but previously retained the old child and parent selection. It now closes both after false disable/uninstall responses, using the existing invalidation path. The error message remains an error, with no success claim. Confirmed uninstall still exposes Reinstall; identity, generation, liveness, fresh-safety and stale-request guards are unchanged. + +## Reproduction and validation + +`before.txt` records all six new regressions failing on unchanged production source: Restore state and UI, plus false disable/uninstall responses in both callers. The mock command changes the synthetic installed/plan state before returning false. The tests require the sheet and parent selection to close, the error to remain visible, a caller refresh, and no stale reopening after reconnect. + +`after-target-guards.txt` records 35 passing row/identity tests. `after-removal-recovery.txt` records seven passing focused browser tests, including both callers retaining Reinstall after a confirmed uninstall. `planner-tests.txt` records all five engine planner tests passing. All 334 combined mobile tests pass in `combined-mobile.txt`; Svelte check is zero errors/warnings and the production build passes with existing lock-matching mobile dependencies. No native source changed; Android compilation and physical-device validation were not repeated for these two frontend fixes. + +## Observable evidence + +All screens and commands use synthetic browser fixtures at 384×812; no device or ADB calls occurred. + +- [Restore detail with Enable](restore-enable-simulated.png), [short video](restore-enable-simulated.webm) (2.16 seconds): the only toggle mutation dispatched is mocked enable_package. +- [Before inconclusive removal](before-inconclusive-simulated.png), [selection cleared with error](inconclusive-cleared-simulated.png), [short video](inconclusive-cleared-simulated.webm) (2.96 seconds): a mocked mutation lands but returns false; the sheet closes and the caller refreshes without claiming success. + +Reproduce evidence with `DETAIL_STATE_EVIDENCE=1 node --test --test-concurrency=1 tests/sheet-opening-identity.test.mjs` from v2/mobile. Raw recorder intermediates are omitted. Desktop screenshot galleries are unchanged because these are mobile-only changes. + +No real app mutations, device queries, installs, credentials, network settings, releases or merges were performed. Physical phone/TV acceptance and independent review remain required. diff --git a/v2/mobile/evidence/detail-state-2026-10-17/after-removal-recovery.txt b/v2/mobile/evidence/detail-state-2026-10-17/after-removal-recovery.txt new file mode 100644 index 00000000..c939d7df --- /dev/null +++ b/v2/mobile/evidence/detail-state-2026-10-17/after-removal-recovery.txt @@ -0,0 +1,15 @@ +✔ diagnostics: Disable landing with a false response discards the stale selection (1801.219166ms) +✔ diagnostics: Uninstall landing with a false response discards the stale selection (829.712375ms) +✔ optimize: Disable landing with a false response discards the stale selection (1623.149708ms) +✔ optimize: Uninstall landing with a false response discards the stale selection (818.534375ms) +✔ Restore detail offers Enable and dispatches only enable_package (1696.246834ms) +✔ diagnostics: successful uninstall retains the explicit Reinstall path (766.414ms) +✔ optimize: successful uninstall retains the explicit Reinstall path (785.596708ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 7 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 8746.7235 diff --git a/v2/mobile/evidence/detail-state-2026-10-17/after-target-guards.txt b/v2/mobile/evidence/detail-state-2026-10-17/after-target-guards.txt new file mode 100644 index 00000000..b793e406 --- /dev/null +++ b/v2/mobile/evidence/detail-state-2026-10-17/after-target-guards.txt @@ -0,0 +1,43 @@ +✔ Optimize recommends exactly what desktop pre-selects (150.383ms) +✔ an uninstall the store can't give back runs as Disable (0.121792ms) +✔ review pill only for resolved, unprotected review rows (0.066083ms) +✔ plan search matches name or package, case-insensitively (0.060292ms) +✔ row safety never reads unresolved as a verdict (0.61525ms) +✔ the sheet sees the plan's state and catalog entry (0.059708ms) +✔ Health: a confirmed catalog app gets the App List's recommendation (0.130375ms) +✔ Health: a name no installed package carries is a process, not an app (0.077208ms) +✔ Health: an unreadable installed list confirms nothing and says so (0.089959ms) +✔ Health: an installed package outside the catalog shows its verdict (0.083542ms) +✔ snapshot preview: Now is what the TV reported, and matching settings read Already set (0.155958ms) +✔ snapshot preview: launcher change, skipped launcher, and an unset Now (0.068375ms) +✔ snapshot preview summary matches desktop's wording (0.057042ms) +✔ Restore enable plans describe a disabled installed app to the detail sheet (0.03925ms) +✔ diagnostics: disconnect requires a new sheet selection before any action (1709.668083ms) +✔ diagnostics: reconnect requires a new sheet selection before any action (829.448959ms) +✔ diagnostics: switch requires a new sheet selection before any action (825.615125ms) +✔ diagnostics: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (891.2235ms) +✔ diagnostics: Disable nested confirmation is cleared by same-endpoint reconnect (483.524666ms) +✔ diagnostics: Uninstall nested confirmation is cleared by same-endpoint reconnect (501.033125ms) +✔ diagnostics: paywall and pending measurements cannot follow the opening identity (554.277166ms) +✔ optimize: disconnect requires a new sheet selection before any action (815.055083ms) +✔ optimize: reconnect requires a new sheet selection before any action (821.20125ms) +✔ optimize: switch requires a new sheet selection before any action (815.939667ms) +✔ optimize: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (891.538916ms) +✔ optimize: Disable nested confirmation is cleared by same-endpoint reconnect (475.472167ms) +✔ optimize: Uninstall nested confirmation is cleared by same-endpoint reconnect (483.383208ms) +✔ optimize: paywall and pending measurements cannot follow the opening identity (556.7115ms) +✔ diagnostics: a failed reconnect or transient liveness loss still invalidates the opening (254.754625ms) +✔ optimize: a failed reconnect or transient liveness loss still invalidates the opening (246.643917ms) +✔ diagnostics: Disable landing with a false response discards the stale selection (799.834334ms) +✔ diagnostics: Uninstall landing with a false response discards the stale selection (806.91125ms) +✔ optimize: Disable landing with a false response discards the stale selection (1582.855417ms) +✔ optimize: Uninstall landing with a false response discards the stale selection (785.965459ms) +✔ Restore detail offers Enable and dispatches only enable_package (1689.20375ms) +ℹ tests 35 +ℹ suites 0 +ℹ pass 35 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 17506.265042 diff --git a/v2/mobile/evidence/detail-state-2026-10-17/before-inconclusive-simulated.png b/v2/mobile/evidence/detail-state-2026-10-17/before-inconclusive-simulated.png new file mode 100644 index 00000000..b0b968c1 Binary files /dev/null and b/v2/mobile/evidence/detail-state-2026-10-17/before-inconclusive-simulated.png differ diff --git a/v2/mobile/evidence/detail-state-2026-10-17/before.txt b/v2/mobile/evidence/detail-state-2026-10-17/before.txt new file mode 100644 index 00000000..540b351f --- /dev/null +++ b/v2/mobile/evidence/detail-state-2026-10-17/before.txt @@ -0,0 +1,114 @@ +✖ Restore enable plans describe a disabled installed app to the detail sheet (747.736333ms) +✖ diagnostics: Disable landing with a false response discards the stale selection (2100.049125ms) +✖ diagnostics: Uninstall landing with a false response discards the stale selection (819.578625ms) +✖ optimize: Disable landing with a false response discards the stale selection (832.755458ms) +✖ optimize: Uninstall landing with a false response discards the stale selection (825.04025ms) +✖ Restore detail offers Enable and dispatches only enable_package (10228.934667ms) +ℹ tests 6 +ℹ suites 0 +ℹ pass 0 +ℹ fail 6 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 16605.85125 + +✖ failing tests: + +test at tests/parity-rows.test.mjs:233:2 +✖ Restore enable plans describe a disabled installed app to the detail sheet (747.736333ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + 'enabled' + - 'disabled' + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/parity-rows.test.mjs:235:10) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1382:25) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: 'enabled', + expected: 'disabled', + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/sheet-opening-identity.test.mjs:179:50 +✖ diagnostics: Disable landing with a false response discards the stale selection (2100.049125ms) + AssertionError [ERR_ASSERTION]: an inconclusive write must clear child and parent selection + + 1 !== 0 + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/sheet-opening-identity.test.mjs:197:12) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 1, + expected: 0, + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/sheet-opening-identity.test.mjs:179:50 +✖ diagnostics: Uninstall landing with a false response discards the stale selection (819.578625ms) + AssertionError [ERR_ASSERTION]: an inconclusive write must clear child and parent selection + + 1 !== 0 + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/sheet-opening-identity.test.mjs:197:12) + at async Test.run (node:internal/test_runner/test:1389:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:960:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 1, + expected: 0, + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/sheet-opening-identity.test.mjs:179:50 +✖ optimize: Disable landing with a false response discards the stale selection (832.755458ms) + AssertionError [ERR_ASSERTION]: an inconclusive write must clear child and parent selection + + 1 !== 0 + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/sheet-opening-identity.test.mjs:197:12) + at async Test.run (node:internal/test_runner/test:1389:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:960:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 1, + expected: 0, + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/sheet-opening-identity.test.mjs:179:50 +✖ optimize: Uninstall landing with a false response discards the stale selection (825.04025ms) + AssertionError [ERR_ASSERTION]: an inconclusive write must clear child and parent selection + + 1 !== 0 + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/sheet-opening-identity.test.mjs:197:12) + at async Test.run (node:internal/test_runner/test:1389:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:960:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: 1, + expected: 0, + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/sheet-opening-identity.test.mjs:206:1 +✖ Restore detail offers Enable and dispatches only enable_package (10228.934667ms) + locator.waitFor: Timeout 10000ms exceeded. + Call log: +  - waiting for locator('.sheet').getByRole('button', { name: 'Enable', exact: true }) to be visible + + at TestContext. (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/sheet-opening-identity.test.mjs:214:16) { + name: 'TimeoutError' + } diff --git a/v2/mobile/evidence/detail-state-2026-10-17/combined-mobile.txt b/v2/mobile/evidence/detail-state-2026-10-17/combined-mobile.txt new file mode 100644 index 00000000..ead1d19d --- /dev/null +++ b/v2/mobile/evidence/detail-state-2026-10-17/combined-mobile.txt @@ -0,0 +1,344 @@ +npm notice run atv-optimizer-mobile@0.1.0 test +npm notice run node --test --test-concurrency=1 tests/*.test.mjs +✔ a disabled katniss is found by package and by name, and can be re-enabled (1634.05525ms) +✔ not-installed recognised apps hide by default and can be revealed (323.790125ms) +✔ long-press opens the action menu without opening the sheet; a tap still opens it (902.721959ms) +✔ menu Disable goes through the safety lookup and confirm, never straight to the TV (451.515083ms) +✔ report opens a prefilled GitHub form through the opener (785.281ms) +✔ without an opener, report falls back to copy and a link to copy (788.411792ms) +✔ rows, chips and labels fit a 384px screen (137.494292ms) +✔ long report keyboard wrap reveals its focused actions without moving the locked page (771.718417ms) +✔ menu Report returns focus to its original app row after closing (480.745125ms) +✔ report includes the successful APK fallback visible in the detail sheet (789.512583ms) +✔ APK report keeps exact 0 bytes, unknown data/cache and identical copied JSON (814.909791ms) +✔ APK report keeps exact 1234567 bytes, unknown data/cache and identical copied JSON (807.649917ms) +✔ diskstats remains the report source when a complete measured row exists (790.554833ms) +✔ APK report keeps storage unknown for a pending fallback (775.468125ms) +✔ APK report keeps storage unknown for a failed fallback (816.953375ms) +✔ APK report keeps storage unknown for a missing fallback (791.388625ms) +✔ APK report keeps storage unknown for a negative fallback (791.647375ms) +✔ remeasure invalidates a successful APK receipt and ignores an older fallback reply (1223.947833ms) +✔ APK fallback cannot cross a same-endpoint reconnect (1217.079083ms) +✔ APK fallback cannot cross a TV switch (1216.11175ms) +✔ an APK result for a closed package cannot replace the next package measurement (1151.011291ms) +✔ system visibility composes with enabled and disabled filters (1207.381917ms) +✔ search reports empty results and can reveal or clear them (566.7485ms) +✔ filtering never mutates packages and protected-app safety stays enforced (245.555542ms) +✔ katniss is in the shared catalogue (138.668708ms) +✔ katniss is found by package and by name when enabled (0.906541ms) +✔ katniss is found by package and by name when disabled (0.099917ms) +✔ a disabled katniss is reachable from the Disabled chip so it can be re-enabled (0.079584ms) +✔ not-installed catalogue rows hide by default and are counted as hidden (0.105417ms) +✔ an unread state is unknown: shown under All, never under Enabled (0.051291ms) +✔ system packages in Everything else follow the toggle and are counted when hidden (0.063334ms) +✔ a package in both lists is listed once, as recognised (0.076167ms) +✔ validatedStates drops malformed values instead of guessing (0.115958ms) +✔ onDeviceCount leaves out only rows known to be missing (0.079792ms) +✔ installed picker hides systems by default and composes reveal with search (1095.497583ms) +✔ one chosen app makes one backup call for the expected TV and package (173.682084ms) +✔ a delayed A picker read cannot replace the newer B result (200.203792ms) +✔ a backup started on A stays owned by A when it finishes after a switch to B (190.142833ms) +✔ a stale displayed A row cannot start a backup on B (160.317834ms) +✔ a restore confirmation opened on A cannot restore onto B (147.310833ms) +✔ picker and backup failures stay explicit and device-owned (178.831541ms) +✔ disconnected state and APK-only limits are stated plainly (128.121875ms) +✔ Dashboard counts enabled among installed recommended apps (1076.328291ms) +✔ Dashboard treats incomplete package state replies as unavailable (125.554208ms) +✔ a stale same-serial count cannot overwrite a newer generation (148.284875ms) +✔ zero recommended candidates opens Optional apps with Keep selected (121.701584ms) +✔ Caution defaults are recommended while Unknown defaults move to Optional (139.60675ms) +✔ protected defaults stay visible and blocked in Optional (167.348042ms) +✔ a plan containing only Unknown defaults opens Optional after safety resolves (134.810542ms) +✔ a user's tab choice survives pending safety resolution (174.0785ms) +✔ optional choices survive tabs and confirmation names every actual action (528.027083ms) +✔ protected and unresolved optional actions remain blocked while Unknown is explicit (136.708583ms) +✔ Restore keeps Recommended and All curated tabs with its prior defaults (181.79275ms) +✔ differing Unknown reasons stay on their own rows (122.394208ms) +✔ Safe defaults are recommended, selected and actionable (130.698417ms) +✔ the confirm summary names the Safe verdict rather than calling it Caution (143.590125ms) +✔ Diagnostics reports the TV's Android version and hardware details (1077.082208ms) +✔ Dashboard shows the Android version beside the address (124.242292ms) +✔ A property the TV did not report reads as unknown, never as a neighbour's value (121.264875ms) +✔ A device with no profile says so instead of showing blank rows (124.251875ms) +✔ Missing health data leaves the totals blank rather than inventing them (142.1575ms) +✔ Diagnostics header fits 320px at 100% text with every control usable (1189.720292ms) +✔ Diagnostics header fits 384px at 100% text with every control usable (245.738959ms) +✔ Diagnostics header fits 320px at 200% text with every control usable (241.445917ms) +✔ Diagnostics header fits 384px at 200% text with every control usable (232.948416ms) +✔ non-Shield and disconnected headers keep the applicable controls without overflow (271.326792ms) +✔ collapses repeated advertisements and separates saved from new hosts (1.684083ms) +✔ the live endpoint is the only connected row (0.155542ms) +✔ a connected TV that does not advertise stays one connected saved row (0.089041ms) +✔ only the saved TV whose id is live claims a shared live endpoint (#115) (0.1745ms) +✔ two id-less saved TVs sharing the live endpoint never both claim connected (#146) (0.131417ms) +✔ a stale advert never names the live row after a different saved TV (#115) (0.077333ms) +✔ a stale advert at an old host never shows the live TV a second time (#115) (0.091708ms) +✔ a legacy advert sharing the live TV's serial prefix is not dropped as stale (#149) (0.061375ms) +✔ a live TV that reports no id is never named after a stale advert at its address (#115) (0.080292ms) +✔ dropping a stale advert keeps the TV now advertising from that address (#115) (0.104167ms) +✔ saved TVs sharing an endpoint that answers unverified each keep their row (#115) (0.090417ms) +✔ an address change reports a new host and a saved endpoint that is absent (0.071959ms) +✔ a rotated port keeps the saved endpoint truthful instead of calling it offline (0.060209ms) +✔ a host advertising only a pairing service is not treated as answering (0.047583ms) +✔ two saved identities at one endpoint stay two real rows (#115) (0.053208ms) +✔ a saved TV that shared an address with one that moved stays in the scan (#115) (0.055333ms) +✔ a stored placeholder id is not a wildcard for serial-less adbd (#116) (0.068958ms) +✔ a saved id only matches its own advertised serial, not a longer one (#117) (6.876ms) +✔ distinct saved endpoints on one host stay distinct rows (0.051042ms) +✔ malformed advertisements are dropped (0.049125ms) +✔ instance names are only used when they are unambiguous and not adb ids (0.040167ms) +✔ legacy connect ports are the only ones flagged as needing no pairing code (0.04775ms) +✔ an advertised serial verifies a saved TV and names it (0.033542ms) +✔ a verified serial recognizes a saved TV that moved to a new address (0.032542ms) +✔ a serial that matches no saved TV leaves the row unclaimed (0.03225ms) +✔ two TVs advertising only adb ids are each named from their own serial (0.043667ms) +✔ rows are ordered by usability then recency, not by mDNS answer order (0.055833ms) +✔ matrix covers every registered mobile command and opener exactly once (234.520875ms) +✔ store observations never unlock; stale, duplicate and ack events are isolated (0.323542ms) +✔ errors/cancel preserve access; revocation preserves independent legacy source (0.092417ms) +✔ cancel and errors never create access; acknowledgment cannot precede verification (0.08425ms) +✔ simulated UI keeps recovery usable through pending/error/revocation (998.689375ms) +✔ screenshot destination, bytes and Close are truthful at 384px / 100% text (1083.371208ms) +✔ screenshot destination, bytes and Close are truthful at 320px / 200% text (211.901792ms) +✔ screenshot failure creates no preview or false saved result and preserves earlier saved bytes (241.117875ms) +✔ file-copy success identifies the actual private destination and failure adds no copied row (160.900625ms) +✔ report copy and oversized GitHub fallback use preview text and never claim a saved file (798.704583ms) +✔ report clipboard failure exposes the selectable preview without false copy success (776.287334ms) +✔ diagnostic JSON and debug log Copy actions state clipboard-only output and report failures (263.717583ms) +✔ file results belong to the live connection that started them (144.17625ms) +✔ reconnecting to the same endpoint invalidates an old file action (0.11725ms) +✔ a lost connection cannot accept a file result or delete confirmation (0.044541ms) +✔ the catalog is desktop's file, not a copy (215.755583ms) +✔ a search that could not run never summarizes as no matches (0.169917ms) +✔ delete is only offered strictly inside /sdcard (0.160041ms) +✔ Find SmartTube backups searches every catalog dir and copies a hit to the phone (884.549291ms) +✔ an unsearched directory reads as a failed search, never as no matches (#86) (183.11375ms) +✔ a rejected search drops the older answer instead of showing it as current (181.976584ms) +✔ delete asks first, sends the exact /sdcard path, and reloads the folder (500.786709ms) +✔ no delete control outside /sdcard (134.428125ms) +✔ a delete confirmed after a TV switch is not sent to the new TV (186.415ms) +✔ reconnecting to the same endpoint clears an old delete confirmation (155.58325ms) +✔ a copy completing after a TV switch does not mark the new TV's row copied (212.889ms) +✔ RAM uses explicit used/total or available complement; zero/full are real values (204.883584ms) +✔ missing and invalid memory is not painted as zero/full (0.14975ms) +✔ storage retains reported percentages and distinguishes missing/invalid from zero (0.090458ms) +✔ temperature comparison uses only a matching sensor own threshold (0.678584ms) +✔ reported device status is independent of hottest sensor status; no temperature-derived grade (0.169959ms) +✔ both visible bars mean used, share neutral styling and show 0/50/100 honestly (824.593708ms) +✔ legacy100C stays unknown; a device-reported critical status is shown even at30C (134.4505ms) +✔ below-threshold light/moderate readings do not describe throttling onset or infer HAL severity (0.19725ms) +✔ threshold provenance stays unspecified for hardware, HIDL-like and unknown sources (0.126167ms) +✔ visible thresholds retain reported status and never claim onset or a severity without provenance (141.355667ms) +✔ checked-in Shield meminfo keeps explicit Used despite non-complementary Free accounting (0.860959ms) +✔ Shield accounting renders explicit used/free without changing the source report (120.48825ms) +✔ Setup Wraith reads off / on / risk from the launcher rows (214.020167ms) +✔ a takeover turns Setup Wraith off only when an enabled stock names it (0.199375ms) +✔ Disable stock launcher stays off until the picked app is Home or stock is in the way (0.216834ms) +✔ the stock-disable message never claims what wasn't verified or re-read (0.115292ms) +✔ picker rows hide system apps, search name and package, dedupe and sort (8.191917ms) +✔ source site links are offered only for http(s) URLs (0.187916ms) +✔ Setup Wraith on with stock off warns and turns it off, then re-reads the TV (842.453334ms) +✔ Setup Wraith off offers Re-enable through enable_package (134.661292ms) +✔ a not-ok default switch re-reads the TV and offers its diagnostics (204.380833ms) +✔ set any app as Home reports Android's answer, then unlocks the separate stock step (243.731209ms) +✔ Save snapshot first leaves stock alone when the snapshot fails (215.455583ms) +✔ a locked Set as Home opens the paywall and calls nothing else (201.12025ms) +✔ source site hands the launcher's page to the phone's browser, not the app (139.3695ms) +✔ source site copies the link when the browser can't be opened (125.681125ms) +✔ after opening the Play Store the list re-reads until the launcher is installed (157.318458ms) +✔ same-endpoint reconnect cancels confirmations and late launcher results (444.084ms) +✔ lost connection removes actionable rows and reconnect discards an old list reply (242.324541ms) +✔ old snapshot completion cannot unlock a newer Home action after reconnect (369.182667ms) +✔ stock takeover dialog locks scrolling, traps focus and Escape cancels without a mutation (272.537375ms) +✔ Setup Wraith row uses the guarded helper command and refreshes confirmed success (165.911ms) +✔ helper row preserves guarded failure: rolled back (166.816583ms) +✔ helper row preserves guarded failure: rollback unconfirmed (166.702333ms) +✔ late helper-row result cannot overwrite a fresh same-endpoint reconnect (557.994625ms) +✔ late helper-row result cannot overwrite a fresh TV switch (563.142333ms) +✔ ordinary stock row keeps its launcher disable command (159.357292ms) +✔ same-update replacement preserves document lock and original opener across an empty stack (1087.270292ms) +✔ async resolve settles genuine close before any later replacement (123.661292ms) +✔ async cancel settles genuine close before any later replacement (116.846625ms) +✔ async reject settles genuine close before any later replacement (128.473958ms) +✔ genuine empty close clears provenance before an unrelated modal opens (129.004834ms) +✔ nested out-of-order teardown and replacement retain the document opener (107.9815ms) +✔ superseded close callbacks cannot steal focus from a new top modal (119.922458ms) +✔ removed opener and duplicate teardown do not resurrect focus or leak a lock (99.154333ms) +✔ alertdialog without enabled controls receives focus and Escape releases the genuine close (108.919792ms) +✔ closing the focused child then a non-top ancestor restores the surviving middle modal (111.830917ms) +✔ Optimize recommends exactly what desktop pre-selects (165.159334ms) +✔ an uninstall the store can't give back runs as Disable (0.132417ms) +✔ review pill only for resolved, unprotected review rows (0.069083ms) +✔ plan search matches name or package, case-insensitively (0.066709ms) +✔ row safety never reads unresolved as a verdict (0.64775ms) +✔ the sheet sees the plan's state and catalog entry (0.065917ms) +✔ Health: a confirmed catalog app gets the App List's recommendation (0.143917ms) +✔ Health: a name no installed package carries is a process, not an app (0.085041ms) +✔ Health: an unreadable installed list confirms nothing and says so (0.094417ms) +✔ Health: an installed package outside the catalog shows its verdict (0.094959ms) +✔ snapshot preview: Now is what the TV reported, and matching settings read Already set (0.168667ms) +✔ snapshot preview: launcher change, skipped launcher, and an unset Now (0.074834ms) +✔ snapshot preview summary matches desktop's wording (0.061792ms) +✔ Restore enable plans describe a disabled installed app to the detail sheet (0.040875ms) +✔ Optimize Back returns to the screen that opened it (1038.519459ms) +✔ Optimize search filters by name and package id (150.631792ms) +✔ tapping an Optimize row opens the app sheet, not a toggle (165.808166ms) +✔ Safe to remove and review chips stay on one line at 384px (155.669583ms) +✔ Health suggests per row: app recommendation, process, and package verdicts (174.182458ms) +✔ Health never calls a package-shaped name Not an app when installed apps can't be read (119.561625ms) +✔ Snapshots: Beta, Preview restore, and a real Now column (157.168375ms) +✔ modal sheet blocks background wheel/edge scroll and restores position, focus and existing styles repeatedly (930.045042ms) +✔ modal nested confirmation keeps the page locked and Escape/Tab affect only the top layer (502.065333ms) +✔ modal backdrop, screen teardown and disconnect release the document without leaving a lock (223.663042ms) +✔ modal paywall dismissal retains the underlying app sheet lock (496.19725ms) +✔ modal lock survives out-of-order owner teardown and restores the original opener after its child (127.743416ms) +✔ modal long-sheet keyboard wrap reveals last action and Close without moving the background (454.823ms) +✔ Free diagnostics reports are explicit, honest and per-interface (1079.092291ms) +✔ resource_sample ignores stale resolve after reconnect to the same serial (163.33025ms) +✔ resource_sample ignores stale reject after reconnect to the same serial (157.372958ms) +✔ media_report ignores stale resolve after reconnect to the same serial (159.685667ms) +✔ media_report ignores stale reject after reconnect to the same serial (156.871208ms) +✔ resource sampling admits one request and no live-refresh polling (162.559959ms) +✔ manual audio toggle preserves unknown and newer encodings through readback (169.670166ms) +✔ playback errors do not replace existing health metrics (133.536583ms) +✔ Free audio writes use the existing paywall and do not fabricate readback (135.550708ms) +✔ late audio mutation failure cannot overwrite another TV's state (166.281875ms) +✔ report fidelity and wrapping at 320px with 100% text (189.538458ms) +✔ report fidelity and wrapping at 384px with 100% text (191.088ms) +✔ report fidelity and wrapping at 320px with 200% text (191.397625ms) +✔ empty reports and failed rereads retain honest unavailable states and keyboard access (158.972458ms) +✔ the shared sideload catalog is served to the webview (173.404166ms) +✔ uninstall is only recommended when the store can give the app back (0.853084ms) +✔ no recommendation label ever says Remove (0.202166ms) +✔ unknown and unresolved safety never read as a default action (0.063875ms) +✔ safety labels match the desktop vocabulary (0.065542ms) +✔ an app report carries no device identifiers and is labelled mobile (0.959417ms) +✔ keeps mixed saved and new TVs and deduplicates repeated advertisements (8.215416ms) +✔ moves one matching hardware identity across an address change (0.19075ms) +✔ moves one matching hardware identity across a port change (0.113958ms) +✔ keeps different hardware identities that claim the same address (0.121875ms) +✔ does not merge a saved id-less row into a live identified TV (0.177583ms) +✔ does not merge an id-less live TV into a saved identified row (0.1835ms) +✔ matches id-less rows only at the exact endpoint (0.178875ms) +✔ forgets identified TVs by id and id-less TVs by exact endpoint (0.210417ms) +✔ returns cached names only for an unambiguous or matching identity (0.136333ms) +✔ a reused address never lends an identified TV's name to a TV that reports no id (#117) (0.107167ms) +✔ a stored placeholder id becomes an id-less row with its own stable key, never merged with another (#116, #146) (0.164792ms) +✔ reconnecting a TV that reports a placeholder id refreshes its row instead of appending (#116) (0.085958ms) +✔ never guesses which of two id-less TVs at one endpoint just reconnected (#146) (0.197792ms) +✔ sorts by lastUsed and truncates the oldest saved rows (0.243459ms) +✔ ignores corrupt records and normalizes recoverable stored values (0.087125ms) +✔ builds stable identity keys for host collisions and identical endpoints (0.076209ms) +✔ a selected reconnect token activates only its saved row (0.049ms) +✔ matches the current TV by verified id or an exact id-less endpoint (0.059ms) +✔ only an unambiguous match counts as the live connection (#146) (0.072333ms) +✔ forgets the exact selected identity at an identical endpoint (0.1235ms) +✔ auto-dials only when the saved list truly has one entry (0.046417ms) +✔ a lone id-less match stores a fingerprint from the live device's properties (#154) (0.077416ms) +✔ a different model or manufacturer at a saved id-less row's address is not silently inherited (#154) (0.635709ms) +✔ a manufacturer-only disagreement also counts as a mismatch (#154) (0.0645ms) +✔ an empty saved fingerprint (an older row, or a device that reported nothing) never blocks a match (#154) (0.057958ms) +✔ a matching fingerprint still refreshes the row normally, and is never treated as proof (#154) (0.0585ms) +✔ a hardware-identified reconnect is never second-guessed by fingerprint (#154) (0.062916ms) +✔ a stored fingerprint survives a read/normalize round trip and sanitizes stray fields (0.056417ms) +✔ hardware Back and direct navigation keep Optimize cancellable (1413.538667ms) +✔ Tweaks is a bottom tab and Optimize is a pushed screen; Back unwinds both to Home (186.860542ms) +✔ remote queue is discarded after navigation and a TV switch (201.781292ms) +✔ ordinary remote taps cannot accumulate an unlimited backlog (155.6375ms) +✔ failed health refresh preserves the last report and exposes the error (106.276167ms) +✔ one failed recovery stays lost until an explicit retry (104.958833ms) +✔ a silent recovery onto a different device does not inherit the old saved row or its name (#154 follow-up) (115.369083ms) +✔ a silent recovery's different-device note shows immediately on whatever screen is already open, and clears itself (#154 follow-up) (4122.877083ms) +✔ the Devices screen hands its different-device note on to Dashboard instead of losing it on navigate (#154 follow-up) (4159.187083ms) +✔ canceled connect cannot update the session when its old reply arrives (103.561667ms) +✔ stale reboot completion cannot disconnect a newer session (104.106625ms) +✔ cold single saved TV waits for its profile, then opens a clean dashboard stack (111.665875ms) +✔ cold multi-TV launch waits for a choice and opens the chosen TV (186.507041ms) +✔ cold saved TV missing profile stays on onboarding (106.501375ms) +✔ cold saved TV wrong profile stays on onboarding (108.3555ms) +✔ cold saved TV rejected request stays on onboarding (107.312042ms) +✔ authorization failure stays honest and a deliberate retry can open Dashboard (163.320125ms) +✔ explicit disconnect suppresses automatic saved-TV redial (146.166833ms) +✔ first-time connection keeps the connected interstitial (164.012417ms) +✔ add-TV connection keeps the connected interstitial (150.46925ms) +✔ saved TV success opens its dashboard once without canceling the connection (110.696666ms) +✔ leaving onboarding cancels a saved TV connection that is still pending (153.523125ms) +✔ cancel and retry of the same saved TV ignores the first completion (198.495042ms) +✔ a late transport error from A cannot start recovery on B (163.61825ms) +✔ a health load that lands after its profile vanished cannot wedge later loads (#118) (96.944666ms) +✔ cancelling a saved-TV reconnect never reports a connection failure (#118) (185.948625ms) +✔ Diagnostics keeps its safety badges when two top apps swap rank (#118) (165.55325ms) +✔ diagnostics: disconnect requires a new sheet selection before any action (1718.18125ms) +✔ diagnostics: reconnect requires a new sheet selection before any action (841.452208ms) +✔ diagnostics: switch requires a new sheet selection before any action (812.153958ms) +✔ diagnostics: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (859.433209ms) +✔ diagnostics: Disable nested confirmation is cleared by same-endpoint reconnect (485.148042ms) +✔ diagnostics: Uninstall nested confirmation is cleared by same-endpoint reconnect (491.032625ms) +✔ diagnostics: paywall and pending measurements cannot follow the opening identity (557.274875ms) +✔ optimize: disconnect requires a new sheet selection before any action (812.362166ms) +✔ optimize: reconnect requires a new sheet selection before any action (824.709709ms) +✔ optimize: switch requires a new sheet selection before any action (817.240542ms) +✔ optimize: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (870.910083ms) +✔ optimize: Disable nested confirmation is cleared by same-endpoint reconnect (500.785917ms) +✔ optimize: Uninstall nested confirmation is cleared by same-endpoint reconnect (499.51525ms) +✔ optimize: paywall and pending measurements cannot follow the opening identity (566.694583ms) +✔ diagnostics: a failed reconnect or transient liveness loss still invalidates the opening (240.02025ms) +✔ optimize: a failed reconnect or transient liveness loss still invalidates the opening (224.978833ms) +✔ diagnostics: Disable landing with a false response discards the stale selection (801.235416ms) +✔ diagnostics: Uninstall landing with a false response discards the stale selection (817.514125ms) +✔ optimize: Disable landing with a false response discards the stale selection (816.331834ms) +✔ optimize: Uninstall landing with a false response discards the stale selection (824.0295ms) +✔ Restore detail offers Enable and dispatches only enable_package (490.670958ms) +✔ diagnostics: successful uninstall retains the explicit Reinstall path (789.606ms) +✔ optimize: successful uninstall retains the explicit Reinstall path (793.278417ms) +✔ Free users see every current value, including hooks, mic and screensaver (970.847292ms) +✔ Turning the hooks back on is free; turning them off opens the paywall (177.308875ms) +✔ hooks row is hidden when the package is missing and on non-Shield read failures (273.42025ms) +✔ Assistant button revokes the mic through set_app_permission and re-reads (134.861042ms) +✔ screensaver: Basic Daydream, Off, then Restore previous brings the vendor one back (197.736041ms) +✔ a refused screensaver flag stops before the component is written (154.259416ms) +✔ Dashboard has one Optimize call to action and Tune tiles for Tweaks and Launcher (187.873959ms) +✔ Pro dashboard drops the PRO tags (109.350667ms) +✔ More is titled More, lists Optimize, and no longer duplicates the Tweaks tab (154.330292ms) +✔ endpoint reuse never restores another TV's screensaver and interrupted writes stop (341.783125ms) +✔ verified TV retains originals across a rotated port, replacement hardware does not (209.417875ms) +✔ a reconnect between screensaver writes prevents the second write (263.601458ms) +✔ current gap: Pro loss blocks restoring an app-changed screensaver even with the original known (180.743792ms) +✔ Free users retain emergency recovery without an unlock or payment (151.035541ms) +✔ scaling readback matches both resolution and density and clears stale selection on failure (179.806875ms) +✔ Custom DNS editing does not claim the TV has applied it; failed read clears selection (160.392583ms) +✔ equivalent Android animation number formats select the same speed (155.408042ms) +✔ DNS confirmed changes survive navigation and reconnect, while unset stays explicit (184.820709ms) +✔ Daydream component refuse never enables an off screensaver with previous component (135.8305ms) +✔ Daydream component reject never enables an off screensaver with previous component (153.823708ms) +✔ Daydream component refuse never enables an off screensaver with unset component (129.247708ms) +✔ Daydream component reject never enables an off screensaver with unset component (152.104167ms) +✔ Basic Daydream does not enable after a refused component; failed enable retains off with configured component (162.269584ms) +✔ Restore previous does not enable after a refused component; failed enable retains off with configured component (191.206958ms) +✔ Off keeps the switch disabled if component clearing fails (127.5075ms) +✔ a reconnect after the Daydream component write prevents enabling (156.091208ms) +✔ screensaver names preserve raw known, custom, disabled and missing component states (1002.61575ms) +✔ catalog failure stays custom and late old catalog names cannot cross connections (230.076291ms) +✔ density-only readback shows the override instead of physical density or device default (155.570083ms) +✔ density-only matching preset, absent size and reset readbacks retain their distinct states (210.74875ms) +✔ scaling preset row fits 320px at 100% text and preserves controls (1430.119959ms) +✔ scaling preset row fits 384px at 100% text and preserves controls (1473.143334ms) +✔ scaling preset row fits 320px at 200% text and preserves controls (1474.009208ms) +✔ scaling preset row fits 384px at 200% text and preserves controls (1462.537125ms) +✔ records uncatalogued packages and unresolved native processes distinctly (1.858375ms) +✔ deduplicates repeat polls, saturates counts, and separates device context (0.175083ms) +✔ drops stale scheduled observations after a device changes (0.083459ms) +✔ rejects paths, addresses, control strings, and malformed package tokens (0.10425ms) +✔ bounds record count and serialized byte size while marking truncation (0.250666ms) +✔ fails harmlessly for corrupt or full storage (0.120583ms) +✔ exports an immutable exact snapshot with allowlisted fields only (0.141583ms) +✔ clear prevents a queued old save from resurrecting records (0.078041ms) +✔ clear failures remain visible and do not claim deletion (0.089ms) +ℹ tests 334 +ℹ suites 0 +ℹ pass 334 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 102986.341792 diff --git a/v2/mobile/evidence/detail-state-2026-10-17/inconclusive-cleared-simulated.png b/v2/mobile/evidence/detail-state-2026-10-17/inconclusive-cleared-simulated.png new file mode 100644 index 00000000..15ef9b99 Binary files /dev/null and b/v2/mobile/evidence/detail-state-2026-10-17/inconclusive-cleared-simulated.png differ diff --git a/v2/mobile/evidence/detail-state-2026-10-17/inconclusive-cleared-simulated.webm b/v2/mobile/evidence/detail-state-2026-10-17/inconclusive-cleared-simulated.webm new file mode 100644 index 00000000..5b37454a Binary files /dev/null and b/v2/mobile/evidence/detail-state-2026-10-17/inconclusive-cleared-simulated.webm differ diff --git a/v2/mobile/evidence/detail-state-2026-10-17/planner-tests.txt b/v2/mobile/evidence/detail-state-2026-10-17/planner-tests.txt new file mode 100644 index 00000000..e178df24 --- /dev/null +++ b/v2/mobile/evidence/detail-state-2026-10-17/planner-tests.txt @@ -0,0 +1,36 @@ + Compiling icu_normalizer v2.2.0 + Compiling objc2-core-foundation v0.3.2 + Compiling objc2-foundation v0.3.2 + Compiling idna_adapter v1.2.2 + Compiling form_urlencoded v1.2.2 + Compiling idna v1.1.0 + Compiling url v2.5.8 + Compiling urlpattern v0.3.0 + Compiling tauri-utils v2.9.2 + Compiling objc2-app-kit v0.3.2 + Compiling objc2-web-kit v0.3.2 + Compiling tauri-build v2.6.2 + Compiling tao v0.35.3 + Compiling tauri v2.11.2 + Compiling wry v0.55.1 + Compiling tauri-runtime v2.11.2 + Compiling zeroize v1.8.2 + Compiling curve25519-dalek v4.1.3 + Compiling muda v0.19.2 + Compiling tauri-runtime-wry v2.11.2 + Compiling window-vibrancy v0.6.0 + Compiling tracing-core v0.1.36 + Compiling tracing v0.1.44 + Compiling ed25519-dalek v2.2.0 + Compiling shield-optimizer-core v2.3.0 (/Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/crates/core) + Finished `test` profile [unoptimized] target(s) in 17.53s + Running unittests src/lib.rs (target/debug/deps/shield_optimizer_core-db81ead7aa107d46) + +running 5 tests +test engine::optimize::tests::uninstall_downgraded_to_disable_when_not_reinstallable ... ok +test engine::optimize::tests::optimize_skips_uninstalled_and_disabled ... ok +test engine::optimize::tests::uninstall_method_chosen_when_optimize_and_installed ... ok +test engine::optimize::tests::restore_skips_enabled_and_uninstalled ... ok +test engine::optimize::tests::memory_map_propagated ... ok + +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 484 filtered out; finished in 0.00s diff --git a/v2/mobile/evidence/detail-state-2026-10-17/restore-enable-simulated.png b/v2/mobile/evidence/detail-state-2026-10-17/restore-enable-simulated.png new file mode 100644 index 00000000..61b951d0 Binary files /dev/null and b/v2/mobile/evidence/detail-state-2026-10-17/restore-enable-simulated.png differ diff --git a/v2/mobile/evidence/detail-state-2026-10-17/restore-enable-simulated.webm b/v2/mobile/evidence/detail-state-2026-10-17/restore-enable-simulated.webm new file mode 100644 index 00000000..04d65e70 Binary files /dev/null and b/v2/mobile/evidence/detail-state-2026-10-17/restore-enable-simulated.webm differ diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/.gitignore b/v2/mobile/evidence/diagnostics-header-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/diagnostics-header-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/README.md b/v2/mobile/evidence/diagnostics-header-2026-10-17/README.md new file mode 100644 index 00000000..bb213bb9 --- /dev/null +++ b/v2/mobile/evidence/diagnostics-header-2026-10-17/README.md @@ -0,0 +1,64 @@ +# Diagnostics header at narrow widths — simulated evidence + +Verified current remote base: `dccfe99a805ffcb0c11f32a4fa9f227306ba1cf3`. +Merged thermal PR188 (`e05891dfbd33bb513eafb6794dcfd03c41d19ba4`) and private-save/copy +PR190 are both present. Before header implementation, the combined base passed **293/293 mobile +tests**, Svelte check 0/0 and build. Active PRs were inspected; no competing header fix was open. + +## Reproduction and focused change + +The [new 320px regression failed on unchanged base](reproduction-before.txt): the single-row +header forced the page wider than its viewport. The separately tracked measurement was 304px of +header content inside 272px. + +Only Diagnostics-scoped CSS changes: the header can wrap, its title can take a separate row and +shrink/wrap within available width, and the Back/Find remote cluster stays together. Title and Live +font sizes use rem units at their original default 22px/11px sizes. Live now keeps at least 44×44px +of target area like the existing icon buttons. No controls, DOM order, accessible names, handlers, +report fields, data/units, shared-header styles, viewport directives or text-size/zoom restrictions +changed. At 384px/default text the header remains a single row; narrow or enlarged-text layouts +flow onto additional rows instead of hiding controls. + +Tests cover 320px/384px at 100%/200% root text; full-page/header overflow; all control bounds/target +sizes; exact scalable text sizes; unchanged control text/names and viewport zoom policy; keyboard +Tab order; Live pressed state and mocked health reads; Back navigation; Shield confirmation with +Cancel only; and non-Shield/disconnected conditional controls. The mock refuses any `find_remote` +IPC, and tests assert none occurred. + +## Evidence + +- [320px/default text](header-320-100-simulated.png) +- [384px/default text](header-384-100-simulated.png) +- [320px/200% root text](header-320-200-simulated.png) +- [384px/200% root text](header-384-200-simulated.png) +- [Live on](live-on-simulated.png) +- [Remote confirmation, canceled without ringing](remote-confirm-cancel-only-simulated.png) +- [Short layout/control recording](header-layout-controls-simulated.webm) + +PNG evidence and sampled video frames are visually inspected. Video remains 384px wide while +changing text size; the 320px states are captured separately to avoid recording resize padding. +All native replies are mocked; no physical device is connected or queried. Raw recordings are +ignored. No desktop gallery screens changed. + +From `v2/mobile`, with existing dependencies and Chromium: + +```sh +HEADER_EVIDENCE=1 node --test tests/diagnostics-header.test.mjs tests/health-presentation.test.mjs tests/playback-audio.test.mjs +npm test +npm run check +npm run build +``` + +Focused suite: 30 passing cases including optional capture; post-change Svelte check 0/0 and build +passed. The existing parent missing `.svelte-kit/tsconfig.json` startup notice remains. Root-font +simulation is not physical Android accessibility scaling or pinch verification. Native inset, +physical device, release/publication and app-store acceptance remain separate gates. No permission, +provider, dependency, device mutation, installation, merge or publication changes/actions. + +The existing Amphetamine timer observed after host recovery did not match the prior recorded +bounded-session timing. Its ownership was not assumed, so it was preserved; this task did not +start or end another wake session. + +Final validation: **298/298 full mobile tests and 30/30 focused cases including capture passed**, +with no failures or skips. Post-change Svelte check 0/0, build and diff check passed. The video +is 3.28 seconds. [Validation receipt](validation.json) includes baseline/final checks and hashes. diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/header-320-100-simulated.png b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-320-100-simulated.png new file mode 100644 index 00000000..1a5154ff Binary files /dev/null and b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-320-100-simulated.png differ diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/header-320-200-simulated.png b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-320-200-simulated.png new file mode 100644 index 00000000..e900a5d2 Binary files /dev/null and b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-320-200-simulated.png differ diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/header-384-100-simulated.png b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-384-100-simulated.png new file mode 100644 index 00000000..f88b99b8 Binary files /dev/null and b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-384-100-simulated.png differ diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/header-384-200-simulated.png b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-384-200-simulated.png new file mode 100644 index 00000000..0e5324d4 Binary files /dev/null and b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-384-200-simulated.png differ diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/header-layout-controls-simulated.webm b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-layout-controls-simulated.webm new file mode 100644 index 00000000..93236558 Binary files /dev/null and b/v2/mobile/evidence/diagnostics-header-2026-10-17/header-layout-controls-simulated.webm differ diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/live-on-simulated.png b/v2/mobile/evidence/diagnostics-header-2026-10-17/live-on-simulated.png new file mode 100644 index 00000000..65e9ebdb Binary files /dev/null and b/v2/mobile/evidence/diagnostics-header-2026-10-17/live-on-simulated.png differ diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/remote-confirm-cancel-only-simulated.png b/v2/mobile/evidence/diagnostics-header-2026-10-17/remote-confirm-cancel-only-simulated.png new file mode 100644 index 00000000..6a23b9d4 Binary files /dev/null and b/v2/mobile/evidence/diagnostics-header-2026-10-17/remote-confirm-cancel-only-simulated.png differ diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/reproduction-before.txt b/v2/mobile/evidence/diagnostics-header-2026-10-17/reproduction-before.txt new file mode 100644 index 00000000..0e6fa8e3 --- /dev/null +++ b/v2/mobile/evidence/diagnostics-header-2026-10-17/reproduction-before.txt @@ -0,0 +1,34 @@ +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ Diagnostics header fits 320px at 100% text with every control usable (1024.760042ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1321.092792 + +✖ failing tests: + +test at tests/diagnostics-header.test.mjs:43:3 +✖ Diagnostics header fits 320px at 100% text with every control usable (1024.760042ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + false !== true + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/diagnostics-header.test.mjs:45:12) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: 'strictEqual', + diff: 'simple' + } diff --git a/v2/mobile/evidence/diagnostics-header-2026-10-17/validation.json b/v2/mobile/evidence/diagnostics-header-2026-10-17/validation.json new file mode 100644 index 00000000..6fc1f5ea --- /dev/null +++ b/v2/mobile/evidence/diagnostics-header-2026-10-17/validation.json @@ -0,0 +1,42 @@ +{ + "base": "dccfe99a805ffcb0c11f32a4fa9f227306ba1cf3", + "merged_fixes_verified": [ + "e05891dfbd33bb513eafb6794dcfd03c41d19ba4", + "dccfe99a805ffcb0c11f32a4fa9f227306ba1cf3" + ], + "pre_change_base": { + "mobile_passed": 293, + "check_errors": 0, + "check_warnings": 0, + "build": "passed" + }, + "post_change": { + "mobile_passed": 298, + "failed": 0, + "skipped": 0, + "focused_with_capture": 30, + "check_errors": 0, + "check_warnings": 0, + "build": "passed", + "diff_check": "passed" + }, + "component_markup_and_script_identical_to_base": true, + "video_seconds": 3.28, + "sha256": { + "v2/mobile/src/screens/Diagnostics.svelte": "4fc0eacd1bfbf3785bc8075c31c9d57480e49dc84035fdaae6d911e43a24ea4f", + "v2/mobile/tests/diagnostics-header.test.mjs": "f722b54cafd51325d0b70438cef1b30ee4024f3ec99612df80806fed61867640", + "v2/mobile/evidence/diagnostics-header-2026-10-17/header-320-100-simulated.png": "1c00daea6ce1a5c52f0a47bfbf316f59a42efc89692ce91417eb4055eac18807", + "v2/mobile/evidence/diagnostics-header-2026-10-17/header-320-200-simulated.png": "32c330f1aaf0b116ce4848affe40fb080b791317ed127cac3f82d0b8534ff4c4", + "v2/mobile/evidence/diagnostics-header-2026-10-17/header-384-100-simulated.png": "c56c352d404e6b81fb9d9c6c67e8c5418af3368813dadb1b30f97191941770e8", + "v2/mobile/evidence/diagnostics-header-2026-10-17/header-384-200-simulated.png": "82095bfbac0afed871cb642bec110cc62a0424128828a8080e330c6435f04982", + "v2/mobile/evidence/diagnostics-header-2026-10-17/live-on-simulated.png": "7d176dd71aca36a440e205d6764933dbdf12b0f9697da36f7a815b1b6c35bcb4", + "v2/mobile/evidence/diagnostics-header-2026-10-17/remote-confirm-cancel-only-simulated.png": "ec9b39c088a85f91c281b1fb279a368ace11505113391ec837b512db1fde33d9", + "v2/mobile/evidence/diagnostics-header-2026-10-17/header-layout-controls-simulated.webm": "2a1e7083c60a798a766571fa01069c878b11942dd1e29c3ede0bdde1cf68ccf9" + }, + "limitations": [ + "Native replies mocked; no device operations or ringing command.", + "Root font scaling and static viewport policy checks are not physical Android accessibility/pinch verification.", + "Existing wake-session ownership uncertain; preserved without a new start or end.", + "Independent source review and current-head CI pending." + ] +} diff --git a/v2/mobile/evidence/entitlement-preview-20261016/README.md b/v2/mobile/evidence/entitlement-preview-20261016/README.md new file mode 100644 index 00000000..53b451e9 --- /dev/null +++ b/v2/mobile/evidence/entitlement-preview-20261016/README.md @@ -0,0 +1,19 @@ +# Isolated entitlement preview evidence + +[Simulated preview video](SIMULATED-entitlement-preview.mp4) shows a test-only component at +384×812. Pending, error and revoked states retain an enabled recovery control; purchase/restore +are blocked during pending. Tips remain disabled. The component is not mounted by the app. + +This is not a Play checkout, Android purchase, backend verification, real Pro grant or physical +TV recovery. The recovery callback in this preview only increments a test counter. Separately, +core mock-ADB tests exercise real existing re-enable/emergency recovery implementations after +downgrade, and a browser test exercises the actual More screen's Free recovery path. + +Reproduce from mobile: + +```sh +ENTITLEMENT_VIDEO_DIR=/tmp/entitlement-video node --test --test-name-pattern='simulated UI' tests/entitlement-preview.test.mjs +``` + +Playwright WebM was transcoded to H.264 MP4 without changing the test. The recorded controls +are a development presentation, not a finalized purchase UI or evidence of store readiness. diff --git a/v2/mobile/evidence/entitlement-preview-20261016/SIMULATED-entitlement-preview.mp4 b/v2/mobile/evidence/entitlement-preview-20261016/SIMULATED-entitlement-preview.mp4 new file mode 100644 index 00000000..1cd2bf6d Binary files /dev/null and b/v2/mobile/evidence/entitlement-preview-20261016/SIMULATED-entitlement-preview.mp4 differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/.gitignore b/v2/mobile/evidence/export-discoverability-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/export-discoverability-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/README.md b/v2/mobile/evidence/export-discoverability-2026-10-17/README.md new file mode 100644 index 00000000..aa3732d6 --- /dev/null +++ b/v2/mobile/evidence/export-discoverability-2026-10-17/README.md @@ -0,0 +1,87 @@ +# Private saves and clipboard-only reports — simulated evidence + +Original capture base: `83f69b77e2b19b643e21ae456d0f597e90ef7d83`. The branch now includes +current release base `557247e78357d4798aabc0d36fcfe45f81db52bc`, including merged modal, +Daydream and diagnosis work. Pending thermal PR188 is not included. This is an existing-flow +frontend clarification, not an export implementation. + +## Scope and retained behavior + +- Screenshot preview labels the returned private app location, distinguishes it from public + Gallery/Downloads and explains that Close only hides the preview. The native capture command, + returned path/base64 bytes, automatic save behavior and close state transition are unchanged. + Its close control now has the accessible name “Close screenshot preview”. +- Files' success toast, actual returned destination and session-only list explicitly describe private + app storage. No phone-file-browser, Files destination, Open or Share shortcut is promised. +- App-report, unknown-diagnostic JSON and debug-log descriptions identify clipboard text rather + than file save. The oversized-report GitHub-opening clipboard fallback is still described and + tested; report serialization, redaction, preview, clipboard/browser fallback and warnings remain. + +Only local layout adjustments needed for readable clarification/actions are included: screenshot +notes use scalable text/line spacing and word-aware path wrapping; the preview image uses border-box +sizing to keep its existing border inside the available width (the new test exposed a two-pixel +preview overflow); More's existing copy toolbar can wrap so Copy diagnostic JSON is not clipped. +No Diagnostics header styles or other general layout changes. The separately tracked 320px +Diagnostics header overflow remains open. + +No native provider/path, Android permission, security, SAF, Share, backend, dependency or export +formatter changes. Existing generated-provider configuration is untouched. + +## Tests and visuals + +New mocked tests cover screenshot success/error/close/recapture at 384px and 320px with 200% root text; +exact returned PNG bytes/path; no deletion/export IPC on Close; actual copied filename/path after +collision naming; failed file copy without false success; report preview/clipboard fidelity; long +GitHub-report copy fallback; failed clipboard fallback; diagnostic JSON and debug-log copy success/ +failure; and copy-toolbar wrapping at 320px/384px. + +All native replies, clipboard writes and browser-opening calls are stubbed. The screenshot fixture +is a canvas-generated PNG explicitly labeled “SIMULATED TV SCREEN”; no device is connected or queried. +This verifies UI behavior, not native Android persistence or an installed app. + +- [Private screenshot preview](screenshot-private-preview-simulated.png) +- [384px preview](preview-384-100-simulated.png) +- [320px / 200% text, top](preview-320-200-simulated.png) and [scroll tail](preview-320-200-tail-simulated.png) +- [Capture failure, without false saved state](screenshot-error-simulated.png) +- [Actual private file-copy destination](private-file-copy-simulated.png) +- [App-report copy-only description](copy-only-report-simulated.png) +- [Diagnostic JSON copy-only description](copy-only-diagnostic-json-simulated.png) +- [Debug-log copy-only description](copy-only-debug-log-simulated.png) +- [Short simulated interaction recording](private-save-copy-clarification-simulated.webm) + +PNG evidence and a sampled video frame are visually inspected. Raw intermediate recordings are +ignored. No desktop gallery screen changes. + +From `v2/mobile`, using existing dependencies and Chromium: + +```sh +EXPORT_COPY_EVIDENCE=1 node --test tests/export-discoverability.test.mjs tests/apps-catalog.test.mjs tests/files.test.mjs tests/unknownDiagnostics.test.mjs +npm test +npm run check +npm run build +``` + +The existing parent missing `.svelte-kit/tsconfig.json` startup notice remains. No physical save, +clipboard, browser, pinch/accessibility-font-scale or provider behavior is claimed. Independent +review and cross-OS CI remain pending; no release, merge, installation or device operation occurred. + +Final host validation: **282/282 full mobile cases and 37/37 focused cases including capture passed**, +with no failures or skips. Svelte check found 0 errors / 0 warnings; Vite build and diff check passed. +The video is5.44 seconds. [Validation receipt](validation.json) records source/artifact hashes. + +## Current release-base integration and review follow-up + +Normal merge `f035d3dd7526552502c32e6b2832bb642fa2f522` integrates verified base557247e7. +The checklist conflict retains every current row, including the separate320px header issue, and +combines the merged source-diagnosis link with this pending clarification status. Modal completion +is inherited intact. + +The one review correction changes the stale-toast absence assertion in `tests/files.test.mjs` +from the obsolete “this app's storage” wording to the actual “this app's private storage” wording. +All product UI source and the new clarification tests remain byte-for-byte unchanged from reviewed +`20cc8c4689924f859e752cb3b788f78fd5938bed`. Existing screenshots/video are retained from their +original capture base; this integration changes no captured product appearance. + +Validation on current release base557247e7: **290/290 combined mobile tests passed**, plus the +corrected stale-toast regression directly (1/1); Svelte check0/0, build and diff check passed. +Retained product-source and visual-artifact hashes match the reviewed receipt. diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-debug-log-simulated.png b/v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-debug-log-simulated.png new file mode 100644 index 00000000..4189d904 Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-debug-log-simulated.png differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-diagnostic-json-simulated.png b/v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-diagnostic-json-simulated.png new file mode 100644 index 00000000..6c169864 Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-diagnostic-json-simulated.png differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-report-simulated.png b/v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-report-simulated.png new file mode 100644 index 00000000..992beb73 Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-report-simulated.png differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/preview-320-200-simulated.png b/v2/mobile/evidence/export-discoverability-2026-10-17/preview-320-200-simulated.png new file mode 100644 index 00000000..2af72015 Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/preview-320-200-simulated.png differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/preview-320-200-tail-simulated.png b/v2/mobile/evidence/export-discoverability-2026-10-17/preview-320-200-tail-simulated.png new file mode 100644 index 00000000..424b7ada Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/preview-320-200-tail-simulated.png differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/preview-384-100-simulated.png b/v2/mobile/evidence/export-discoverability-2026-10-17/preview-384-100-simulated.png new file mode 100644 index 00000000..70d28ae0 Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/preview-384-100-simulated.png differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/private-file-copy-simulated.png b/v2/mobile/evidence/export-discoverability-2026-10-17/private-file-copy-simulated.png new file mode 100644 index 00000000..1dd917f6 Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/private-file-copy-simulated.png differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/private-save-copy-clarification-simulated.webm b/v2/mobile/evidence/export-discoverability-2026-10-17/private-save-copy-clarification-simulated.webm new file mode 100644 index 00000000..7de94672 Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/private-save-copy-clarification-simulated.webm differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/screenshot-error-simulated.png b/v2/mobile/evidence/export-discoverability-2026-10-17/screenshot-error-simulated.png new file mode 100644 index 00000000..f604cfb0 Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/screenshot-error-simulated.png differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/screenshot-private-preview-simulated.png b/v2/mobile/evidence/export-discoverability-2026-10-17/screenshot-private-preview-simulated.png new file mode 100644 index 00000000..70d28ae0 Binary files /dev/null and b/v2/mobile/evidence/export-discoverability-2026-10-17/screenshot-private-preview-simulated.png differ diff --git a/v2/mobile/evidence/export-discoverability-2026-10-17/validation.json b/v2/mobile/evidence/export-discoverability-2026-10-17/validation.json new file mode 100644 index 00000000..a143da8a --- /dev/null +++ b/v2/mobile/evidence/export-discoverability-2026-10-17/validation.json @@ -0,0 +1,61 @@ +{ + "base": "557247e78357d4798aabc0d36fcfe45f81db52bc", + "full_mobile": { + "passed": 290, + "failed": 0, + "skipped": 0 + }, + "focused_including_capture": 37, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "vite_build": "passed", + "diff_check": "passed", + "video_seconds": 5.44, + "sha256": { + "v2/mobile/src/screens/Dashboard.svelte": "29a9db50e764c48ec98d2de6d873c5d195ddffbf3feb86c64d4bbfedc3cc7b72", + "v2/mobile/src/screens/Files.svelte": "7ef9e0b82c6612cedf519439e0a8aac324a441bb8aa9673f2a6856d5276be2a0", + "v2/mobile/src/screens/More.svelte": "2998a7f94459a3979d7258bbcc3eeee0d91bab5d0566f0743f873deb5bbd8626", + "v2/mobile/src/components/AppReportSheet.svelte": "ac6e0d7e391cf5ab450179532a813fa2be63b1e227df8a8b696112c26cd4727a", + "v2/mobile/tests/export-discoverability.test.mjs": "73cd8cd84dd03472cf6ab98b869aaf650aeab9964ee5bb989d4062e3e0cc0ad6", + "v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-debug-log-simulated.png": "0b2ec849e86676d81927a15585ea1bf4d2020437ed2b8e4872663517eb24020b", + "v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-diagnostic-json-simulated.png": "6529c836227bcdefa3ee285d1a5436c083321f06560530e1074655cd5c5667cd", + "v2/mobile/evidence/export-discoverability-2026-10-17/copy-only-report-simulated.png": "b278a7b2a5f869b79fe9feded3e12de09f614ee31f3b97e2fd38c1bb2a39f770", + "v2/mobile/evidence/export-discoverability-2026-10-17/preview-320-200-simulated.png": "0bed812cb51d8571985b4832519f891120ad7f7580e53fb1766a4e80c4122fa6", + "v2/mobile/evidence/export-discoverability-2026-10-17/preview-320-200-tail-simulated.png": "345942fb630f768ef770482886f447befa8c8f7a9c8667ae1f5ef0c1bd812dae", + "v2/mobile/evidence/export-discoverability-2026-10-17/preview-384-100-simulated.png": "f5f6500f76690d54e7b21d4a720faf36bab85f6ba39674d28ec0deccc363e8a5", + "v2/mobile/evidence/export-discoverability-2026-10-17/private-file-copy-simulated.png": "e659042d9e8ac83c75bdade6cf27015135c0c787c56c2168d23b802c6b3149eb", + "v2/mobile/evidence/export-discoverability-2026-10-17/screenshot-error-simulated.png": "ceb02d345a8a93de9ff138fb8c225243a11661d607c9d4f69a0893a89fc2456e", + "v2/mobile/evidence/export-discoverability-2026-10-17/screenshot-private-preview-simulated.png": "f5f6500f76690d54e7b21d4a720faf36bab85f6ba39674d28ec0deccc363e8a5", + "v2/mobile/evidence/export-discoverability-2026-10-17/private-save-copy-clarification-simulated.webm": "6c8ddbb06b45d58d60ccac039911f91d78f77635171251783660682c344168a4", + "v2/mobile/tests/files.test.mjs": "790dca4f14aa6cf70e719168fa2d8ddba4b1be483fb14eb09259ca2848787a6a" + }, + "scope": [ + "Private-storage and clipboard-only wording, accessible screenshot close label.", + "Local screenshot-note/image fit and More copy-toolbar wrapping only.", + "No backend/provider/permission/SAF/Share/formatter changes." + ], + "limitations": [ + "Native replies, clipboard and browser opening are mocked; no device queries or installations.", + "Root-font enlargement is simulation, not physical Android accessibility verification.", + "Diagnostics 320px header overflow remains a separate issue.", + "Independent source review and cross-OS CI pending." + ], + "original_base": "83f69b77e2b19b643e21ae456d0f597e90ef7d83", + "original_full_mobile": { + "passed": 282, + "failed": 0, + "skipped": 0 + }, + "integration": { + "merge": "f035d3dd7526552502c32e6b2832bb642fa2f522", + "product_source_and_new_tests_unchanged_from": "20cc8c4689924f859e752cb3b788f78fd5938bed", + "test_maintenance": "files.test.mjs stale-toast absence assertion uses actual 'Copied old.zip into this app's private storage.'", + "targeted_stale_toast_test": "1 passed", + "retained_hashes_revalidated": true, + "check": "0 errors / 0 warnings", + "build": "passed", + "diff_check": "passed" + } +} diff --git a/v2/mobile/evidence/launcher-curation-2026-10-23/README.md b/v2/mobile/evidence/launcher-curation-2026-10-23/README.md new file mode 100644 index 00000000..cc7388e5 --- /dev/null +++ b/v2/mobile/evidence/launcher-curation-2026-10-23/README.md @@ -0,0 +1,9 @@ +# Mobile launcher curation — simulated evidence + +The shared catalog correction from PR220 is merged normally onto mobile base `9e8b0543abff01a332e7a7caac71fecb32edce41`, preserving PR218 and the contributor’s phone gallery commit. No mobile launcher command, safety or lifecycle code changes. + +`launcher-simulated.png` and `launcher-simulated.webm` capture the existing mobile screenshot harness at 412×915, with a visible simulation label added before recording. Fixtures only; no real device queried or changed. The full phone contact sheet and gallery are regenerated through `npm run screenshots` from `v2/mobile/`. + +The seven pre209 custom entries remain curated. The removed shared package is no longer offered as a missing recommendation. Generic installed HOME support is unchanged and covered by the shared Rust catalog/row regression and desktop browser installed/disabled/current fixture matrix in `v2/evidence/launcher-curation-2026-10-23/`. + +Physical phone/TV acceptance and PR163 release gates remain open. The preexisting mobile tooling base-config warning is still nonfatal; the standalone build regression preserves PR218’s fix. diff --git a/v2/mobile/evidence/launcher-curation-2026-10-23/launcher-simulated.png b/v2/mobile/evidence/launcher-curation-2026-10-23/launcher-simulated.png new file mode 100644 index 00000000..9a6d8773 Binary files /dev/null and b/v2/mobile/evidence/launcher-curation-2026-10-23/launcher-simulated.png differ diff --git a/v2/mobile/evidence/launcher-curation-2026-10-23/launcher-simulated.webm b/v2/mobile/evidence/launcher-curation-2026-10-23/launcher-simulated.webm new file mode 100644 index 00000000..b8fa7735 Binary files /dev/null and b/v2/mobile/evidence/launcher-curation-2026-10-23/launcher-simulated.webm differ diff --git a/v2/mobile/evidence/launcher-protection-integration-2026-10-19/README.md b/v2/mobile/evidence/launcher-protection-integration-2026-10-19/README.md new file mode 100644 index 00000000..9affc84d --- /dev/null +++ b/v2/mobile/evidence/launcher-protection-integration-2026-10-19/README.md @@ -0,0 +1,27 @@ +# Mobile integration of reviewed PR206 + +Mobile base: `d388cad1e3da9c17e0a5c0bdeec52022fd8d9970`. Incoming main: `c78176d5ea640a0e230633a3f0b0bc0720c01af4`, the squash merge of reviewed PR206 head `ef72305198af19d7457b97e02cd71cb00b00f7f5`. A normal merge applied without conflicts, preserving both branch histories. This is an integration draft, not a claim that the default mobile branch has merged it. + +## Exact scope and preservation + +Shared launcher production code is byte-identical to reviewed main. Its only whole-file difference from main is mobile's preexisting setup-helper rollback test. Mobile `src`, `src-tauri` and `tauri-plugin-atv-adb` trees are unchanged from the mobile base. Existing desktop/shared differences on mobile are retained; this integration does not overwrite them with main's versions. Imported PR206 test and evidence files are byte-identical to main. + +The README additionally replaces the absolute assertion that missing devices must be a discovery/pairing issue: firmware must expose usable ADB, authorize the connection and provide network reachability; version alone establishes no compatibility guarantee. No workaround, settings change, new safety classification or Fire TV support removal is introduced. + +Release documentation records merged PR204, its [final independent review](https://github.com/bryanroscoe/shield_optimizer/pull/204#issuecomment-6235587609) and [six passing exact-head CI jobs](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39043175482). PR204's original local evidence body is retained, with its pending sentence explicitly dated to that original run and a subsequent completion receipt appended. Raw receipts, 343-test provenance and screenshots/video remain unchanged. PR202/203 completion and physical release gates remain intact. + +The parent handoff confirms independent source approval of PR206's exact head; [its six CI jobs passed](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39185141432), and [PR206](https://github.com/bryanroscoe/shield_optimizer/pull/206) merged to main as above. Its public automated review summary still said running when inspected, so that summary is not claimed complete. Those receipts establish PR206's source branch status, not validation of this new integration head. + +## Combined local validation + +Workspace formatting check and all-target Clippy with warnings denied pass (`clippy.txt`). Complete serial workspace tests pass (`workspace-tests.txt`): core 493, desktop 109 with one preexisting live test ignored, mobile Rust 26, license 2 and registry 9; doc tests pass. This includes the imported explicit-protection/unrelated-error/rollback/Home tests and existing mobile helper coverage. Test counts reflect this combined mobile tree, rather than overwriting PR206's historical counts. + +The device-free message regression passes on the combined tree (`message-test.txt`), using the unchanged serialized mock result, production wrapping rule and existing cached Playwright 1.62.1 / Chromium headless shell 1223. No device command is dispatched. + +Rust ran on macOS with existing offline dependencies, two jobs, incremental off, and dev/test debug info off, reusing this task's previous target cache. Local frontend type checks/builds, full browser/E2E suites and Android APK compilation were not run: this isolated checkout has no installed frontend dependencies, and the previously available Vite/Svelte cache remains offloaded (`compressed,dataless`). No installation or hydration workaround was attempted. Fresh exact-head CI is required to establish frontend/cross-platform success for this integration; prior PR204/206 CI is not substituted for it. Independent review and the new draft's checks remain the merge gate. + +## Nonvisual integration rationale + +No new UI markup or styles changed relative to reviewed PR206, and the mobile frontend is untouched. The shared message is already covered by [PR206's labeled simulated screenshots and short video](../../../evidence/firetv-protection-205-20261019/README.md); these are preserved as original message-fixture evidence, not recaptured or claimed as native/app acceptance. The only additional user-facing content is README text. New screenshots/video and gallery regeneration would add no new integration evidence, so they are not generated. + +Reporter Fire OS/build diagnostics, real protected-package behavior, Android scaling/touch/lifecycle, native export, shared-client/updater, signing/store/release and other physical acceptance gates remain open. No device queries/mutations, installs, security changes, paid calls, releases or merge to a shared branch occurred. diff --git a/v2/mobile/evidence/launcher-protection-integration-2026-10-19/clippy.txt b/v2/mobile/evidence/launcher-protection-integration-2026-10-19/clippy.txt new file mode 100644 index 00000000..95a719e5 --- /dev/null +++ b/v2/mobile/evidence/launcher-protection-integration-2026-10-19/clippy.txt @@ -0,0 +1,8 @@ + Checking shield-optimizer-core v2.3.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/crates/core) + Compiling tauri-plugin-atv-adb v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/mobile/tauri-plugin-atv-adb) + Compiling shield-optimizer-v2 v2.3.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/src-tauri) + Compiling atv-optimizer-mobile v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/mobile/src-tauri) + Checking adb_client v3.2.2 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/vendor/adb_client) + Checking atvopt-license v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/tools/atvopt-license) + Checking registry-triage v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/tools/registry-triage) + Finished `dev` profile [unoptimized] target(s) in 22.66s diff --git a/v2/mobile/evidence/launcher-protection-integration-2026-10-19/message-test.txt b/v2/mobile/evidence/launcher-protection-integration-2026-10-19/message-test.txt new file mode 100644 index 00000000..7662094b --- /dev/null +++ b/v2/mobile/evidence/launcher-protection-integration-2026-10-19/message-test.txt @@ -0,0 +1 @@ +Protected-package message fixture: exact error text, separate restore/Home outcomes, raw diagnostics and wrapping at 980/384px and 200% text pass. No app/device calls. diff --git a/v2/mobile/evidence/launcher-protection-integration-2026-10-19/workspace-tests.txt b/v2/mobile/evidence/launcher-protection-integration-2026-10-19/workspace-tests.txt new file mode 100644 index 00000000..ea5f6378 --- /dev/null +++ b/v2/mobile/evidence/launcher-protection-integration-2026-10-19/workspace-tests.txt @@ -0,0 +1,719 @@ + Compiling shield-optimizer-core v2.3.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/crates/core) + Compiling tauri-plugin-atv-adb v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/mobile/tauri-plugin-atv-adb) + Compiling atv-optimizer-mobile v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/mobile/src-tauri) + Compiling shield-optimizer-v2 v2.3.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/src-tauri) + Compiling adb_client v3.2.2 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/vendor/adb_client) + Compiling atvopt-license v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/tools/atvopt-license) + Compiling registry-triage v0.1.0 (/Users/bryanroscoe/Developer/shield-mobile-206-20261019/v2/tools/registry-triage) + Finished `test` profile [unoptimized] target(s) in 39.79s + Running unittests src/lib.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/atv_optimizer_mobile_lib-354c841cea3930c4) + +running 26 tests +test file_commands::tests::complete_bundle_round_trips ... ok +test file_commands::tests::concurrent_downloads_publish_distinct_complete_files ... ok +test file_commands::tests::delete_target_must_be_a_top_level_backup ... ok +test file_commands::tests::download_names_dedupe_instead_of_clobbering ... ok +test file_commands::tests::failed_download_removes_partial_output_and_preserves_existing_files ... ok +test file_commands::tests::parses_every_split_apk_path ... ok +test file_commands::tests::rejects_unsafe_reported_apk_path ... ok +test file_commands::tests::restore_path_must_stay_inside_backup_root ... ok +test license_tests::persisted_valid_license_round_trips ... ok +test license_tests::persistence_failure_is_reported ... ok +test limited_writer::tests::preserves_destination_errors ... ok +test limited_writer::tests::rejects_growth_without_writing_past_the_limit ... ok +test remote_lifecycle::tests::current_background_epoch_can_claim_cleanup_once ... ok +test remote_lifecycle::tests::newer_suspend_invalidates_an_older_timer ... ok +test remote_lifecycle::tests::resume_invalidates_pending_cleanup ... ok +test scrcpy_resource::tests::embedded_server_matches_audited_release ... ok +test scrcpy_resource::tests::materialize_creates_and_repairs_server ... ok +test wireless_adb::tests::canceled_handshakes_cannot_publish_or_supersede_a_retry ... ok +test wireless_adb::tests::connect_timeout_explains_the_tv_approval_prompt ... ok +test wireless_adb::tests::installs_get_the_slow_shell_timeout ... ok +test wireless_adb::tests::only_socket_level_errors_are_fatal ... ok +test wireless_adb::tests::pairing_codes_are_normalized_before_the_one_allowed_guess ... ok +test wireless_adb::tests::pairing_errors_name_the_next_step ... ok +test wireless_adb::tests::refused_connect_explains_network_debugging ... ok +test wireless_adb::tests::requested_serial_must_match_active_connection ... ok +test wireless_commands::tests::find_remote_requires_a_successful_shell_result ... ok + +test result: ok. 26 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.20s + + Running unittests src/main.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/atv_optimizer_mobile-f9318574c2dbaf8e) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running unittests src/main.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/atvopt_license-ae39b7a189fbf8a8) + +running 2 tests +test tests::private_key_file_round_trips ... ok +test tests::rejects_a_key_file_that_is_not_hex ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running unittests src/main.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/registry_triage-8414bee1b54060de) + +running 9 tests +test tests::a_batch_aggregates_by_package_and_reports_today_without_promoting ... ok +test tests::a_desktop_app_report_is_read_and_never_promotes_the_app ... ok +test tests::a_truncated_export_is_named_and_its_counts_are_lower_bounds ... ok +test tests::desktop_bundles_yield_home_handlers_and_never_the_serial ... ok +test tests::mobile_records_that_break_the_contract_are_rejected ... ok +test tests::overlapping_folders_count_each_report_once ... ok +test tests::the_verdict_names_the_source_that_decided_it ... ok +test tests::tokens_follow_the_mobile_collector_rules ... ok +test tests::triage_leaves_the_report_folder_untouched ... ok + +test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running unittests src/lib.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/shield_optimizer_core-29c4a2a32901dc1d) + +running 493 tests +test adb::batch::tests::checked_batch_records_each_real_shell_exit_even_if_the_last_succeeds ... ok +test adb::batch::tests::checked_reads_reject_failed_or_missing_required_sections ... ok +test adb::batch::tests::empty_batch_is_empty ... ok +test adb::batch::tests::empty_sections_stay_empty_and_keep_position ... ok +test adb::batch::tests::extra_sections_beyond_n_are_dropped ... ok +test adb::batch::tests::joins_with_semicolons_and_discards_stderr ... ok +test adb::batch::tests::preserves_interior_indentation_and_blank_lines ... ok +test adb::batch::tests::round_trips_a_realistic_batch ... ok +test adb::batch::tests::single_command_gets_no_separator ... ok +test adb::batch::tests::splits_into_exactly_n_sections ... ok +test adb::batch::tests::tolerates_sentinel_at_the_very_end ... ok +test adb::batch::tests::truncated_output_pads_with_empty_sections ... ok +test adb::driver::bounded_shell_tests::drivers_without_bounded_execution_are_unsupported ... ok +test adb::lifetimes::tests::a_closed_counter_refuses_new_leases_but_keeps_counting_old_ones ... ok +test adb::lifetimes::tests::a_closed_snapshot_refuses_every_spawning_method ... ok +test adb::lifetimes::tests::a_leased_snapshot_forwards_every_method_to_its_own_counterpart ... ok +test adb::lifetimes::tests::processes::a_child_dropped_with_no_runtime_to_reap_it_stays_counted ... ok +test adb::lifetimes::tests::processes::a_dropped_child_is_reaped_before_its_lease_goes ... ok +test adb::lifetimes::tests::processes::kill_and_reap_releases_once_the_process_is_gone ... ok +test adb::lifetimes::tests::processes::waiting_out_the_process_releases_it ... ok +test adb::lifetimes::tests::waiting_ends_when_the_last_lease_goes ... ok +test adb::lifetimes::tests::waiting_gives_up_at_the_deadline ... ok +test adb::parse::tests::a_bracketed_ipv6_transport_is_a_network_connection ... ok +test adb::parse::tests::audio_device_missing_returns_none ... ok +test adb::parse::tests::display_mode_sdr_only_when_hdr_list_empty ... ok +test adb::parse::tests::display_modes_degrade_to_empty_rather_than_guessing ... ok +test adb::parse::tests::display_reports_scope_modes_and_hdr_to_the_same_default_record ... ok +test adb::parse::tests::duplicate_modes_collapse_but_keep_the_active_flag ... ok +test adb::parse::tests::empty_mdns_output_is_not_an_error ... ok +test adb::parse::tests::hardware_properties_temp_none_when_empty ... ok +test adb::parse::tests::hardware_reading_pairs_limits_by_family_index_and_exact_length ... ok +test adb::parse::tests::ignores_header_and_blank_lines ... ok +test adb::parse::tests::instance_serial_rejects_malformed_or_unknown_names ... ok +test adb::parse::tests::ipv6_endpoint_is_bracketed_like_the_adb_transport_key ... ok +test adb::parse::tests::ls_parse_returns_empty_on_error_output ... ok +test adb::parse::tests::mdns_endpoint_keeps_an_ipv6_host_intact ... ok +test adb::parse::tests::mdns_wireless_debugging_serial_is_network_not_usb ... ok +test adb::parse::tests::meminfo_returns_empty_when_section_missing ... ok +test adb::parse::tests::multiple_displays_without_a_unique_default_are_unavailable ... ok +test adb::parse::tests::net_dev_preserves_interfaces_and_skips_loopback ... ok +test adb::parse::tests::net_dev_with_no_usable_interfaces_is_empty ... ok +test adb::parse::tests::one_eligible_display_does_not_prove_the_current_default ... ok +test adb::parse::tests::package_for_process_strips_only_a_subprocess_suffix ... ok +test adb::parse::tests::pairing_service_is_not_offered_as_a_connect_endpoint ... ok +test adb::parse::tests::parses_active_audio_device ... ok +test adb::parse::tests::parses_device_list_with_mixed_states ... ok +test adb::parse::tests::parses_df_data_storage ... ok +test adb::parse::tests::parses_df_data_storage_when_filesystem_name_wraps ... ok +test adb::parse::tests::parses_display_mode_shield_4k60_hdr10 ... ok +test adb::parse::tests::parses_every_display_mode_and_flags_the_active_one ... ok +test adb::parse::tests::parses_hardware_properties_temp_from_cpu_and_gpu ... ok +test adb::parse::tests::parses_ls_rows_including_spaced_names_and_symlinks ... ok +test adb::parse::tests::parses_meminfo_summary_kb ... ok +test adb::parse::tests::parses_multiple_hdr_types ... ok +test adb::parse::tests::parses_permission_grant_state ... ok +test adb::parse::tests::parses_pm_list_packages_output ... ok +test adb::parse::tests::parses_real_mdns_services_output ... ok +test adb::parse::tests::parses_real_shield_df_output ... ok +test adb::parse::tests::parses_thermal_max_temp ... ok +test adb::parse::tests::parses_thermal_rejects_garbage_values ... ok +test adb::parse::tests::parses_total_pss_section_and_sums_per_package ... ok +test adb::parse::tests::parses_usage_stats_last_used_and_never_opened ... ok +test adb::parse::tests::path_named_processes_are_kept_not_dropped ... ok +test adb::parse::tests::pm_list_packages_skips_garbage_lines ... ok +test adb::parse::tests::proc_stat_counts_iowait_as_idle ... ok +test adb::parse::tests::proc_stat_ignores_per_core_lines_and_missing_input ... ok +test adb::parse::tests::proc_stat_rejects_invalid_or_overflowing_counters ... ok +test adb::parse::tests::real_meminfo_keeps_every_row_with_its_full_name_and_pid ... ok +test adb::parse::tests::real_meminfo_package_map_never_trims_a_hal_into_a_package ... ok +test adb::parse::tests::real_top_names_only_yield_candidates_of_package_shape ... ok +test adb::parse::tests::repeated_single_display_records_do_not_create_ambiguity ... ok +test adb::parse::tests::thermal_current_hal_wins_equal_temperature_with_new_status ... ok +test adb::parse::tests::thermal_current_hal_wins_even_when_cache_is_hotter ... ok +test adb::parse::tests::thermal_empty_current_section_does_not_resurrect_cache_but_legacy_falls_back ... ok +test adb::parse::tests::thermal_reading_preserves_sensor_context_and_excludes_non_celsius_objects ... ok +test adb::parse::tests::thermal_unknown_context_and_invalid_status_never_become_cpu_or_normal ... ok +test adb::parse::tests::unparseable_mdns_rows_are_dropped_not_guessed ... ok +test adb::remote_input::tests::android_resident_server_command_is_exact ... ok +test adb::remote_input::tests::format_scid_is_8_hex_digits_with_high_bit_cleared ... ok +test adb::remote_input::tests::forward_and_push_args_are_well_formed ... ok +test adb::remote_input::tests::inject_keycode_down_select_is_exact_14_bytes ... ok +test adb::remote_input::tests::inject_keycode_up_dpad_with_repeat_and_meta ... ok +test adb::remote_input::tests::inject_text_clamps_to_300_chars ... ok +test adb::remote_input::tests::inject_text_encodes_type_len_then_utf8 ... ok +test adb::remote_input::tests::inject_text_length_is_utf8_byte_count_not_char_count ... ok +test adb::remote_input::tests::pick_free_local_port_returns_a_usable_port ... ok +test adb::remote_input::tests::server_spawn_args_match_phase0_launch_line ... ok +test adb::remote_input::tests::start_fails_when_driver_cannot_spawn ... ok +test adb::sim::replay::tests::a_recorded_cached_limit_survives_a_generated_profile ... ok +test adb::sim::replay::tests::batches_split_back_into_reads ... ok +test adb::sim::replay::tests::pins_compare_redacted ... ok +test adb::sim::replay::tests::polls_stay_in_their_phase_and_u_packages_survive ... ok +test adb::sim::replay::tests::reads_may_swap_but_writes_keep_their_order ... ok +test adb::sim::replay::tests::remote_ports_and_scids_match_by_shape_and_extra_calls_diverge ... ok +test adb::sim::replay::tests::replays_in_order_and_reports_divergence ... ok +test adb::sim::shell::tests::globs ... ok +test adb::sim::shell::tests::last_command_status_wins_and_status_markers_expand ... ok +test adb::sim::shell::tests::quotes_and_and_or ... ok +test adb::sim::tests::a_phone_answering_no_to_leanback_still_profiles ... ok +test adb::sim::tests::a_replayed_screenshot_is_a_png ... ok +test adb::sim::tests::disable_setup_helper_is_guarded_and_verified ... ok +test adb::sim::tests::faults_fire_on_one_batched_section ... ok +test adb::sim::tests::forget_drops_every_transport_of_one_device ... ok +test adb::sim::tests::google_tv_takeover_disables_setup_wraith_with_stock ... ok +test adb::sim::tests::google_tv_takeover_failure_re_enables_stock_and_wraith ... ok +test adb::sim::tests::google_tv_without_the_paired_disable_wraith_takes_home_back ... ok +test adb::sim::tests::launcher_rows_are_home_handlers_and_switching_moves_home ... ok +test adb::sim::tests::setup_wraith_outranking_the_role_holder_is_not_the_current_launcher ... ok +test adb::sim::tests::shield_profile_lists_as_a_shield ... ok +test adb::sim::tests::shield_takeover_is_unchanged ... ok +test adb::sim::tests::takeover_without_setup_wraith_never_touches_it ... ok +test adb::sim::tests::the_background_process_limit_key_does_not_move_the_real_limit ... ok +test adb::sim::tests::unknown_commands_are_named ... ok +test adb::storage::tests::a_misaligned_column_is_dropped_not_guessed ... ok +test adb::storage::tests::a_negative_size_is_unknown_not_zero ... ok +test adb::storage::tests::diskstats_data_includes_cache_on_real_output ... ok +test adb::storage::tests::diskstats_reads_every_column_for_a_package ... ok +test adb::storage::tests::diskstats_without_a_package_table_is_unavailable ... ok +test adb::storage::tests::pm_path_and_stat_sum_a_split_apk ... ok +test adb::storage::tests::pm_path_refuses_shell_metacharacters ... ok +test adb::storage::tests::stat_short_of_one_size_per_file_is_unavailable ... ok +test commands::apps::tests::a_process_name_never_inherits_a_catalog_verdict ... ok +test commands::apps::tests::a_process_that_looks_protected_is_still_flagged ... ok +test commands::apps::tests::an_unreadable_handler_list_does_not_block_disabling ... ok +test commands::apps::tests::app_apk_size_refuses_an_invalid_package ... ok +test commands::apps::tests::app_apk_size_sums_the_split_apks ... ok +test commands::apps::tests::app_memory_map_parses_running_processes ... ok +test commands::apps::tests::app_memory_map_without_a_pss_table_is_an_error_not_empty ... ok +test commands::apps::tests::app_permission_state_missing_when_not_listed ... ok +test commands::apps::tests::app_permission_state_reads_grant ... ok +test commands::apps::tests::app_storage_map_reads_diskstats ... ok +test commands::apps::tests::app_storage_map_without_a_table_is_an_error_not_empty ... ok +test commands::apps::tests::app_usage_map_reports_last_used ... ok +test commands::apps::tests::app_usage_map_without_rows_is_an_error_not_empty ... ok +test commands::apps::tests::decodes_not_installed_for_user ... ok +test commands::apps::tests::decodes_protected_system_app ... ok +test commands::apps::tests::disabled_read_failure_never_becomes_enabled_state ... ok +test commands::apps::tests::disabling_a_launcher_is_allowed_while_another_remains ... ok +test commands::apps::tests::disabling_the_only_launcher_is_refused_from_the_app_list ... ok +test commands::apps::tests::free_entitlement_blocks_appops_before_adb ... ok +test commands::apps::tests::free_entitlement_blocks_permission_writes_before_adb ... ok +test commands::apps::tests::free_user_can_reenable_a_package_after_downgrade ... ok +test commands::apps::tests::installed_view_includes_catalog_and_other_packages_without_changing_other_view ... ok +test commands::apps::tests::known_names_never_change_a_verdict ... ok +test commands::apps::tests::list_installed_packages_errors_when_the_primary_listing_is_empty ... ok +test commands::apps::tests::list_other_packages_attaches_known_friendly_names ... ok +test commands::apps::tests::list_other_packages_errors_when_the_primary_listing_is_empty ... ok +test commands::apps::tests::package_states_categorizes_from_one_batched_call ... ok +test commands::apps::tests::package_states_errors_when_the_installed_section_is_empty ... ok +test commands::apps::tests::play_installed_vendor_packages_read_as_third_party ... ok +test commands::apps::tests::safety_info_consults_the_shipped_catalog ... ok +test commands::apps::tests::set_app_permission_rejects_injection ... ok +test commands::apps::tests::set_app_permission_revoke_succeeds_silently ... ok +test commands::apps::tests::unknown_is_not_the_answer_for_curated_apps ... ok +test commands::apps::tests::unrecognized_returns_none ... ok +test commands::devices::tests::a_bracketed_ipv6_transport_still_counts_as_advertised ... ok +test commands::devices::tests::a_collapsed_alias_resolves_to_its_row_by_hardware_id ... ok +test commands::devices::tests::a_device_that_answers_no_to_leanback_is_still_profiled ... ok +test commands::devices::tests::a_disconnected_twin_does_not_merge_with_a_live_one ... ok +test commands::devices::tests::a_shell_that_prints_nothing_is_still_a_failure ... ok +test commands::devices::tests::a_stale_advertisement_pointing_at_another_attached_device_is_refused ... ok +test commands::devices::tests::a_usb_serial_is_never_displaced_by_an_address ... ok +test commands::devices::tests::an_alias_without_a_verified_id_resolves_to_nothing ... ok +test commands::devices::tests::an_attached_transport_is_accepted_only_when_its_serial_matches ... ok +test commands::devices::tests::an_identified_device_never_absorbs_an_unidentified_one ... ok +test commands::devices::tests::an_offline_alias_does_not_resolve_to_a_live_twin ... ok +test commands::devices::tests::connect_paired_drops_a_different_device ... ok +test commands::devices::tests::connect_paired_keeps_the_device_whose_serial_matches ... ok +test commands::devices::tests::connect_paired_refuses_without_a_parseable_serial ... ok +test commands::devices::tests::device_name_quoting_and_validation ... ok +test commands::devices::tests::device_profile_answers_for_a_collapsed_alias ... ok +test commands::devices::tests::devices_without_an_id_are_never_merged_into_each_other ... ok +test commands::devices::tests::different_hardware_ids_stay_separate_however_alike_the_addresses_look ... ok +test commands::devices::tests::every_property_read_maps_to_a_field ... ok +test commands::devices::tests::explicit_connection_port_is_used_instead_of_the_pairing_port ... ok +test commands::devices::tests::failed_pair_remains_a_failure_and_does_not_connect ... ok +test commands::devices::tests::forget_drops_every_transport_of_the_same_hardware ... ok +test commands::devices::tests::forget_never_matches_an_alias_on_address_or_missing_id ... ok +test commands::devices::tests::forget_reports_a_disconnect_adb_refused ... ok +test commands::devices::tests::forget_says_plainly_when_adb_will_reattach_it ... ok +test commands::devices::tests::invalid_pairing_pin_fails_before_adb ... ok +test commands::devices::tests::list_devices_impl_parses_authorized_and_unauthorized ... ok +test commands::devices::tests::lost_socket_during_profiling_is_not_an_authorized_device ... ok +test commands::devices::tests::multiline_settings_exception_stays_inside_its_own_section ... ok +test commands::devices::tests::normalize_accepts_a_scoped_ipv6_endpoint_from_discovery ... ok +test commands::devices::tests::normalize_accepts_an_mdns_service_name_from_discovery ... ok +test commands::devices::tests::normalize_accepts_bare_ip ... ok +test commands::devices::tests::normalize_accepts_ip_and_port ... ok +test commands::devices::tests::normalize_keeps_a_bracketed_ipv6_host_whole ... ok +test commands::devices::tests::normalize_rejects_bad_port ... ok +test commands::devices::tests::normalize_rejects_empty ... ok +test commands::devices::tests::normalize_rejects_non_ipv4 ... ok +test commands::devices::tests::normalize_still_rejects_things_that_are_not_endpoints ... ok +test commands::devices::tests::normalize_trims_whitespace ... ok +test commands::devices::tests::one_device_reached_two_ways_collapses_to_the_usable_address ... ok +test commands::devices::tests::pair_records_the_instance_advertised_on_that_pairing_port ... ok +test commands::devices::tests::pairing_address_requires_the_tv_supplied_port ... ok +test commands::devices::tests::pairing_instance_is_the_service_on_that_exact_pairing_port ... ok +test commands::devices::tests::placeholder_hardware_ids_are_not_identities ... ok +test commands::devices::tests::probe_brackets_an_ipv6_connect_endpoint_and_sees_it_attached ... ok +test commands::devices::tests::probe_does_not_guess_between_two_connect_ports ... ok +test commands::devices::tests::probe_matches_the_connect_service_by_its_embedded_serial ... ok +test commands::devices::tests::probe_never_takes_another_device_on_the_same_address ... ok +test commands::devices::tests::probe_picks_the_paired_serial_when_two_connect_services_share_an_ip ... ok +test commands::devices::tests::probe_reports_a_transport_adb_already_attached ... ok +test commands::devices::tests::probe_waits_until_the_connect_service_appears ... ok +test commands::devices::tests::probe_with_a_malformed_pairing_instance_claims_nothing ... ok +test commands::devices::tests::probe_without_an_instance_claims_nothing ... ok +test commands::devices::tests::silent_device_name_read_does_not_shift_the_android_version ... ok +test commands::devices::tests::successful_pair_establishes_trust_without_connecting_to_5555 ... ok +test commands::devices::tests::the_property_batch_exits_zero_when_the_last_read_says_no ... ok +test commands::devices::tests::the_service_name_survives_when_it_is_the_only_transport ... ok +test commands::files::tests::a_failed_call_or_a_cut_off_stream_is_unsearched ... ok +test commands::files::tests::accepts_sdcard_paths ... ok +test commands::files::tests::delete_is_confined_and_quoted ... ok +test commands::files::tests::delete_reports_device_side_failures ... ok +test commands::files::tests::find_patterns_validated ... ok +test commands::files::tests::find_rejects_bad_dirs_before_touching_the_device ... ok +test commands::files::tests::hits_are_capped_and_deduplicated ... ok +test commands::files::tests::missing_directories_are_no_matches_not_unsearched ... ok +test commands::files::tests::power_user_mode_allows_system_paths_but_keeps_injection_guards ... ok +test commands::files::tests::protected_paths_are_never_deletable ... ok +test commands::files::tests::rejects_escapes_and_system_paths ... ok +test commands::health::tests::a_failed_batch_preserves_the_transport_error ... ok +test commands::health::tests::codec_glob_preserves_readable_xml_among_missing_paths ... ok +test commands::health::tests::health_report_prefers_current_hardware_to_cached_only_then_marks_cached_fallback ... ok +test commands::health::tests::health_report_reads_every_section_from_one_batched_call ... ok +test commands::health::tests::health_report_retains_status_and_matching_fallback_limits_without_new_reads ... ok +test commands::health::tests::health_report_uses_current_hal_context_instead_of_cached_ties ... ok +test commands::health::tests::media_report_decodes_every_section_from_one_round_trip ... ok +test commands::health::tests::media_report_errors_when_the_device_returns_nothing ... ok +test commands::health::tests::media_report_keeps_codecs_when_optional_display_read_fails ... ok +test commands::health::tests::media_report_rejects_failed_and_truncated_settings_reads ... ok +test commands::health::tests::media_report_survives_an_unreadable_codec_file ... ok +test commands::health::tests::media_report_treats_a_null_setting_as_unset ... ok +test commands::health::tests::one_empty_section_does_not_blank_the_rest_of_the_report ... ok +test commands::health::tests::proc_meminfo_fallback_needs_no_extra_round_trip ... ok +test commands::health::tests::resource_sample_degrades_when_proc_is_unreadable ... ok +test commands::health::tests::resource_sample_derives_rates_from_two_device_side_reads ... ok +test commands::health::tests::resource_sample_keeps_tunnels_separate_and_missing_interfaces_unknown ... ok +test commands::health::tests::resource_sample_rejects_invalid_cpu_deltas_and_elapsed_time ... ok +test commands::health::tests::resource_sample_rejects_truncated_batches ... ok +test commands::health::tests::resource_sample_reports_none_rather_than_a_bogus_spike ... ok +test commands::health::tests::the_codec_glob_covers_the_path_a_real_shield_uses ... ok +test commands::health::tests::top_memory_keeps_each_process_whole ... ok +test commands::health::tests::truncated_batch_only_blanks_the_sections_that_never_arrived ... ok +test commands::home_tracking::tests::record_then_prune_round_trip ... ok +test commands::home_tracking::tests::unreadable_or_missing_file_degrades_to_empty ... ok +test commands::input::tests::encodes_spaces_as_percent_s ... ok +test commands::input::tests::quotes_shell_specials ... ok +test commands::input::tests::rejects_unicode_control_and_empty ... ok +test commands::input::tests::remote_keys_map_to_android_keycodes ... ok +test commands::launcher::tests::a_failed_switch_records_every_stage_it_went_through ... ok +test commands::launcher::tests::a_leanback_only_app_is_not_listed_as_a_home_app ... ok +test commands::launcher::tests::a_rejected_cmd_package_setter_still_tries_the_pm_spelling ... ok +test commands::launcher::tests::android_8_registers_home_before_disabling_stock ... ok +test commands::launcher::tests::disable_stock_behind_the_active_target_restores_if_home_moves ... ok +test commands::launcher::tests::disable_stock_hands_home_over_when_stock_holds_it ... ok +test commands::launcher::tests::disable_stock_refuses_a_settings_recovery_handler ... ok +test commands::launcher::tests::disable_stock_refuses_a_stock_target ... ok +test commands::launcher::tests::disable_stock_refuses_when_the_target_has_no_home_screen ... ok +test commands::launcher::tests::ignores_unrelated_lines_with_slashes_or_paths ... ok +test commands::launcher::tests::invalid_target_package_runs_no_commands ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_helper_re_enable_is_visible_on_a_successful_switch ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_helper_rollback_is_reported_not_called_re_enabled ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_stock_enable_never_turns_the_helper_on ... ok +test commands::launcher::tests::launcher_verification_122::a_failed_stock_restore_after_a_helper_error_is_reported ... ok +test commands::launcher::tests::launcher_verification_122::a_generic_stock_disable_leaves_the_helper_on_without_another_home ... ok +test commands::launcher::tests::launcher_verification_122::a_generic_stock_disable_turns_the_paired_helper_off_too ... ok +test commands::launcher::tests::launcher_verification_122::a_landed_disable_with_a_lost_reply_still_settles_the_helper ... ok +test commands::launcher::tests::launcher_verification_122::a_shared_enable_with_a_failed_helper_restore_is_not_reported_as_done ... ok +test commands::launcher::tests::launcher_verification_122::an_empty_home_inventory_never_leaves_stock_off_with_the_helper_on ... ok +test commands::launcher::tests::launcher_verification_122::an_unreadable_stock_preflight_still_restores_the_helper ... ok +test commands::launcher::tests::launcher_verification_122::enable_and_set_default_on_stock_re_enables_the_paired_helper ... ok +test commands::launcher::tests::launcher_verification_122::helper_disable_re_enables_after_home_verification_fails ... ok +test commands::launcher::tests::launcher_verification_122::resolver_lag_then_target_confirms_without_rollback ... ok +test commands::launcher::tests::launcher_verification_122::restores_stock_when_the_resolver_never_answers_during_verification ... ok +test commands::launcher::tests::launcher_verification_122::role_holder_confirms_through_a_stuck_resolver_value ... ok +test commands::launcher::tests::launcher_verification_122::rolls_back_only_when_stock_positively_still_holds_home ... ok +test commands::launcher::tests::launcher_verification_122::the_advanced_picker_restores_the_paired_helper_with_stock ... ok +test commands::launcher::tests::launcher_verification_122::the_shared_enable_command_restores_the_paired_helper ... ok +test commands::launcher::tests::launcher_verification_122::unreadable_home_apps_never_leave_stock_off_with_the_helper_on ... ok +test commands::launcher::tests::list_launchers_degrades_when_the_home_query_is_empty ... ok +test commands::launcher::tests::list_launchers_errors_when_the_installed_section_is_empty ... ok +test commands::launcher::tests::list_launchers_reads_every_section_from_one_batched_call ... ok +test commands::launcher::tests::list_launchers_rejects_failed_disabled_reads_before_pruning_tracking ... ok +test commands::launcher::tests::parses_home_handler_packages_from_resolveinfo ... ok +test commands::launcher::tests::protected_package_guidance_requires_explicit_matching_security_evidence ... ok +test commands::launcher::tests::protected_stock_rejection_preserves_errors_restore_and_home_readback ... ok +test commands::launcher::tests::protection_behind_the_active_target_keeps_precautionary_restore_and_readback ... ok +test commands::launcher::tests::restore_failure_variants_keep_primary_failure_and_fresh_home_observation ... ok +test commands::launcher::tests::set_home_any_refuses_honestly_for_an_app_without_a_home_screen ... ok +test commands::launcher::tests::set_home_any_rejects_injected_activity_names ... ok +test commands::launcher::tests::set_home_any_reports_stock_holding_home_without_taking_it_over ... ok +test commands::launcher::tests::set_home_any_succeeds_when_home_verifies_and_tries_the_given_activity_first ... ok +test commands::launcher::tests::set_launcher_falls_back_to_set_home_activity_when_role_unsupported ... ok +test commands::launcher::tests::set_launcher_first_strategy_wins_and_skips_the_rest ... ok +test commands::launcher::tests::set_launcher_from_stock_offers_takeover_without_grinding_the_ladder ... ok +test commands::launcher::tests::set_launcher_reports_failure_with_reason_when_all_strategies_fail ... ok +test commands::launcher::tests::set_launcher_stock_takeover_does_not_revert_when_it_verifies ... ok +test commands::launcher::tests::set_launcher_stock_takeover_reverts_when_it_does_not_verify ... ok +test commands::launcher::tests::stock_disable_uncertainty_restores_once_and_reports_unavailable_home ... ok +test commands::launcher::tests::target_enable_failures_stop_before_any_launcher_mutation ... ok +test commands::launcher::tests::unrelated_exit_255_does_not_claim_protection_even_if_restore_is_rejected ... ok +test commands::loader::tests::channel_provider_entry_has_high_risk ... ok +test commands::loader::tests::device_scope_uses_known_families_and_agrees_with_its_list ... ok +test commands::loader::tests::embedded_data_includes_known_defunct_apps ... ok +test commands::loader::tests::embedded_json_loads_and_parses ... ok +test commands::loader::tests::embedded_launchers_parse_with_every_shipped_entry ... ok +test commands::loader::tests::every_custom_launcher_has_a_source_url_and_no_package_repeats ... ok +test commands::loader::tests::every_shipped_catalog_entry_gets_a_reviewed_verdict ... ok +test commands::loader::tests::every_shipped_entry_has_a_usable_description ... ok +test commands::loader::tests::find_resolves_every_package_exactly_once ... ok +test commands::loader::tests::known_names_accept_a_bare_name_or_a_described_entry ... ok +test commands::loader::tests::known_names_do_not_duplicate_catalog_entries ... ok +test commands::loader::tests::known_names_map_parses_and_has_expected_entries ... ok +test commands::loader::tests::no_duplicate_packages_within_any_bundled_list ... ok +test commands::loader::tests::no_shipped_entry_overrides_the_protected_list ... ok +test commands::loader::tests::non_reinstallable_uninstall_entries_are_gated_to_disable ... ok +test commands::loader::tests::review_apps_are_never_auto_selected ... ok +test commands::loader::tests::review_dates_parse_and_are_not_in_the_future ... ok +test commands::loader::tests::safe_entries_carry_a_review_date_or_are_listed_as_gaps ... ok +test commands::loader::tests::shipped_known_names_are_display_only ... ok +test commands::logcat::tests::app_capture_resolves_the_main_pid_each_time ... ok +test commands::logcat::tests::device_failure_is_not_an_empty_success ... ok +test commands::logcat::tests::failed_or_ambiguous_pid_never_reads_all_logs ... ok +test commands::logcat::tests::invalid_filter_never_touches_the_driver ... ok +test commands::logcat::tests::unsuccessful_pid_command_never_uses_its_stdout ... ok +test commands::optimize::tests::prepare_optimize_builds_plan_from_device_state ... ok +test commands::optimize::tests::prepare_optimize_errors_when_the_installed_section_is_empty ... ok +test commands::optimize::tests::restore_refuses_a_failed_disabled_read_even_when_memory_succeeds ... ok +test commands::recovery::tests::free_user_can_recover_disabled_packages_after_downgrade ... ok +test commands::screenshot::tests::accepts_png_bytes ... ok +test commands::screenshot::tests::concurrent_same_timestamp_captures_never_replace_each_other ... ok +test commands::screenshot::tests::exhausted_names_fail_without_overwriting_or_leaving_staging_files ... ok +test commands::screenshot::tests::existing_symlink_target_is_not_followed_or_overwritten ... ok +test commands::screenshot::tests::failed_partial_write_publishes_nothing_and_preserves_prior_capture ... ok +test commands::screenshot::tests::flattens_serial_for_filenames ... ok +test commands::screenshot::tests::rejects_empty_output ... ok +test commands::screenshot::tests::rejects_error_text_with_message ... ok +test commands::screenshot::tests::same_timestamp_retains_each_capture ... ok +test commands::screenshot::tests::unavailable_directory_returns_an_error_without_a_saved_path ... ok +test commands::shell::tests::a_blocked_command_never_reaches_the_device ... ok +test commands::shell::tests::a_chained_command_hiding_a_disable_is_still_refused_whole ... ok +test commands::shell::tests::a_transport_failure_surfaces_as_an_error_not_an_empty_result ... ok +test commands::shell::tests::an_empty_command_is_rejected_before_the_driver ... ok +test commands::shell::tests::disabling_an_unprotected_package_is_allowed_through ... ok +test commands::shell::tests::runs_a_plain_command_and_returns_both_streams ... ok +test commands::snapshot::tests::current_settings_map_pairs_keys_to_values_in_order ... ok +test commands::snapshot::tests::disable_from_plan_records_invalid_name_without_aborting ... ok +test commands::snapshot::tests::disable_from_plan_records_pm_failure_without_aborting ... ok +test commands::snapshot::tests::installed_and_disabled_errors_when_the_installed_section_is_empty ... ok +test commands::snapshot::tests::installed_and_disabled_reads_both_lists_in_one_round_trip ... ok +test commands::snapshot::tests::installed_and_disabled_surfaces_a_transport_failure ... ok +test commands::snapshot::tests::setting_apply_distinguishes_empty_put_delete_and_failure ... ok +test commands::snapshot::tests::settings_capture_rejects_failed_and_truncated_reads ... ok +test commands::snapshot::tests::snapshot_plan_requires_a_successful_disabled_read ... ok +test commands::state::tests::a_driver_without_a_server_skips_stop_without_dispatching_commands ... ok +test commands::state::tests::closing_admission_refuses_new_snapshots_but_not_running_ones ... ok +test commands::state::tests::every_snapshot_counts_until_its_last_clone_goes ... ok +test commands::state::tests::snapshots_of_a_swapped_driver_share_one_count ... ok +test commands::tuning::tests::accepts_real_dot_hostnames ... ok +test commands::tuning::tests::an_unreadable_cached_limit_is_unknown_not_fatal ... ok +test commands::tuning::tests::basic_daydream_is_the_aosp_component_name ... ok +test commands::tuning::tests::builds_put_with_quoted_value ... ok +test commands::tuning::tests::cached_limit_parses_only_the_current_value ... ok +test commands::tuning::tests::empty_value_deletes ... ok +test commands::tuning::tests::rejects_bad_namespace_and_bad_key ... ok +test commands::tuning::tests::rejects_bad_or_unsafe_hostnames ... ok +test commands::tuning::tests::tweaks_preserve_empty_values_without_shifting_audio_settings ... ok +test commands::tuning::tests::tweaks_reject_failed_or_truncated_readback ... ok +test commands::tuning::tests::value_metacharacters_are_quoted_not_rejected ... ok +test commands::tuning::tests::value_quoting_handles_spaces_and_single_quotes ... ok +test engine::app_lists::tests::device_specific_overrides_common ... ok +test engine::app_lists::tests::duplicate_packages_within_a_list_are_collapsed ... ok +test engine::app_lists::tests::googletv_excludes_shield_entries ... ok +test engine::app_lists::tests::shield_merges_common_and_shield ... ok +test engine::app_lists::tests::unknown_returns_common_only ... ok +test engine::detection::tests::a_device_that_said_nothing_either_way_is_unknown ... ok +test engine::detection::tests::a_leanback_no_outweighs_nothing_else ... ok +test engine::detection::tests::a_phone_says_so_and_we_believe_it ... ok +test engine::detection::tests::detects_googletv_by_onn_brand ... ok +test engine::detection::tests::detects_googletv_chromecast ... ok +test engine::detection::tests::detects_googletv_streamer ... ok +test engine::detection::tests::detects_shield_by_brand ... ok +test engine::detection::tests::detects_shield_by_codename_only ... ok +test engine::detection::tests::generic_box_reporting_tv_characteristic_is_google_tv ... ok +test engine::detection::tests::google_branded_phone_is_not_google_tv ... ok +test engine::detection::tests::google_branded_tv_with_tv_characteristic_is_google_tv ... ok +test engine::detection::tests::leanback_alone_is_enough_to_call_it_a_tv ... ok +test engine::detection::tests::non_tv_tokens_are_matched_per_token_and_case_insensitively ... ok +test engine::detection::tests::shield_friendly_model_known ... ok +test engine::detection::tests::shield_friendly_model_unknown_passes_through ... ok +test engine::detection::tests::tv_characteristic_still_wins_over_a_leanback_no ... ok +test engine::detection::tests::unknown_when_no_signals ... ok +test engine::diagnostics::tests::a_host_only_bundle_says_no_device_rather_than_inventing_one ... ok +test engine::diagnostics::tests::a_package_inventory_is_never_part_of_the_bundle ... ok +test engine::diagnostics::tests::an_unreadable_device_claims_nothing_about_itself ... ok +test engine::diagnostics::tests::setup_helper_state_says_not_installed_or_unknown_without_guessing ... ok +test engine::diagnostics::tests::the_device_section_carries_both_tv_signals_and_the_verdict ... ok +test engine::launcher::tests::a_pairing_must_name_a_catalogued_transient_holder ... ok +test engine::launcher::tests::current_home_agreeing_sources_keep_the_activity ... ok +test engine::launcher::tests::current_home_disagreement_shows_the_role_holder_with_a_note ... ok +test engine::launcher::tests::current_home_falls_back_to_the_resolver_without_a_role ... ok +test engine::launcher::tests::current_home_keeps_a_stock_launcher_that_overrides_the_role ... ok +test engine::launcher::tests::current_home_prefers_the_role_holder_over_a_transient_resolver_answer ... ok +test engine::launcher::tests::current_home_role_without_a_resolver_answer_is_the_role ... ok +test engine::launcher::tests::disabled_setup_wraith_keeps_its_row_without_being_tracked ... ok +test engine::launcher::tests::last_enabled_home_handler_guard ... ok +test engine::launcher::tests::launcher_rows_carry_the_catalog_source_url ... ok +test engine::launcher::tests::launcher_rows_include_other_home_handlers_but_never_safe_fallbacks ... ok +test engine::launcher::tests::launcher_rows_keep_tracked_disabled_handlers_visible ... ok +test engine::launcher::tests::launcher_rows_mark_disabled_stock ... ok +test engine::launcher::tests::launcher_rows_mark_setup_wraith_as_a_setup_helper ... ok +test engine::launcher::tests::launcher_rows_omit_stock_not_on_device ... ok +test engine::launcher::tests::launcher_rows_put_installed_stock_first ... ok +test engine::launcher::tests::package_name_validation_accepts_valid ... ok +test engine::launcher::tests::package_name_validation_rejects_invalid ... ok +test engine::launcher::tests::paired_holders_come_only_from_the_stock_being_disabled ... ok +test engine::launcher::tests::wraith_alone_is_not_a_home_so_it_is_never_the_survivor ... ok +test engine::logcat::tests::a_tag_is_an_allowlist_not_an_additional_global_filter ... ok +test engine::logcat::tests::filters_cannot_inject_shell_words_or_logcat_flags ... ok +test engine::logcat::tests::process_resolution_is_strict ... ok +test engine::logcat::tests::snapshots_are_finite_and_never_clear_or_change_logging ... ok +test engine::logcat::tests::unresolved_app_filter_never_falls_back_to_all_apps ... ok +test engine::media::tests::absent_frame_setting_and_display_data_stay_unknown ... ok +test engine::media::tests::audio_codes_preserve_unknowns_and_do_not_confuse_mpegh_with_dts ... ok +test engine::media::tests::duplicate_documents_do_not_duplicate_decoders ... ok +test engine::media::tests::empty_and_malformed_xml_do_not_panic_or_invent_support ... ok +test engine::media::tests::reads_multiline_nested_types_comments_and_concatenated_documents ... ok +test engine::media::tests::software_metadata_and_mixed_decoders_preserve_presence ... ok +test engine::media::tests::vendor_name_does_not_prove_hardware_acceleration ... ok +test engine::optimize::tests::memory_map_propagated ... ok +test engine::optimize::tests::optimize_skips_uninstalled_and_disabled ... ok +test engine::optimize::tests::restore_skips_enabled_and_uninstalled ... ok +test engine::optimize::tests::uninstall_downgraded_to_disable_when_not_reinstallable ... ok +test engine::optimize::tests::uninstall_method_chosen_when_optimize_and_installed ... ok +test engine::safety::tests::a_catalog_entry_with_no_description_still_reads_cleanly ... ok +test engine::safety::tests::a_hand_written_caution_outranks_a_generic_risk_tier ... ok +test engine::safety::tests::a_reviewed_safe_app_is_safe_not_unknown ... ok +test engine::safety::tests::a_risky_reviewed_app_is_caution_and_says_why ... ok +test engine::safety::tests::all_verdicts_serialize_with_tag_and_reason ... ok +test engine::safety::tests::an_uncatalogued_package_is_still_unknown ... ok +test engine::safety::tests::caution_rules_keep_their_reasons_unless_protected_precedence_wins ... ok +test engine::safety::tests::channel_provider_is_caution_not_never ... ok +test engine::safety::tests::every_protected_rule_keeps_its_reason_and_membership ... ok +test engine::safety::tests::framework_is_never_disable ... ok +test engine::safety::tests::gms_is_never_disable ... ok +test engine::safety::tests::representative_protected_packages_remain_protected ... ok +test engine::safety::tests::safe_fallback_settings_is_never_disable ... ok +test engine::safety::tests::safe_is_never_reached_without_a_catalog_entry ... ok +test engine::safety::tests::shell_is_never_disable_for_obvious_reasons ... ok +test engine::safety::tests::shell_runner_allows_disabling_an_unprotected_package ... ok +test engine::safety::tests::shell_runner_allows_reading_a_protected_package ... ok +test engine::safety::tests::shell_runner_blocks_chained_and_quoted_attempts ... ok +test engine::safety::tests::shell_runner_blocks_disabling_a_never_disable_package ... ok +test engine::safety::tests::shell_runner_blocks_every_destructive_spelling ... ok +test engine::safety::tests::shell_runner_blocks_literal_clear_updates_and_component_targets ... ok +test engine::safety::tests::shell_runner_gate_agrees_with_the_never_disable_list ... ok +test engine::safety::tests::shell_runner_gate_does_not_pretend_to_stop_deliberate_evasion ... ok +test engine::safety::tests::system_keyboards_are_never_disable ... ok +test engine::safety::tests::the_protected_list_outranks_any_catalog_entry ... ok +test engine::safety::tests::unmatched_packages_are_unknown_with_the_exact_reason ... ok +test engine::snapshot::tests::absent_and_empty_settings_roundtrip_and_plan_separately ... ok +test engine::snapshot::tests::apply_plan_categorizes_packages ... ok +test engine::snapshot::tests::apply_plan_skips_settings_already_at_target ... ok +test engine::snapshot::tests::apply_plan_warns_on_cross_device ... ok +test engine::snapshot::tests::current_values_cover_every_setting_the_snapshot_mentions ... ok +test engine::snapshot::tests::label_roundtrips ... ok +test engine::snapshot::tests::launcher_already_home_is_not_a_change ... ok +test engine::snapshot::tests::launcher_not_installed_is_skipped_and_named ... ok +test engine::snapshot::tests::launcher_that_differs_or_is_unknown_is_set ... ok +test engine::snapshot::tests::legacy_omissions_never_become_deletions ... ok +test engine::snapshot::tests::rejects_conflicting_present_and_absent_values ... ok +test engine::snapshot::tests::rejects_future_schema ... ok +test engine::snapshot::tests::rejects_malformed_json ... ok +test engine::snapshot::tests::rejects_missing_schema_version ... ok +test engine::snapshot::tests::rejects_zero_schema_version ... ok +test engine::snapshot::tests::roundtrip_json ... ok +test engine::snapshot::tests::v1_snapshot_loads_with_no_label ... ok +test license::tests::base32_round_trips_arbitrary_bytes ... ok +test license::tests::dev_test_key_is_accepted_in_debug_builds ... ok +test license::tests::expired_license_is_rejected ... ok +test license::tests::issue_then_verify_round_trips ... ok +test license::tests::issued_license_length_stays_typeable ... ok +test license::tests::issuing_rejects_bad_licensee ... ok +test license::tests::license_survives_lowercase_and_confusable_characters ... ok +test license::tests::malformed_strings_are_rejected ... ok +test license::tests::perpetual_license_has_no_expiry ... ok +test license::tests::production_key_id_is_trusted ... ok +test license::tests::signature_from_another_key_is_rejected ... ok +test license::tests::tampered_payload_is_rejected ... ok +test license::tests::unknown_key_id_is_rejected ... ok + +test result: ok. 493 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.41s + + Running unittests src/lib.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/shield_optimizer_v2_lib-42621b022cfda0c9) + +running 110 tests +test adb::driver::tests::a_pairing_pin_never_reaches_the_log ... ok +test adb::driver::tests::a_server_target_override_is_left_alone ... ok +test adb::driver::tests::a_session_line_carries_the_schema_the_replay_parser_reads ... ok +test adb::driver::tests::a_session_line_never_carries_the_pin_or_typed_text ... ok +test adb::driver::tests::a_timeout_is_recorded_as_an_error_without_an_exit_code ... ok +test adb::driver::tests::binary_output_is_recorded_as_its_size ... ok +test adb::driver::tests::bounded_shell_deadline_keeps_partial_output ... ok +test adb::driver::tests::bounded_shell_preserves_nonzero_and_both_streams ... ok +test adb::driver::tests::bounded_shell_stops_continuous_output ... ok +test adb::driver::tests::bounded_utf8_handles_incomplete_and_invalid_characters ... ok +test adb::driver::tests::discovery_does_not_search_path_after_valid_managed_install ... ok +test adb::driver::tests::discovery_does_not_search_path_after_valid_override ... ok +test adb::driver::tests::discovery_never_touches_the_launch_directory_when_adb_is_installed ... ok +test adb::driver::tests::discovery_uses_path_after_higher_priority_candidates_fail ... ok +test adb::driver::tests::logged_stdout_is_capped_without_splitting_a_character ... ok +test adb::driver::tests::missing_binary_returns_typed_error ... ok +test adb::driver::tests::ordinary_subprocess_nonzero_contract_is_unchanged ... ok +test adb::driver::tests::output_success_check ... ok +test adb::driver::tests::package_listing_stdout_is_redacted_not_truncated ... ok +test adb::driver::tests::process_lifetimes::a_cancelled_call_stays_counted_until_its_process_is_reaped ... ok +test adb::driver::tests::process_lifetimes::a_resident_child_counts_until_it_is_reaped ... ok +test adb::driver::tests::process_lifetimes::a_stop_that_times_out_is_killed_and_reported_unknown ... ok +test adb::driver::tests::process_lifetimes::a_timed_out_call_is_reaped_before_it_returns ... ok +test adb::driver::tests::process_lifetimes::closed_process_admission_refuses_to_spawn ... ok +test adb::driver::tests::process_lifetimes::inherited_output_pipes_respect_timeout_after_direct_child_exit ... ok +test adb::driver::tests::process_lifetimes::replacement_and_old_driver_clones_share_closed_process_admission ... ok +test adb::driver::tests::process_lifetimes::the_stop_reports_what_kill_server_exited_with ... ok +test adb::driver::tests::repeated_discovery_resolves_once_until_explicitly_forgotten ... ok +test adb::driver::tests::the_expert_shell_is_recorded_without_its_command_or_output ... ok +test adb::driver::tests::typed_remote_text_never_reaches_the_log ... ok +test adb::install::tests::extract_zip_rejects_zip_slip ... ok +test adb::install::tests::install_root_is_under_data_local_dir ... ok +test adb::install::tests::platform_tools_url_matches_current_os ... ok +test adb::scan::tests::parses_dotted_prefix ... ok +test adb::scan::tests::parses_linux_le_hex_ip ... ok +test adb::scan::tests::rejects_bad_hex ... ok +test adb::scan::tests::rejects_malformed_prefix ... ok +test commands::app_report::tests::writes_a_report_and_nothing_else ... ok +test commands::backup::tests::ignores_noise_lines ... ok +test commands::backup::tests::names_single_and_split_backups ... ok +test commands::backup::tests::parses_single_apk_path ... ok +test commands::backup::tests::parses_split_apk_paths ... ok +test commands::backup::tests::parses_version_name_from_dumpsys ... ok +test commands::diagnostics::tests::an_empty_installed_list_is_unknown_not_absent ... ok +test commands::diagnostics::tests::home_handlers_are_read_from_the_real_resolveinfo_shape ... ok +test commands::diagnostics::tests::session_recordings_never_reach_the_bug_report_tail ... ok +test commands::diagnostics::tests::the_tail_is_the_last_n_lines_and_an_absent_log_is_empty ... ok +test commands::diagnostics::tests::ui_breadcrumbs_are_one_clean_bounded_line ... ok +test commands::files::tests::quotes_single_quotes_in_paths ... ok +test commands::install::tests::restart_adb_reports_failure_when_network_reconnect_fails ... ok +test commands::install::tests::restart_adb_reports_failure_when_start_server_errors ... ok +test commands::install::tests::restart_adb_retries_a_reconnect_that_fails_immediately ... ok +test commands::quit::tests::a_change_from_an_outdated_view_cannot_turn_the_stop_on ... ok +test commands::quit::tests::a_restart_for_an_update_leaves_the_server_running ... ok +test commands::quit::tests::after_admission_closes_new_work_is_refused_before_it_reaches_adb ... ok +test commands::quit::tests::an_adb_process_still_alive_leaves_the_server_running ... ok +test commands::quit::tests::an_operation_that_outlasts_the_drain_leaves_the_server_running ... ok +test commands::quit::tests::canceled_install_waiter_keeps_blocking_extraction_counted ... ok +test commands::quit::tests::canceled_pending_install_releases_admission_without_replacing_driver ... ok +test commands::quit::tests::install_after_closed_admission_has_no_installer_effects ... ok +test commands::quit::tests::once_quit_has_begun_the_setting_no_longer_changes ... ok +test commands::quit::tests::pending_install_prevents_stop_until_driver_replacement_finishes ... ok +test commands::quit::tests::quit_waits_for_an_operation_already_running_then_stops ... ok +test commands::quit::tests::quit_waits_for_old_snapshot_after_replacement_then_stops_live_driver ... ok +test commands::quit::tests::quitting_twice_stops_at_most_once ... ok +test commands::quit::tests::the_setting_starts_off ... ok +test commands::quit::tests::with_a_real_process::spoofed_start_output_and_restarts_do_not_stop_the_server ... ok +test commands::quit::tests::with_a_real_process::with_the_setting_on_quit_runs_kill_server_once ... ok +test commands::quit::tests::with_the_setting_off_quit_never_stops_the_server ... ok +test commands::scan::live::live_forget_then_auto_connect ... ignored +test commands::scan::tests::a_device_adb_already_holds_is_not_connected_a_second_time ... ok +test commands::scan::tests::a_device_adb_does_not_hold_is_still_connected ... ok +test commands::scan::tests::a_device_attached_at_the_very_endpoint_we_would_dial_is_still_dialled ... ok +test commands::scan::tests::a_pairing_only_device_is_reported_rather_than_connected_to ... ok +test commands::scan::tests::a_redial_drops_the_offline_alias_of_the_same_service_only ... ok +test commands::scan::tests::a_similar_instance_name_is_not_treated_as_the_same_device ... ok +test commands::scan::tests::an_advertised_host_adb_already_holds_is_still_not_probed_on_5555 ... ok +test commands::scan::tests::an_advertised_host_is_not_also_probed_on_5555 ... ok +test commands::scan::tests::an_advertised_port_is_used_instead_of_guessing_5555 ... ok +test commands::scan::tests::an_already_paired_device_is_not_asked_to_pair_again ... ok +test commands::scan::tests::an_offline_transport_does_not_stop_the_scan_redialling ... ok +test commands::scan::tests::an_unauthorized_transport_is_not_dialled_again_and_reports_unauthorized ... ok +test commands::scan::tests::classifies_connected_despite_daemon_startup_noise ... ok +test commands::scan::tests::classifies_failures ... ok +test commands::scan::tests::classifies_fresh_and_already_connected ... ok +test commands::scan::tests::classifies_unauthorized ... ok +test commands::scan::tests::legacy_devices_still_reach_5555_when_nothing_is_advertised ... ok +test commands::scan::tests::legacy_sweep_targets_are_unaffected_by_the_attached_list ... ok +test commands::scan::tests::real_world_scan_output_connects_the_shield_and_leaves_the_phone_alone ... ok +test commands::scan::tests::repeated_advertisements_produce_one_target_each ... ok +test commands::scan::tests::summary_mentions_unauthorized_devices ... ok +test commands::scan::tests::summary_plain_when_all_connected ... ok +test commands::scan::tests::summary_says_nothing_about_pairing_when_nothing_is_waiting ... ok +test commands::scan::tests::summary_tells_the_user_how_to_pair_a_waiting_device ... ok +test commands::sideload::tests::a_manifest_package_id_with_shell_syntax_is_treated_as_unreadable ... ok +test commands::sideload::tests::decodes_common_install_failures ... ok +test commands::update::tests::a_build_with_no_published_release_has_no_notes ... ok +test commands::update::tests::an_empty_release_body_is_not_notes ... ok +test commands::update::tests::drafts_are_ignored_for_both_answers ... ok +test commands::update::tests::finds_the_notes_for_the_version_actually_running ... ok +test commands::update::tests::higher_core_beats_any_prerelease ... ok +test commands::update::tests::newer_patch_and_minor ... ok +test commands::update::tests::non_v2_tags_are_skipped ... ok +test commands::update::tests::prerelease_numeric_segments_compare_numerically ... ok +test commands::update::tests::stable_beats_prerelease_same_core ... ok +test session::tests::an_unwritable_dir_is_swallowed ... ok +test session::tests::file_names_are_filesystem_safe_utc_timestamps ... ok +test session::tests::nothing_is_created_until_the_first_line_then_a_header_leads ... ok +test session::tests::rotation_keeps_the_newest_ten_files ... ok +test session::tests::the_size_cap_writes_one_notice_and_stops ... ok + +test result: ok. 109 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 4.75s + + Running unittests src/main.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/shield_optimizer_v2-7e20e0fcff340859) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running unittests src/lib.rs (/Users/bryanroscoe/Developer/shield-issue205-20261019/v2/target/debug/deps/tauri_plugin_atv_adb-1564aed60f182287) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests atv_optimizer_mobile_lib + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests shield_optimizer_core + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests shield_optimizer_v2_lib + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests tauri_plugin_atv_adb + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s diff --git a/v2/mobile/evidence/metric-context-2026-10-17/.gitignore b/v2/mobile/evidence/metric-context-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/metric-context-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/metric-context-2026-10-17/100c-reported-limit-simulated.png b/v2/mobile/evidence/metric-context-2026-10-17/100c-reported-limit-simulated.png new file mode 100644 index 00000000..8b735667 Binary files /dev/null and b/v2/mobile/evidence/metric-context-2026-10-17/100c-reported-limit-simulated.png differ diff --git a/v2/mobile/evidence/metric-context-2026-10-17/100c-unknown-context-simulated.png b/v2/mobile/evidence/metric-context-2026-10-17/100c-unknown-context-simulated.png new file mode 100644 index 00000000..79b02d41 Binary files /dev/null and b/v2/mobile/evidence/metric-context-2026-10-17/100c-unknown-context-simulated.png differ diff --git a/v2/mobile/evidence/metric-context-2026-10-17/30c-device-critical-simulated.png b/v2/mobile/evidence/metric-context-2026-10-17/30c-device-critical-simulated.png new file mode 100644 index 00000000..68fb2f65 Binary files /dev/null and b/v2/mobile/evidence/metric-context-2026-10-17/30c-device-critical-simulated.png differ diff --git a/v2/mobile/evidence/metric-context-2026-10-17/README.md b/v2/mobile/evidence/metric-context-2026-10-17/README.md new file mode 100644 index 00000000..9d2795d2 --- /dev/null +++ b/v2/mobile/evidence/metric-context-2026-10-17/README.md @@ -0,0 +1,76 @@ +# Capacity and thermal context — simulated evidence + +Base: `mobile-parity-pass` at `cd2244b970e2d579627e89bc3fb59a44da41d75e`, including merged PR182/183. No changes to their modal/identity implementations. + +## Confirmed root causes and behavior + +- Both original bars were **used** percentages, not reversed. RAM filled from reported used/total (or total minus reported free/available); storage used `df`'s used percentage. RAM green/storage amber were static category gradients, with no health thresholds. Dashboard separately labels its free-RAM number. Diagnostics now labels both bars **used**, shows their percentages and free/available figures, and uses the same neutral fill. Missing/invalid data is absent rather than a fabricated0% meter. Cached/reclaimable RAM is not a memory-pressure verdict. +- Thermal parsing previously discarded sensor identity/status, selected any matching scalar, and could treat BCL virtual sensors or cooling-device values as °C. New context is additive to the legacy `temperature_c` field. Known BCL voltage/current/percentage types, cooling-device records and threshold objects are excluded from current temperature selection. The prior 10–<120°C input-sanity range is retained, not presented as a device health range. +- Sensor name/type and its own severity are retained. Framework-wide status is separate; if both statuses differ, both are shown. No numeric temperature is translated into an invented normal/hot status. A missing or unknown status stays unknown, including for 100°C. A real device-reported critical status remains visible even when the selected reading is 30°C or temperature is absent. +- Existing CPU/GPU hardware arrays can supply a throttle/shutdown point. Pairing is by the **same report, family, array index and exact array length**, preserving NaN/missing slots. A missing/mismatched slot cannot borrow a neighboring or other-family limit. Service-record readings never borrow fallback array limits. Comparison is relative to that reported sensor point, not a universal CPU limit. Names such as `CPU reading 2` identify the device-report array position, not an invented kernel sensor name. +- The old generic 20–100 scale and vague hotness inference are removed. The UI shows a reported value, its available provenance, device/sensor status and matching reported points. These reads are observational; no automatic thermal action is added. + +The health command still performs the same seven batched shell reads. Parser/command tests pin one batch/no new device commands. These changes do not query a real device during development. Older client shapes can ignore added fields; legacy fixtures without context render explicit unknowns. Previously incorrect non-Celsius scalar selection can legitimately change. + +## Sources and limits + +- [Android memory allocation](https://developer.android.com/topic/performance/memory-management): used memory includes reclaimable cache; capacity use alone is not a pressure measurement. +- [AOSP thermal mitigation](https://source.android.com/docs/core/power/thermal-mitigation): manufacturers report device/sensor severity; temperature values and thresholds are device policy dependent and may include guardbands. +- [Thermal HAL 2.0 types](https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/android10-release/thermal/2.0/types.hal): BCL values are mV/mA/percent, other temperature types °C; severity and sensor-matched thresholds are distinct fields. + +No universal device threshold or safe-temperature promise is introduced. This is one snapshot, not a sustained-load/pressure test. Native hardware accuracy, vendor formats, physical layout/large-text behavior and real thermal limits remain device acceptance gates. Unsupported/missing data remains unknown. + +## Visual evidence + +Chromium/macOS, 384×812, invented report payloads, no real TV/phone or personal data: + +- [Used-capacity direction and 30°C](usage-and-30c-simulated.png) +- [100°C versus a synthetic reported 89°C point](100c-reported-limit-simulated.png) +- [100°C with no sensor/status context](100c-unknown-context-simulated.png) +- [30°C with separately reported critical device status](30c-device-critical-simulated.png) +- [Missing readings](missing-readings-simulated.png) +- [Short boundary transition video](metric-boundaries-simulated.webm) + +**89°C and 102.5°C are fixture data, not claimed universal or newly measured Shield limits.** The deliberately independent critical-status fixture demonstrates that the app does not infer severity from the displayed number. Images were visually inspected. The recording changes only mocked replies and triggers a mock refresh; it does not operate a TV. Raw intermediate recordings are ignored. + +## Reproduce + +From `v2/mobile` with installed dependencies/Chromium: + +```sh +METRICS_EVIDENCE=1 node --test tests/health-presentation.test.mjs +npm test +npm run check +npm run build +``` + +From `v2`: + +```sh +cargo fmt --all --check +cargo test -p shield-optimizer-core --offline --lib +cargo clippy -p shield-optimizer-core --all-targets --offline -- -D warnings +``` + +Boundary coverage includes 0/50/100% used, unavailable/invalid capacity, fallback RAM complement, matching versus mismatched thermal context, 30/89/100°C relative to a fixture's point, status 0–6, unknown status, differing global/sensor severity, BCL/cooling/threshold exclusion, numeric garbage, CPU/GPU family/index/length matching and NaN slots. The full current-base suite also retains the merged modal/keyboard/identity safety regressions. + +## Validation receipt + +On current merged base `cd2244b`: **259 mobile tests passed**, **459 shared-core tests passed**, **8 focused checks including capture passed**; Svelte check 0 errors/0 warnings, Vite build, Rustfmt and core Clippy all-targets with warnings denied passed. The full mobile suite includes merged PR182/183 safety tests. See `validation.json` for source/artifact hashes. + +Local aarch64 debug APK build passed with installed JDK 17, SDK 36 and NDK 28.2.13676358 using cached offline Rust dependencies. No APK was installed/launched. The first attempt with another worktree's target cache lacked generated TauriActivity; rerunning the supported build with its own default target regenerated the classes and succeeded. Existing Gradle deprecation/release-signing notices and the fresh-worktree parent tsconfig notice remain. This is not a signed Play release or physical acceptance. + +Independent source review and CI remain pending. No merge/release/deploy is authorized by this receipt. + +## Independent-review follow-up: current HAL beats cached records + +Original sealed head `cee8e81757d6b9f3fc4952b18b19cc09ac766d7b` is preserved in Git history. AOSP prints cached temperatures before current HAL temperatures; selecting a maximum across both can retain old status on ties or select a hotter old value. Both requested regression fixtures fail against that original code. + +The parser now isolates the current HAL section when its heading is present. An empty/unreadable current section does **not** resurrect cached readings; it may use the existing current hardware fallback or remain unavailable. Only absent current-section headings permit an explicitly marked cached-only/legacy fallback. A current CPU/GPU hardware reading is preferred over a recognized cached thermal reading. Old unsectioned formats retain their prior precedence because freshness cannot be reliably classified. + +Context distinguishes current, cached and legacy thermal sources. A cached fallback is labeled as last-known data and its sensor severity is not described as current. Framework-wide status remains separate. See the [AOSP dump order](https://github.com/aosp-mirror/platform_frameworks_base/blob/android10-release/services/core/java/com/android/server/power/ThermalManagerService.java#L481-L490). + +- [Current HAL80°C/status4 with framework status0](current-hal-80c-critical-simulated.png) +- [Explicit cached-only fallback](cached-only-80c-simulated.png) + +The refreshed video includes both states. New tests cover equal cached/current80°C with status0→4; hotter cache100°C versus current70°C/status1; empty current section; cached-only/legacy behavior; and current hardware versus cached-only precedence. Final validation:459 core tests,259 mobile tests,8 focused checks, Clippy/Rustfmt/check/build and refreshed isolated-target Android debug APK build passed. No physical queries or settings changes. Report-layout work is checkpointed separately on `fix/mobile-report-readability-20261017` and is not included here. diff --git a/v2/mobile/evidence/metric-context-2026-10-17/cached-only-80c-simulated.png b/v2/mobile/evidence/metric-context-2026-10-17/cached-only-80c-simulated.png new file mode 100644 index 00000000..f73ec779 Binary files /dev/null and b/v2/mobile/evidence/metric-context-2026-10-17/cached-only-80c-simulated.png differ diff --git a/v2/mobile/evidence/metric-context-2026-10-17/current-hal-80c-critical-simulated.png b/v2/mobile/evidence/metric-context-2026-10-17/current-hal-80c-critical-simulated.png new file mode 100644 index 00000000..23bb89b9 Binary files /dev/null and b/v2/mobile/evidence/metric-context-2026-10-17/current-hal-80c-critical-simulated.png differ diff --git a/v2/mobile/evidence/metric-context-2026-10-17/metric-boundaries-simulated.webm b/v2/mobile/evidence/metric-context-2026-10-17/metric-boundaries-simulated.webm new file mode 100644 index 00000000..e39333a8 Binary files /dev/null and b/v2/mobile/evidence/metric-context-2026-10-17/metric-boundaries-simulated.webm differ diff --git a/v2/mobile/evidence/metric-context-2026-10-17/missing-readings-simulated.png b/v2/mobile/evidence/metric-context-2026-10-17/missing-readings-simulated.png new file mode 100644 index 00000000..7a484002 Binary files /dev/null and b/v2/mobile/evidence/metric-context-2026-10-17/missing-readings-simulated.png differ diff --git a/v2/mobile/evidence/metric-context-2026-10-17/usage-and-30c-simulated.png b/v2/mobile/evidence/metric-context-2026-10-17/usage-and-30c-simulated.png new file mode 100644 index 00000000..311c75f6 Binary files /dev/null and b/v2/mobile/evidence/metric-context-2026-10-17/usage-and-30c-simulated.png differ diff --git a/v2/mobile/evidence/metric-context-2026-10-17/validation.json b/v2/mobile/evidence/metric-context-2026-10-17/validation.json new file mode 100644 index 00000000..839423b7 --- /dev/null +++ b/v2/mobile/evidence/metric-context-2026-10-17/validation.json @@ -0,0 +1,50 @@ +{ + "base": "cd2244b970e2d579627e89bc3fb59a44da41d75e", + "source_sha256": { + "v2/crates/core/src/adb/parse.rs": "6c6fa7306cac428de6df1c37086404fc5c171a469240f5e0a0656b31d2b13835", + "v2/crates/core/src/adb/mod.rs": "c7e4bfe30e720f747774c7fac40863d792c5d63ef28ddac290ac91fd08f82679", + "v2/crates/core/src/commands/health.rs": "8d0d2c48397fac0c30d1dda0db651fb040f6b1da55112b6db24aa609d9b387b8", + "v2/mobile/src/lib/types.ts": "f3bc7a6ded5ac5afc9aeb22c03dbd669d11cf8dd2c7407074a7ed774d4a5b3d9", + "v2/mobile/src/lib/healthPresentation.ts": "0dd733dddd99e1ece255cd754bc24c4873a0a1620c3f9038ff7cb5ad5589ac71", + "v2/mobile/src/screens/Diagnostics.svelte": "f0f6c5914b6982961883d6cdc02970d0fef058f7327ed5f879c7da93ee375d72", + "v2/mobile/tests/health-presentation.test.mjs": "b9e26fb1caec5fe0cab65a11f7661ff33a6f4103473840b67a64e56d1b1c2d8a" + }, + "evidence_sha256": { + "metric-boundaries-simulated.webm": "978162e659068eba9419b1ffb1528bd39e5567399dda765ebfcb5608afb98c86", + "30c-device-critical-simulated.png": "562f5ddcc60ba2587359e52a0aaaf62bb09a4a9198ee385ffe9654e5383f7b7d", + "usage-and-30c-simulated.png": "15dadf9c5d1e00dc88df58db51205391d16cf624ad1bc4c3b430bbbc7e954420", + "100c-unknown-context-simulated.png": "92d7269ab63698613383e17190136849e553f41ea090211184aa574458befc03", + "current-hal-80c-critical-simulated.png": "ab3541d5c43cd1af803c8d5850c0cb060b90e0945fcf10dd2894e526e80e415c", + "100c-reported-limit-simulated.png": "71fd149c66627eb691cc43e00748faba5bbe029f2fabfaf66af1be01b7b5e8d1", + "cached-only-80c-simulated.png": "5d1994e5592bd4c4cdbb7aeb5f83a368a6158f5f9373d7638e498ff42dda9195", + "missing-readings-simulated.png": "d44dfe85ec8e810d288422520edc1162f4cef3427f2371b551740e40f0ba415a" + }, + "full_current_base_mobile_tests": 259, + "focused_with_capture": 8, + "shared_core_tests": 459, + "clippy_all_targets_warnings_denied": "passed", + "rustfmt_check": "passed", + "svelte_errors": 0, + "svelte_warnings": 0, + "vite_build": "passed", + "android_debug_apk": { + "result": "passed using isolated default target", + "file_sha256": "f05c9ca03f9b6c18228386da74c05d37ee6432cc31c5ddea17580cbf3d1ac4f7", + "architecture": "aarch64", + "jdk": 17, + "compile_sdk": 36, + "ndk": "28.2.13676358", + "installed": false + }, + "physical_device_tested": false, + "additional_device_commands": 0, + "universal_temperature_threshold_added": false, + "independent_review": "pending", + "current_hal_reviewer_regressions": { + "before_failed": 2, + "after_passed": 2, + "empty_current_no_cached_resurrection": true, + "current_hardware_preferred_to_cached": true, + "current_cached_legacy_provenance_explicit": true + } +} diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/.gitignore b/v2/mobile/evidence/modal-handoffs-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/modal-handoffs-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/README.md b/v2/mobile/evidence/modal-handoffs-2026-10-17/README.md new file mode 100644 index 00000000..585f7716 --- /dev/null +++ b/v2/mobile/evidence/modal-handoffs-2026-10-17/README.md @@ -0,0 +1,104 @@ +# Modal handoffs and stock takeover — simulated evidence + +Saved implementation starts from `mobile-parity-pass` at +`3a1881e9b73c768be3a417abbea14cf524cfbe40` and is preserved in checkpoint `8df9b7c`. +After host recovery, merge `e6274e3a8a331bc988c562faccf6fa98f5f16275` integrates the +reviewed report PR185 base `56aa7c56450464b2060c27838373029b6b90c14f` without conflicts. +Report implementation/evidence files are inherited unchanged; the release-checklist entry is +reconciled to point at this separate modal follow-up. + +## Reproduced before fixing + +Both new screen regressions failed on the unchanged base. Menu → Report → Escape failed to +return focus to the original app row. Stock takeover left the document unlocked when its +confirmation opened. [Original failing test output](reproduction-before.txt) records both failures. + +## Scoped changes + +- Stock takeover registers with the existing shared modal action, declares `aria-modal`, and + scrolls internally on short screens. Focus trapping, Escape cancellation and document locking + now use the same ownership rules as other dialogs. The shared empty-dialog fallback recognizes + `alertdialog` too. +- Empty-stack cleanup waits one microtask so Svelte can mount a replacement in the same update. + The replacement inherits the retained original opener and document lock. Stack revisions prevent + superseded cleanup callbacks from restoring focus into a new modal. A genuine close still + clears the lock/opener before a later asynchronous opening, which then captures current focus. + +No launcher logic, report fields, exports, device commands or safety gates changed. The two modal +P2s documented separately in the report PR are implemented here; independent review is still pending. + +## Evidence + +All screens use mocked Tauri replies. No destructive confirmation is accepted by the new modal +screen cases; assertions pin zero stock-disable/save-snapshot calls in the takeover case. + +- [Report opened from app menu](menu-to-report-simulated.png) +- [Original app row focused after closing](original-app-row-restored-simulated.png) +- [Scrolled Launcher before opening](launcher-before-simulated.png) +- [Stock takeover heading on a 384×420 viewport](stock-takeover-heading-simulated.png) +- [Keyboard wrap reveals Cancel while the document remains locked](stock-takeover-keyboard-simulated.png) +- [Original Launcher scroll and opener restored](launcher-restored-simulated.png) +- [2.12-second Menu → Report → close video, 384×812](menu-report-close-simulated.webm) +- [2.76-second takeover cancellation video, 384×420](takeover-cancel-simulated.webm) + +Raw intermediate recordings are ignored. Screenshots and sampled video frames are visually inspected. +No desktop gallery screens changed. + +## Reproduce + +From `v2/mobile`, with existing dependencies and Chromium: + +```sh +node --test tests/modal-lifecycle.test.mjs tests/apps-catalog.test.mjs tests/launcher.test.mjs tests/parity-screens.test.mjs +MODAL_HANDOFF_EVIDENCE=1 node --test --test-name-pattern='menu Report returns|stock takeover dialog locks' tests/apps-catalog.test.mjs tests/launcher.test.mjs +npm test +npm run check +npm run build +``` + +The focused suites cover real menu/report and takeover flows plus same-update replacement, +resolved/canceled/rejected asynchronous transitions, genuine close followed by an unrelated opener, +nested/out-of-order teardown, superseded cleanup, removed opener, duplicate teardown, empty +alertdialog focus, Escape/Tab, short-screen Cancel visibility, backdrop cancellation, scroll and +style restoration. Existing nested app-sheet/paywall/confirmation and connection-invalidated +selection regressions remain part of the full suite. + +## Limits + +Host Chromium simulation on macOS only; no phone/TV query, installation, real launcher mutation, +physical touch/pinch/text-scaling claim, release or merge. Native Android callbacks, cross-OS CI and +independent review remain pending. The existing parent v2 missing `.svelte-kit/tsconfig.json` notice +appears before successful mobile checks/build. No dependencies were installed. + +## Recovery and verification + +The pre-disconnect full run completed with 269 passes and one navigation timeout in the Tweaks +fixture during the host interruption. That run is not claimed green. The video-size adjustment +had not run, so it was applied after recovery and both videos were regenerated on the merged base. +The existing bounded Amphetamine session was inspected and reused; no user session was replaced. + +Before the review follow-up, all 64 focused cases passed on the merged base, Svelte check found 0 errors / 0 warnings, and Vite +build passed. That full combined mobile suite passed 274/274 with no failures or skips. +[Validation receipt with source/artifact hashes](validation.json) records the final host checks. +The two screen regressions still demonstrate their original failures in the preserved before log. + +## Independent review follow-up: reverse teardown order + +Review of `31b93a7821f02a55c8d0e2d272b24f19c8044125` identified one additional focus-ordering +case: after settled A → B → C, destroying focused C and then non-top A in the same turn leaves +B. A's revision superseded C's pending restoration, then the non-top cleanup returned without +restoring focus. The document lock stayed held while focus fell to the body. + +The [new regression failed before this fix](reverse-order-reproduction-before.txt). Latest cleanup +now repairs focus when it is outside the surviving top modal, preferring that modal's last valid +control. It leaves focus already inside the survivor alone and retains the revision guard, so a +superseded callback cannot steal focus from a new top modal. The test asserts B's exact last +control, retained document lock, and eventual original-opener/scroll/style restoration after B closes. + +The follow-up changes no visual layout; existing screenshots/videos remain representative. Its +new evidence is the failing-before regression plus the refreshed source hashes and combined checks +in `validation.json`. Independent verification of the new commit remains pending. + +Review-fix validation: **275/275 full mobile cases and 65/65 focused combined cases passed**, +Svelte check found 0 errors / 0 warnings, Vite build passed, and `git diff --check` passed. +No screenshot/video assets changed because this correction changes only teardown focus ownership. diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/launcher-before-simulated.png b/v2/mobile/evidence/modal-handoffs-2026-10-17/launcher-before-simulated.png new file mode 100644 index 00000000..40f3900c Binary files /dev/null and b/v2/mobile/evidence/modal-handoffs-2026-10-17/launcher-before-simulated.png differ diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/launcher-restored-simulated.png b/v2/mobile/evidence/modal-handoffs-2026-10-17/launcher-restored-simulated.png new file mode 100644 index 00000000..40f3900c Binary files /dev/null and b/v2/mobile/evidence/modal-handoffs-2026-10-17/launcher-restored-simulated.png differ diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/menu-report-close-simulated.webm b/v2/mobile/evidence/modal-handoffs-2026-10-17/menu-report-close-simulated.webm new file mode 100644 index 00000000..d95ea7c1 Binary files /dev/null and b/v2/mobile/evidence/modal-handoffs-2026-10-17/menu-report-close-simulated.webm differ diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/menu-to-report-simulated.png b/v2/mobile/evidence/modal-handoffs-2026-10-17/menu-to-report-simulated.png new file mode 100644 index 00000000..4526ec1a Binary files /dev/null and b/v2/mobile/evidence/modal-handoffs-2026-10-17/menu-to-report-simulated.png differ diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/original-app-row-restored-simulated.png b/v2/mobile/evidence/modal-handoffs-2026-10-17/original-app-row-restored-simulated.png new file mode 100644 index 00000000..d663fe0a Binary files /dev/null and b/v2/mobile/evidence/modal-handoffs-2026-10-17/original-app-row-restored-simulated.png differ diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/reproduction-before.txt b/v2/mobile/evidence/modal-handoffs-2026-10-17/reproduction-before.txt new file mode 100644 index 00000000..a692f85d --- /dev/null +++ b/v2/mobile/evidence/modal-handoffs-2026-10-17/reproduction-before.txt @@ -0,0 +1,60 @@ +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ menu Report returns focus to its original app row after closing (1817.022167ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ stock takeover dialog locks scrolling, traps focus and Escape cancels without a mutation (1601.41675ms) +ℹ tests 2 +ℹ suites 0 +ℹ pass 0 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2163.652458 + +✖ failing tests: + +test at tests/apps-catalog.test.mjs:328:1 +✖ menu Report returns focus to its original app row after closing (1817.022167ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + false !== true + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/apps-catalog.test.mjs:338:10) + at runNextTicks (node:internal/process/task_queues:65:5) + at process.processImmediate (node:internal/timers:472:9) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/launcher.test.mjs:438:1 +✖ stock takeover dialog locks scrolling, traps focus and Escape cancels without a mutation (1601.41675ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + '' !== 'fixed' + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/launcher.test.mjs:450:10) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: '', + expected: 'fixed', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/reverse-order-reproduction-before.txt b/v2/mobile/evidence/modal-handoffs-2026-10-17/reverse-order-reproduction-before.txt new file mode 100644 index 00000000..1bd375f9 --- /dev/null +++ b/v2/mobile/evidence/modal-handoffs-2026-10-17/reverse-order-reproduction-before.txt @@ -0,0 +1,36 @@ +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ closing the focused child then a non-top ancestor restores the surviving middle modal (967.593416ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1247.225625 + +✖ failing tests: + +test at tests/modal-lifecycle.test.mjs:140:1 +✖ closing the focused child then a non-top ancestor restores the surviving middle modal (967.593416ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + 'Original rowOther rowBLast B' + - 'Last B' + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/modal-lifecycle.test.mjs:150:10) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: 'Original rowOther rowBLast B', + expected: 'Last B', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/stock-takeover-heading-simulated.png b/v2/mobile/evidence/modal-handoffs-2026-10-17/stock-takeover-heading-simulated.png new file mode 100644 index 00000000..86e9212e Binary files /dev/null and b/v2/mobile/evidence/modal-handoffs-2026-10-17/stock-takeover-heading-simulated.png differ diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/stock-takeover-keyboard-simulated.png b/v2/mobile/evidence/modal-handoffs-2026-10-17/stock-takeover-keyboard-simulated.png new file mode 100644 index 00000000..5fabbc15 Binary files /dev/null and b/v2/mobile/evidence/modal-handoffs-2026-10-17/stock-takeover-keyboard-simulated.png differ diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/takeover-cancel-simulated.webm b/v2/mobile/evidence/modal-handoffs-2026-10-17/takeover-cancel-simulated.webm new file mode 100644 index 00000000..ddbe2078 Binary files /dev/null and b/v2/mobile/evidence/modal-handoffs-2026-10-17/takeover-cancel-simulated.webm differ diff --git a/v2/mobile/evidence/modal-handoffs-2026-10-17/validation.json b/v2/mobile/evidence/modal-handoffs-2026-10-17/validation.json new file mode 100644 index 00000000..e61bf2ee --- /dev/null +++ b/v2/mobile/evidence/modal-handoffs-2026-10-17/validation.json @@ -0,0 +1,67 @@ +{ + "base": "56aa7c56450464b2060c27838373029b6b90c14f", + "integration": "e6274e3a8a331bc988c562faccf6fa98f5f16275", + "preserved_checkpoint": "8df9b7c", + "environment": "macOS host Chromium with mocked Tauri", + "checks": { + "full_mobile": { + "passed": 275, + "failed": 0, + "skipped": 0 + }, + "focused_combined": { + "passed": 65, + "failed": 0 + }, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "vite_build": "passed", + "git_diff_check": "passed" + }, + "videos": { + "menu-report-close-simulated.webm": { + "seconds": 2.12, + "width": 384, + "height": 812 + }, + "takeover-cancel-simulated.webm": { + "seconds": 2.76, + "width": 384, + "height": 420 + } + }, + "sha256": { + "v2/mobile/src/lib/modal.ts": "2ca5d88ed43210fb5847cb03b0cd20f231015f5bb12dcdf8a868ed4ccc038be4", + "v2/mobile/src/components/StockTakeoverDialog.svelte": "1f0698913f84e566d5191f4926bc173997b6e67b3617a8b1e62d0a8612b8233f", + "v2/mobile/tests/modal-lifecycle.test.mjs": "9f664113265513446f649dd99a77fd7d0ff37328f8cc7d00081d478a4cb94c4c", + "v2/mobile/tests/apps-catalog.test.mjs": "7815678f5f451c37be40ef68a79051e414aed93784aaad47a5487d0dbbcb7c65", + "v2/mobile/tests/launcher.test.mjs": "0700e3b3c9e328fea8d436913a452785bd749da8cbd13454b6a59c12af16386c", + "v2/mobile/evidence/modal-handoffs-2026-10-17/launcher-before-simulated.png": "d22f37efc9b65ea880f4450e6e5b238239124b59e5eb33d9a05fb2e7f1c0434a", + "v2/mobile/evidence/modal-handoffs-2026-10-17/launcher-restored-simulated.png": "d22f37efc9b65ea880f4450e6e5b238239124b59e5eb33d9a05fb2e7f1c0434a", + "v2/mobile/evidence/modal-handoffs-2026-10-17/menu-to-report-simulated.png": "dc7e762eea6c750f2f3327c132dd8b34504bdeec802dc624fc9b37b5f6ba7935", + "v2/mobile/evidence/modal-handoffs-2026-10-17/original-app-row-restored-simulated.png": "6bffdeb7cd07373b0b6aa962ac8824207fd87411744e32648a984a4a2485be77", + "v2/mobile/evidence/modal-handoffs-2026-10-17/stock-takeover-heading-simulated.png": "406e9a451f89d84879f5e5f0af1b2a47ba6a285eab465ea9fe33538512091dec", + "v2/mobile/evidence/modal-handoffs-2026-10-17/stock-takeover-keyboard-simulated.png": "f3c12e8b163eeff208d3caef2de65f8126f3a27058a64ad0103405f198992e7d", + "v2/mobile/evidence/modal-handoffs-2026-10-17/menu-report-close-simulated.webm": "3a5f4f0c9b0177ed0d0fd4f51cd29c1f5d9ef335a1925b0f33bac0b86c0e332b", + "v2/mobile/evidence/modal-handoffs-2026-10-17/takeover-cancel-simulated.webm": "35f4071dea887d898b12da88f918edb3a6e90bf2028aa990d51d44c44e9ff256" + }, + "limitations": [ + "No physical device queries, installs or launcher mutations.", + "No Android native/touch validation, release or merge.", + "Independent review and cross-OS CI pending.", + "Existing parent missing .svelte-kit/tsconfig.json notice precedes successful mobile checks/build." + ], + "review_followup": { + "previous_head": "31b93a7821f02a55c8d0e2d272b24f19c8044125", + "finding": "Focused C closes then non-top A closes in one turn while B survives.", + "reproduction": "reverse-order-reproduction-before.txt", + "assertions": [ + "Exact surviving B last-control focus", + "Document lock retained", + "Original opener, scroll and styles restored after B closes" + ], + "visual_layout_changed": false + } +} diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/.gitignore b/v2/mobile/evidence/modal-scroll-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/modal-scroll-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/README.md b/v2/mobile/evidence/modal-scroll-2026-10-17/README.md new file mode 100644 index 00000000..2baf25ee --- /dev/null +++ b/v2/mobile/evidence/modal-scroll-2026-10-17/README.md @@ -0,0 +1,51 @@ +# App-detail modal scroll/focus evidence + +Base: `mobile-parity-pass` at `5f4fc141ceda0307cb86b68bfc50433b9ffcc49d`. Separate from PR181; no settings/inset/icon changes in this branch. + +Root cause: AppDetailSheet used a fixed overlay, but neither it nor its hosts locked the document. Its independently scrolling panel allowed scroll chaining. Closing a child dialog also had no shared ownership or focus-restoration rule. + +The `modal` Svelte action locks the document once, preserves individual inline style values/priorities and both scroll coordinates, and releases only when its last owner closes. App detail, action menu, report, confirmation and paywall layers share this ownership. Scrolling stays available inside long sheets with overscroll containment. Only the top layer handles Tab/Escape; closing it restores its opener. Last focus is remembered across asynchronous safety checks that temporarily disable a triggering button. Destroyed/disconnected launchers are not refocused. No global touch/pinch/text-size restriction is introduced. + +## Reproduce + +From `v2/mobile`, with existing dependencies and Playwright Chromium: + +```sh +MODAL_EVIDENCE=1 node --test --test-name-pattern='modal' tests/parity-screens.test.mjs +npm test +npm run check +npm run build +``` + +Focused checks cover background wheel/edge scroll, actual inner-sheet scrolling, repeated open/close, exact existing style/scroll restoration, focus return, nested confirmation and paywall, Tab wrap, Escape/backdrop dismissal, screen teardown, disconnect, and out-of-order parent/child destruction. App catalog/report/menu tests run in the full suite. + +## Visual evidence + +Synthetic TV data, Chromium/macOS, 384×812; these are browser simulations, not phone or TV recordings. No real device was connected, no app/device setting changed, and no app installed. The mock Disable confirmation is canceled, not executed. + +- [Scrolled diagnostic page before opening](diagnostics-before-simulated.png) +- [App details open](app-details-simulated.png) +- [Nested confirmation](nested-confirm-simulated.png) +- [Diagnostic page restored](diagnostics-restored-simulated.png) +- [Short open/scroll/nest/dismiss video](modal-scroll-simulated.webm) + +Frames were visually inspected. The evidence fixture uses invented app/package names and contains no personal data. The named video is the deliverable; intermediate raw recordings are ignored. + +Browser coverage does not prove Android touch/overscroll, software keyboard or accessibility behavior. On a phone, check long-sheet touch scroll at both edges, backdrop dismissal, keyboard, rotation, pinch zoom, enlarged text and repeated nested flows. No physical validation is claimed. + +## Deferred owner findings + +The durable general list is [PR181's release checklist](https://github.com/bryanroscoe/shield_optimizer/blob/3c15c2147a0e4b2cf0768592d33b8f682811bcbc/v2/mobile/RELEASE-POLISH.md). RAM/storage, thermal context, reports, exports and app inventory remain separate tasks. Independent review also found an inherited density-only display override label issue: `scaleLine` can show physical density and “device default” while selection recognizes the override. Preserve PR181's approved head; add a density-only fixture and label correction in the next settings slice. + +Validation: **232 full mobile tests passed**, **8 focused modal checks including capture passed**, Svelte check zero errors/warnings, Vite build passed, and `git diff --check` passed. The existing fresh-worktree parent `.svelte-kit/tsconfig.json` Vite notice remains. No native source changed; no native APK/phone validation is claimed for this slice. + +## Keyboard visibility follow-up (independent P2 review) + +The sealed `c852eb7` helper kept focus in the modal but could wrap to an action outside a long sheet's visible scrollport. New tests reproduce this after entrance animations settle in both AppDetail and Report at 384×420. The revised helper focuses without scrolling the page, then reveals the control by adjusting only scrolling ancestors inside that modal. Both Shift+Tab from Close and Tab from the last action verify visible controls and unchanged background position. + +- [Last action revealed at short viewport](keyboard-last-action-simulated.png) +- [Close revealed after wrapping back](keyboard-close-simulated.png) + +The refreshed video includes that short-viewport keyboard check (not a physical phone/software-keyboard simulation). Both new tests failed against the sealed helper on current base plus PR182, then pass with the revised helper. Standalone final suite:232 passed. Isolated current base `6f3deefa5e46a03d292aa4ef1e84927440b03766` plus revised PR182 source:236 passed; check/build passed. No merge conflicts; PR181 and the old sealed modal commit were not rewritten. One interrupted combined run had235 passes/one session timeout during Mac disconnection; the clean post-reconnection rerun is236/236. + +A separate P1 report identifies stale app selection being rebound to a new connection by AppSheetHost. This modal/keyboard slice does **not** clear that safety blocker; it requires a separate identity-lifecycle fix before release. diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/app-details-simulated.png b/v2/mobile/evidence/modal-scroll-2026-10-17/app-details-simulated.png new file mode 100644 index 00000000..4124dee0 Binary files /dev/null and b/v2/mobile/evidence/modal-scroll-2026-10-17/app-details-simulated.png differ diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/diagnostics-before-simulated.png b/v2/mobile/evidence/modal-scroll-2026-10-17/diagnostics-before-simulated.png new file mode 100644 index 00000000..bc8eb0e3 Binary files /dev/null and b/v2/mobile/evidence/modal-scroll-2026-10-17/diagnostics-before-simulated.png differ diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/diagnostics-restored-simulated.png b/v2/mobile/evidence/modal-scroll-2026-10-17/diagnostics-restored-simulated.png new file mode 100644 index 00000000..bc8eb0e3 Binary files /dev/null and b/v2/mobile/evidence/modal-scroll-2026-10-17/diagnostics-restored-simulated.png differ diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/keyboard-close-simulated.png b/v2/mobile/evidence/modal-scroll-2026-10-17/keyboard-close-simulated.png new file mode 100644 index 00000000..cf1ac96d Binary files /dev/null and b/v2/mobile/evidence/modal-scroll-2026-10-17/keyboard-close-simulated.png differ diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/keyboard-last-action-simulated.png b/v2/mobile/evidence/modal-scroll-2026-10-17/keyboard-last-action-simulated.png new file mode 100644 index 00000000..35d346fc Binary files /dev/null and b/v2/mobile/evidence/modal-scroll-2026-10-17/keyboard-last-action-simulated.png differ diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/modal-scroll-simulated.webm b/v2/mobile/evidence/modal-scroll-2026-10-17/modal-scroll-simulated.webm new file mode 100644 index 00000000..55d07781 Binary files /dev/null and b/v2/mobile/evidence/modal-scroll-2026-10-17/modal-scroll-simulated.webm differ diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/nested-confirm-simulated.png b/v2/mobile/evidence/modal-scroll-2026-10-17/nested-confirm-simulated.png new file mode 100644 index 00000000..bd56ed34 Binary files /dev/null and b/v2/mobile/evidence/modal-scroll-2026-10-17/nested-confirm-simulated.png differ diff --git a/v2/mobile/evidence/modal-scroll-2026-10-17/validation.json b/v2/mobile/evidence/modal-scroll-2026-10-17/validation.json new file mode 100644 index 00000000..aad6e1ac --- /dev/null +++ b/v2/mobile/evidence/modal-scroll-2026-10-17/validation.json @@ -0,0 +1,35 @@ +{ + "base": "5f4fc141ceda0307cb86b68bfc50433b9ffcc49d", + "source_sha256": { + "src/lib/modal.ts": "ce146c553cfb012a265ddc75ce6add4d826ff22fac387d621e3b45f843095ca9", + "tests/parity-screens.test.mjs": "c228e21292d0ca520d20fb6ec3094518bd92288a017b9eebd4079fedb7781458", + "src/components/AppDetailSheet.svelte": "061c3fb72140e2bff0f4dc5045d0b94c64a769f747f220f218e32d0fcad784ff", + "src/components/AppActionMenu.svelte": "51a3c3e9502b4ca6a9dfa3f306a05154d7fcb10e9d008d2d9b7bf1f034acb443", + "src/components/AppReportSheet.svelte": "0a9e9883712b0e2fbfc46772d2ac8c7e6d9b6aa623dca6d64e58afce087765e5", + "src/components/ConfirmDialog.svelte": "df5b632d94cd98a75a183fe84636345d5b0ffb92eba765b1331b07bacc6abbf0", + "src/components/PaywallSheet.svelte": "e91933a8760bb03d41304672083142b35c4c3ded996d46e0936321082c8cb22f", + "tests/apps-catalog.test.mjs": "15e01b6d59e542268c50aa885ab3a317e2937eef2b09d391b61640e1f3097c80" + }, + "evidence_sha256": { + "modal-scroll-simulated.webm": "29edb12a16489132e2b6e3392373d69034cfb881f17d0fa99cb7f285af34106e", + "app-details-simulated.png": "365b1353a38aeece728135ea1d0cd3364242c9eb8fa2344bf628b5bc709b8831", + "diagnostics-before-simulated.png": "2c9ede8a1b9e037da852f2753a1fe3f0a0e6858fb3c953e5e4e309318432a625", + "keyboard-last-action-simulated.png": "49d87ac5e8cbd130ddf109066434ec14c9dc9f10fa6305228eca6bbf1abcf055", + "nested-confirm-simulated.png": "15825a27b558bf148782ad47cdffe9d4a873b07e67e7ea80612bb86cdafb42ef", + "diagnostics-restored-simulated.png": "2c9ede8a1b9e037da852f2753a1fe3f0a0e6858fb3c953e5e4e309318432a625", + "keyboard-close-simulated.png": "d0ea634bde4d2230fa140b9ab8ffa412bed542fdaf82d181be16d0c82e03b9ad" + }, + "full_mobile_tests": 232, + "focused_modal_with_capture": 8, + "svelte_errors": 0, + "svelte_warnings": 0, + "vite_build": "passed", + "physical_device_tested": false, + "native_files_modified": false, + "pinch_zoom_disabled": false, + "combined_current_base": "6f3deefa5e46a03d292aa4ef1e84927440b03766", + "combined_full_mobile_tests": 236, + "keyboard_regressions_before": 2, + "keyboard_regressions_after_pass": 2, + "p1_stale_host_selection": "separate unresolved release blocker" +} diff --git a/v2/mobile/evidence/parity-2026-10-06/apps-katniss.png b/v2/mobile/evidence/parity-2026-10-06/apps-katniss.png new file mode 100644 index 00000000..73fa9188 Binary files /dev/null and b/v2/mobile/evidence/parity-2026-10-06/apps-katniss.png differ diff --git a/v2/mobile/evidence/parity-2026-10-06/dashboard.png b/v2/mobile/evidence/parity-2026-10-06/dashboard.png new file mode 100644 index 00000000..dedee2dc Binary files /dev/null and b/v2/mobile/evidence/parity-2026-10-06/dashboard.png differ diff --git a/v2/mobile/evidence/parity-2026-10-06/files.png b/v2/mobile/evidence/parity-2026-10-06/files.png new file mode 100644 index 00000000..fbe7666a Binary files /dev/null and b/v2/mobile/evidence/parity-2026-10-06/files.png differ diff --git a/v2/mobile/evidence/parity-2026-10-06/launcher.png b/v2/mobile/evidence/parity-2026-10-06/launcher.png new file mode 100644 index 00000000..1b1a8663 Binary files /dev/null and b/v2/mobile/evidence/parity-2026-10-06/launcher.png differ diff --git a/v2/mobile/evidence/parity-2026-10-06/tweaks.png b/v2/mobile/evidence/parity-2026-10-06/tweaks.png new file mode 100644 index 00000000..00b9020c Binary files /dev/null and b/v2/mobile/evidence/parity-2026-10-06/tweaks.png differ diff --git a/v2/mobile/evidence/ram-accounting-2026-10-17/README.md b/v2/mobile/evidence/ram-accounting-2026-10-17/README.md new file mode 100644 index 00000000..175b3416 --- /dev/null +++ b/v2/mobile/evidence/ram-accounting-2026-10-17/README.md @@ -0,0 +1,15 @@ +# RAM accounting regression — 2026-10-17 + +Originally based on `dccfe99a805ffcb0c11f32a4fa9f227306ba1cf3`; now integrates mobile-parity-pass `5e80c0efd6620060bb03d5c354815990303628ab`, including independently reviewed and merged header PR191. The source-reviewed RAM implementation, tests and fixture are unchanged. Screenshot-retention PR193 remains separate. + +The checked-in Shield TV Pro meminfo fixture reports Total 3,016,708K, Used 1,752,149K and Free 1,418,054K. The existing parser converts these to 2946/1711/1384 MB. Before this fix, `memoryUsage` returned null because Used + Free exceeded Total; the new regression failed with that exact result. It now preserves explicit Used and reports 58%, while retaining individual finite/range validation and the available complement only when Used is absent. + +[AOSP accounting source](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/AppProfiler.java): Free includes cached PSS, cached kernel and free memory; Used uses total PSS minus cached PSS plus kernel allocations. Lost separately reconciles swap and ZRAM. These are not a guaranteed complementary two-bucket partition. This does not diagnose the exact vendor-specific discrepancy in the historical capture. + +Retained pre-header-integration simulated Playwright screenshots show the saved fixture at 384px, with the existing warning and units unchanged. `shield-ram-large-text.png` uses a 24px root font; existing fixed-pixel text is unaffected. The short MP4 shows the normal reading, enlarged root text and missing-data transition. No physical device, Android accessibility scaling or pinch gesture verification is claimed. + +Validation: focused health presentation tests 12/12; mobile Svelte check 0 errors/0 warnings; production build passed. Full mobile suite result recorded in the PR handoff. + +The RAM card content and fixtures are unchanged, so its existing visual evidence is retained rather than regenerated. The upstream header has changed: current header pixels are covered by [PR191 evidence](../diagnostics-header-2026-10-17/README.md), and the combined browser suite verifies its integration. Existing RAM screenshots are historical evidence from before that header integration, not new captures of this head. + +Integration validation on base `5e80c0e`: all 300 mobile browser tests passed, including header and RAM fidelity regressions; Svelte check reports 0 errors/0 warnings and the production build passed. See [integration receipt](integration-validation.json). No Rust source changed on this RAM branch. diff --git a/v2/mobile/evidence/ram-accounting-2026-10-17/integration-validation.json b/v2/mobile/evidence/ram-accounting-2026-10-17/integration-validation.json new file mode 100644 index 00000000..e4dc42a4 --- /dev/null +++ b/v2/mobile/evidence/ram-accounting-2026-10-17/integration-validation.json @@ -0,0 +1,23 @@ +{ + "date": "2026-10-17", + "base": "5e80c0efd6620060bb03d5c354815990303628ab", + "reviewed_ram_head": "eafd6960ee31d3e8acb7f0b1bcfd2582fc15cd07", + "changes": "Merge approved header base and update checklist/evidence provenance; reviewed RAM implementation and tests unchanged.", + "mobile_browser_tests": { + "passed": 300, + "failed": 0 + }, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "mobile_build": "passed", + "retained_evidence": "RAM screenshots and video are byte-for-byte unchanged historical captures from before header integration. Current header evidence retained from PR191.", + "fixture": "Checked-in Shield TV Pro meminfo is byte-for-byte unchanged.", + "limitations": [ + "Browser simulation and macOS host only", + "No phone/TV query or mutation", + "No physical accessibility scaling or pinch gesture acceptance claimed" + ], + "separate_work": "PR193 screenshot retention remains on its own branch." +} diff --git a/v2/mobile/evidence/ram-accounting-2026-10-17/ram-accounting.mp4 b/v2/mobile/evidence/ram-accounting-2026-10-17/ram-accounting.mp4 new file mode 100644 index 00000000..06d06bb5 Binary files /dev/null and b/v2/mobile/evidence/ram-accounting-2026-10-17/ram-accounting.mp4 differ diff --git a/v2/mobile/evidence/ram-accounting-2026-10-17/shield-ram-large-text.png b/v2/mobile/evidence/ram-accounting-2026-10-17/shield-ram-large-text.png new file mode 100644 index 00000000..b0de34c8 Binary files /dev/null and b/v2/mobile/evidence/ram-accounting-2026-10-17/shield-ram-large-text.png differ diff --git a/v2/mobile/evidence/ram-accounting-2026-10-17/shield-ram.png b/v2/mobile/evidence/ram-accounting-2026-10-17/shield-ram.png new file mode 100644 index 00000000..cc1c0a4a Binary files /dev/null and b/v2/mobile/evidence/ram-accounting-2026-10-17/shield-ram.png differ diff --git a/v2/mobile/evidence/reconnect-20261015/README.md b/v2/mobile/evidence/reconnect-20261015/README.md new file mode 100644 index 00000000..434fc6bd --- /dev/null +++ b/v2/mobile/evidence/reconnect-20261015/README.md @@ -0,0 +1,32 @@ +# Simulated reconnect safety evidence — 2026-10-15 + +These Playwright videos use mocked Tauri commands and synthetic TVs. No physical TV was +contacted or mutated. They demonstrate host browser behavior, not Android/native transport. + +Base: PR163 `94079cc45c034fac4761f8fcee8dfc5fc01b3961`. + +The three original new scenarios fail on the unchanged base (late takeover response survives, +wrong endpoint-cached original is retained, and a rotated port loses the restore target), and +pass with the fixes. Separate checks cover interrupted multi-writes and late list replies after +liveness loss. Existing launcher tests exercise successful actions and repeated switches. + +Run from `v2/mobile`: + +```sh +npm run check +npm run build +npm test +RECONNECT_VIDEO_DIR=/tmp/shield-reconnect-video node --test --test-concurrency=1 --test-name-pattern='same-endpoint|endpoint reuse|verified TV|between screensaver' tests/launcher.test.mjs tests/tweaks-tab.test.mjs +``` + +Videos are captured at 384×812, then transcoded from Playwright WebM to H.264 MP4. They are +short automated assertion runs. The test source provides exact state/command assertions. + +- [a-reconnect-between-screensaver-writes-prevents-the-second-write](SIMULATED-a-reconnect-between-screensaver-writes-prevents-the-second-write.mp4) +- [endpoint-reuse-never-restores-another-TV-s-screensaver-and-interrupted-writes-stop](SIMULATED-endpoint-reuse-never-restores-another-TV-s-screensaver-and-interrupted-writes-stop.mp4) +- [same-endpoint-reconnect-cancels-confirmations-and-late-launcher-results](SIMULATED-same-endpoint-reconnect-cancels-confirmations-and-late-launcher-results.mp4) +- [verified-TV-retains-originals-across-a-rotated-port-replacement-hardware-does-not](SIMULATED-verified-TV-retains-originals-across-a-rotated-port-replacement-hardware-does-not.mp4) + +No separate mobile lint script is configured; Svelte/TypeScript check and diff whitespace checks are used. Rust was unchanged. Desktop galleries are unchanged because no captured desktop screen changed. + +Final validation: **217 mobile tests passed, 0 failed**; mobile check reports 0 errors / 0 warnings, build succeeds, and test-file syntax plus diff whitespace checks pass. The unchanged base fails all three original new scenarios. No Rust code changed or local multi-platform Rust validation was performed. Parent SvelteKit generated config is absent in this isolated checkout and Vite prints that existing parent-config warning; mobile svelte-check itself reports no diagnostics. diff --git a/v2/mobile/evidence/reconnect-20261015/SIMULATED-a-reconnect-between-screensaver-writes-prevents-the-second-write.mp4 b/v2/mobile/evidence/reconnect-20261015/SIMULATED-a-reconnect-between-screensaver-writes-prevents-the-second-write.mp4 new file mode 100644 index 00000000..63ba6761 Binary files /dev/null and b/v2/mobile/evidence/reconnect-20261015/SIMULATED-a-reconnect-between-screensaver-writes-prevents-the-second-write.mp4 differ diff --git a/v2/mobile/evidence/reconnect-20261015/SIMULATED-endpoint-reuse-never-restores-another-TV-s-screensaver-and-interrupted-writes-stop.mp4 b/v2/mobile/evidence/reconnect-20261015/SIMULATED-endpoint-reuse-never-restores-another-TV-s-screensaver-and-interrupted-writes-stop.mp4 new file mode 100644 index 00000000..bfada3aa Binary files /dev/null and b/v2/mobile/evidence/reconnect-20261015/SIMULATED-endpoint-reuse-never-restores-another-TV-s-screensaver-and-interrupted-writes-stop.mp4 differ diff --git a/v2/mobile/evidence/reconnect-20261015/SIMULATED-same-endpoint-reconnect-cancels-confirmations-and-late-launcher-results.mp4 b/v2/mobile/evidence/reconnect-20261015/SIMULATED-same-endpoint-reconnect-cancels-confirmations-and-late-launcher-results.mp4 new file mode 100644 index 00000000..7e85ff34 Binary files /dev/null and b/v2/mobile/evidence/reconnect-20261015/SIMULATED-same-endpoint-reconnect-cancels-confirmations-and-late-launcher-results.mp4 differ diff --git a/v2/mobile/evidence/reconnect-20261015/SIMULATED-verified-TV-retains-originals-across-a-rotated-port-replacement-hardware-does-not.mp4 b/v2/mobile/evidence/reconnect-20261015/SIMULATED-verified-TV-retains-originals-across-a-rotated-port-replacement-hardware-does-not.mp4 new file mode 100644 index 00000000..91f01dcf Binary files /dev/null and b/v2/mobile/evidence/reconnect-20261015/SIMULATED-verified-TV-retains-originals-across-a-rotated-port-replacement-hardware-does-not.mp4 differ diff --git a/v2/mobile/evidence/reconnect-20261015/receipt.json b/v2/mobile/evidence/reconnect-20261015/receipt.json new file mode 100644 index 00000000..ae8e70b5 --- /dev/null +++ b/v2/mobile/evidence/reconnect-20261015/receipt.json @@ -0,0 +1,32 @@ +{ + "reviewed_base": "94079cc45c034fac4761f8fcee8dfc5fc01b3961", + "code_commit": "de5dda6eaead4605ce18d49e3b1cd087cb236586", + "simulated_only": true, + "physical_device_validation": false, + "tests": { + "full_mobile": { + "passed": 217, + "failed": 0 + }, + "unchanged_base_new_scenarios": { + "passed": 0, + "failed": 3 + }, + "typecheck": "0 errors, 0 warnings", + "build": "passed", + "lint": "no separate mobile lint command; node --check and git diff --check pass" + }, + "log_sha256": { + "final-build.log": "c9bc72e24496025d16ffaaac3d7a7c782e3bc854a1e2f47472f55632de280b26", + "full-tests.log": "28cd8b3b3104d6de025c57fd9e42e0754fc4d6b22511b72d9a449b27828d5a0b", + "baseline-regressions.log": "9e420df66e606887e384beb7fb1c050d2f854cc469ccaf7f1bcaba1c6ff23752", + "final-check.log": "f2f965bef65e9acbf1e00d70deaa53a215937f12f78747c5055c324b41f383fe", + "final-tests.log": "ac55b9cb8029b3c81f494c45329bedb648f077dce71ad18b70bb6caaad5fa9a8" + }, + "videos": { + "SIMULATED-verified-TV-retains-originals-across-a-rotated-port-replacement-hardware-does-not.mp4": "fba6f32fe90836c75f6f71634ffce512325a362a6569e9b43cfea7a7c9b009eb", + "SIMULATED-endpoint-reuse-never-restores-another-TV-s-screensaver-and-interrupted-writes-stop.mp4": "49dd4d148d39a05438fb1bf5fa95f331b6c639bd365e52f2a8110eb3146c94de", + "SIMULATED-a-reconnect-between-screensaver-writes-prevents-the-second-write.mp4": "509159b5313075f56345dcf3c5f339dccd9287d2e5ffcd687efe75c33664c7d4", + "SIMULATED-same-endpoint-reconnect-cancels-confirmations-and-late-launcher-results.mp4": "2b1288deaee4d2a091b8c03a1bd2d44eff5fab0c2187f3d75e9587aec34c0cec" + } +} diff --git a/v2/mobile/evidence/report-readability-2026-10-17/.gitignore b/v2/mobile/evidence/report-readability-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/report-readability-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/report-readability-2026-10-17/README.md b/v2/mobile/evidence/report-readability-2026-10-17/README.md new file mode 100644 index 00000000..d70da749 --- /dev/null +++ b/v2/mobile/evidence/report-readability-2026-10-17/README.md @@ -0,0 +1,55 @@ +# Diagnostic report readability — simulated evidence + +Preserves checkpoint `5bc1f5e7fb59b80317accd46057fd9cfc363f581` and normally merges +`mobile-parity-pass` at `3a1881e9b73c768be3a417abbea14cf524cfbe40` (integration `51d7195`). +The only conflict was Diagnostics imports; both the report component and reviewed health helpers remain. + +Playback now has separate video, display, audio/frame-rate and interpretation groups. Each display +mode has its own row and active label. Resource samples separate CPU/duration and each interface's +receive/send rates. Labels precede values, notes stay beside the report, and warning text is retained. +Rem text sizing, wrapping, flexible columns, 44px minimum actions and keyboard focus outlines support +small screens and text enlargement. The viewport still permits pinch zoom. No backend, report payload, +export formatter, precision, unit conversion or request lifecycle changes are made by this slice. +Failed rereads retain the existing behavior: clear the old playback report and show the error. + +## Evidence + +All values are deliberately synthetic. PNGs and a sampled video frame were visually inspected. + +| Viewport / root text | Playback | Resources | +|---|---|---| +| 320px / 100% | [Full Diagnostics screen](playback-320-100-simulated.png) | [Resource card](resources-320-100-simulated.png) | +| 384px / 100% | [Full Diagnostics screen](playback-384-100-simulated.png) | [Resource card](resources-384-100-simulated.png) | +| 320px / 200% | [Full Diagnostics screen](playback-320-200-simulated.png) | [Resource card](resources-320-200-simulated.png) | + +[5.44-second scrolling report video](reports-simulated.webm) shows the playback sections and resource +interfaces. Raw intermediate recordings are ignored. No desktop gallery screen changes in this PR. + +## Reproduce + +From `v2/mobile`, with existing dependencies and Chromium: + +```sh +REPORTS_EVIDENCE=1 node --test tests/playback-audio.test.mjs +npm test +npm run check +npm run build +``` + +The 14 focused cases include narrow/large-text overflow checks, long unbroken labels, warnings, +unknown policy/encoding text, precise mode rates, null versus zero counters, large rates, unchanged +source payloads, empty/error states, keyboard activation, reconnect invalidation and request admission. +The full combined mobile suite passed 263 tests, including current settings/modal/selection/thermal +regressions. Mobile Svelte check: 0 errors / 0 warnings; build passed. The parent v2 tsconfig emitted +its existing missing `.svelte-kit/tsconfig.json` startup notice before those successful mobile gates. + +Rustfmt workspace check, all-target Clippy with warnings denied for core/desktop/mobile/plugin/license, +and offline core/mobile/license tests passed (459 core tests). Vendored adb_client tests passed +54 with one ignored after allowing local loopback listeners; the restricted-sandbox attempt blocked +four socket tests. This is macOS host validation; cross-OS CI and independent review remain pending. + +No phone/TV queries, installations, physical text-scaling/pinch gestures, Android native behavior, +release, or merge were performed. Root-font enlargement is browser simulation, not Android font-scale +verification. Desktop frontend check could not start because the existing checkout lacks `svelte-kit`; its build +was therefore not run, and no dependencies were installed. Follow-up modal P2s are recorded +separately in [the release checklist](../../RELEASE.md). diff --git a/v2/mobile/evidence/report-readability-2026-10-17/playback-320-100-simulated.png b/v2/mobile/evidence/report-readability-2026-10-17/playback-320-100-simulated.png new file mode 100644 index 00000000..5483a672 Binary files /dev/null and b/v2/mobile/evidence/report-readability-2026-10-17/playback-320-100-simulated.png differ diff --git a/v2/mobile/evidence/report-readability-2026-10-17/playback-320-200-simulated.png b/v2/mobile/evidence/report-readability-2026-10-17/playback-320-200-simulated.png new file mode 100644 index 00000000..271b97f5 Binary files /dev/null and b/v2/mobile/evidence/report-readability-2026-10-17/playback-320-200-simulated.png differ diff --git a/v2/mobile/evidence/report-readability-2026-10-17/playback-384-100-simulated.png b/v2/mobile/evidence/report-readability-2026-10-17/playback-384-100-simulated.png new file mode 100644 index 00000000..9e42f719 Binary files /dev/null and b/v2/mobile/evidence/report-readability-2026-10-17/playback-384-100-simulated.png differ diff --git a/v2/mobile/evidence/report-readability-2026-10-17/reports-simulated.webm b/v2/mobile/evidence/report-readability-2026-10-17/reports-simulated.webm new file mode 100644 index 00000000..d4274099 Binary files /dev/null and b/v2/mobile/evidence/report-readability-2026-10-17/reports-simulated.webm differ diff --git a/v2/mobile/evidence/report-readability-2026-10-17/resources-320-100-simulated.png b/v2/mobile/evidence/report-readability-2026-10-17/resources-320-100-simulated.png new file mode 100644 index 00000000..33510ff1 Binary files /dev/null and b/v2/mobile/evidence/report-readability-2026-10-17/resources-320-100-simulated.png differ diff --git a/v2/mobile/evidence/report-readability-2026-10-17/resources-320-200-simulated.png b/v2/mobile/evidence/report-readability-2026-10-17/resources-320-200-simulated.png new file mode 100644 index 00000000..9eadeb6c Binary files /dev/null and b/v2/mobile/evidence/report-readability-2026-10-17/resources-320-200-simulated.png differ diff --git a/v2/mobile/evidence/report-readability-2026-10-17/resources-384-100-simulated.png b/v2/mobile/evidence/report-readability-2026-10-17/resources-384-100-simulated.png new file mode 100644 index 00000000..a8e951fe Binary files /dev/null and b/v2/mobile/evidence/report-readability-2026-10-17/resources-384-100-simulated.png differ diff --git a/v2/mobile/evidence/report-safety-2026-10-17/README.md b/v2/mobile/evidence/report-safety-2026-10-17/README.md new file mode 100644 index 00000000..3af16e68 --- /dev/null +++ b/v2/mobile/evidence/report-safety-2026-10-17/README.md @@ -0,0 +1,30 @@ +# Report safety fidelity — simulated evidence + +Base: mobile `0c78c06823b4827aa3a4345b526f688b2813d739`, after merged PR203 documentation and PR202 Restore/removal-selection fixes. This follow-up changes only Apps.knownVerdict lookup precedence; it does not reclassify packages or change native safety enforcement. + +## Contract and reproduction + +AppDetailSheet calls `onVerdict(pkg, null)` only after its current guarded safety lookup fails; a successful response supplies the concrete verdict. Apps stores that result in sheetVerdicts. Previously `sheetVerdicts[pkg] ?? catalog verdict` erased the distinction between a present null and an absent sheet result, so an older concrete catalog verdict could appear in a report while the sheet said Safety unavailable. + +`before.txt` records the failure on unchanged source: stale Caution and Protected verdicts appeared instead of unavailable. The fix uses an own-property check: a present sheet result, including null, takes precedence; an absent result still permits the catalog fallback. Existing openReport behavior retries a null verdict, initially exports unavailable with null source, retains unavailable after retry failure, and accepts a successful fresh result only for the current report request/package/session. Unknown remains unknown; unavailable is not recast as Safe or Protected. A successful report retry updates that report, not the underlying sheet's failed read. + +## Checks + +Nine focused mocked browser regressions pass in `focused.txt`. They cover stale Safe/Caution/Protected catalog values followed by sheet failure; exact preview/copied JSON while the report retry is pending and failed; successful retry to Unknown/no_record; late report responses after close/reopen, package change and same-endpoint reconnect; absent sheet cache fallback; late sheet replies across package/session changes; and reuse of a successful current sheet verdict without another report retry. Existing target, request, generation and liveness checks are unchanged. All 343 combined mobile tests pass in `combined-mobile.txt`; frontend check is zero errors/warnings and production build passes with existing lock-matching mobile dependencies. + +No Rust or Android-native source changed, so native compilation was not repeated. At the time of this original local receipt, independent review and exact-head CI remained gates; the subsequent completion receipt below supersedes that status. Historical validation and completed PR202/203 statuses remain intact. + +## Simulated visual evidence + +All lookups, clipboard writes and devices are browser fixtures at 384×812. Screenshots were visually inspected; no physical safety query, app mutation or device operation occurred. + +- [Sheet reports Safety unavailable](sheet-unavailable-simulated.png). +- [Report retains unavailable with null source](report-unavailable-simulated.png), with [exact copied report](unavailable-report-simulated.json). +- [Fresh report retry returns Unknown](retry-unknown-simulated.png), with [exact copied report](unknown-report-simulated.json). +- [4.88-second interaction video](report-safety-simulated.webm) shows the failed sheet lookup, report copy, failed retry and fresh successful retry. Raw recorder intermediates are omitted. + +Reproduce from v2/mobile using existing dependencies: `REPORT_SAFETY_EVIDENCE=1 node --test --test-concurrency=1 --test-name-pattern='report safety:' tests/apps-catalog.test.mjs`. Run the full suite with `npm test`, plus `npm run check` and `npm run build`. Desktop gallery regeneration is inapplicable because no captured desktop UI changed. Physical Android/clipboard acceptance, release and publication remain outside this slice. + +## Subsequent review, CI and merge receipt + +[Final independent source review](https://github.com/bryanroscoe/shield_optimizer/pull/204#issuecomment-6235587609) approved exact head `b7405034aaba11f911d9e0916a221148238c84cd`; [all six exact-head CI jobs](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39043175482) passed. [PR204](https://github.com/bryanroscoe/shield_optimizer/pull/204) merged as `d388cad1e3da9c17e0a5c0bdeec52022fd8d9970`. The original 343-test local receipt and simulated evidence above remain historical and unchanged; physical-device acceptance remains open. This is not validation of later integration heads. diff --git a/v2/mobile/evidence/report-safety-2026-10-17/before.txt b/v2/mobile/evidence/report-safety-2026-10-17/before.txt new file mode 100644 index 00000000..5cabf9fb --- /dev/null +++ b/v2/mobile/evidence/report-safety-2026-10-17/before.txt @@ -0,0 +1,50 @@ +✖ report safety: failed sheet overrides old catalog for com.google.android.katniss and retries honestly (2085.82ms) +✖ report safety: failed sheet overrides old catalog for com.example.launcherguard and retries honestly (832.291833ms) +ℹ tests 2 +ℹ suites 0 +ℹ pass 0 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3222.762958 + +✖ failing tests: + +test at tests/apps-catalog.test.mjs:510:62 +✖ report safety: failed sheet overrides old catalog for com.google.android.katniss and retries honestly (2085.82ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + 'caution' + - 'unavailable' + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/apps-catalog.test.mjs:519:10) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: 'caution', + expected: 'unavailable', + operator: 'strictEqual', + diff: 'simple' + } + +test at tests/apps-catalog.test.mjs:510:62 +✖ report safety: failed sheet overrides old catalog for com.example.launcherguard and retries honestly (832.291833ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + 'never_disable' + - 'unavailable' + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/apps-catalog.test.mjs:519:10) + at async Test.run (node:internal/test_runner/test:1389:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:960:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: 'never_disable', + expected: 'unavailable', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/v2/mobile/evidence/report-safety-2026-10-17/combined-mobile.txt b/v2/mobile/evidence/report-safety-2026-10-17/combined-mobile.txt new file mode 100644 index 00000000..6ecfb9fc --- /dev/null +++ b/v2/mobile/evidence/report-safety-2026-10-17/combined-mobile.txt @@ -0,0 +1,353 @@ +npm notice run atv-optimizer-mobile@0.1.0 test +npm notice run node --test --test-concurrency=1 tests/*.test.mjs +✔ a disabled katniss is found by package and by name, and can be re-enabled (1561.841875ms) +✔ not-installed recognised apps hide by default and can be revealed (376.123667ms) +✔ long-press opens the action menu without opening the sheet; a tap still opens it (914.615875ms) +✔ menu Disable goes through the safety lookup and confirm, never straight to the TV (477.3095ms) +✔ report opens a prefilled GitHub form through the opener (798.341625ms) +✔ without an opener, report falls back to copy and a link to copy (807.895125ms) +✔ rows, chips and labels fit a 384px screen (158.189583ms) +✔ long report keyboard wrap reveals its focused actions without moving the locked page (774.78725ms) +✔ menu Report returns focus to its original app row after closing (463.618917ms) +✔ report includes the successful APK fallback visible in the detail sheet (785.851375ms) +✔ APK report keeps exact 0 bytes, unknown data/cache and identical copied JSON (810.150167ms) +✔ APK report keeps exact 1234567 bytes, unknown data/cache and identical copied JSON (832.053958ms) +✔ diskstats remains the report source when a complete measured row exists (799.315291ms) +✔ APK report keeps storage unknown for a pending fallback (800.707833ms) +✔ APK report keeps storage unknown for a failed fallback (807.509709ms) +✔ APK report keeps storage unknown for a missing fallback (792.63175ms) +✔ APK report keeps storage unknown for a negative fallback (799.709708ms) +✔ remeasure invalidates a successful APK receipt and ignores an older fallback reply (1233.6915ms) +✔ APK fallback cannot cross a same-endpoint reconnect (1271.644708ms) +✔ APK fallback cannot cross a TV switch (1231.408208ms) +✔ an APK result for a closed package cannot replace the next package measurement (1153.79125ms) +✔ report safety: failed sheet overrides old caution catalog and retries honestly (3549.412333ms) +✔ report safety: failed sheet overrides old safe catalog and retries honestly (3524.590459ms) +✔ report safety: failed sheet overrides old never_disable catalog and retries honestly (3558.28575ms) +✔ report safety: late retry cannot cross reopen (1184.084583ms) +✔ report safety: late retry cannot cross package (1499.462916ms) +✔ report safety: late retry cannot cross reconnect (1508.360125ms) +✔ report safety: an absent sheet result retains the catalog fallback without retry (800.065584ms) +✔ report safety: late sheet lookup cannot cross package and overwrite the current result (1149.962583ms) +✔ report safety: late sheet lookup cannot cross reconnect and overwrite the current result (1167.182958ms) +✔ system visibility composes with enabled and disabled filters (1055.821166ms) +✔ search reports empty results and can reveal or clear them (585.977917ms) +✔ filtering never mutates packages and protected-app safety stays enforced (243.427125ms) +✔ katniss is in the shared catalogue (129.8715ms) +✔ katniss is found by package and by name when enabled (0.830292ms) +✔ katniss is found by package and by name when disabled (0.101ms) +✔ a disabled katniss is reachable from the Disabled chip so it can be re-enabled (0.056ms) +✔ not-installed catalogue rows hide by default and are counted as hidden (0.09275ms) +✔ an unread state is unknown: shown under All, never under Enabled (0.047ms) +✔ system packages in Everything else follow the toggle and are counted when hidden (0.056958ms) +✔ a package in both lists is listed once, as recognised (0.073542ms) +✔ validatedStates drops malformed values instead of guessing (0.103625ms) +✔ onDeviceCount leaves out only rows known to be missing (0.073292ms) +✔ installed picker hides systems by default and composes reveal with search (1079.9075ms) +✔ one chosen app makes one backup call for the expected TV and package (177.522542ms) +✔ a delayed A picker read cannot replace the newer B result (210.760333ms) +✔ a backup started on A stays owned by A when it finishes after a switch to B (197.121125ms) +✔ a stale displayed A row cannot start a backup on B (157.462708ms) +✔ a restore confirmation opened on A cannot restore onto B (155.6315ms) +✔ picker and backup failures stay explicit and device-owned (180.094291ms) +✔ disconnected state and APK-only limits are stated plainly (129.711292ms) +✔ Dashboard counts enabled among installed recommended apps (1017.389959ms) +✔ Dashboard treats incomplete package state replies as unavailable (117.863583ms) +✔ a stale same-serial count cannot overwrite a newer generation (137.864ms) +✔ zero recommended candidates opens Optional apps with Keep selected (117.191667ms) +✔ Caution defaults are recommended while Unknown defaults move to Optional (135.172708ms) +✔ protected defaults stay visible and blocked in Optional (158.464625ms) +✔ a plan containing only Unknown defaults opens Optional after safety resolves (110.96125ms) +✔ a user's tab choice survives pending safety resolution (160.312542ms) +✔ optional choices survive tabs and confirmation names every actual action (517.055583ms) +✔ protected and unresolved optional actions remain blocked while Unknown is explicit (144.490584ms) +✔ Restore keeps Recommended and All curated tabs with its prior defaults (151.9645ms) +✔ differing Unknown reasons stay on their own rows (110.683958ms) +✔ Safe defaults are recommended, selected and actionable (110.671333ms) +✔ the confirm summary names the Safe verdict rather than calling it Caution (126.40775ms) +✔ Diagnostics reports the TV's Android version and hardware details (975.370125ms) +✔ Dashboard shows the Android version beside the address (112.783458ms) +✔ A property the TV did not report reads as unknown, never as a neighbour's value (112.732583ms) +✔ A device with no profile says so instead of showing blank rows (115.362375ms) +✔ Missing health data leaves the totals blank rather than inventing them (114.462042ms) +✔ Diagnostics header fits 320px at 100% text with every control usable (1095.75675ms) +✔ Diagnostics header fits 384px at 100% text with every control usable (237.903ms) +✔ Diagnostics header fits 320px at 200% text with every control usable (233.431916ms) +✔ Diagnostics header fits 384px at 200% text with every control usable (244.046375ms) +✔ non-Shield and disconnected headers keep the applicable controls without overflow (240.213ms) +✔ collapses repeated advertisements and separates saved from new hosts (1.663917ms) +✔ the live endpoint is the only connected row (0.1575ms) +✔ a connected TV that does not advertise stays one connected saved row (0.08925ms) +✔ only the saved TV whose id is live claims a shared live endpoint (#115) (0.162208ms) +✔ two id-less saved TVs sharing the live endpoint never both claim connected (#146) (0.128958ms) +✔ a stale advert never names the live row after a different saved TV (#115) (0.102583ms) +✔ a stale advert at an old host never shows the live TV a second time (#115) (0.092375ms) +✔ a legacy advert sharing the live TV's serial prefix is not dropped as stale (#149) (0.059917ms) +✔ a live TV that reports no id is never named after a stale advert at its address (#115) (0.09625ms) +✔ dropping a stale advert keeps the TV now advertising from that address (#115) (0.103583ms) +✔ saved TVs sharing an endpoint that answers unverified each keep their row (#115) (0.088708ms) +✔ an address change reports a new host and a saved endpoint that is absent (0.064541ms) +✔ a rotated port keeps the saved endpoint truthful instead of calling it offline (0.060291ms) +✔ a host advertising only a pairing service is not treated as answering (0.047917ms) +✔ two saved identities at one endpoint stay two real rows (#115) (0.053375ms) +✔ a saved TV that shared an address with one that moved stays in the scan (#115) (0.052375ms) +✔ a stored placeholder id is not a wildcard for serial-less adbd (#116) (0.067167ms) +✔ a saved id only matches its own advertised serial, not a longer one (#117) (6.373375ms) +✔ distinct saved endpoints on one host stay distinct rows (0.049875ms) +✔ malformed advertisements are dropped (0.046917ms) +✔ instance names are only used when they are unambiguous and not adb ids (0.034208ms) +✔ legacy connect ports are the only ones flagged as needing no pairing code (0.051042ms) +✔ an advertised serial verifies a saved TV and names it (0.036541ms) +✔ a verified serial recognizes a saved TV that moved to a new address (0.036292ms) +✔ a serial that matches no saved TV leaves the row unclaimed (0.037667ms) +✔ two TVs advertising only adb ids are each named from their own serial (0.044583ms) +✔ rows are ordered by usability then recency, not by mDNS answer order (0.068791ms) +✔ matrix covers every registered mobile command and opener exactly once (209.971958ms) +✔ store observations never unlock; stale, duplicate and ack events are isolated (0.482541ms) +✔ errors/cancel preserve access; revocation preserves independent legacy source (0.167666ms) +✔ cancel and errors never create access; acknowledgment cannot precede verification (0.123125ms) +✔ simulated UI keeps recovery usable through pending/error/revocation (965.43275ms) +✔ screenshot destination, bytes and Close are truthful at 384px / 100% text (1017.895917ms) +✔ screenshot destination, bytes and Close are truthful at 320px / 200% text (172.530542ms) +✔ screenshot failure creates no preview or false saved result and preserves earlier saved bytes (258.485625ms) +✔ file-copy success identifies the actual private destination and failure adds no copied row (161.094083ms) +✔ report copy and oversized GitHub fallback use preview text and never claim a saved file (799.156125ms) +✔ report clipboard failure exposes the selectable preview without false copy success (772.8005ms) +✔ diagnostic JSON and debug log Copy actions state clipboard-only output and report failures (257.260417ms) +✔ file results belong to the live connection that started them (134.796375ms) +✔ reconnecting to the same endpoint invalidates an old file action (0.119916ms) +✔ a lost connection cannot accept a file result or delete confirmation (0.048583ms) +✔ the catalog is desktop's file, not a copy (207.650708ms) +✔ a search that could not run never summarizes as no matches (0.193875ms) +✔ delete is only offered strictly inside /sdcard (0.196459ms) +✔ Find SmartTube backups searches every catalog dir and copies a hit to the phone (842.750125ms) +✔ an unsearched directory reads as a failed search, never as no matches (#86) (184.835667ms) +✔ a rejected search drops the older answer instead of showing it as current (188.631625ms) +✔ delete asks first, sends the exact /sdcard path, and reloads the folder (513.169875ms) +✔ no delete control outside /sdcard (138.923292ms) +✔ a delete confirmed after a TV switch is not sent to the new TV (179.35175ms) +✔ reconnecting to the same endpoint clears an old delete confirmation (154.888875ms) +✔ a copy completing after a TV switch does not mark the new TV's row copied (205.975458ms) +✔ RAM uses explicit used/total or available complement; zero/full are real values (205.264166ms) +✔ missing and invalid memory is not painted as zero/full (0.415ms) +✔ storage retains reported percentages and distinguishes missing/invalid from zero (0.131125ms) +✔ temperature comparison uses only a matching sensor own threshold (0.389292ms) +✔ reported device status is independent of hottest sensor status; no temperature-derived grade (0.259458ms) +✔ both visible bars mean used, share neutral styling and show 0/50/100 honestly (792.82475ms) +✔ legacy100C stays unknown; a device-reported critical status is shown even at30C (132.164375ms) +✔ below-threshold light/moderate readings do not describe throttling onset or infer HAL severity (0.205875ms) +✔ threshold provenance stays unspecified for hardware, HIDL-like and unknown sources (0.11975ms) +✔ visible thresholds retain reported status and never claim onset or a severity without provenance (137.29375ms) +✔ checked-in Shield meminfo keeps explicit Used despite non-complementary Free accounting (0.810792ms) +✔ Shield accounting renders explicit used/free without changing the source report (116.275041ms) +✔ Setup Wraith reads off / on / risk from the launcher rows (209.378167ms) +✔ a takeover turns Setup Wraith off only when an enabled stock names it (0.159917ms) +✔ Disable stock launcher stays off until the picked app is Home or stock is in the way (0.198292ms) +✔ the stock-disable message never claims what wasn't verified or re-read (0.112583ms) +✔ picker rows hide system apps, search name and package, dedupe and sort (7.82525ms) +✔ source site links are offered only for http(s) URLs (0.175333ms) +✔ Setup Wraith on with stock off warns and turns it off, then re-reads the TV (806.876ms) +✔ Setup Wraith off offers Re-enable through enable_package (133.497959ms) +✔ a not-ok default switch re-reads the TV and offers its diagnostics (207.019084ms) +✔ set any app as Home reports Android's answer, then unlocks the separate stock step (243.0825ms) +✔ Save snapshot first leaves stock alone when the snapshot fails (215.451792ms) +✔ a locked Set as Home opens the paywall and calls nothing else (201.227458ms) +✔ source site hands the launcher's page to the phone's browser, not the app (139.520667ms) +✔ source site copies the link when the browser can't be opened (125.6445ms) +✔ after opening the Play Store the list re-reads until the launcher is installed (157.98375ms) +✔ same-endpoint reconnect cancels confirmations and late launcher results (441.713208ms) +✔ lost connection removes actionable rows and reconnect discards an old list reply (281.021625ms) +✔ old snapshot completion cannot unlock a newer Home action after reconnect (378.657666ms) +✔ stock takeover dialog locks scrolling, traps focus and Escape cancels without a mutation (275.738292ms) +✔ Setup Wraith row uses the guarded helper command and refreshes confirmed success (157.103958ms) +✔ helper row preserves guarded failure: rolled back (158.7225ms) +✔ helper row preserves guarded failure: rollback unconfirmed (158.262292ms) +✔ late helper-row result cannot overwrite a fresh same-endpoint reconnect (547.008292ms) +✔ late helper-row result cannot overwrite a fresh TV switch (587.344459ms) +✔ ordinary stock row keeps its launcher disable command (171.295708ms) +✔ same-update replacement preserves document lock and original opener across an empty stack (960.968458ms) +✔ async resolve settles genuine close before any later replacement (100.322375ms) +✔ async cancel settles genuine close before any later replacement (100.882ms) +✔ async reject settles genuine close before any later replacement (102.762583ms) +✔ genuine empty close clears provenance before an unrelated modal opens (103.86175ms) +✔ nested out-of-order teardown and replacement retain the document opener (100.629375ms) +✔ superseded close callbacks cannot steal focus from a new top modal (110.543333ms) +✔ removed opener and duplicate teardown do not resurrect focus or leak a lock (102.457125ms) +✔ alertdialog without enabled controls receives focus and Escape releases the genuine close (115.714667ms) +✔ closing the focused child then a non-top ancestor restores the surviving middle modal (121.059875ms) +✔ Optimize recommends exactly what desktop pre-selects (200.189959ms) +✔ an uninstall the store can't give back runs as Disable (0.312625ms) +✔ review pill only for resolved, unprotected review rows (0.150083ms) +✔ plan search matches name or package, case-insensitively (0.09225ms) +✔ row safety never reads unresolved as a verdict (0.771458ms) +✔ the sheet sees the plan's state and catalog entry (0.073667ms) +✔ Health: a confirmed catalog app gets the App List's recommendation (0.192458ms) +✔ Health: a name no installed package carries is a process, not an app (0.107417ms) +✔ Health: an unreadable installed list confirms nothing and says so (0.107125ms) +✔ Health: an installed package outside the catalog shows its verdict (0.100208ms) +✔ snapshot preview: Now is what the TV reported, and matching settings read Already set (0.188375ms) +✔ snapshot preview: launcher change, skipped launcher, and an unset Now (0.079083ms) +✔ snapshot preview summary matches desktop's wording (0.065083ms) +✔ Restore enable plans describe a disabled installed app to the detail sheet (0.04625ms) +✔ Optimize Back returns to the screen that opened it (1095.864958ms) +✔ Optimize search filters by name and package id (142.762292ms) +✔ tapping an Optimize row opens the app sheet, not a toggle (165.844ms) +✔ Safe to remove and review chips stay on one line at 384px (145.914ms) +✔ Health suggests per row: app recommendation, process, and package verdicts (176.796333ms) +✔ Health never calls a package-shaped name Not an app when installed apps can't be read (118.63425ms) +✔ Snapshots: Beta, Preview restore, and a real Now column (158.321459ms) +✔ modal sheet blocks background wheel/edge scroll and restores position, focus and existing styles repeatedly (927.938458ms) +✔ modal nested confirmation keeps the page locked and Escape/Tab affect only the top layer (508.36975ms) +✔ modal backdrop, screen teardown and disconnect release the document without leaving a lock (243.433792ms) +✔ modal paywall dismissal retains the underlying app sheet lock (504.948167ms) +✔ modal lock survives out-of-order owner teardown and restores the original opener after its child (147.310458ms) +✔ modal long-sheet keyboard wrap reveals last action and Close without moving the background (453.267833ms) +✔ Free diagnostics reports are explicit, honest and per-interface (1021.874875ms) +✔ resource_sample ignores stale resolve after reconnect to the same serial (155.624958ms) +✔ resource_sample ignores stale reject after reconnect to the same serial (159.303167ms) +✔ media_report ignores stale resolve after reconnect to the same serial (156.674208ms) +✔ media_report ignores stale reject after reconnect to the same serial (158.578417ms) +✔ resource sampling admits one request and no live-refresh polling (161.587958ms) +✔ manual audio toggle preserves unknown and newer encodings through readback (160.273166ms) +✔ playback errors do not replace existing health metrics (126.441292ms) +✔ Free audio writes use the existing paywall and do not fabricate readback (135.659958ms) +✔ late audio mutation failure cannot overwrite another TV's state (162.149416ms) +✔ report fidelity and wrapping at 320px with 100% text (187.607625ms) +✔ report fidelity and wrapping at 384px with 100% text (182.387083ms) +✔ report fidelity and wrapping at 320px with 200% text (182.465208ms) +✔ empty reports and failed rereads retain honest unavailable states and keyboard access (157.946958ms) +✔ the shared sideload catalog is served to the webview (164.160542ms) +✔ uninstall is only recommended when the store can give the app back (0.785459ms) +✔ no recommendation label ever says Remove (0.199708ms) +✔ unknown and unresolved safety never read as a default action (0.065584ms) +✔ safety labels match the desktop vocabulary (0.065833ms) +✔ an app report carries no device identifiers and is labelled mobile (0.895125ms) +✔ keeps mixed saved and new TVs and deduplicates repeated advertisements (8.016208ms) +✔ moves one matching hardware identity across an address change (0.183417ms) +✔ moves one matching hardware identity across a port change (0.118458ms) +✔ keeps different hardware identities that claim the same address (0.126292ms) +✔ does not merge a saved id-less row into a live identified TV (0.196166ms) +✔ does not merge an id-less live TV into a saved identified row (0.174ms) +✔ matches id-less rows only at the exact endpoint (0.159208ms) +✔ forgets identified TVs by id and id-less TVs by exact endpoint (0.202583ms) +✔ returns cached names only for an unambiguous or matching identity (0.14175ms) +✔ a reused address never lends an identified TV's name to a TV that reports no id (#117) (0.103792ms) +✔ a stored placeholder id becomes an id-less row with its own stable key, never merged with another (#116, #146) (0.152333ms) +✔ reconnecting a TV that reports a placeholder id refreshes its row instead of appending (#116) (0.081333ms) +✔ never guesses which of two id-less TVs at one endpoint just reconnected (#146) (0.203ms) +✔ sorts by lastUsed and truncates the oldest saved rows (0.244208ms) +✔ ignores corrupt records and normalizes recoverable stored values (0.081208ms) +✔ builds stable identity keys for host collisions and identical endpoints (0.074458ms) +✔ a selected reconnect token activates only its saved row (0.049333ms) +✔ matches the current TV by verified id or an exact id-less endpoint (0.070083ms) +✔ only an unambiguous match counts as the live connection (#146) (0.069ms) +✔ forgets the exact selected identity at an identical endpoint (0.119959ms) +✔ auto-dials only when the saved list truly has one entry (0.046292ms) +✔ a lone id-less match stores a fingerprint from the live device's properties (#154) (0.073042ms) +✔ a different model or manufacturer at a saved id-less row's address is not silently inherited (#154) (0.119125ms) +✔ a manufacturer-only disagreement also counts as a mismatch (#154) (0.058ms) +✔ an empty saved fingerprint (an older row, or a device that reported nothing) never blocks a match (#154) (0.610459ms) +✔ a matching fingerprint still refreshes the row normally, and is never treated as proof (#154) (0.11125ms) +✔ a hardware-identified reconnect is never second-guessed by fingerprint (#154) (0.074708ms) +✔ a stored fingerprint survives a read/normalize round trip and sanitizes stray fields (0.06075ms) +✔ hardware Back and direct navigation keep Optimize cancellable (1405.446459ms) +✔ Tweaks is a bottom tab and Optimize is a pushed screen; Back unwinds both to Home (183.47525ms) +✔ remote queue is discarded after navigation and a TV switch (193.043667ms) +✔ ordinary remote taps cannot accumulate an unlimited backlog (150.890833ms) +✔ failed health refresh preserves the last report and exposes the error (101.301291ms) +✔ one failed recovery stays lost until an explicit retry (100.533375ms) +✔ a silent recovery onto a different device does not inherit the old saved row or its name (#154 follow-up) (109.009042ms) +✔ a silent recovery's different-device note shows immediately on whatever screen is already open, and clears itself (#154 follow-up) (4119.399042ms) +✔ the Devices screen hands its different-device note on to Dashboard instead of losing it on navigate (#154 follow-up) (4173.9985ms) +✔ canceled connect cannot update the session when its old reply arrives (120.933083ms) +✔ stale reboot completion cannot disconnect a newer session (102.632542ms) +✔ cold single saved TV waits for its profile, then opens a clean dashboard stack (104.866166ms) +✔ cold multi-TV launch waits for a choice and opens the chosen TV (189.623333ms) +✔ cold saved TV missing profile stays on onboarding (103.313125ms) +✔ cold saved TV wrong profile stays on onboarding (104.67225ms) +✔ cold saved TV rejected request stays on onboarding (104.017666ms) +✔ authorization failure stays honest and a deliberate retry can open Dashboard (150.88925ms) +✔ explicit disconnect suppresses automatic saved-TV redial (140.985666ms) +✔ first-time connection keeps the connected interstitial (165.011541ms) +✔ add-TV connection keeps the connected interstitial (142.544375ms) +✔ saved TV success opens its dashboard once without canceling the connection (114.568541ms) +✔ leaving onboarding cancels a saved TV connection that is still pending (142.563ms) +✔ cancel and retry of the same saved TV ignores the first completion (197.867167ms) +✔ a late transport error from A cannot start recovery on B (180.425208ms) +✔ a health load that lands after its profile vanished cannot wedge later loads (#118) (98.2725ms) +✔ cancelling a saved-TV reconnect never reports a connection failure (#118) (183.069292ms) +✔ Diagnostics keeps its safety badges when two top apps swap rank (#118) (162.258458ms) +✔ diagnostics: disconnect requires a new sheet selection before any action (1671.738291ms) +✔ diagnostics: reconnect requires a new sheet selection before any action (826.86325ms) +✔ diagnostics: switch requires a new sheet selection before any action (840.3875ms) +✔ diagnostics: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (905.70925ms) +✔ diagnostics: Disable nested confirmation is cleared by same-endpoint reconnect (509.03675ms) +✔ diagnostics: Uninstall nested confirmation is cleared by same-endpoint reconnect (518.383166ms) +✔ diagnostics: paywall and pending measurements cannot follow the opening identity (563.079541ms) +✔ optimize: disconnect requires a new sheet selection before any action (813.313666ms) +✔ optimize: reconnect requires a new sheet selection before any action (821.966875ms) +✔ optimize: switch requires a new sheet selection before any action (819.019208ms) +✔ optimize: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (887.705875ms) +✔ optimize: Disable nested confirmation is cleared by same-endpoint reconnect (494.009583ms) +✔ optimize: Uninstall nested confirmation is cleared by same-endpoint reconnect (492.526125ms) +✔ optimize: paywall and pending measurements cannot follow the opening identity (563.883125ms) +✔ diagnostics: a failed reconnect or transient liveness loss still invalidates the opening (240.1055ms) +✔ optimize: a failed reconnect or transient liveness loss still invalidates the opening (233.174542ms) +✔ diagnostics: Disable landing with a false response discards the stale selection (801.799958ms) +✔ diagnostics: Uninstall landing with a false response discards the stale selection (825.158875ms) +✔ optimize: Disable landing with a false response discards the stale selection (832.601458ms) +✔ optimize: Uninstall landing with a false response discards the stale selection (808.642667ms) +✔ Restore detail offers Enable and dispatches only enable_package (498.736ms) +✔ diagnostics: successful uninstall retains the explicit Reinstall path (790.127167ms) +✔ optimize: successful uninstall retains the explicit Reinstall path (782.347583ms) +✔ Free users see every current value, including hooks, mic and screensaver (982.101042ms) +✔ Turning the hooks back on is free; turning them off opens the paywall (184.226ms) +✔ hooks row is hidden when the package is missing and on non-Shield read failures (274.451083ms) +✔ Assistant button revokes the mic through set_app_permission and re-reads (133.137833ms) +✔ screensaver: Basic Daydream, Off, then Restore previous brings the vendor one back (206.281416ms) +✔ a refused screensaver flag stops before the component is written (127.376458ms) +✔ Dashboard has one Optimize call to action and Tune tiles for Tweaks and Launcher (183.188417ms) +✔ Pro dashboard drops the PRO tags (112.556458ms) +✔ More is titled More, lists Optimize, and no longer duplicates the Tweaks tab (128.327542ms) +✔ endpoint reuse never restores another TV's screensaver and interrupted writes stop (338.163375ms) +✔ verified TV retains originals across a rotated port, replacement hardware does not (209.988417ms) +✔ a reconnect between screensaver writes prevents the second write (263.341541ms) +✔ current gap: Pro loss blocks restoring an app-changed screensaver even with the original known (180.503209ms) +✔ Free users retain emergency recovery without an unlock or payment (150.726541ms) +✔ scaling readback matches both resolution and density and clears stale selection on failure (188.492291ms) +✔ Custom DNS editing does not claim the TV has applied it; failed read clears selection (161.670791ms) +✔ equivalent Android animation number formats select the same speed (156.692208ms) +✔ DNS confirmed changes survive navigation and reconnect, while unset stays explicit (190.208334ms) +✔ Daydream component refuse never enables an off screensaver with previous component (136.162833ms) +✔ Daydream component reject never enables an off screensaver with previous component (133.16825ms) +✔ Daydream component refuse never enables an off screensaver with unset component (133.165333ms) +✔ Daydream component reject never enables an off screensaver with unset component (133.661834ms) +✔ Basic Daydream does not enable after a refused component; failed enable retains off with configured component (158.016291ms) +✔ Restore previous does not enable after a refused component; failed enable retains off with configured component (189.49075ms) +✔ Off keeps the switch disabled if component clearing fails (135.907709ms) +✔ a reconnect after the Daydream component write prevents enabling (157.020042ms) +✔ screensaver names preserve raw known, custom, disabled and missing component states (1018.913041ms) +✔ catalog failure stays custom and late old catalog names cannot cross connections (228.154625ms) +✔ density-only readback shows the override instead of physical density or device default (137.710875ms) +✔ density-only matching preset, absent size and reset readbacks retain their distinct states (215.639666ms) +✔ scaling preset row fits 320px at 100% text and preserves controls (1449.476584ms) +✔ scaling preset row fits 384px at 100% text and preserves controls (1474.173208ms) +✔ scaling preset row fits 320px at 200% text and preserves controls (1503.432625ms) +✔ scaling preset row fits 384px at 200% text and preserves controls (1500.642166ms) +✔ records uncatalogued packages and unresolved native processes distinctly (1.895584ms) +✔ deduplicates repeat polls, saturates counts, and separates device context (0.1745ms) +✔ drops stale scheduled observations after a device changes (0.083542ms) +✔ rejects paths, addresses, control strings, and malformed package tokens (0.10475ms) +✔ bounds record count and serialized byte size while marking truncation (0.2385ms) +✔ fails harmlessly for corrupt or full storage (0.120167ms) +✔ exports an immutable exact snapshot with allowlisted fields only (0.146208ms) +✔ clear prevents a queued old save from resurrecting records (0.080375ms) +✔ clear failures remain visible and do not claim deletion (0.091667ms) +ℹ tests 343 +ℹ suites 0 +ℹ pass 343 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 119777.281375 diff --git a/v2/mobile/evidence/report-safety-2026-10-17/focused.txt b/v2/mobile/evidence/report-safety-2026-10-17/focused.txt new file mode 100644 index 00000000..e997e3ac --- /dev/null +++ b/v2/mobile/evidence/report-safety-2026-10-17/focused.txt @@ -0,0 +1,17 @@ +✔ report safety: failed sheet overrides old caution catalog and retries honestly (5026.216916ms) +✔ report safety: failed sheet overrides old safe catalog and retries honestly (3518.316125ms) +✔ report safety: failed sheet overrides old never_disable catalog and retries honestly (3563.247833ms) +✔ report safety: late retry cannot cross reopen (1233.52775ms) +✔ report safety: late retry cannot cross package (1501.292375ms) +✔ report safety: late retry cannot cross reconnect (1548.170583ms) +✔ report safety: an absent sheet result retains the catalog fallback without retry (800.531833ms) +✔ report safety: late sheet lookup cannot cross package and overwrite the current result (1141.48425ms) +✔ report safety: late sheet lookup cannot cross reconnect and overwrite the current result (1157.218083ms) +ℹ tests 9 +ℹ suites 0 +ℹ pass 9 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 19789.955709 diff --git a/v2/mobile/evidence/report-safety-2026-10-17/report-safety-simulated.webm b/v2/mobile/evidence/report-safety-2026-10-17/report-safety-simulated.webm new file mode 100644 index 00000000..2abddaec Binary files /dev/null and b/v2/mobile/evidence/report-safety-2026-10-17/report-safety-simulated.webm differ diff --git a/v2/mobile/evidence/report-safety-2026-10-17/report-unavailable-simulated.png b/v2/mobile/evidence/report-safety-2026-10-17/report-unavailable-simulated.png new file mode 100644 index 00000000..d306b0bb Binary files /dev/null and b/v2/mobile/evidence/report-safety-2026-10-17/report-unavailable-simulated.png differ diff --git a/v2/mobile/evidence/report-safety-2026-10-17/retry-unknown-simulated.png b/v2/mobile/evidence/report-safety-2026-10-17/retry-unknown-simulated.png new file mode 100644 index 00000000..20fb86f2 Binary files /dev/null and b/v2/mobile/evidence/report-safety-2026-10-17/retry-unknown-simulated.png differ diff --git a/v2/mobile/evidence/report-safety-2026-10-17/sheet-unavailable-simulated.png b/v2/mobile/evidence/report-safety-2026-10-17/sheet-unavailable-simulated.png new file mode 100644 index 00000000..69e20a4e Binary files /dev/null and b/v2/mobile/evidence/report-safety-2026-10-17/sheet-unavailable-simulated.png differ diff --git a/v2/mobile/evidence/report-safety-2026-10-17/unavailable-report-simulated.json b/v2/mobile/evidence/report-safety-2026-10-17/unavailable-report-simulated.json new file mode 100644 index 00000000..8383b880 --- /dev/null +++ b/v2/mobile/evidence/report-safety-2026-10-17/unavailable-report-simulated.json @@ -0,0 +1,36 @@ +{ + "schema_version": 1, + "generated_at": "2026-10-17T14:59:26.022Z", + "truncated": false, + "source": "mobile_app_report", + "records": [ + { + "kind": "installed_package", + "token": "com.google.android.katniss", + "reason": "user_report_other", + "app_version": "0.1.0", + "registry_version": null, + "device_family": "shield", + "device_os": "11", + "first_seen": "2026-10-17T14:59:26.022Z", + "last_seen": "2026-10-17T14:59:26.022Z", + "count": 1, + "app_name": "Google Assistant (Katniss)", + "current_verdict": "unavailable", + "verdict_source": null, + "note": null, + "state": { + "installed": true, + "enabled": false, + "running": false, + "ram_mb": null, + "storage": { + "source": "diskstats", + "app_bytes": 12582912, + "data_bytes": 3145728, + "cache_bytes": 1048576 + } + } + } + ] +} diff --git a/v2/mobile/evidence/report-safety-2026-10-17/unknown-report-simulated.json b/v2/mobile/evidence/report-safety-2026-10-17/unknown-report-simulated.json new file mode 100644 index 00000000..94c4620e --- /dev/null +++ b/v2/mobile/evidence/report-safety-2026-10-17/unknown-report-simulated.json @@ -0,0 +1,36 @@ +{ + "schema_version": 1, + "generated_at": "2026-10-17T14:59:28.729Z", + "truncated": false, + "source": "mobile_app_report", + "records": [ + { + "kind": "installed_package", + "token": "com.google.android.katniss", + "reason": "user_report_not_listed", + "app_version": "0.1.0", + "registry_version": null, + "device_family": "shield", + "device_os": "11", + "first_seen": "2026-10-17T14:59:28.729Z", + "last_seen": "2026-10-17T14:59:28.729Z", + "count": 1, + "app_name": "Google Assistant (Katniss)", + "current_verdict": "unknown", + "verdict_source": "no_record", + "note": null, + "state": { + "installed": true, + "enabled": false, + "running": false, + "ram_mb": null, + "storage": { + "source": "diskstats", + "app_bytes": 12582912, + "data_bytes": 3145728, + "cache_bytes": 1048576 + } + } + } + ] +} diff --git a/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/README.md b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/README.md new file mode 100644 index 00000000..8e75c822 --- /dev/null +++ b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/README.md @@ -0,0 +1,21 @@ +# Display scaling preset wrapping — 2026-10-17 + +Original base: `b095f697b2a24c3d2fb8f56cef7056586b6355ef`. Now integrates mobile-parity-pass `ba7c9d78d31b0a579f5ffd03d92cd403ebf47227`, including reviewed and merged density summary PR194 and screensaver-label PR195. PR196 remains unmerged. The scoped reviewed layout code is unchanged; only the merged density summary branch is added. + +The existing display-scaling preset row overflows at 320px with all card text doubled: 271px of content inside a 240px row in this fixture. [Before screenshot](before-320-200-simulated.png) clips Reset; the [pre-fix regression](reproduction-before.txt) fails on those dimensions. + +Only this segmented row now permits wrapping, uses intrinsic button widths and retains minimum 44×44px targets. At 320px/200%, Reset moves onto a second line instead of clipping. Other segmented controls are untouched. Labels, preset values, active selection, confirmation wording, readback and setting writes are unchanged; PR194's merged summary fix is now included without changing the scoped layout rules. + +Four browser fixtures at 320/384px and 100/200% card text verify row and card horizontal fit, all labels, minimum touch targets, viewport-contained buttons, keyboard Tab order, Enter opening every confirmation and Cancel restoring focus. The same confirmed 1080p selection remains active after all cancellations, and no setting/scaling writes are issued. + +Simulated row captures: [320px normal](after-320-100-simulated.png), [320px doubled text](after-320-200-simulated.png), [384px normal](after-384-100-simulated.png), [384px doubled text](after-384-200-simulated.png). These crop the preset row after scrolling it into view and retain visible keyboard focus on Reset. Enlarged text doubles computed card text sizes, not just the root font. This is browser simulation rather than Android accessibility or physical touch verification. Viewport zoom restrictions were not added. + +Focused layout tests: 4/4 passed. All 304 mobile tests passed, Svelte check reports 0 errors/0 warnings and the production build passed; see [validation receipt](validation.json). No Rust, device, catalog, icon, native-export, permission or setting-value changes occurred. + +Integration provenance: all existing preset screenshots are retained byte-for-byte. Their fixture uses both size and density overrides, so PR194’s density-only summary branch does not change these pixels. Imported PR194 captures of the old full-card overflow remain historical evidence from before this CSS fix; the preset-wrap captures demonstrate the prepared fix. + +Current integration on `35b5deaa`: all 306 mobile tests passed, Svelte check reports 0 errors/0 warnings and the production build passed. Existing 304-test results above describe the original review head. Both density readback and preset-layout regressions are retained; no screenshots were regenerated. + +Latest integration provenance: screensaver naming and raw details are imported unchanged from merged PR195. The scaling preset class and CSS rules remain exactly the reviewed layout delta; removing only those additions reproduces the current base source byte-for-byte. Existing preset PNGs remain unchanged because the screenshots crop only the scaling row and PR195 changes a different card. + +Latest integration on `ba7c9d78`: all 308 combined mobile tests passed, Svelte check reports 0 errors/0 warnings and the production build passed. Prior 304/306-test receipts above are historical validations. No focused CSS changes or new visual captures were required. diff --git a/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-320-100-simulated.png b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-320-100-simulated.png new file mode 100644 index 00000000..75341ab9 Binary files /dev/null and b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-320-100-simulated.png differ diff --git a/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-320-200-simulated.png b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-320-200-simulated.png new file mode 100644 index 00000000..67ed65e3 Binary files /dev/null and b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-320-200-simulated.png differ diff --git a/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-384-100-simulated.png b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-384-100-simulated.png new file mode 100644 index 00000000..b816deac Binary files /dev/null and b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-384-100-simulated.png differ diff --git a/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-384-200-simulated.png b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-384-200-simulated.png new file mode 100644 index 00000000..8f38b33e Binary files /dev/null and b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/after-384-200-simulated.png differ diff --git a/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/before-320-200-simulated.png b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/before-320-200-simulated.png new file mode 100644 index 00000000..3701ed12 Binary files /dev/null and b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/before-320-200-simulated.png differ diff --git a/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/reproduction-before.txt b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/reproduction-before.txt new file mode 100644 index 00000000..adb94694 --- /dev/null +++ b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/reproduction-before.txt @@ -0,0 +1,31 @@ +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ scaling preset row fits 320px at 200% text and preserves controls (1053.578375ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1328.444041 + +✖ failing tests: + +test at tests/tweaks-tab.test.mjs:503:72 +✖ scaling preset row fits 320px at 200% text and preserves controls (1053.578375ms) + AssertionError [ERR_ASSERTION]: {"width":240,"content":271} + at TestContext. (file:///v2/mobile/tests/tweaks-tab.test.mjs:520:10) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } diff --git a/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/validation.json b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/validation.json new file mode 100644 index 00000000..abed82d0 --- /dev/null +++ b/v2/mobile/evidence/scaling-preset-wrap-2026-10-17/validation.json @@ -0,0 +1,56 @@ +{ + "date": "2026-10-17", + "base": "ba7c9d78d31b0a579f5ffd03d92cd403ebf47227", + "source_delta": "One scoped class and CSS wrapping/intrinsic width/minimum touch target rules; no logic or value changes.", + "before_failure": { + "viewport": 320, + "text_percent": 200, + "row_width": 240, + "content_width": 271 + }, + "focused_layout_tests": { + "passed": 4, + "failed": 0 + }, + "full_mobile_tests": { + "passed": 308, + "failed": 0 + }, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "build": "passed", + "fixtures": [ + "320px 100%", + "384px 100%", + "320px 200%", + "384px 200%" + ], + "verified": [ + "all four labels and minimum 44x44 targets", + "row/card horizontal fit and viewport-contained controls", + "Tab order and Enter/Cancel for every confirmation", + "focus return and 1080p selection retained", + "no setting/scaling writes" + ], + "isolated_from": [], + "limitations": [ + "Synthetic browser only", + "No physical accessibility, touch or Android acceptance claimed" + ], + "previous_validation": { + "base": "b095f697b2a24c3d2fb8f56cef7056586b6355ef", + "full_mobile_passed": 304, + "head": "1c5bc6b76ef6dc851d8c34a8b477775ef77b3539" + }, + "integration_delta": "Merge reviewed screensaver PR195, reconcile checklist completion and update provenance. CSS/class delta is unchanged; removing only it reproduces the current base Tweaks source exactly.", + "unchanged_visual_evidence": "Original layout PNGs retained byte-for-byte; merged screensaver names change a different card.", + "integration_history": [ + { + "base": "35b5deaa044bb2218c720eeaa64858486ffc195f", + "full_mobile_passed": 306, + "head": "02e2747b83dcea4a28c3f9a7b528cd580e8bcc15" + } + ] +} diff --git a/v2/mobile/evidence/screensaver-labels-2026-10-17/README.md b/v2/mobile/evidence/screensaver-labels-2026-10-17/README.md new file mode 100644 index 00000000..32c9d530 --- /dev/null +++ b/v2/mobile/evidence/screensaver-labels-2026-10-17/README.md @@ -0,0 +1,19 @@ +# Screensaver display names — 2026-10-17 + +Original base: `b095f697b2a24c3d2fb8f56cef7056586b6355ef`. Now integrates mobile-parity-pass `35b5deaa044bb2218c720eeaa64858486ffc195f`, including reviewed and merged density summary PR194. Scaling preset-row PR196 remains separate. Screensaver source is unchanged from the original PR195 head apart from the upstream density summary branch. + +Screensaver Current and Restore previous now use the embedded catalog's name for the exact owner package of a single well-formed Android component. For the existing checked-in `com.google.android.backdrop/.Backdrop` fixture, the common catalog supplies Ambient Mode. Basic Daydream retains its existing exact-component label. Unknown packages, lookalike package prefixes, malformed strings and multiple-component lists remain Custom screensaver; names are not guessed from class/package suffixes. Off and missing values retain their existing display states. + +A native keyboard-accessible Component details disclosure keeps both raw Current and Previous values visible on request, including the configured component while Off. Missing null is shown as Not reported; an explicit empty value is None set. Friendly names describe the catalog package, not proof of an installed service or a service-specific display label. + +The existing local `app_list_for_device` command reads compiled-in app-list data only; it performs no ADB/device lookup. Its result uses the same load-generation and connection guard as the other Tweaks reads, and names clear on connection changes. Catalog errors degrade to Custom screensaver. No catalog data, setting values, original-value identity rules, preset selection, write order, lock/paywall behavior or restore writes changed. + +Tests use the actual checked-in Ambient Mode catalog entry and cover known/custom/Off/missing, lookalike and multiple-component values, raw details via keyboard, catalog error, stale catalog responses across reconnect, exact existing restoration and partial-write guards. All new display fixture flows assert no setting writes. Existing write tests still verify the exact component strings and write order. + +Screenshots at 320px show [known](known-320-simulated.png), [custom long component](custom-320-simulated.png), [Off with retained raw component](off-320-simulated.png) and [missing](missing-320-simulated.png). These are synthetic browser fixtures. No phone/TV query, install, permission, launcher-icon or native-export change occurred. No physical accessibility or Android acceptance is claimed. + +Focused suite with evidence: 29/29 passed; Svelte check 0 errors/0 warnings and production build passed. All 302 mobile tests passed; see [validation receipt](validation.json). No Rust source changed. + +Integration provenance: the existing screensaver PNGs are retained byte-for-byte. PR194 only changes the Display scaling summary, outside this captured card; catalog data, screensaver fixture values, raw details and naming behavior are unchanged. Both density readback tests and screensaver tests are preserved. + +Current integration on `35b5deaa`: all 304 mobile tests passed, Svelte check reports 0 errors/0 warnings and the production build passed. Original 302-test and 29-test evidence receipts above describe the original PR195 head. Naming/state behavior is unchanged; source review remains pending and this PR is unmerged. diff --git a/v2/mobile/evidence/screensaver-labels-2026-10-17/custom-320-simulated.png b/v2/mobile/evidence/screensaver-labels-2026-10-17/custom-320-simulated.png new file mode 100644 index 00000000..f683d5e8 Binary files /dev/null and b/v2/mobile/evidence/screensaver-labels-2026-10-17/custom-320-simulated.png differ diff --git a/v2/mobile/evidence/screensaver-labels-2026-10-17/known-320-simulated.png b/v2/mobile/evidence/screensaver-labels-2026-10-17/known-320-simulated.png new file mode 100644 index 00000000..7db2d947 Binary files /dev/null and b/v2/mobile/evidence/screensaver-labels-2026-10-17/known-320-simulated.png differ diff --git a/v2/mobile/evidence/screensaver-labels-2026-10-17/missing-320-simulated.png b/v2/mobile/evidence/screensaver-labels-2026-10-17/missing-320-simulated.png new file mode 100644 index 00000000..45fb97ad Binary files /dev/null and b/v2/mobile/evidence/screensaver-labels-2026-10-17/missing-320-simulated.png differ diff --git a/v2/mobile/evidence/screensaver-labels-2026-10-17/off-320-simulated.png b/v2/mobile/evidence/screensaver-labels-2026-10-17/off-320-simulated.png new file mode 100644 index 00000000..5749dc1f Binary files /dev/null and b/v2/mobile/evidence/screensaver-labels-2026-10-17/off-320-simulated.png differ diff --git a/v2/mobile/evidence/screensaver-labels-2026-10-17/validation.json b/v2/mobile/evidence/screensaver-labels-2026-10-17/validation.json new file mode 100644 index 00000000..7355b382 --- /dev/null +++ b/v2/mobile/evidence/screensaver-labels-2026-10-17/validation.json @@ -0,0 +1,34 @@ +{ + "date": "2026-10-17", + "base": "35b5deaa044bb2218c720eeaa64858486ffc195f", + "full_mobile_tests": { + "passed": 304, + "failed": 0 + }, + "focused_with_evidence": { + "passed": 29, + "failed": 0 + }, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "build": "passed", + "raw_values": "Preserved in native details disclosure; existing restoration/write-order tests unchanged in behavior.", + "name_source": "Existing embedded app_list_for_device command; exact package match of one valid flattened component; no device lookup.", + "limitations": [ + "Simulated browser only", + "No phone/TV operation", + "Not a claim of an installed service or a service-specific display name" + ], + "isolated_from": [ + "PR196 scaling preset-row CSS slice" + ], + "previous_validation": { + "base": "b095f697b2a24c3d2fb8f56cef7056586b6355ef", + "full_mobile_passed": 302, + "head": "07fb26475d6302972a1c70094fd2c47d40c415fc" + }, + "integration_delta": "Merge PR194 density summary and keep both regression sets; reconcile merged checklist completion and evidence provenance. Screensaver implementation is unchanged apart from upstream density summary.", + "unchanged_visual_evidence": "All original screensaver PNGs retained byte-for-byte; the density summary is outside the captured card." +} diff --git a/v2/mobile/evidence/screenshot-retention-2026-10-17/README.md b/v2/mobile/evidence/screenshot-retention-2026-10-17/README.md new file mode 100644 index 00000000..914e0b8e --- /dev/null +++ b/v2/mobile/evidence/screenshot-retention-2026-10-17/README.md @@ -0,0 +1,17 @@ +# Screenshot retention — 2026-10-17 + +Originally based on `dccfe99a805ffcb0c11f32a4fa9f227306ba1cf3`; now integrates `mobile-parity-pass` `9265e5fdbf4c9c73f7cea5522e50c50dbcd90fe0`, including reviewed header PR191 and merged RAM PR192. PR193 remains unmerged. Storage behavior and fixtures are unchanged by integration. Final independent source review found no code blocker and all six pre-integration CI jobs passed; local inspection of tempfile 3.27.0 confirms Windows omits replacement flags and the Unix/Android fallback uses hard-link then best-effort unlink. These source checks are not actual platform acceptance. Header evidence is retained from PR191; no new UI pixels are introduced by this storage fix. + +The previous shared `take_screenshot` generated a filename at second resolution and used `tokio::fs::write`, so a second capture for the same serial/time replaced the earlier PNG. It now stages complete bytes in the same directory, then uses `NamedTempFile::persist_noclobber` to claim the original filename without replacing an existing destination or bounded numeric suffixes (2) through (1000). A racing capture retries the next candidate. There is no preflight existence-check race. The existing `tempfile` dependency moves from dev-only to production; mobile already uses the same facility for downloaded files. + +The directory, first filename format, PNG validation, and `{ path, base64 }` response remain unchanged. Returned path names the actual published file; base64 still encodes the captured bytes. Existing captures are neither deleted nor migrated. Partial write, staging/publication errors and name exhaustion return errors before any success response; temporary-file ownership attempts cleanup of failed staging on a best-effort basis. On some platforms tempfile uses a non-overwriting hard-link fallback: if unlink fails, an additional temporary link can remain even after the complete destination is saved. This does not guarantee universal atomic rename, power-loss durability or cleanup under every filesystem failure. + +Deterministic tests in `crates/core/src/commands/screenshot.rs` cover sequential identical timestamps, twelve concurrent fixed-time saves beside an existing capture, distinct returned paths and exact bytes, injected failure after a partial write, all 1000 names occupied, a missing destination directory and (on Unix) an existing symlink whose target must remain untouched. Existing PNG rejection tests remain intact. + +Focused screenshot tests: 10/10. Core fmt, all-target Clippy with warnings denied, and 465 core tests passed on macOS. Host desktop tests passed 83 with one pre-existing ignored test; mobile Rust tests passed 26. Both host crates passed all-target Clippy with warnings denied. Full browser-suite results are reported in the handoff. Existing browser export tests cover truthful paths/base64, Close retention and failure without a new success receipt, at 384px and 320px/200% simulated text. + +This is filesystem behavior with no visible UI change, so new UI screenshots/video would not demonstrate the fix. See the existing [simulated export UI evidence](../export-discoverability-2026-10-17/README.md) for the unchanged contract. No phone/TV query, Android build/install, storage permission, sharing, security setting or credential operation was performed. macOS host tests do not establish Windows/Linux/Android filesystem coverage. + +Integration validation on base `5e80c0e`: 298/298 mobile browser tests, Svelte 0 errors/0 warnings, production build, workspace fmt and all three affected Rust crates’ all-target Clippy passed. Rust tests: core 465, mobile 26, desktop 83 passed with one existing ignored live-device test. See [integration receipt](integration-validation.json). + +Current integration validation on base `9265e5fdbf4c9c73f7cea5522e50c50dbcd90fe0`: 300/300 mobile browser tests, Svelte 0 errors/0 warnings, production build, workspace fmt and all three affected Rust crates’ all-target Clippy passed. Rust tests remain core 465, mobile 26, desktop 83 passed with one existing ignored live-device test. The approved screenshot source and dependency declaration match `6a43e6c` byte-for-byte. RAM code/tests/evidence are imported unchanged from merged PR192. Only checklist reconciliation and this validation receipt are new integration work; no visual evidence was regenerated. diff --git a/v2/mobile/evidence/screenshot-retention-2026-10-17/integration-validation.json b/v2/mobile/evidence/screenshot-retention-2026-10-17/integration-validation.json new file mode 100644 index 00000000..d0298909 --- /dev/null +++ b/v2/mobile/evidence/screenshot-retention-2026-10-17/integration-validation.json @@ -0,0 +1,38 @@ +{ + "date": "2026-10-17", + "base": "9265e5fdbf4c9c73f7cea5522e50c50dbcd90fe0", + "reviewed_storage_head": "6a43e6c464369733f6898b3da5a746a2b89fcced", + "changes": "Merge approved RAM base, preserve both checklist completion entries and update evidence receipts; approved screenshot source and dependency declaration are byte-for-byte unchanged.", + "mobile_browser_tests": { + "passed": 300, + "failed": 0 + }, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "mobile_build": "passed", + "rust_fmt": "passed", + "rust_clippy_all_targets_warnings_denied": [ + "shield-optimizer-core", + "shield-optimizer-v2", + "atv-optimizer-mobile" + ], + "rust_tests": { + "core_passed": 465, + "mobile_passed": 26, + "desktop_passed": 83, + "desktop_ignored_live_test": 1 + }, + "visual_evidence": "Existing header, RAM and screenshot-contract evidence retained unchanged from approved branches; no new UI or fixture changes.", + "limitations": [ + "macOS host and simulated browser only", + "Android acceptance open", + "Cleanup best effort; no universal atomic rename or power-loss durability guarantee" + ], + "previous_integration": { + "base": "5e80c0efd6620060bb03d5c354815990303628ab", + "browser_passed": 298, + "head": "6a43e6c464369733f6898b3da5a746a2b89fcced" + } +} diff --git a/v2/mobile/evidence/settings-polish-2026-10-17/.gitignore b/v2/mobile/evidence/settings-polish-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/settings-polish-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/settings-polish-2026-10-17/README.md b/v2/mobile/evidence/settings-polish-2026-10-17/README.md new file mode 100644 index 00000000..b548503c --- /dev/null +++ b/v2/mobile/evidence/settings-polish-2026-10-17/README.md @@ -0,0 +1,36 @@ +# Mobile settings polish — simulated evidence + +Branch base: `mobile-parity-pass` at `5f4fc141ceda0307cb86b68bfc50433b9ffcc49d`. + +These are synthetic TV replies rendered in Chromium on macOS at 384×812. No phone or TV was connected; no DNS, background limits, display settings, installed applications, or permissions were changed on a real device. Fixtures contain no personal/device data. + +- [Settings readback screenshot](settings-simulated.png): 1080p selected for an override of 1920×1080 at density 320; numerically equal animation values; cached cap 32 independently of legacy stored value 2. +- [DNS draft screenshot](dns-draft-simulated.png): opening Custom leaves Automatic selected until confirmed readback changes. The example hostname is reserved `.test` data. +- [Short settings interaction video](settings-simulated.webm): same mock fixture, opening/editing the Custom DNS draft and returning to Automatic. +- [Launcher optical comparison](icon-optical-comparison.png): current adaptive artwork versus a proposed 3dp downward translation inside its 108dp canvas, approximately 1.8 CSS pixels at 64px. Circle, rounded square and squircle are illustrative masks, not actual launcher captures. Existing artwork, safe-zone scale and generated icon assets are unchanged pending visual review. + +Reproduce from `v2/mobile` with installed dependencies and Playwright Chromium: + +```sh +MOBILE_POLISH_EVIDENCE=1 node --test tests/tweaks-tab.test.mjs +npm test +npm run check +npm run build +``` + +The opt-in evidence test is in `tests/tweaks-tab.test.mjs`; the ordinary suite does not record video. Raw temporary recordings are ignored; the named video is the reviewed deliverable. + +The Android host now applies system-bar and cutout insets as native container padding and removes only those inset types before dispatching to web content. Keyboard insets remain available. This follows [Android's WebView inset handling guidance](https://developer.android.com/develop/ui/views/layout/webapps/understand-window-insets). Browser evidence cannot prove native inset dispatch, keyboard behavior, physical pinch zoom, or Android accessibility scaling. Pinch zoom and text scaling settings have not been disabled or overridden; the Remote gesture pad retains its existing localized touch handling. + +The two supplied phone screenshots were not visually inspected: current Library helper materialization was unavailable. Root causes and fixes above are from code and reproducible mock readbacks, not assertions about screenshot pixels. A phone check remains necessary for status/navigation bars, keyboard, rotation, pinch/scroll interaction, accessibility text size, and actual launcher optical preference. + +## Validation receipt + +- Full mobile browser suite: **229 passed**, zero failures. +- Focused Tweaks suite with evidence: **19 passed**, zero failures. +- Svelte check: **0 errors, 0 warnings** (Vite separately prints the existing missing parent `.svelte-kit/tsconfig.json` notice in a fresh mobile-only worktree). +- Vite production build: passed. +- Native Android `tauri android build --debug --target aarch64 --apk --ci`: passed, local debug APK produced. Installed SDK/NDK/JDK used; offline Cargo dependencies. No APK installation or launch. Existing Gradle deprecation/signing-configuration notices remain; this is not a Play release build. +- `git diff --check`: passed. + +Native build command used the existing JDK 17, Android SDK and NDK 28.2.13676358. Phone/TV acceptance is still open as listed above. The first Gradle task inspection could not run before Tauri generated its ignored settings; the supported Tauri build subsequently completed successfully. diff --git a/v2/mobile/evidence/settings-polish-2026-10-17/dns-draft-simulated.png b/v2/mobile/evidence/settings-polish-2026-10-17/dns-draft-simulated.png new file mode 100644 index 00000000..91a1b4e9 Binary files /dev/null and b/v2/mobile/evidence/settings-polish-2026-10-17/dns-draft-simulated.png differ diff --git a/v2/mobile/evidence/settings-polish-2026-10-17/icon-optical-comparison.png b/v2/mobile/evidence/settings-polish-2026-10-17/icon-optical-comparison.png new file mode 100644 index 00000000..7440e213 Binary files /dev/null and b/v2/mobile/evidence/settings-polish-2026-10-17/icon-optical-comparison.png differ diff --git a/v2/mobile/evidence/settings-polish-2026-10-17/settings-simulated.png b/v2/mobile/evidence/settings-polish-2026-10-17/settings-simulated.png new file mode 100644 index 00000000..1a4ff095 Binary files /dev/null and b/v2/mobile/evidence/settings-polish-2026-10-17/settings-simulated.png differ diff --git a/v2/mobile/evidence/settings-polish-2026-10-17/settings-simulated.webm b/v2/mobile/evidence/settings-polish-2026-10-17/settings-simulated.webm new file mode 100644 index 00000000..72daa65c Binary files /dev/null and b/v2/mobile/evidence/settings-polish-2026-10-17/settings-simulated.webm differ diff --git a/v2/mobile/evidence/settings-polish-2026-10-17/validation.json b/v2/mobile/evidence/settings-polish-2026-10-17/validation.json new file mode 100644 index 00000000..29efd912 --- /dev/null +++ b/v2/mobile/evidence/settings-polish-2026-10-17/validation.json @@ -0,0 +1,23 @@ +{ + "base": "5f4fc141ceda0307cb86b68bfc50433b9ffcc49d", + "source_sha256": { + "src/screens/Tweaks.svelte": "e6a009982f741b588366081513bdfe0fd12f52e5845eb2e04fcaba06aaa4348c", + "src-tauri/gen/android/app/src/main/java/com/atvoptimizer/mobile/MainActivity.kt": "2e52b363c297df7721c56a86b1ec254229be2582171caae6d786c040009b8c3c", + "tests/tweaks-tab.test.mjs": "c3cdd4118e3f61d799c7ed52db90d949df3c40865eeef652f99a923ee9a993aa" + }, + "evidence_sha256": { + "dns-draft-simulated.png": "ea287a26e69dbfa1b1b873376777ab2731f54a5f2640a0c13dcd613f0283ea59", + "settings-simulated.webm": "2acb907e5862a6a3c4a89809f7f93b55b1336bb8a2e8f48c1890eb1a33c02acd", + "icon-optical-comparison.png": "db51eda861e36ca527120b3600d46c9fc09bc7724b9f72407cc7ff896beb7c30", + "settings-simulated.png": "fb09e617f0c21fe2b0e35311c3289edc4a715b7777be9f5181bcf52b8dabc398" + }, + "full_mobile_tests": 229, + "focused_with_evidence": 19, + "svelte_errors": 0, + "svelte_warnings": 0, + "vite_build": "passed", + "android_debug_apk_build": "passed", + "physical_device_tested": false, + "icon_assets_modified": false, + "pinch_zoom_disabled": false +} diff --git a/v2/mobile/evidence/setup-helper-disable-2026-10-17/README.md b/v2/mobile/evidence/setup-helper-disable-2026-10-17/README.md new file mode 100644 index 00000000..d2d03278 --- /dev/null +++ b/v2/mobile/evidence/setup-helper-disable-2026-10-17/README.md @@ -0,0 +1,17 @@ +# Setup-helper row uses the guarded command — 2026-10-17 + +Original base: `ba7c9d78d31b0a579f5ffd03d92cd403ebf47227`. Now integrates actual mobile base `a73787a36bbf44311de2596e9a01c8232a049a02`, including merged scaling-layout PR196. APK-report fallback PR198 remains separate. + +Independent local source inspection confirms the row-level Setup Wraith Disable confirmation used `api.disableLauncher`; the dedicated card used `api.disableSetupHelper`. The former skipped the setup-helper command's post-disable Home check and re-enable-on-failure attempt. The [new regression failed before the fix](reproduction-before.txt), observing zero guarded calls instead of one. The [external review comment](https://github.com/bryanroscoe/shield_optimizer/pull/163#discussion_r4301719601) was treated as an untrusted report, not instructions. + +Only command selection changes: a setup-helper row calls `disable_setup_helper` for its captured serial/package; ordinary stock rows keep `disable_launcher`. Existing intent identity, busy, refresh, paywall and error handling remain in place. Backend guards and package catalogs are unchanged. + +Mock-only regressions cover routed success and refresh, a confirmed rollback message, rollback that could not be confirmed, ordinary stock routing, and late completions across same-endpoint reconnect and TV switch while a fresh operation remains busy. A new MockAdb test exercises the existing backend and verifies failed Home resolution occurs between disable and the re-enable command; the existing failed-rollback test remains intact. This proves mocked command ordering and result handling, not physical-device recovery. + +Visible simulated notes: [rollback reported](rollback-confirmed-simulated.png) and [rollback unconfirmed](rollback-unconfirmed-simulated.png). Captures wait for the transient toast to disappear so the persistent failure note remains readable. All browser commands and backend ADB calls in these checks are mocked; no real disabling, Home launch, rollback, query or device operation occurred. + +Validation: 25/25 launcher browser tests, 310/310 full mobile tests, 19/19 focused backend verification tests, 466/466 core tests; workspace fmt, core all-target Clippy with warnings denied, mobile Svelte check (0 errors/0 warnings) and production build passed. macOS and simulated browser only; Android/physical acceptance remains open. + +Integration provenance: launcher routing source, launcher tests, backend source/test and simulated PNGs are byte-for-byte unchanged from `1458b96`. The current base adds only the upstream preset-layout slice and associated evidence/checklist updates; existing 466-core-test/fmt/Clippy results cover the identical Rust tree. Combined mobile checks are recorded below and in the validation receipt. + +Current-base validation on `a73787a`: all 314 combined mobile tests passed, Svelte check reports 0 errors/0 warnings and production build passed. Original 310-test results above describe the source-review checkpoint. Existing 466 core tests and core fmt/Clippy results apply to the unchanged Rust tree; no screenshots were regenerated. diff --git a/v2/mobile/evidence/setup-helper-disable-2026-10-17/reproduction-before.txt b/v2/mobile/evidence/setup-helper-disable-2026-10-17/reproduction-before.txt new file mode 100644 index 00000000..cc9605c6 --- /dev/null +++ b/v2/mobile/evidence/setup-helper-disable-2026-10-17/reproduction-before.txt @@ -0,0 +1,34 @@ +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ Setup Wraith row uses the guarded helper command and refreshes confirmed success (1343.969125ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1646.546875 + +✖ failing tests: + +test at tests/launcher.test.mjs:507:1 +✖ Setup Wraith row uses the guarded helper command and refreshes confirmed success (1343.969125ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + 0 !== 1 + + at TestContext. (file:///v2/mobile/tests/launcher.test.mjs:516:10) + at async Test.run (node:internal/test_runner/test:1389:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: 0, + expected: 1, + operator: 'strictEqual', + diff: 'simple' + } diff --git a/v2/mobile/evidence/setup-helper-disable-2026-10-17/rollback-confirmed-simulated.png b/v2/mobile/evidence/setup-helper-disable-2026-10-17/rollback-confirmed-simulated.png new file mode 100644 index 00000000..3e82f000 Binary files /dev/null and b/v2/mobile/evidence/setup-helper-disable-2026-10-17/rollback-confirmed-simulated.png differ diff --git a/v2/mobile/evidence/setup-helper-disable-2026-10-17/rollback-unconfirmed-simulated.png b/v2/mobile/evidence/setup-helper-disable-2026-10-17/rollback-unconfirmed-simulated.png new file mode 100644 index 00000000..9c35b283 Binary files /dev/null and b/v2/mobile/evidence/setup-helper-disable-2026-10-17/rollback-unconfirmed-simulated.png differ diff --git a/v2/mobile/evidence/setup-helper-disable-2026-10-17/validation.json b/v2/mobile/evidence/setup-helper-disable-2026-10-17/validation.json new file mode 100644 index 00000000..77c0bc82 --- /dev/null +++ b/v2/mobile/evidence/setup-helper-disable-2026-10-17/validation.json @@ -0,0 +1,21 @@ +{ + "base": "a73787a36bbf44311de2596e9a01c8232a049a02", + "mobile_tests": 314, + "launcher_tests": 25, + "backend_guard_tests": 19, + "core_tests": 466, + "core_clippy": "all targets, warnings denied, passed", + "fmt": "passed", + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "build": "passed", + "limits": [ + "Mocks only; no device operations", + "No backend guard behavior changed" + ], + "original_base": "ba7c9d78d31b0a579f5ffd03d92cd403ebf47227", + "original_mobile_tests": 310, + "integration": "Current base adds preset layout only; Launcher source/tests and Rust source/test unchanged from 1458b96bafb09705cc818941f398fcc767b44ca8. Identical Rust tree retains original fmt/Clippy/466-test validation." +} diff --git a/v2/mobile/evidence/sheet-identity-2026-10-17/.gitignore b/v2/mobile/evidence/sheet-identity-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/sheet-identity-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/sheet-identity-2026-10-17/README.md b/v2/mobile/evidence/sheet-identity-2026-10-17/README.md new file mode 100644 index 00000000..44100b08 --- /dev/null +++ b/v2/mobile/evidence/sheet-identity-2026-10-17/README.md @@ -0,0 +1,53 @@ +# Immutable app-sheet opening identity — P1 safety fix + +Base: current `mobile-parity-pass` `6f3deefa5e46a03d292aa4ef1e84927440b03766`. This separate branch changes AppSheetHost, its regression fixtures, evidence and checklist only; it does not rewrite PR181 or either sealed PR182 commit. + +## Reproduction and change + +Diagnostics and Optimize retain their selected app in parent state. The previous Host effect observed that app **and** current connection state, hid the sheet on disconnect without clearing the parent, then assigned the old package a new serial/generation on reconnect. Its later `current(t)` check accepted that rebound target. Six pre-fix cases (two screens × disconnect/same-endpoint reconnect/different-TV switch) reproduce stale-sheet resurrection in mocks. + +Only a new explicit app selection now captures a frozen opening target. Session/liveness changes invalidate it instead of rebinding it: clear shown app, target, pending request/read tokens, measurements, busy/uninstalled state, confirmation and paywall, and call the parent close callback. Closing ordinarily does the same cleanup. `current(t)` also requires the **exact opening object**, rejecting a pending result from a closed selection even if a new selection uses the same serial and generation. Existing safety/verdict and native transport gates remain in place. + +## Regression matrix + +Both Diagnostics and Optimize cover: + +- Disconnect/reconnect, direct same-endpoint reconnect and different-TV switch; no old sheet or action survives. A fresh explicit selection can target the current mock connection. +- Pending safety reply across a switch, and across close/reopen on the same connection; neither opens a stale confirmation. +- Disable and Uninstall confirmation cleanup on reconnect; no mutation from those old intents. +- Paywall cleanup and stale measurement rejection; a new opening shows its own 55.0 MB fixture, not the old 999 MB reply. +- Failed reconnect and transient liveness loss; returning live does not resurrect the old selection. +- When combined with PR182, invalidation also releases the shared document lock. Its separate keyboard/focus/scroll tests remain part of the combined suite. + +The fixture supplies fresh synthetic diagnostic metrics before making a new selection after reconnect; reconnect clearing the metrics cache is not permission to resurrect a previous app sheet. Commands are intercepted through the fake Tauri bridge. Some regression cases deliberately confirm a synthetic Disable to assert the new serial, never a real device command. + +## Visual evidence and reproduction + +Synthetic TV A/B, Chromium on macOS, 384×812. No phone/TV connection, device query, install or setting change occurred. + +- [Opened on mock A](opened-on-a-simulated.png) +- [A's pending confirmation](a-confirmation-simulated.png) +- [B requires a new selection](b-requires-fresh-selection-simulated.png) +- [Fresh B selection](fresh-b-selection-simulated.png) +- [Short A-to-B invalidation video](sheet-opening-identity-simulated.webm) +- [Scenario receipt](scenario.json): zero mutation calls in this recording; both confirmations are canceled/invalidated. + +Frames were visually inspected; all identities/data are invented. The video uses a programmatic mock reconnection, not an actual phone or native transport. It demonstrates the rendered state transition; tests bind the mutation/identity assertions. Raw intermediate videos are ignored. + +```sh +SHEET_IDENTITY_EVIDENCE=1 node --test tests/sheet-opening-identity.test.mjs +npm test +npm run check +npm run build +``` + +Physical connection/lifecycle/device enforcement remains a phone/TV acceptance gate. Browser results do not prove native command timing or Android behavior. No broad release approval is implied; independent review of this safety fix is required. + +## Validation receipt + +- Pre-fix: all six stale-opening resurrection cases fail on base6f3deef. +- Focused matrix:16 passed; with opt-in capture:17 passed. +- Standalone current-base safety branch:245 mobile tests passed, zero failures. +- Isolated current base + PR182 `1dd9bef7f949f66e4a6b6e94ba11e5c319c9baa0` + identical safety source/tests:252 mobile tests passed, zero failures; no source overlap/conflict. This includes both modal long-sheet keyboard regressions. +- Both trees: Svelte check zero errors/warnings; Vite build passed. `git diff --check` passed. Existing fresh-worktree parent tsconfig notice remains. +- No native source changed or physical device tested. Independent source review and CI remain required before release. diff --git a/v2/mobile/evidence/sheet-identity-2026-10-17/a-confirmation-simulated.png b/v2/mobile/evidence/sheet-identity-2026-10-17/a-confirmation-simulated.png new file mode 100644 index 00000000..e64201e5 Binary files /dev/null and b/v2/mobile/evidence/sheet-identity-2026-10-17/a-confirmation-simulated.png differ diff --git a/v2/mobile/evidence/sheet-identity-2026-10-17/b-requires-fresh-selection-simulated.png b/v2/mobile/evidence/sheet-identity-2026-10-17/b-requires-fresh-selection-simulated.png new file mode 100644 index 00000000..5fbc14db Binary files /dev/null and b/v2/mobile/evidence/sheet-identity-2026-10-17/b-requires-fresh-selection-simulated.png differ diff --git a/v2/mobile/evidence/sheet-identity-2026-10-17/fresh-b-selection-simulated.png b/v2/mobile/evidence/sheet-identity-2026-10-17/fresh-b-selection-simulated.png new file mode 100644 index 00000000..69218f0b Binary files /dev/null and b/v2/mobile/evidence/sheet-identity-2026-10-17/fresh-b-selection-simulated.png differ diff --git a/v2/mobile/evidence/sheet-identity-2026-10-17/opened-on-a-simulated.png b/v2/mobile/evidence/sheet-identity-2026-10-17/opened-on-a-simulated.png new file mode 100644 index 00000000..5a6f5a86 Binary files /dev/null and b/v2/mobile/evidence/sheet-identity-2026-10-17/opened-on-a-simulated.png differ diff --git a/v2/mobile/evidence/sheet-identity-2026-10-17/scenario.json b/v2/mobile/evidence/sheet-identity-2026-10-17/scenario.json new file mode 100644 index 00000000..8003af1e --- /dev/null +++ b/v2/mobile/evidence/sheet-identity-2026-10-17/scenario.json @@ -0,0 +1,8 @@ +{ + "fixture": "synthetic TV A -> B", + "real_device": false, + "recorded_mutations": [], + "current_serial": "B:5555", + "requires_fresh_selection": true, + "confirmation_canceled": true +} diff --git a/v2/mobile/evidence/sheet-identity-2026-10-17/sheet-opening-identity-simulated.webm b/v2/mobile/evidence/sheet-identity-2026-10-17/sheet-opening-identity-simulated.webm new file mode 100644 index 00000000..8aaed566 Binary files /dev/null and b/v2/mobile/evidence/sheet-identity-2026-10-17/sheet-opening-identity-simulated.webm differ diff --git a/v2/mobile/evidence/sheet-identity-2026-10-17/validation.json b/v2/mobile/evidence/sheet-identity-2026-10-17/validation.json new file mode 100644 index 00000000..1a70022e --- /dev/null +++ b/v2/mobile/evidence/sheet-identity-2026-10-17/validation.json @@ -0,0 +1,26 @@ +{ + "base": "6f3deefa5e46a03d292aa4ef1e84927440b03766", + "source_sha256": { + "src/components/AppSheetHost.svelte": "10a890165d779c56f3f89bb524100a1d4e053fe2c567666e8f22c6864dd2d0cc", + "tests/sheet-opening-identity.test.mjs": "4c670e623322bc174fe4ba3d53158a20df92a1da5d31c5d075ec676641fa6298" + }, + "evidence_sha256": { + "b-requires-fresh-selection-simulated.png": "6f38cc439422158c58c9d9458a240a19a510d5dfa0dd8c45172e5f4c6ce5458a", + "fresh-b-selection-simulated.png": "d9e1d481fb9ccf74ab76b4a381043713dd5dc2968b81755d00e2910bc2ab4bb2", + "opened-on-a-simulated.png": "2f1e8d54f4b83aaea17293e702927e396ac99484c6c4fc7f0d57406df7377dae", + "a-confirmation-simulated.png": "43667a31038e325061117ad3bb76ec1e56a898ea540ac7721db0bad7bb13edb6", + "sheet-opening-identity-simulated.webm": "da71ae17c6afa5e591e12e9cd13a49458671b9586c0fe25418b64bcb2b523042" + }, + "pre_fix_resurrection_failures": 6, + "focused_safety_tests": 16, + "focused_with_capture": 17, + "standalone_full_mobile_tests": 245, + "combined_with_modal_head": "1dd9bef7f949f66e4a6b6e94ba11e5c319c9baa0", + "combined_full_mobile_tests": 252, + "svelte_errors": 0, + "svelte_warnings": 0, + "vite_build": "passed", + "physical_device_tested": false, + "native_files_modified": false, + "independent_review": "pending" +} diff --git a/v2/mobile/evidence/standalone-fixture-build-2026-10-23/README.md b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/README.md new file mode 100644 index 00000000..12972722 --- /dev/null +++ b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/README.md @@ -0,0 +1,52 @@ +# Standalone mobile fixture build repair + +Base: contributor commit `222aecf0b741f27dfd62548c8825e7c13d01e5a5` on +`mobile-parity-pass`. Its original commit and authorship are preserved. Ownership +reconciliation found a clean primary checkout and the originating Claude session's +completed 00:43 UTC handoff, with no subsequent repair activity or overlapping PR. +All repair work uses a separate Developer worktree. + +[Failed mobile CI job](https://github.com/bryanroscoe/shield_optimizer/actions/runs/39879563781/job/120173389492) +passed type checking, then failed production build while transforming the newly +shared desktop `demo-mock.ts`: desktop's tsconfig extends generated +`.svelte-kit/tsconfig.json`, absent on the isolated mobile runner. Browser tests +were consequently skipped. A fresh local checkout with only mobile dependencies +reproduced that exact failure (`baseline-build.txt`). + +The mobile Vite config now supplies its existing compiler options as a JSON +**string** to `esbuild.tsconfigRaw`. In Vite6, the string form bypasses per-file +configuration discovery; an object still loads each file's nearest tsconfig first. +The build therefore uses the mobile options for imported fixture/shared TypeScript +without needing desktop-generated files. No dependency, compiler-option value, +fixture, device command, app screen or safety logic was changed. + +`tests/standalone-build.test.mjs` creates a temporary source tree with a dangling +desktop tsconfig and only the mobile dependency cache. It builds production and +explicit demo modes separately, verifies fixtures are absent from production and +present in demo output, and asserts no desktop dependency directory or generated +SvelteKit directory exists. The regression fails with the old Vite config at the +same missing-extends error and passes with the fix. It is included by the existing +`npm test` glob, without changes to CI setup or generating desktop artifacts. + +Local validation on macOS: + +- Isolated production and demo build regression passed (`isolated-builds.txt`). +- Fresh mobile production build passed with no desktop dependencies or SvelteKit + files present. `npm run check` reports 0 errors and 0 warnings. Tool configuration + discovery still prints a nonfatal missing-desktop-base warning; it is not a + Svelte diagnostic or a required generated-file dependency after this fix. +- All **344 mobile tests passed**, zero failures/skips (`mobile-tests.txt`). +- The contributor's `npm run screenshots` captured all **16 simulated screens** and + contact sheet (`capture.txt`), using only fixture responses. Capture output stays + gitignored as designed. + +Screenshots/video are inapplicable to this configuration-only repair: rendered UI +and fixture content are unchanged. The full capture smoke test verifies the new +contributor workflow still runs; the isolated build test verifies production does +not include it. No real phone/TV query or mutation, APK operation, permission or +security change, paid model call, deployment or release occurred. + +Exact-head CI and independent review gate this repair. This evidence does not +satisfy PR163's physical-device or release acceptance gates and does not authorize +merging PR163. PR213's explicit merge block, PR211/216 catalog holds, PR180 and +other drafts remain separate and unchanged. diff --git a/v2/mobile/evidence/standalone-fixture-build-2026-10-23/baseline-build.txt b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/baseline-build.txt new file mode 100644 index 00000000..b9e45858 --- /dev/null +++ b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/baseline-build.txt @@ -0,0 +1,23 @@ +npm notice run atv-optimizer-mobile@0.1.0 build +npm notice run vite build +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +vite v6.4.3 building for production... +transforming... +✓ 188 modules transformed. +✗ Build failed in 696ms +error during build: +[vite:esbuild] failed to resolve "extends":"./.svelte-kit/tsconfig.json" in /Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/tsconfig.json +file: /Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/src/lib/demo-mock.ts + at resolveExtends (file:///Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/node_modules/vite/dist/node/chunks/dep-Dm0c1Wj2.js:8739:8) + at parseExtends (file:///Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/node_modules/vite/dist/node/chunks/dep-Dm0c1Wj2.js:8674:24) + at parse$e (file:///Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/node_modules/vite/dist/node/chunks/dep-Dm0c1Wj2.js:8531:23) + at async loadTsconfigJsonForFile (file:///Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/node_modules/vite/dist/node/chunks/dep-Dm0c1Wj2.js:9360:38) + at async transformWithEsbuild (file:///Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/node_modules/vite/dist/node/chunks/dep-Dm0c1Wj2.js:9088:60) + at async Object.transform (file:///Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/node_modules/vite/dist/node/chunks/dep-Dm0c1Wj2.js:9199:24) + at async transform (file:///Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/node_modules/rollup/dist/es/shared/node-entry.js:21726:16) + at async ModuleLoader.addModuleSource (file:///Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/node_modules/rollup/dist/es/shared/node-entry.js:21946:36) diff --git a/v2/mobile/evidence/standalone-fixture-build-2026-10-23/capture.txt b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/capture.txt new file mode 100644 index 00000000..682a8c76 --- /dev/null +++ b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/capture.txt @@ -0,0 +1,26 @@ +npm notice run atv-optimizer-mobile@0.1.0 screenshots +npm notice run node screenshots/capture.mjs +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Capturing 16 screens at 412x915@2x → /Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/screenshots/frames + onboarding + dashboard + apps + app-detail + tweaks + remote + more + optimize + diagnostics + launcher + snapshots + devices + files + backups + riskguide + add-tv +Contact sheet → /Users/bryanroscoe/Developer/shield-mobile-fixture-build-20261023/v2/mobile/screenshots/contact-sheet.png diff --git a/v2/mobile/evidence/standalone-fixture-build-2026-10-23/isolated-builds.txt b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/isolated-builds.txt new file mode 100644 index 00000000..1e555038 --- /dev/null +++ b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/isolated-builds.txt @@ -0,0 +1,9 @@ +✔ mobile production and demo builds need no desktop generated files or dependencies (2576.472458ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 1 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2646.4205 diff --git a/v2/mobile/evidence/standalone-fixture-build-2026-10-23/mobile-tests.txt b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/mobile-tests.txt new file mode 100644 index 00000000..1f4d4fb2 --- /dev/null +++ b/v2/mobile/evidence/standalone-fixture-build-2026-10-23/mobile-tests.txt @@ -0,0 +1,480 @@ +npm notice run atv-optimizer-mobile@0.1.0 test +npm notice run node --test --test-concurrency=1 tests/*.test.mjs +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ a disabled katniss is found by package and by name, and can be re-enabled (1818.125792ms) +✔ not-installed recognised apps hide by default and can be revealed (369.959542ms) +✔ long-press opens the action menu without opening the sheet; a tap still opens it (1023.531834ms) +✔ menu Disable goes through the safety lookup and confirm, never straight to the TV (458.626625ms) +✔ report opens a prefilled GitHub form through the opener (798.4135ms) +✔ without an opener, report falls back to copy and a link to copy (789.818916ms) +✔ rows, chips and labels fit a 384px screen (143.635792ms) +✔ long report keyboard wrap reveals its focused actions without moving the locked page (783.741417ms) +✔ menu Report returns focus to its original app row after closing (473.044542ms) +✔ report includes the successful APK fallback visible in the detail sheet (794.3695ms) +✔ APK report keeps exact 0 bytes, unknown data/cache and identical copied JSON (812.90775ms) +✔ APK report keeps exact 1234567 bytes, unknown data/cache and identical copied JSON (825.768042ms) +✔ diskstats remains the report source when a complete measured row exists (789.623625ms) +✔ APK report keeps storage unknown for a pending fallback (785.274583ms) +✔ APK report keeps storage unknown for a failed fallback (785.680042ms) +✔ APK report keeps storage unknown for a missing fallback (798.327ms) +✔ APK report keeps storage unknown for a negative fallback (791.361125ms) +✔ remeasure invalidates a successful APK receipt and ignores an older fallback reply (1211.507583ms) +✔ APK fallback cannot cross a same-endpoint reconnect (1214.733416ms) +✔ APK fallback cannot cross a TV switch (1216.586125ms) +✔ an APK result for a closed package cannot replace the next package measurement (1174.311125ms) +✔ report safety: failed sheet overrides old caution catalog and retries honestly (3547.95775ms) +✔ report safety: failed sheet overrides old safe catalog and retries honestly (3532.670375ms) +✔ report safety: failed sheet overrides old never_disable catalog and retries honestly (3526.890459ms) +✔ report safety: late retry cannot cross reopen (1183.342958ms) +✔ report safety: late retry cannot cross package (1491.386875ms) +✔ report safety: late retry cannot cross reconnect (1499.947541ms) +✔ report safety: an absent sheet result retains the catalog fallback without retry (800.568833ms) +✔ report safety: late sheet lookup cannot cross package and overwrite the current result (1149.590583ms) +✔ report safety: late sheet lookup cannot cross reconnect and overwrite the current result (1150.109958ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ system visibility composes with enabled and disabled filters (1067.458833ms) +✔ search reports empty results and can reveal or clear them (617.59875ms) +✔ filtering never mutates packages and protected-app safety stays enforced (242.83ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ katniss is in the shared catalogue (124.541833ms) +✔ katniss is found by package and by name when enabled (0.82325ms) +✔ katniss is found by package and by name when disabled (0.099417ms) +✔ a disabled katniss is reachable from the Disabled chip so it can be re-enabled (0.058958ms) +✔ not-installed catalogue rows hide by default and are counted as hidden (0.092042ms) +✔ an unread state is unknown: shown under All, never under Enabled (0.045459ms) +✔ system packages in Everything else follow the toggle and are counted when hidden (0.053416ms) +✔ a package in both lists is listed once, as recognised (0.076209ms) +✔ validatedStates drops malformed values instead of guessing (0.105ms) +✔ onDeviceCount leaves out only rows known to be missing (0.073334ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ installed picker hides systems by default and composes reveal with search (1005.037625ms) +✔ one chosen app makes one backup call for the expected TV and package (162.518167ms) +✔ a delayed A picker read cannot replace the newer B result (191.916208ms) +✔ a backup started on A stays owned by A when it finishes after a switch to B (174.921834ms) +✔ a stale displayed A row cannot start a backup on B (163.858333ms) +✔ a restore confirmation opened on A cannot restore onto B (135.923084ms) +✔ picker and backup failures stay explicit and device-owned (171.696208ms) +✔ disconnected state and APK-only limits are stated plainly (119.708958ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ Dashboard counts enabled among installed recommended apps (977.252291ms) +✔ Dashboard treats incomplete package state replies as unavailable (117.099875ms) +✔ a stale same-serial count cannot overwrite a newer generation (140.207166ms) +✔ zero recommended candidates opens Optional apps with Keep selected (118.210542ms) +✔ Caution defaults are recommended while Unknown defaults move to Optional (134.963542ms) +✔ protected defaults stay visible and blocked in Optional (157.854291ms) +✔ a plan containing only Unknown defaults opens Optional after safety resolves (113.306958ms) +✔ a user's tab choice survives pending safety resolution (159.289125ms) +✔ optional choices survive tabs and confirmation names every actual action (516.226542ms) +✔ protected and unresolved optional actions remain blocked while Unknown is explicit (153.204459ms) +✔ Restore keeps Recommended and All curated tabs with its prior defaults (158.402666ms) +✔ differing Unknown reasons stay on their own rows (112.684792ms) +✔ Safe defaults are recommended, selected and actionable (110.73075ms) +✔ the confirm summary names the Safe verdict rather than calling it Caution (125.498625ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ Diagnostics reports the TV's Android version and hardware details (941.578458ms) +✔ Dashboard shows the Android version beside the address (116.904458ms) +✔ A property the TV did not report reads as unknown, never as a neighbour's value (116.832458ms) +✔ A device with no profile says so instead of showing blank rows (113.297584ms) +✔ Missing health data leaves the totals blank rather than inventing them (118.424834ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ Diagnostics header fits 320px at 100% text with every control usable (1083.923625ms) +✔ Diagnostics header fits 384px at 100% text with every control usable (246.338833ms) +✔ Diagnostics header fits 320px at 200% text with every control usable (233.410459ms) +✔ Diagnostics header fits 384px at 200% text with every control usable (232.699167ms) +✔ non-Shield and disconnected headers keep the applicable controls without overflow (234.836167ms) +✔ collapses repeated advertisements and separates saved from new hosts (1.533958ms) +✔ the live endpoint is the only connected row (0.14225ms) +✔ a connected TV that does not advertise stays one connected saved row (0.116625ms) +✔ only the saved TV whose id is live claims a shared live endpoint (#115) (0.166333ms) +✔ two id-less saved TVs sharing the live endpoint never both claim connected (#146) (0.120625ms) +✔ a stale advert never names the live row after a different saved TV (#115) (0.072916ms) +✔ a stale advert at an old host never shows the live TV a second time (#115) (0.083625ms) +✔ a legacy advert sharing the live TV's serial prefix is not dropped as stale (#149) (0.056ms) +✔ a live TV that reports no id is never named after a stale advert at its address (#115) (0.078625ms) +✔ dropping a stale advert keeps the TV now advertising from that address (#115) (0.102333ms) +✔ saved TVs sharing an endpoint that answers unverified each keep their row (#115) (0.081125ms) +✔ an address change reports a new host and a saved endpoint that is absent (0.073083ms) +✔ a rotated port keeps the saved endpoint truthful instead of calling it offline (0.0575ms) +✔ a host advertising only a pairing service is not treated as answering (0.048167ms) +✔ two saved identities at one endpoint stay two real rows (#115) (0.052042ms) +✔ a saved TV that shared an address with one that moved stays in the scan (#115) (0.052833ms) +✔ a stored placeholder id is not a wildcard for serial-less adbd (#116) (0.088125ms) +✔ a saved id only matches its own advertised serial, not a longer one (#117) (5.953709ms) +✔ distinct saved endpoints on one host stay distinct rows (0.056083ms) +✔ malformed advertisements are dropped (0.054417ms) +✔ instance names are only used when they are unambiguous and not adb ids (0.043625ms) +✔ legacy connect ports are the only ones flagged as needing no pairing code (0.050292ms) +✔ an advertised serial verifies a saved TV and names it (0.037167ms) +✔ a verified serial recognizes a saved TV that moved to a new address (0.034084ms) +✔ a serial that matches no saved TV leaves the row unclaimed (0.033791ms) +✔ two TVs advertising only adb ids are each named from their own serial (0.044583ms) +✔ rows are ordered by usability then recency, not by mDNS answer order (0.058083ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ matrix covers every registered mobile command and opener exactly once (196.920084ms) +✔ store observations never unlock; stale, duplicate and ack events are isolated (0.555833ms) +✔ errors/cancel preserve access; revocation preserves independent legacy source (0.14525ms) +✔ cancel and errors never create access; acknowledgment cannot precede verification (0.114958ms) +✔ simulated UI keeps recovery usable through pending/error/revocation (948.156833ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ screenshot destination, bytes and Close are truthful at 384px / 100% text (1004.134833ms) +✔ screenshot destination, bytes and Close are truthful at 320px / 200% text (172.324042ms) +✔ screenshot failure creates no preview or false saved result and preserves earlier saved bytes (241.186416ms) +✔ file-copy success identifies the actual private destination and failure adds no copied row (161.305542ms) +✔ report copy and oversized GitHub fallback use preview text and never claim a saved file (801.366083ms) +✔ report clipboard failure exposes the selectable preview without false copy success (773.248083ms) +✔ diagnostic JSON and debug log Copy actions state clipboard-only output and report failures (280.105125ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ file results belong to the live connection that started them (127.7285ms) +✔ reconnecting to the same endpoint invalidates an old file action (0.102875ms) +✔ a lost connection cannot accept a file result or delete confirmation (0.044791ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ the catalog is desktop's file, not a copy (193.074584ms) +✔ a search that could not run never summarizes as no matches (0.209125ms) +✔ delete is only offered strictly inside /sdcard (0.166917ms) +✔ Find SmartTube backups searches every catalog dir and copies a hit to the phone (812.112875ms) +✔ an unsearched directory reads as a failed search, never as no matches (#86) (175.104209ms) +✔ a rejected search drops the older answer instead of showing it as current (181.639958ms) +✔ delete asks first, sends the exact /sdcard path, and reloads the folder (493.391ms) +✔ no delete control outside /sdcard (145.060333ms) +✔ a delete confirmed after a TV switch is not sent to the new TV (185.218292ms) +✔ reconnecting to the same endpoint clears an old delete confirmation (152.087291ms) +✔ a copy completing after a TV switch does not mark the new TV's row copied (205.439625ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ RAM uses explicit used/total or available complement; zero/full are real values (188.301583ms) +✔ missing and invalid memory is not painted as zero/full (0.1445ms) +✔ storage retains reported percentages and distinguishes missing/invalid from zero (0.081916ms) +✔ temperature comparison uses only a matching sensor own threshold (0.368375ms) +✔ reported device status is independent of hottest sensor status; no temperature-derived grade (0.1525ms) +✔ both visible bars mean used, share neutral styling and show 0/50/100 honestly (781.681667ms) +✔ legacy100C stays unknown; a device-reported critical status is shown even at30C (131.459292ms) +✔ below-threshold light/moderate readings do not describe throttling onset or infer HAL severity (0.352334ms) +✔ threshold provenance stays unspecified for hardware, HIDL-like and unknown sources (0.226333ms) +✔ visible thresholds retain reported status and never claim onset or a severity without provenance (139.392833ms) +✔ checked-in Shield meminfo keeps explicit Used despite non-complementary Free accounting (0.798416ms) +✔ Shield accounting renders explicit used/free without changing the source report (118.499709ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ Setup Wraith reads off / on / risk from the launcher rows (196.608292ms) +✔ a takeover turns Setup Wraith off only when an enabled stock names it (0.175416ms) +✔ Disable stock launcher stays off until the picked app is Home or stock is in the way (0.209083ms) +✔ the stock-disable message never claims what wasn't verified or re-read (0.103417ms) +✔ picker rows hide system apps, search name and package, dedupe and sort (7.827625ms) +✔ source site links are offered only for http(s) URLs (0.185083ms) +✔ Setup Wraith on with stock off warns and turns it off, then re-reads the TV (806.738208ms) +✔ Setup Wraith off offers Re-enable through enable_package (137.979042ms) +✔ a not-ok default switch re-reads the TV and offers its diagnostics (203.960209ms) +✔ set any app as Home reports Android's answer, then unlocks the separate stock step (243.389167ms) +✔ Save snapshot first leaves stock alone when the snapshot fails (215.516416ms) +✔ a locked Set as Home opens the paywall and calls nothing else (200.639375ms) +✔ source site hands the launcher's page to the phone's browser, not the app (129.181583ms) +✔ source site copies the link when the browser can't be opened (128.091083ms) +✔ after opening the Play Store the list re-reads until the launcher is installed (157.259ms) +✔ same-endpoint reconnect cancels confirmations and late launcher results (424.047875ms) +✔ lost connection removes actionable rows and reconnect discards an old list reply (265.413208ms) +✔ old snapshot completion cannot unlock a newer Home action after reconnect (378.648583ms) +✔ stock takeover dialog locks scrolling, traps focus and Escape cancels without a mutation (276.96425ms) +✔ Setup Wraith row uses the guarded helper command and refreshes confirmed success (164.6785ms) +✔ helper row preserves guarded failure: rolled back (166.59425ms) +✔ helper row preserves guarded failure: rollback unconfirmed (159.068166ms) +✔ late helper-row result cannot overwrite a fresh same-endpoint reconnect (542.790333ms) +✔ late helper-row result cannot overwrite a fresh TV switch (574.580375ms) +✔ ordinary stock row keeps its launcher disable command (164.642375ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ same-update replacement preserves document lock and original opener across an empty stack (938.145584ms) +✔ async resolve settles genuine close before any later replacement (101.134958ms) +✔ async cancel settles genuine close before any later replacement (100.670834ms) +✔ async reject settles genuine close before any later replacement (101.258667ms) +✔ genuine empty close clears provenance before an unrelated modal opens (104.501667ms) +✔ nested out-of-order teardown and replacement retain the document opener (99.810209ms) +✔ superseded close callbacks cannot steal focus from a new top modal (108.250375ms) +✔ removed opener and duplicate teardown do not resurrect focus or leak a lock (98.481917ms) +✔ alertdialog without enabled controls receives focus and Escape releases the genuine close (104.157292ms) +✔ closing the focused child then a non-top ancestor restores the surviving middle modal (106.772708ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ Optimize recommends exactly what desktop pre-selects (163.302792ms) +✔ an uninstall the store can't give back runs as Disable (0.124583ms) +✔ review pill only for resolved, unprotected review rows (0.065541ms) +✔ plan search matches name or package, case-insensitively (0.060459ms) +✔ row safety never reads unresolved as a verdict (0.638833ms) +✔ the sheet sees the plan's state and catalog entry (0.060583ms) +✔ Health: a confirmed catalog app gets the App List's recommendation (0.143458ms) +✔ Health: a name no installed package carries is a process, not an app (0.079625ms) +✔ Health: an unreadable installed list confirms nothing and says so (0.087333ms) +✔ Health: an installed package outside the catalog shows its verdict (0.085833ms) +✔ snapshot preview: Now is what the TV reported, and matching settings read Already set (0.154209ms) +✔ snapshot preview: launcher change, skipped launcher, and an unset Now (0.068958ms) +✔ snapshot preview summary matches desktop's wording (0.057792ms) +✔ Restore enable plans describe a disabled installed app to the detail sheet (0.040125ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ Optimize Back returns to the screen that opened it (956.830875ms) +✔ Optimize search filters by name and package id (144.107875ms) +✔ tapping an Optimize row opens the app sheet, not a toggle (180.927125ms) +✔ Safe to remove and review chips stay on one line at 384px (137.27875ms) +✔ Health suggests per row: app recommendation, process, and package verdicts (175.537667ms) +✔ Health never calls a package-shaped name Not an app when installed apps can't be read (111.98975ms) +✔ Snapshots: Beta, Preview restore, and a real Now column (139.779417ms) +✔ modal sheet blocks background wheel/edge scroll and restores position, focus and existing styles repeatedly (929.17925ms) +✔ modal nested confirmation keeps the page locked and Escape/Tab affect only the top layer (498.086625ms) +✔ modal backdrop, screen teardown and disconnect release the document without leaving a lock (229.14725ms) +✔ modal paywall dismissal retains the underlying app sheet lock (482.980791ms) +✔ modal lock survives out-of-order owner teardown and restores the original opener after its child (137.513875ms) +✔ modal long-sheet keyboard wrap reveals last action and Close without moving the background (442.224833ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ Free diagnostics reports are explicit, honest and per-interface (997.214917ms) +✔ resource_sample ignores stale resolve after reconnect to the same serial (160.264208ms) +✔ resource_sample ignores stale reject after reconnect to the same serial (157.5425ms) +✔ media_report ignores stale resolve after reconnect to the same serial (159.720167ms) +✔ media_report ignores stale reject after reconnect to the same serial (156.596834ms) +✔ resource sampling admits one request and no live-refresh polling (163.313875ms) +✔ manual audio toggle preserves unknown and newer encodings through readback (161.848209ms) +✔ playback errors do not replace existing health metrics (124.482291ms) +✔ Free audio writes use the existing paywall and do not fabricate readback (137.316875ms) +✔ late audio mutation failure cannot overwrite another TV's state (154.247542ms) +✔ report fidelity and wrapping at 320px with 100% text (182.9515ms) +✔ report fidelity and wrapping at 384px with 100% text (181.589542ms) +✔ report fidelity and wrapping at 320px with 200% text (183.286291ms) +✔ empty reports and failed rereads retain honest unavailable states and keyboard access (160.431167ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ the shared sideload catalog is served to the webview (158.633583ms) +✔ uninstall is only recommended when the store can give the app back (0.784458ms) +✔ no recommendation label ever says Remove (0.186ms) +✔ unknown and unresolved safety never read as a default action (0.076ms) +✔ safety labels match the desktop vocabulary (0.062542ms) +✔ an app report carries no device identifiers and is labelled mobile (0.8995ms) +✔ keeps mixed saved and new TVs and deduplicates repeated advertisements (7.754834ms) +✔ moves one matching hardware identity across an address change (0.178542ms) +✔ moves one matching hardware identity across a port change (0.104125ms) +✔ keeps different hardware identities that claim the same address (0.113708ms) +✔ does not merge a saved id-less row into a live identified TV (0.182125ms) +✔ does not merge an id-less live TV into a saved identified row (0.193209ms) +✔ matches id-less rows only at the exact endpoint (0.159834ms) +✔ forgets identified TVs by id and id-less TVs by exact endpoint (0.20725ms) +✔ returns cached names only for an unambiguous or matching identity (0.137333ms) +✔ a reused address never lends an identified TV's name to a TV that reports no id (#117) (0.097833ms) +✔ a stored placeholder id becomes an id-less row with its own stable key, never merged with another (#116, #146) (0.170417ms) +✔ reconnecting a TV that reports a placeholder id refreshes its row instead of appending (#116) (0.07525ms) +✔ never guesses which of two id-less TVs at one endpoint just reconnected (#146) (0.205542ms) +✔ sorts by lastUsed and truncates the oldest saved rows (0.223625ms) +✔ ignores corrupt records and normalizes recoverable stored values (0.080375ms) +✔ builds stable identity keys for host collisions and identical endpoints (0.076375ms) +✔ a selected reconnect token activates only its saved row (0.045959ms) +✔ matches the current TV by verified id or an exact id-less endpoint (0.0575ms) +✔ only an unambiguous match counts as the live connection (#146) (0.071333ms) +✔ forgets the exact selected identity at an identical endpoint (0.118083ms) +✔ auto-dials only when the saved list truly has one entry (0.533708ms) +✔ a lone id-less match stores a fingerprint from the live device's properties (#154) (0.12525ms) +✔ a different model or manufacturer at a saved id-less row's address is not silently inherited (#154) (0.14425ms) +✔ a manufacturer-only disagreement also counts as a mismatch (#154) (0.063417ms) +✔ an empty saved fingerprint (an older row, or a device that reported nothing) never blocks a match (#154) (0.061417ms) +✔ a matching fingerprint still refreshes the row normally, and is never treated as proof (#154) (0.062875ms) +✔ a hardware-identified reconnect is never second-guessed by fingerprint (#154) (0.082667ms) +✔ a stored fingerprint survives a read/normalize round trip and sanitizes stray fields (0.05375ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ hardware Back and direct navigation keep Optimize cancellable (1368.386792ms) +✔ Tweaks is a bottom tab and Optimize is a pushed screen; Back unwinds both to Home (181.40475ms) +✔ remote queue is discarded after navigation and a TV switch (195.372416ms) +✔ ordinary remote taps cannot accumulate an unlimited backlog (157.786375ms) +✔ failed health refresh preserves the last report and exposes the error (101.040084ms) +✔ one failed recovery stays lost until an explicit retry (99.123292ms) +✔ a silent recovery onto a different device does not inherit the old saved row or its name (#154 follow-up) (110.495209ms) +✔ a silent recovery's different-device note shows immediately on whatever screen is already open, and clears itself (#154 follow-up) (4121.604917ms) +✔ the Devices screen hands its different-device note on to Dashboard instead of losing it on navigate (#154 follow-up) (4170.810084ms) +✔ canceled connect cannot update the session when its old reply arrives (139.21775ms) +✔ stale reboot completion cannot disconnect a newer session (102.871542ms) +✔ cold single saved TV waits for its profile, then opens a clean dashboard stack (114.528542ms) +✔ cold multi-TV launch waits for a choice and opens the chosen TV (177.419875ms) +✔ cold saved TV missing profile stays on onboarding (108.42075ms) +✔ cold saved TV wrong profile stays on onboarding (101.74225ms) +✔ cold saved TV rejected request stays on onboarding (103.431166ms) +✔ authorization failure stays honest and a deliberate retry can open Dashboard (151.234375ms) +✔ explicit disconnect suppresses automatic saved-TV redial (139.264ms) +✔ first-time connection keeps the connected interstitial (150.252416ms) +✔ add-TV connection keeps the connected interstitial (149.605333ms) +✔ saved TV success opens its dashboard once without canceling the connection (111.347167ms) +✔ leaving onboarding cancels a saved TV connection that is still pending (146.264ms) +✔ cancel and retry of the same saved TV ignores the first completion (196.480834ms) +✔ a late transport error from A cannot start recovery on B (181.7045ms) +✔ a health load that lands after its profile vanished cannot wedge later loads (#118) (95.661792ms) +✔ cancelling a saved-TV reconnect never reports a connection failure (#118) (178.42025ms) +✔ Diagnostics keeps its safety badges when two top apps swap rank (#118) (162.176792ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ diagnostics: disconnect requires a new sheet selection before any action (1654.254834ms) +✔ diagnostics: reconnect requires a new sheet selection before any action (828.304583ms) +✔ diagnostics: switch requires a new sheet selection before any action (824.1875ms) +✔ diagnostics: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (880.719625ms) +✔ diagnostics: Disable nested confirmation is cleared by same-endpoint reconnect (501.729625ms) +✔ diagnostics: Uninstall nested confirmation is cleared by same-endpoint reconnect (491.386917ms) +✔ diagnostics: paywall and pending measurements cannot follow the opening identity (566.464292ms) +✔ optimize: disconnect requires a new sheet selection before any action (820.038958ms) +✔ optimize: reconnect requires a new sheet selection before any action (815.167917ms) +✔ optimize: switch requires a new sheet selection before any action (826.619916ms) +✔ optimize: old pending safety cannot open a confirmation after switch or a fresh same-connection selection (860.79975ms) +✔ optimize: Disable nested confirmation is cleared by same-endpoint reconnect (491.623958ms) +✔ optimize: Uninstall nested confirmation is cleared by same-endpoint reconnect (475.76975ms) +✔ optimize: paywall and pending measurements cannot follow the opening identity (559.23075ms) +✔ diagnostics: a failed reconnect or transient liveness loss still invalidates the opening (239.077708ms) +✔ optimize: a failed reconnect or transient liveness loss still invalidates the opening (224.74475ms) +✔ diagnostics: Disable landing with a false response discards the stale selection (801.71425ms) +✔ diagnostics: Uninstall landing with a false response discards the stale selection (816.484125ms) +✔ optimize: Disable landing with a false response discards the stale selection (808.144333ms) +✔ optimize: Uninstall landing with a false response discards the stale selection (806.954208ms) +✔ Restore detail offers Enable and dispatches only enable_package (524.042166ms) +✔ diagnostics: successful uninstall retains the explicit Reinstall path (772.374375ms) +✔ optimize: successful uninstall retains the explicit Reinstall path (769.069458ms) +✔ mobile production and demo builds need no desktop generated files or dependencies (2893.428625ms) +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✔ Free users see every current value, including hooks, mic and screensaver (1005.498833ms) +✔ Turning the hooks back on is free; turning them off opens the paywall (216.073084ms) +✔ hooks row is hidden when the package is missing and on non-Shield read failures (294.080541ms) +✔ Assistant button revokes the mic through set_app_permission and re-reads (147.183917ms) +✔ screensaver: Basic Daydream, Off, then Restore previous brings the vendor one back (197.590708ms) +✔ a refused screensaver flag stops before the component is written (156.298875ms) +✔ Dashboard has one Optimize call to action and Tune tiles for Tweaks and Launcher (186.235542ms) +✔ Pro dashboard drops the PRO tags (117.614625ms) +✔ More is titled More, lists Optimize, and no longer duplicates the Tweaks tab (142.153334ms) +✔ endpoint reuse never restores another TV's screensaver and interrupted writes stop (359.7455ms) +✔ verified TV retains originals across a rotated port, replacement hardware does not (211.968584ms) +✔ a reconnect between screensaver writes prevents the second write (272.267542ms) +✔ current gap: Pro loss blocks restoring an app-changed screensaver even with the original known (188.961792ms) +✔ Free users retain emergency recovery without an unlock or payment (159.773292ms) +✔ scaling readback matches both resolution and density and clears stale selection on failure (195.061291ms) +✔ Custom DNS editing does not claim the TV has applied it; failed read clears selection (169.289334ms) +✔ equivalent Android animation number formats select the same speed (165.847625ms) +✔ DNS confirmed changes survive navigation and reconnect, while unset stays explicit (190.491917ms) +✔ Daydream component refuse never enables an off screensaver with previous component (136.266ms) +✔ Daydream component reject never enables an off screensaver with previous component (132.643625ms) +✔ Daydream component refuse never enables an off screensaver with unset component (133.882625ms) +✔ Daydream component reject never enables an off screensaver with unset component (142.22675ms) +✔ Basic Daydream does not enable after a refused component; failed enable retains off with configured component (181.168292ms) +✔ Restore previous does not enable after a refused component; failed enable retains off with configured component (199.206625ms) +✔ Off keeps the switch disabled if component clearing fails (143.683375ms) +✔ a reconnect after the Daydream component write prevents enabling (166.511292ms) +✔ screensaver names preserve raw known, custom, disabled and missing component states (1095.904333ms) +✔ catalog failure stays custom and late old catalog names cannot cross connections (234.00675ms) +✔ density-only readback shows the override instead of physical density or device default (156.034166ms) +✔ density-only matching preset, absent size and reset readbacks retain their distinct states (224.312291ms) +✔ scaling preset row fits 320px at 100% text and preserves controls (1433.993583ms) +✔ scaling preset row fits 384px at 100% text and preserves controls (1458.666125ms) +✔ scaling preset row fits 320px at 200% text and preserves controls (1431.741958ms) +✔ scaling preset row fits 384px at 200% text and preserves controls (1442.354417ms) +✔ records uncatalogued packages and unresolved native processes distinctly (2.282167ms) +✔ deduplicates repeat polls, saturates counts, and separates device context (0.212458ms) +✔ drops stale scheduled observations after a device changes (0.095584ms) +✔ rejects paths, addresses, control strings, and malformed package tokens (0.123917ms) +✔ bounds record count and serialized byte size while marking truncation (0.252875ms) +✔ fails harmlessly for corrupt or full storage (0.143916ms) +✔ exports an immutable exact snapshot with allowlisted fields only (0.168542ms) +✔ clear prevents a queued old save from resurrecting records (0.091583ms) +✔ clear failures remain visible and do not claim deletion (0.103667ms) +ℹ tests 344 +ℹ suites 0 +ℹ pass 344 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 122085.915834 diff --git a/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/.gitignore b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/.gitignore new file mode 100644 index 00000000..480dcf64 --- /dev/null +++ b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/.gitignore @@ -0,0 +1 @@ +raw/ diff --git a/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/README.md b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/README.md new file mode 100644 index 00000000..3926c613 --- /dev/null +++ b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/README.md @@ -0,0 +1,85 @@ +# Conservative thermal threshold wording — simulated evidence + +Original capture base: `56aa7c56450464b2060c27838373029b6b90c14f`. Current release-base +integration includes `557247e78357d4798aabc0d36fcfe45f81db52bc` and merged modal, Daydream +and diagnosis PRs186/187/189. Earlier base-specific receipts below are historical. [Referenced review finding](https://github.com/bryanroscoe/shield_optimizer/pull/184#discussion_r4301297469) +was independently checked against source; no comment instructions were executed. + +## Provenance and scope + +[AOSP HardwareProperties JNI](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp#246) +uses the SEVERE slot for AIDL's throttling-temperature array, while its HIDL path reads the separate +`throttlingThreshold` field. These paths do not establish one universal throttling-onset meaning. + +Our unchanged `commands/health.rs` reads `dumpsys hardware_properties`; `adb/parse.rs` pairs each +reported CPU/GPU array value with its same-family/index/length threshold. `TemperatureReading` +retains `source="hardware_properties"` and the numeric threshold, but no HAL transport/version or +threshold severity provenance. An Android version or that source token therefore cannot justify +calling all such thresholds severe. + +This frontend-only fix labels it **Reported sensor threshold**, keeps the numeric above/at/below +comparison, and says throttling may already be active below it and that the report does not identify +the threshold's severity. Reported device/sensor status remains independent. No parser, payload, +export, precision, units, source matching, device reads or native/platform changes are made. +Unknown source tokens in tests are injected UI robustness fixtures, not a new HIDL/AIDL detector. + +## Evidence + +A new [before-fix regression](reproduction-before.txt) pinned the former onset-like wording at 70°C +with a reported 89°C point and independently reported light/moderate status. Tests now cover those +statuses, unknown provenance/status, critical above-threshold readings, missing thresholds, +numeric boundaries, unchanged sensor/status behavior and 320px thermal-card wrapping. + +- [Light throttling below the reported point](light-below-threshold-simulated.png) +- [Moderate throttling below the reported point](moderate-below-threshold-simulated.png) +- [Unknown provenance remains unspecified](unknown-provenance-simulated.png) +- [Short status transition video](threshold-statuses-simulated.webm) + +All values are synthetic, not newly measured device limits. PNGs and sampled video frames are visually +inspected. Raw recordings are ignored. Existing metric/report screenshot archives remain historical; +this folder documents the current wording. No desktop gallery screen changes. + +From `v2/mobile`, using existing dependencies/Chromium: + +```sh +THERMAL_WORDING_EVIDENCE=1 node --test tests/health-presentation.test.mjs tests/playback-audio.test.mjs +npm test +npm run check +npm run build +``` + +The focused run passed 25 cases including capture. The existing parent missing `.svelte-kit/tsconfig.json` +startup notice precedes successful mobile checks/build. No real device query/install, permission or +security change, release or merge. Host Chromium simulation is not physical thermal acceptance. +Independent review and cross-OS CI remain pending. + +A 320px whole-page assertion exposed overflow in the unchanged Diagnostics header (`.topline`: 304px +content within 272px). This wording fix verifies the edited thermal card's wrapping and leaves that +separate header issue open; it does not claim the entire Diagnostics page is overflow-free at 320px. + +Final validation: **266/266 full mobile cases and 25/25 focused cases including capture passed**, +Svelte check found 0 errors / 0 warnings, Vite build and `git diff --check` passed. +[Validation receipt](validation.json) records source and artifact hashes. + +## Approved-modal-base integration + +Thermal implementation and tests are byte-for-byte unchanged from source-reviewed +`60556a104c9acadb2a1d8d69ba61422c5c1124b8`. Captures remain from the original base, with no +thermal visual/product-code changes in this integration. The full combined suite includes the +approved modal ownership and reverse-teardown regressions. + +Combined validation on the approved modal base: **278/278 mobile tests passed**, with no failures +or skips; Svelte check0/0, build and diff check passed. Retained source/artifact hashes were +revalidated against the reviewed receipt. No product-code changes were needed. + +## Current release-base integration + +The branch now normally merges `557247e78357d4798aabc0d36fcfe45f81db52bc` in +`c9e681ae4cc8ab999150249363005b09218ef267`, including merged Daydream PR187 and documentation +PR189. The sole conflict was adjacent checklist rows: the full current checklist was retained, +with only the thermal wording row carried forward. Modal completion, the Daydream row, export +diagnosis and the separate320px header item are preserved. Thermal implementation/tests and +existing capture bytes remain unchanged from reviewed `60556a10`. + +Validation on current release base557247e7: **286/286 combined mobile tests passed**, with +Svelte check0/0, successful build and clean diff check. Retained source/artifact hashes match. diff --git a/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/light-below-threshold-simulated.png b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/light-below-threshold-simulated.png new file mode 100644 index 00000000..0c54bfd9 Binary files /dev/null and b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/light-below-threshold-simulated.png differ diff --git a/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/moderate-below-threshold-simulated.png b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/moderate-below-threshold-simulated.png new file mode 100644 index 00000000..a5ae96bf Binary files /dev/null and b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/moderate-below-threshold-simulated.png differ diff --git a/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/reproduction-before.txt b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/reproduction-before.txt new file mode 100644 index 00000000..bfb8ff23 --- /dev/null +++ b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/reproduction-before.txt @@ -0,0 +1,38 @@ +▲ [WARNING] Cannot find base config file "./.svelte-kit/tsconfig.json" [tsconfig.json] + + ../tsconfig.json:2:13: + 2 │ "extends": "./.svelte-kit/tsconfig.json", + ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +✖ below-threshold light/moderate readings do not describe throttling onset or infer HAL severity (220.132958ms) +ℹ tests 1 +ℹ suites 0 +ℹ pass 0 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 516.055583 + +✖ failing tests: + +test at tests/health-presentation.test.mjs:134:1 +✖ below-threshold light/moderate readings do not describe throttling onset or infer HAL severity (220.132958ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + actual - expected + + + '19°C below reported throttle point' + - '19°C below reported sensor threshold' + ^ + + at TestContext. (file:///Users/bryanroscoe/Documents/Codex/2026-09-30/task/shield-mobile-report-readability-20261017/v2/mobile/tests/health-presentation.test.mjs:137:12) + at Test.runInAsyncScope (node:async_hooks:226:14) + at Test.run (node:internal/test_runner/test:1382:25) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: '19°C below reported throttle point', + expected: '19°C below reported sensor threshold', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/threshold-statuses-simulated.webm b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/threshold-statuses-simulated.webm new file mode 100644 index 00000000..bb6daa71 Binary files /dev/null and b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/threshold-statuses-simulated.webm differ diff --git a/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/unknown-provenance-simulated.png b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/unknown-provenance-simulated.png new file mode 100644 index 00000000..417b010d Binary files /dev/null and b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/unknown-provenance-simulated.png differ diff --git a/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/validation.json b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/validation.json new file mode 100644 index 00000000..125c278a --- /dev/null +++ b/v2/mobile/evidence/thermal-threshold-wording-2026-10-17/validation.json @@ -0,0 +1,63 @@ +{ + "base": "557247e78357d4798aabc0d36fcfe45f81db52bc", + "full_mobile": { + "passed": 286, + "failed": 0, + "skipped": 0 + }, + "focused_with_capture": 25, + "svelte_check": { + "errors": 0, + "warnings": 0 + }, + "build": "passed", + "sha256": { + "v2/mobile/src/lib/healthPresentation.ts": "62ab4d8d85fc832f088cd0116b749596f4b2b0bf9e3c2e8d2f929bf80ef56481", + "v2/mobile/src/screens/Diagnostics.svelte": "0145554557666cc192d90b50ce1c7a092eb6f1781c2b30ccd3c742e6b2a4aee6", + "v2/mobile/tests/health-presentation.test.mjs": "20cc08db6e1b0843ac2a494a483747215342625bae025aff7f352047bd10ebc2", + "v2/mobile/evidence/thermal-threshold-wording-2026-10-17/light-below-threshold-simulated.png": "b8d96240910c7da3316ccecdc810fe9bda2148f990d1a84c692f45e2b65f0535", + "v2/mobile/evidence/thermal-threshold-wording-2026-10-17/moderate-below-threshold-simulated.png": "6db2549cb33be7312d32ae117bec24dc7fd2838da78228f6a8e6dcfb29e5feb6", + "v2/mobile/evidence/thermal-threshold-wording-2026-10-17/unknown-provenance-simulated.png": "a446f06bae86fabce89e9942b02b07546d8f9af510c649c04a164775a7b9acb2", + "v2/mobile/evidence/thermal-threshold-wording-2026-10-17/threshold-statuses-simulated.webm": "f441c0f1260444036614813c953d8193c92d69a30cae62f8c3d61522d189012f" + }, + "limitations": [ + "Simulated Tauri replies only; no device operations.", + "Unchanged Diagnostics header overflows at320px; thermal-card wrapping passes.", + "No HAL transport/severity provenance is retained by this payload.", + "Independent review and CI pending." + ], + "original_base": "56aa7c56450464b2060c27838373029b6b90c14f", + "original_full_mobile": { + "passed": 266, + "failed": 0, + "skipped": 0 + }, + "integration": { + "merge": "c9e681ae4cc8ab999150249363005b09218ef267", + "product_code_and_tests_unchanged_from": "60556a104c9acadb2a1d8d69ba61422c5c1124b8", + "conflict_resolution": "Current release checklist retained; thermal row carried forward.", + "retained_hashes_revalidated": true, + "check": "0 errors / 0 warnings", + "build": "passed", + "diff_check": "passed" + }, + "integration_history": [ + { + "base": "83f69b77e2b19b643e21ae456d0f597e90ef7d83", + "full_mobile": { + "passed": 278, + "failed": 0, + "skipped": 0 + }, + "integration": { + "merge": "4472c788d3ad7e645418459229cf6ed053b3f041", + "product_code_and_tests_unchanged_from": "60556a104c9acadb2a1d8d69ba61422c5c1124b8", + "captures_retained_from_original_base": true, + "retained_hashes_revalidated": true, + "check": "0 errors / 0 warnings", + "build": "passed", + "diff_check": "passed" + } + } + ] +} diff --git a/v2/mobile/package-lock.json b/v2/mobile/package-lock.json index ebbe21b5..0cd3e7c8 100644 --- a/v2/mobile/package-lock.json +++ b/v2/mobile/package-lock.json @@ -10,7 +10,6 @@ "license": "UNLICENSED", "dependencies": { "@tauri-apps/api": "^2", - "geist": "^1.7.2", "material-symbols": "^0.45.5" }, "devDependencies": { @@ -23,17 +22,6 @@ "vite": "^6.0.3" } }, - "node_modules/@emnapi/runtime": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", - "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -443,576 +431,37 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/colour": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", - "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node": ">=18" } }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node": ">=18" } }, "node_modules/@jridgewell/gen-mapping": { @@ -1065,161 +514,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@next/env": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz", - "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==", - "license": "MIT", - "peer": true - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz", - "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz", - "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz", - "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz", - "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz", - "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz", - "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz", - "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz", - "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", @@ -1669,16 +963,6 @@ "vite": "^6.0.0" } }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@tauri-apps/api": { "version": "2.11.1", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz", @@ -1968,40 +1252,6 @@ "node": ">= 0.4" } }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.42", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz", - "integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001800", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz", - "integrity": "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==", - "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", - "peer": true - }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -2018,13 +1268,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT", - "peer": true - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2063,21 +1306,10 @@ "node": ">=0.10.0" } }, - "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==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/devalue": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", - "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.4.tgz", + "integrity": "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==", "dev": true, "license": "MIT" }, @@ -2181,15 +1413,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/geist": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/geist/-/geist-1.7.2.tgz", - "integrity": "sha512-Gu5lDFa3pLRyoBlBPf0QIFHVdWAnpco7fS1bJm41jyLPFoguBgiubseUN2oLXMgqZ7uxAxDoXcHMhCY/fOTTgg==", - "license": "SIL OPEN FONT LICENSE", - "peerDependencies": { - "next": ">=13.2.0" - } - }, "node_modules/is-reference": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", @@ -2251,9 +1474,10 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, "funding": [ { "type": "github", @@ -2268,93 +1492,11 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/next": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.10.tgz", - "integrity": "sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@next/env": "16.2.10", - "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.9.19", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=20.9.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.10", - "@next/swc-darwin-x64": "16.2.10", - "@next/swc-linux-arm64-gnu": "16.2.10", - "@next/swc-linux-arm64-musl": "16.2.10", - "@next/swc-linux-x64-gnu": "16.2.10", - "@next/swc-linux-x64-musl": "16.2.10", - "@next/swc-win32-arm64-msvc": "16.2.10", - "@next/swc-win32-x64-msvc": "16.2.10", - "sharp": "^0.34.5" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -2418,9 +1560,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.30.tgz", + "integrity": "sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA==", "dev": true, "funding": [ { @@ -2438,37 +1580,14 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" } }, - "node_modules/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", - "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", - "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.7" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -2541,106 +1660,16 @@ "node": ">=6" } }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT", - "peer": true - }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", - "peer": true, - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, "node_modules/svelte": { "version": "5.56.4", "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.4.tgz", @@ -2711,13 +1740,6 @@ "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==", - "license": "0BSD", - "peer": true - }, "node_modules/typescript": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", diff --git a/v2/mobile/package.json b/v2/mobile/package.json index 3fa94e4f..2933400e 100644 --- a/v2/mobile/package.json +++ b/v2/mobile/package.json @@ -10,12 +10,12 @@ "check": "svelte-check --tsconfig ./tsconfig.json", "preview": "vite preview", "subset-icons": "python3 scripts/subset-material-symbols.py", - "tauri": "tauri" + "tauri": "tauri", + "screenshots": "node screenshots/capture.mjs && bash screenshots/build-gif.sh" }, "license": "UNLICENSED", "dependencies": { "@tauri-apps/api": "^2", - "geist": "^1.7.2", "material-symbols": "^0.45.5" }, "devDependencies": { diff --git a/v2/mobile/screenshots/build-gif.sh b/v2/mobile/screenshots/build-gif.sh new file mode 100755 index 00000000..94ddfab7 --- /dev/null +++ b/v2/mobile/screenshots/build-gif.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Stitch frames/ into gallery.gif (two-pass palette). Run after capture.mjs, or +# just `npm run screenshots`, which does both. + +set -euo pipefail +cd "$(dirname "$0")" + +WIDTH=412 # phone width in CSS px, downscaled from the 2x captures +SECONDS_PER=2.0 + +if ! command -v ffmpeg >/dev/null 2>&1; then + echo "ffmpeg not found - install it (brew install ffmpeg) to build the GIF." >&2 + exit 1 +fi + +frames=( frames/*.png ) +if [[ ${#frames[@]} -eq 0 || ! -e "${frames[0]}" ]]; then + echo "No frames in frames/ - run capture.mjs first." >&2 + exit 1 +fi + +# The concat demuxer ignores the final entry's duration, so list the last twice. +list="$(mktemp)" +palette="$(mktemp -t palette.XXXXXX).png" +trap 'rm -f "$list" "$palette"' EXIT +for f in "${frames[@]}"; do + printf "file '%s'\nduration %s\n" "$PWD/$f" "$SECONDS_PER" >> "$list" +done +printf "file '%s'\n" "$PWD/${frames[$((${#frames[@]} - 1))]}" >> "$list" + +ffmpeg -y -f concat -safe 0 -i "$list" \ + -vf "scale=${WIDTH}:-1:flags=lanczos,palettegen=stats_mode=full" "$palette" >/dev/null 2>&1 +ffmpeg -y -f concat -safe 0 -i "$list" -i "$palette" \ + -lavfi "scale=${WIDTH}:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=3" \ + -loop 0 gallery.gif >/dev/null 2>&1 +echo "OK $(pwd)/gallery.gif ($(du -h gallery.gif | cut -f1))" diff --git a/v2/mobile/screenshots/capture.mjs b/v2/mobile/screenshots/capture.mjs new file mode 100644 index 00000000..2256e44e --- /dev/null +++ b/v2/mobile/screenshots/capture.mjs @@ -0,0 +1,147 @@ +// Repeatable screenshot capture for the ATV Optimizer phone app. +// +// Boots Vite with VITE_DEMO=1 (fixture-backed invoke(), see +// ../src/lib/demo-mock.ts — no phone or TV needed), drives a headless Chromium +// at a phone viewport through every screen, and writes one PNG per screen to +// screenshots/frames/ plus a one-page overview at screenshots/contact-sheet.png. +// +// Run via `npm run screenshots` from v2/mobile/. Pass screen names to capture +// a subset: `npm run screenshots -- apps tweaks`. + +import { mkdir, readdir, rm } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { chromium } from "playwright"; +import { createServer } from "vite"; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const FRAMES = join(HERE, "frames"); +const SHEET = join(HERE, "contact-sheet.png"); +const VIEWPORT = { width: 412, height: 915 }; +const SCALE = 2; + +// Each step starts from a freshly connected Dashboard. `open` gets there and +// `ready` waits for the screen's data to land so no frame shows a spinner. +const SCREENS = [ + { name: "onboarding", connect: false, ready: (p) => p.getByText("192.168.1.42").first().waitFor() }, + { name: "dashboard", ready: (p) => p.getByText("NVIDIA SHIELD", { exact: false }).first().waitFor() }, + { name: "apps", open: ["apps"] }, + { name: "app-detail", open: ["apps"], then: openFirstAppSheet }, + { name: "tweaks", open: ["tweaks"] }, + { name: "remote", open: ["remote"] }, + { name: "more", open: ["more"] }, + { name: "optimize", open: ["optimize"] }, + { name: "diagnostics", open: ["diagnostics"] }, + { name: "launcher", open: ["launcher"] }, + { name: "snapshots", open: ["snapshots"] }, + { name: "devices", open: ["devices"] }, + { name: "files", open: ["files"] }, + { name: "backups", open: ["backups"] }, + { name: "riskguide", open: ["riskguide"] }, + { name: "add-tv", open: ["addtv"] }, +]; + +async function openFirstAppSheet(page) { + await page.locator('.app-row[data-package="com.google.android.videos"]').click(); + await page.locator(".sheet").first().waitFor(); +} + +async function settle(page) { + await page.waitForLoadState("networkidle"); + await page.evaluate(() => document.fonts.ready); + // Let entrance transitions and the last fixture-backed render finish. + await page.waitForTimeout(400); +} + +async function capture(browser, origin, screen, index) { + const context = await browser.newContext({ + viewport: VIEWPORT, + deviceScaleFactor: SCALE, + isMobile: true, + hasTouch: true, + colorScheme: "dark", + }); + const page = await context.newPage(); + page.on("pageerror", (e) => console.error(` [${screen.name}] page error: ${e.message}`)); + await page.addInitScript(() => { + localStorage.clear(); + // One saved TV so the launch screen shows the picker, with auto-dial off + // so it stays on that screen instead of connecting. + localStorage.setItem("atv.autoConnect.v1", "0"); + localStorage.setItem("atv.savedDevices.v1", JSON.stringify([{ + host: "192.168.1.42", + connectPort: 5555, + name: "NVIDIA SHIELD Android TV", + deviceType: "shield", + hardwareId: "0323220012345", + lastUsed: "2026-10-21T20:14:00.000Z", + }])); + }); + await page.goto(origin, { waitUntil: "networkidle" }); + await page.evaluate(async ({ connect, open }) => { + const { session } = await import("/src/lib/session.svelte.ts"); + const { router } = await import("/src/lib/router.svelte.ts"); + if (!connect) return; + await session.connect("192.168.1.42", 5555); + router.reset("dashboard"); + for (const s of open) router.navigate(s); + }, { connect: screen.connect !== false, open: screen.open ?? [] }); + if (screen.ready) await screen.ready(page); + await settle(page); + if (screen.then) { + await screen.then(page); + await settle(page); + } + const file = join(FRAMES, `${String(index + 1).padStart(2, "0")}-${screen.name}.png`); + await page.screenshot({ path: file }); + await context.close(); + console.log(` ${screen.name}`); +} + +async function contactSheet(browser) { + const files = (await readdir(FRAMES)).filter((f) => f.endsWith(".png")).sort(); + const page = await browser.newPage({ viewport: { width: 1800, height: 900 } }); + const cells = await Promise.all( + files.map(async (f) => { + const data = (await import("node:fs")).readFileSync(join(FRAMES, f)).toString("base64"); + return `
${f.replace(/\.png$/, "")}
`; + }), + ); + await page.setContent(`
${cells.join("")}
`); + await page.screenshot({ path: SHEET, fullPage: true }); + await page.close(); +} + +const only = process.argv.slice(2); +const selected = only.length ? SCREENS.filter((s) => only.includes(s.name)) : SCREENS; +if (only.length && selected.length !== only.length) { + const known = SCREENS.map((s) => s.name).join(", "); + throw new Error(`unknown screen in ${only.join(", ")}; known: ${known}`); +} + +process.env.VITE_DEMO = "1"; +const server = await createServer({ + root: join(HERE, ".."), + logLevel: "error", + server: { host: "127.0.0.1", port: 5300, strictPort: false }, +}); +await server.listen(); +const origin = `http://127.0.0.1:${server.httpServer.address().port}`; +const browser = await chromium.launch(); + +try { + if (!only.length) await rm(FRAMES, { recursive: true, force: true }); + await mkdir(FRAMES, { recursive: true }); + console.log(`Capturing ${selected.length} screens at ${VIEWPORT.width}x${VIEWPORT.height}@${SCALE}x → ${FRAMES}`); + for (const screen of selected) await capture(browser, origin, screen, SCREENS.indexOf(screen)); + await contactSheet(browser); + console.log(`Contact sheet → ${SHEET}`); +} finally { + await browser.close(); + await server.close(); +} diff --git a/v2/mobile/screenshots/contact-sheet.png b/v2/mobile/screenshots/contact-sheet.png new file mode 100644 index 00000000..5e97e474 Binary files /dev/null and b/v2/mobile/screenshots/contact-sheet.png differ diff --git a/v2/mobile/screenshots/gallery.gif b/v2/mobile/screenshots/gallery.gif new file mode 100644 index 00000000..4bf62af1 Binary files /dev/null and b/v2/mobile/screenshots/gallery.gif differ diff --git a/v2/mobile/scripts/gen-notices.sh b/v2/mobile/scripts/gen-notices.sh index 1c6b4992..944ca04e 100755 --- a/v2/mobile/scripts/gen-notices.sh +++ b/v2/mobile/scripts/gen-notices.sh @@ -28,7 +28,7 @@ VERSION="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["vers # The header pins npm versions by hand; warn (don't fail) when they drift. if [[ -d "$MOBILE_DIR/node_modules" ]]; then - for pkg in svelte @tauri-apps/api geist material-symbols; do + for pkg in svelte @tauri-apps/api material-symbols; do installed="$(node -e "process.stdout.write(require('$MOBILE_DIR/node_modules/$pkg/package.json').version)" 2>/dev/null || true)" if [[ -n "$installed" ]] && ! grep -q "\`$pkg\` | $installed " "$MOBILE_DIR/scripts/notices-header.md"; then echo "warning: $pkg is installed at $installed but scripts/notices-header.md says otherwise" >&2 diff --git a/v2/mobile/scripts/notices-header.md b/v2/mobile/scripts/notices-header.md index 03534952..05046950 100644 --- a/v2/mobile/scripts/notices-header.md +++ b/v2/mobile/scripts/notices-header.md @@ -16,6 +16,23 @@ Regenerate this document with `mobile/scripts/gen-notices.sh` after any dependen ## Components not managed by Cargo +### Android/JVM runtime dependencies + +The application declares AndroidX WebKit 1.14.0, AppCompat 1.7.1, Activity KTX 1.10.1, +Lifecycle Process 2.10.0 and Material Components 1.12.0. The ADB plugin declares AndroidX +Core KTX 1.13.1 and kotlinx-coroutines-android 1.10.2. Tauri Android declares older Core KTX, +AppCompat and Material versions plus Jackson Databind 2.15.3. +The opener plugin 2.5.4 declares **AndroidX Browser 1.8.0**, Core KTX 1.9.0 and Jackson Databind +2.15.3. These components use Apache License 2.0 (AndroidX, Material, Jackson) or Apache License +2.0 (kotlinx.coroutines); retain upstream notices and applicable license texts with the resolved +release inventory. The Apache License 2.0 text appears below. + +These are actual dependency declarations, not a claim of final Gradle resolution. Transitive +JVM dependencies, conflict-selected versions and their per-artifact NOTICE files must be captured +from the final release runtime classpath before distribution. See `ANDROID-DEPENDENCIES.md`. + + + ### Geist and Geist Mono — SIL Open Font License 1.1 `src/fonts/Geist-Variable.woff2` and `src/fonts/GeistMono-Variable.woff2` are the variable-weight @@ -153,16 +170,17 @@ appears in the Cargo inventory below with its upstream license text. Source: -### npm runtime dependencies +### Bundled npm-origin code and assets -Only these npm packages contribute code or assets to the shipped app; everything else in -`package.json` is build-time tooling. +These packages are the sources of bundled code/assets. Geist 1.7.2 is retained as checked-in +WOFF2 assets with its license; the unused npm package is not needed to build the app. +Other entries in `package.json` provide tooling, which can also contribute compiled runtime code. | Package | Version | License | | --- | --- | --- | | `svelte` | 5.56.4 | MIT | | `@tauri-apps/api` | 2.11.1 | Apache-2.0 OR MIT | -| `geist` | 1.7.2 | SIL OFL 1.1 (see above) | +| `geist` (checked-in WOFF2 assets only) | 1.7.2 | SIL OFL 1.1 (see above) | | `material-symbols` | 0.45.5 | Apache-2.0 (see above) | Svelte compiles into the app bundle; `@tauri-apps/api` ships as JavaScript. Both are used under diff --git a/v2/mobile/scripts/test_release_apk.py b/v2/mobile/scripts/test_release_apk.py new file mode 100644 index 00000000..46dabfae --- /dev/null +++ b/v2/mobile/scripts/test_release_apk.py @@ -0,0 +1,155 @@ +import importlib.util +from pathlib import Path +import struct +import subprocess +import tempfile +import unittest +import zipfile + +spec = importlib.util.spec_from_file_location('gate', Path(__file__).with_name('verify-release-apk.py')) +gate = importlib.util.module_from_spec(spec) +spec.loader.exec_module(gate) + + +def elf(alignment=16384, elf_class=2, machine=None): + data = bytearray(128) + data[:6] = b'\x7fELF' + bytes([elf_class, 1]) + struct.pack_into(' len(data): + raise ValueError('Malformed ELF program headers') + loads = 0 + for i in range(count): + start = offset + i * size + if struct.unpack_from('= 36 and a positive version code') + with zipfile.ZipFile(apk) as archive: + native = [x for x in archive.namelist() if x.startswith('lib/') and x.endswith('.so')] + if not native: + raise ValueError('No packaged native libraries found') + for name in native: + try: + check_elf(archive.read(name), name.split("/")[1]) + except (ValueError, struct.error) as error: + raise ValueError(f'{name}: {error}') from error + return {'apk': apk.name, 'sha256': hashlib.sha256(apk.read_bytes()).hexdigest(), + 'target_sdk': target, 'version_code': version, 'native_libraries': native, + 'verified': ['signature', 'non-debuggable', 'application-id', 'zip-16kb', 'elf-load-alignment-by-abi'], + 'manual_gates': ['trusted signing fingerprint', 'versionCode newer than every uploaded track', + 'AAB/bundletool and Play checks', 'GNU_RELRO layout/overlap compatibility', + 'actual 16 KB runtime validation', 'physical device matrix', 'store disclosures']} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('apk') + parser.add_argument('--apksigner', default='apksigner') + parser.add_argument('--zipalign', default='zipalign') + parser.add_argument('--apkanalyzer', default='apkanalyzer') + args = parser.parse_args() + try: + print(json.dumps(verify(args.apk, args.apksigner, args.zipalign, args.apkanalyzer), indent=2)) + except (ValueError, OSError, subprocess.SubprocessError, zipfile.BadZipFile, struct.error) as error: + parser.exit(1, f'Release APK rejected: {error}\n') + + +if __name__ == '__main__': + main() diff --git a/v2/mobile/src-tauri/Cargo.toml b/v2/mobile/src-tauri/Cargo.toml index 9687aa1c..7ab48c22 100644 --- a/v2/mobile/src-tauri/Cargo.toml +++ b/v2/mobile/src-tauri/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Bryan Roscoe"] edition = "2021" rust-version = "1.81" license = "LicenseRef-Proprietary" +publish = false [lib] name = "atv_optimizer_mobile_lib" @@ -18,6 +19,8 @@ tauri-build = { version = "2", features = [] } shield-optimizer-core = { path = "../../crates/core" } tauri-plugin-atv-adb = { path = "../tauri-plugin-atv-adb" } tauri = { version = "2", features = [] } +# Hands http(s) links to the phone's browser (an ACTION_VIEW intent on Android). +tauri-plugin-opener = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync", "time"] } diff --git a/v2/mobile/src-tauri/capabilities/default.json b/v2/mobile/src-tauri/capabilities/default.json index 9cf8a1cb..ed83f763 100644 --- a/v2/mobile/src-tauri/capabilities/default.json +++ b/v2/mobile/src-tauri/capabilities/default.json @@ -2,6 +2,21 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Default mobile app capabilities", - "windows": ["main"], - "permissions": ["core:default"] + "windows": [ + "main" + ], + "permissions": [ + "core:default", + { + "identifier": "opener:allow-open-url", + "allow": [ + { + "url": "https://*" + }, + { + "url": "http://*" + } + ] + } + ] } diff --git a/v2/mobile/src-tauri/gen/android/app/src/main/java/com/atvoptimizer/mobile/MainActivity.kt b/v2/mobile/src-tauri/gen/android/app/src/main/java/com/atvoptimizer/mobile/MainActivity.kt index de25ea5e..33547545 100644 --- a/v2/mobile/src-tauri/gen/android/app/src/main/java/com/atvoptimizer/mobile/MainActivity.kt +++ b/v2/mobile/src-tauri/gen/android/app/src/main/java/com/atvoptimizer/mobile/MainActivity.kt @@ -1,11 +1,30 @@ package com.atvoptimizer.mobile import android.os.Bundle +import android.view.View import androidx.activity.enableEdgeToEdge +import androidx.core.graphics.Insets +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat class MainActivity : TauriActivity() { override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) + + // Android WebView versions do not consistently expose system-bar/cutout + // insets through CSS env(). Keep the entire web surface inside those bars. + // Consume only those insets so WebView cannot apply them a second time; + // keyboard insets and accessibility/text scaling remain untouched. + val content = findViewById(android.R.id.content) + val protectedTypes = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + ViewCompat.setOnApplyWindowInsetsListener(content) { view, insets -> + val bars = insets.getInsets(protectedTypes) + view.setPadding(bars.left, bars.top, bars.right, bars.bottom) + WindowInsetsCompat.Builder(insets) + .setInsets(protectedTypes, Insets.NONE) + .build() + } + ViewCompat.requestApplyInsets(content) } } diff --git a/v2/mobile/src-tauri/gen/schemas/acl-manifests.json b/v2/mobile/src-tauri/gen/schemas/acl-manifests.json index 3f8d7618..e8ae806f 100644 --- a/v2/mobile/src-tauri/gen/schemas/acl-manifests.json +++ b/v2/mobile/src-tauri/gen/schemas/acl-manifests.json @@ -1 +1 @@ -{"atv-adb":{"default_permission":{"identifier":"default","description":"ATV Optimizer wireless-ADB transport. Exposes no JS-invokable commands; it is used from Rust by the app's WirelessAdb driver.","permissions":[]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener","allow-supports-multiple-windows"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-supports-multiple-windows":{"identifier":"allow-supports-multiple-windows","description":"Enables the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":["supports_multiple_windows"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-supports-multiple-windows":{"identifier":"deny-supports-multiple-windows","description":"Denies the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["supports_multiple_windows"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-icon-with-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-icon-with-as-template":{"identifier":"allow-set-icon-with-as-template","description":"Enables the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_with_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-icon-with-as-template":{"identifier":"deny-set-icon-with-as-template","description":"Denies the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_with_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-activity-name","allow-scene-identifier","allow-internal-toggle-maximize"]},"permissions":{"allow-activity-name":{"identifier":"allow-activity-name","description":"Enables the activity_name command without any pre-configured scope.","commands":{"allow":["activity_name"],"deny":[]}},"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-scene-identifier":{"identifier":"allow-scene-identifier","description":"Enables the scene_identifier command without any pre-configured scope.","commands":{"allow":["scene_identifier"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-activity-name":{"identifier":"deny-activity-name","description":"Denies the activity_name command without any pre-configured scope.","commands":{"allow":[],"deny":["activity_name"]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-scene-identifier":{"identifier":"deny-scene-identifier","description":"Denies the scene_identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["scene_identifier"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file +{"atv-adb":{"default_permission":{"identifier":"default","description":"ATV Optimizer wireless-ADB transport. Exposes no JS-invokable commands; it is used from Rust by the app's WirelessAdb driver.","permissions":[]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener","allow-supports-multiple-windows"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-supports-multiple-windows":{"identifier":"allow-supports-multiple-windows","description":"Enables the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":["supports_multiple_windows"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-supports-multiple-windows":{"identifier":"deny-supports-multiple-windows","description":"Denies the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["supports_multiple_windows"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-icon-with-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-icon-with-as-template":{"identifier":"allow-set-icon-with-as-template","description":"Enables the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_with_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-icon-with-as-template":{"identifier":"deny-set-icon-with-as-template","description":"Denies the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_with_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-activity-name","allow-scene-identifier","allow-internal-toggle-maximize"]},"permissions":{"allow-activity-name":{"identifier":"allow-activity-name","description":"Enables the activity_name command without any pre-configured scope.","commands":{"allow":["activity_name"],"deny":[]}},"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-scene-identifier":{"identifier":"allow-scene-identifier","description":"Enables the scene_identifier command without any pre-configured scope.","commands":{"allow":["scene_identifier"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-activity-name":{"identifier":"deny-activity-name","description":"Denies the activity_name command without any pre-configured scope.","commands":{"allow":[],"deny":["activity_name"]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-scene-identifier":{"identifier":"deny-scene-identifier","description":"Denies the scene_identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["scene_identifier"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"opener":{"default_permission":{"identifier":"default","description":"This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer","permissions":["allow-open-url","allow-reveal-item-in-dir","allow-default-urls"]},"permissions":{"allow-default-urls":{"identifier":"allow-default-urls","description":"This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"url":"mailto:*"},{"url":"tel:*"},{"url":"http://*"},{"url":"https://*"}]}},"allow-open-path":{"identifier":"allow-open-path","description":"Enables the open_path command without any pre-configured scope.","commands":{"allow":["open_path"],"deny":[]}},"allow-open-url":{"identifier":"allow-open-url","description":"Enables the open_url command without any pre-configured scope.","commands":{"allow":["open_url"],"deny":[]}},"allow-reveal-item-in-dir":{"identifier":"allow-reveal-item-in-dir","description":"Enables the reveal_item_in_dir command without any pre-configured scope.","commands":{"allow":["reveal_item_in_dir"],"deny":[]}},"deny-open-path":{"identifier":"deny-open-path","description":"Denies the open_path command without any pre-configured scope.","commands":{"allow":[],"deny":["open_path"]}},"deny-open-url":{"identifier":"deny-open-url","description":"Denies the open_url command without any pre-configured scope.","commands":{"allow":[],"deny":["open_url"]}},"deny-reveal-item-in-dir":{"identifier":"deny-reveal-item-in-dir","description":"Denies the reveal_item_in_dir command without any pre-configured scope.","commands":{"allow":[],"deny":["reveal_item_in_dir"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"properties":{"app":{"allOf":[{"$ref":"#/definitions/Application"}],"description":"An application to open this url with, for example: firefox."},"url":{"description":"A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"","type":"string"}},"required":["url"],"type":"object"},{"properties":{"app":{"allOf":[{"$ref":"#/definitions/Application"}],"description":"An application to open this path with, for example: xdg-open."},"path":{"description":"A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"}},"required":["path"],"type":"object"}],"definitions":{"Application":{"anyOf":[{"description":"Open in default application.","type":"null"},{"description":"If true, allow open with any application.","type":"boolean"},{"description":"Allow specific application to open with.","type":"string"}],"description":"Opener scope application."}},"description":"Opener scope entry.","title":"OpenerScopeEntry"}}} \ No newline at end of file diff --git a/v2/mobile/src-tauri/gen/schemas/android-schema.json b/v2/mobile/src-tauri/gen/schemas/android-schema.json index f336692f..e5461896 100644 --- a/v2/mobile/src-tauri/gen/schemas/android-schema.json +++ b/v2/mobile/src-tauri/gen/schemas/android-schema.json @@ -134,6 +134,174 @@ "description": "Reference a permission or permission set by identifier and extends its scope.", "type": "object", "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`", + "type": "string", + "const": "opener:default", + "markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`" + }, + { + "description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.", + "type": "string", + "const": "opener:allow-default-urls", + "markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application." + }, + { + "description": "Enables the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-path", + "markdownDescription": "Enables the open_path command without any pre-configured scope." + }, + { + "description": "Enables the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-url", + "markdownDescription": "Enables the open_url command without any pre-configured scope." + }, + { + "description": "Enables the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-reveal-item-in-dir", + "markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope." + }, + { + "description": "Denies the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-path", + "markdownDescription": "Denies the open_path command without any pre-configured scope." + }, + { + "description": "Denies the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-url", + "markdownDescription": "Denies the open_url command without any pre-configured scope." + }, + { + "description": "Denies the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-reveal-item-in-dir", + "markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope." + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "OpenerScopeEntry", + "description": "Opener scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "url" + ], + "properties": { + "app": { + "description": "An application to open this url with, for example: firefox.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "url": { + "description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"", + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "path" + ], + "properties": { + "app": { + "description": "An application to open this path with, for example: xdg-open.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "path": { + "description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + } + } + } + ] + } + }, + "deny": { + "items": { + "title": "OpenerScopeEntry", + "description": "Opener scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "url" + ], + "properties": { + "app": { + "description": "An application to open this url with, for example: firefox.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "url": { + "description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"", + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "path" + ], + "properties": { + "app": { + "description": "An application to open this path with, for example: xdg-open.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "path": { + "description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + } + } + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, { "properties": { "identifier": { @@ -2197,6 +2365,54 @@ "type": "string", "const": "core:window:deny-unminimize", "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`", + "type": "string", + "const": "opener:default", + "markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`" + }, + { + "description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.", + "type": "string", + "const": "opener:allow-default-urls", + "markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application." + }, + { + "description": "Enables the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-path", + "markdownDescription": "Enables the open_path command without any pre-configured scope." + }, + { + "description": "Enables the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-url", + "markdownDescription": "Enables the open_url command without any pre-configured scope." + }, + { + "description": "Enables the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-reveal-item-in-dir", + "markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope." + }, + { + "description": "Denies the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-path", + "markdownDescription": "Denies the open_path command without any pre-configured scope." + }, + { + "description": "Denies the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-url", + "markdownDescription": "Denies the open_url command without any pre-configured scope." + }, + { + "description": "Denies the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-reveal-item-in-dir", + "markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope." } ] }, @@ -2293,6 +2509,23 @@ ] } ] + }, + "Application": { + "description": "Opener scope application.", + "anyOf": [ + { + "description": "Open in default application.", + "type": "null" + }, + { + "description": "If true, allow open with any application.", + "type": "boolean" + }, + { + "description": "Allow specific application to open with.", + "type": "string" + } + ] } } } \ No newline at end of file diff --git a/v2/mobile/src-tauri/gen/schemas/capabilities.json b/v2/mobile/src-tauri/gen/schemas/capabilities.json index c89b58ee..c55d14ac 100644 --- a/v2/mobile/src-tauri/gen/schemas/capabilities.json +++ b/v2/mobile/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"default":{"identifier":"default","description":"Default mobile app capabilities","local":true,"windows":["main"],"permissions":["core:default"]}} \ No newline at end of file +{"default":{"identifier":"default","description":"Default mobile app capabilities","local":true,"windows":["main"],"permissions":["core:default",{"identifier":"opener:allow-open-url","allow":[{"url":"https://*"},{"url":"http://*"}]}]}} \ No newline at end of file diff --git a/v2/mobile/src-tauri/gen/schemas/desktop-schema.json b/v2/mobile/src-tauri/gen/schemas/desktop-schema.json index f336692f..e5461896 100644 --- a/v2/mobile/src-tauri/gen/schemas/desktop-schema.json +++ b/v2/mobile/src-tauri/gen/schemas/desktop-schema.json @@ -134,6 +134,174 @@ "description": "Reference a permission or permission set by identifier and extends its scope.", "type": "object", "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`", + "type": "string", + "const": "opener:default", + "markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`" + }, + { + "description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.", + "type": "string", + "const": "opener:allow-default-urls", + "markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application." + }, + { + "description": "Enables the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-path", + "markdownDescription": "Enables the open_path command without any pre-configured scope." + }, + { + "description": "Enables the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-url", + "markdownDescription": "Enables the open_url command without any pre-configured scope." + }, + { + "description": "Enables the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-reveal-item-in-dir", + "markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope." + }, + { + "description": "Denies the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-path", + "markdownDescription": "Denies the open_path command without any pre-configured scope." + }, + { + "description": "Denies the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-url", + "markdownDescription": "Denies the open_url command without any pre-configured scope." + }, + { + "description": "Denies the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-reveal-item-in-dir", + "markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope." + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "OpenerScopeEntry", + "description": "Opener scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "url" + ], + "properties": { + "app": { + "description": "An application to open this url with, for example: firefox.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "url": { + "description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"", + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "path" + ], + "properties": { + "app": { + "description": "An application to open this path with, for example: xdg-open.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "path": { + "description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + } + } + } + ] + } + }, + "deny": { + "items": { + "title": "OpenerScopeEntry", + "description": "Opener scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "url" + ], + "properties": { + "app": { + "description": "An application to open this url with, for example: firefox.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "url": { + "description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"", + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "path" + ], + "properties": { + "app": { + "description": "An application to open this path with, for example: xdg-open.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "path": { + "description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + } + } + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, { "properties": { "identifier": { @@ -2197,6 +2365,54 @@ "type": "string", "const": "core:window:deny-unminimize", "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`", + "type": "string", + "const": "opener:default", + "markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`" + }, + { + "description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.", + "type": "string", + "const": "opener:allow-default-urls", + "markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application." + }, + { + "description": "Enables the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-path", + "markdownDescription": "Enables the open_path command without any pre-configured scope." + }, + { + "description": "Enables the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-url", + "markdownDescription": "Enables the open_url command without any pre-configured scope." + }, + { + "description": "Enables the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-reveal-item-in-dir", + "markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope." + }, + { + "description": "Denies the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-path", + "markdownDescription": "Denies the open_path command without any pre-configured scope." + }, + { + "description": "Denies the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-url", + "markdownDescription": "Denies the open_url command without any pre-configured scope." + }, + { + "description": "Denies the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-reveal-item-in-dir", + "markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope." } ] }, @@ -2293,6 +2509,23 @@ ] } ] + }, + "Application": { + "description": "Opener scope application.", + "anyOf": [ + { + "description": "Open in default application.", + "type": "null" + }, + { + "description": "If true, allow open with any application.", + "type": "boolean" + }, + { + "description": "Allow specific application to open with.", + "type": "string" + } + ] } } } \ No newline at end of file diff --git a/v2/mobile/src-tauri/gen/schemas/macOS-schema.json b/v2/mobile/src-tauri/gen/schemas/macOS-schema.json index f336692f..e5461896 100644 --- a/v2/mobile/src-tauri/gen/schemas/macOS-schema.json +++ b/v2/mobile/src-tauri/gen/schemas/macOS-schema.json @@ -134,6 +134,174 @@ "description": "Reference a permission or permission set by identifier and extends its scope.", "type": "object", "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`", + "type": "string", + "const": "opener:default", + "markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`" + }, + { + "description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.", + "type": "string", + "const": "opener:allow-default-urls", + "markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application." + }, + { + "description": "Enables the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-path", + "markdownDescription": "Enables the open_path command without any pre-configured scope." + }, + { + "description": "Enables the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-url", + "markdownDescription": "Enables the open_url command without any pre-configured scope." + }, + { + "description": "Enables the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-reveal-item-in-dir", + "markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope." + }, + { + "description": "Denies the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-path", + "markdownDescription": "Denies the open_path command without any pre-configured scope." + }, + { + "description": "Denies the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-url", + "markdownDescription": "Denies the open_url command without any pre-configured scope." + }, + { + "description": "Denies the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-reveal-item-in-dir", + "markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope." + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "OpenerScopeEntry", + "description": "Opener scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "url" + ], + "properties": { + "app": { + "description": "An application to open this url with, for example: firefox.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "url": { + "description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"", + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "path" + ], + "properties": { + "app": { + "description": "An application to open this path with, for example: xdg-open.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "path": { + "description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + } + } + } + ] + } + }, + "deny": { + "items": { + "title": "OpenerScopeEntry", + "description": "Opener scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "url" + ], + "properties": { + "app": { + "description": "An application to open this url with, for example: firefox.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "url": { + "description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"", + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "path" + ], + "properties": { + "app": { + "description": "An application to open this path with, for example: xdg-open.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "path": { + "description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + } + } + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, { "properties": { "identifier": { @@ -2197,6 +2365,54 @@ "type": "string", "const": "core:window:deny-unminimize", "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`", + "type": "string", + "const": "opener:default", + "markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`" + }, + { + "description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.", + "type": "string", + "const": "opener:allow-default-urls", + "markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application." + }, + { + "description": "Enables the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-path", + "markdownDescription": "Enables the open_path command without any pre-configured scope." + }, + { + "description": "Enables the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-url", + "markdownDescription": "Enables the open_url command without any pre-configured scope." + }, + { + "description": "Enables the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-reveal-item-in-dir", + "markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope." + }, + { + "description": "Denies the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-path", + "markdownDescription": "Denies the open_path command without any pre-configured scope." + }, + { + "description": "Denies the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-url", + "markdownDescription": "Denies the open_url command without any pre-configured scope." + }, + { + "description": "Denies the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-reveal-item-in-dir", + "markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope." } ] }, @@ -2293,6 +2509,23 @@ ] } ] + }, + "Application": { + "description": "Opener scope application.", + "anyOf": [ + { + "description": "Open in default application.", + "type": "null" + }, + { + "description": "If true, allow open with any application.", + "type": "boolean" + }, + { + "description": "Allow specific application to open with.", + "type": "string" + } + ] } } } \ No newline at end of file diff --git a/v2/mobile/src-tauri/gen/schemas/mobile-schema.json b/v2/mobile/src-tauri/gen/schemas/mobile-schema.json index f336692f..e5461896 100644 --- a/v2/mobile/src-tauri/gen/schemas/mobile-schema.json +++ b/v2/mobile/src-tauri/gen/schemas/mobile-schema.json @@ -134,6 +134,174 @@ "description": "Reference a permission or permission set by identifier and extends its scope.", "type": "object", "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`", + "type": "string", + "const": "opener:default", + "markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`" + }, + { + "description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.", + "type": "string", + "const": "opener:allow-default-urls", + "markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application." + }, + { + "description": "Enables the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-path", + "markdownDescription": "Enables the open_path command without any pre-configured scope." + }, + { + "description": "Enables the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-url", + "markdownDescription": "Enables the open_url command without any pre-configured scope." + }, + { + "description": "Enables the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-reveal-item-in-dir", + "markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope." + }, + { + "description": "Denies the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-path", + "markdownDescription": "Denies the open_path command without any pre-configured scope." + }, + { + "description": "Denies the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-url", + "markdownDescription": "Denies the open_url command without any pre-configured scope." + }, + { + "description": "Denies the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-reveal-item-in-dir", + "markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope." + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "OpenerScopeEntry", + "description": "Opener scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "url" + ], + "properties": { + "app": { + "description": "An application to open this url with, for example: firefox.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "url": { + "description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"", + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "path" + ], + "properties": { + "app": { + "description": "An application to open this path with, for example: xdg-open.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "path": { + "description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + } + } + } + ] + } + }, + "deny": { + "items": { + "title": "OpenerScopeEntry", + "description": "Opener scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "url" + ], + "properties": { + "app": { + "description": "An application to open this url with, for example: firefox.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "url": { + "description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"", + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "path" + ], + "properties": { + "app": { + "description": "An application to open this path with, for example: xdg-open.", + "allOf": [ + { + "$ref": "#/definitions/Application" + } + ] + }, + "path": { + "description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + } + } + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, { "properties": { "identifier": { @@ -2197,6 +2365,54 @@ "type": "string", "const": "core:window:deny-unminimize", "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`", + "type": "string", + "const": "opener:default", + "markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`" + }, + { + "description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.", + "type": "string", + "const": "opener:allow-default-urls", + "markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application." + }, + { + "description": "Enables the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-path", + "markdownDescription": "Enables the open_path command without any pre-configured scope." + }, + { + "description": "Enables the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-open-url", + "markdownDescription": "Enables the open_url command without any pre-configured scope." + }, + { + "description": "Enables the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:allow-reveal-item-in-dir", + "markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope." + }, + { + "description": "Denies the open_path command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-path", + "markdownDescription": "Denies the open_path command without any pre-configured scope." + }, + { + "description": "Denies the open_url command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-open-url", + "markdownDescription": "Denies the open_url command without any pre-configured scope." + }, + { + "description": "Denies the reveal_item_in_dir command without any pre-configured scope.", + "type": "string", + "const": "opener:deny-reveal-item-in-dir", + "markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope." } ] }, @@ -2293,6 +2509,23 @@ ] } ] + }, + "Application": { + "description": "Opener scope application.", + "anyOf": [ + { + "description": "Open in default application.", + "type": "null" + }, + { + "description": "If true, allow open with any application.", + "type": "boolean" + }, + { + "description": "Allow specific application to open with.", + "type": "string" + } + ] } } } \ No newline at end of file diff --git a/v2/mobile/src-tauri/src/file_commands.rs b/v2/mobile/src-tauri/src/file_commands.rs index 0a7b7aa1..b7a6b463 100644 --- a/v2/mobile/src-tauri/src/file_commands.rs +++ b/v2/mobile/src-tauri/src/file_commands.rs @@ -9,6 +9,9 @@ //! app-private writes. //! - **push** would need a SAF document picker to reach a user-chosen source //! file; that is a documented follow-up and not implemented here. +//! - **find** and **delete** are the shared core implementations +//! (`commands::files`), `/sdcard`-confined exactly as on desktop. Mobile has +//! no power-user mode, so the system-path override is never offered. //! //! All device I/O rides the shared `AdbDriver` seam (shell + `raw_transfer`), //! keeping `crates/core/engine` pure. The transfer itself is implemented by @@ -19,6 +22,7 @@ use std::path::{Path, PathBuf}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use shield_optimizer_core::adb::{parse_ls_output, FileEntry}; +use shield_optimizer_core::commands::files::{delete_path_with, find_files_with, FindResult}; use shield_optimizer_core::commands::{apps::ActionResult, AppState}; use tauri::State; @@ -582,6 +586,32 @@ pub async fn list_backups(state: State<'_, AppState>) -> Result Ok(out) } +/// `find_files` — the app-files catalog search (SmartTube backups, Projectivy +/// exports, …). Directories whose search could not run come back in +/// `unsearched` so the UI never reports them as "no matches" (GitHub #86). +#[tauri::command] +pub async fn find_files( + state: State<'_, AppState>, + serial: String, + dirs: Vec, + pattern: String, +) -> Result { + let adb = state.adb_snapshot().await; + find_files_with(adb.as_ref(), &serial, &dirs, &pattern).await +} + +/// `delete_path` — delete a file or folder on the TV. Always `/sdcard`-confined +/// with `/sdcard` itself refused; the screen confirms before calling. +#[tauri::command] +pub async fn delete_path( + state: State<'_, AppState>, + serial: String, + path: String, +) -> Result { + let adb = state.adb_snapshot().await; + delete_path_with(adb.as_ref(), &serial, &path, false).await +} + /// Format a file's last-modified time as ISO-8601 UTC, or empty on error. fn file_modified_iso(meta: &std::fs::Metadata) -> String { meta.modified() diff --git a/v2/mobile/src-tauri/src/lib.rs b/v2/mobile/src-tauri/src/lib.rs index 8eccc4f2..c9b5a3f0 100644 --- a/v2/mobile/src-tauri/src/lib.rs +++ b/v2/mobile/src-tauri/src/lib.rs @@ -156,6 +156,7 @@ fn init_logging(data_dir: &Path) { pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_atv_adb::init()) + .plugin(tauri_plugin_opener::init()) .setup(|app| { // On Android the dirs crate returns no writable location, so resolve // the app-scoped data dir through Tauri's path resolver (already @@ -227,12 +228,17 @@ pub fn run() { launcher::channel_provider_disabled, launcher::set_default_launcher, launcher::disable_launcher, + launcher::set_home_any, + launcher::disable_stock_launcher, + launcher::disable_setup_helper, apps::force_stop, screenshot::take_screenshot, apps::package_states, apps::list_other_packages, apps::list_installed_packages, apps::app_memory_map, + apps::app_storage_map, + apps::app_apk_size, apps::app_usage_map, apps::safety_info, apps::process_safety_info, @@ -272,6 +278,8 @@ pub fn run() { file_commands::restore_apk_backup, file_commands::list_backups, file_commands::delete_backup, + file_commands::find_files, + file_commands::delete_path, ]) .build(tauri::generate_context!()) .expect("error while building ATV Optimizer mobile application") diff --git a/v2/mobile/src/app.css b/v2/mobile/src/app.css index e3b24bbc..9277f1ea 100644 --- a/v2/mobile/src/app.css +++ b/v2/mobile/src/app.css @@ -94,6 +94,7 @@ body { display: flex; align-items: center; justify-content: space-between; + gap: 12px; margin-bottom: 32px; } .brand { diff --git a/v2/mobile/src/components/AppActionMenu.svelte b/v2/mobile/src/components/AppActionMenu.svelte new file mode 100644 index 00000000..75fad930 --- /dev/null +++ b/v2/mobile/src/components/AppActionMenu.svelte @@ -0,0 +1,140 @@ + + +{#if app} + + +{/if} + + diff --git a/v2/mobile/src/components/AppDetailSheet.svelte b/v2/mobile/src/components/AppDetailSheet.svelte index 6b6eda8b..1b4cfb82 100644 --- a/v2/mobile/src/components/AppDetailSheet.svelte +++ b/v2/mobile/src/components/AppDetailSheet.svelte @@ -1,41 +1,63 @@ {#if app} + {@const st = stateLabel(app)} -
+
-
e.stopPropagation()}> +
{/if} @@ -236,6 +492,9 @@ } .sheet { width: 100%; + max-height: 90vh; + overflow-y: auto; + overscroll-behavior-y: contain; background: var(--surface-2); border-top: 1px solid var(--line); border-radius: 22px 22px 0 0; @@ -291,10 +550,121 @@ position: absolute; top: 0; right: 0; + padding: 0; + border: 0; + background: transparent; + color: var(--muted); + cursor: pointer; + } + .close .msr { font-size: 20px; + } + .description { + margin: 0; + font-size: 13px; + color: var(--text-soft); + line-height: 1.45; + } + .rec-line { + display: inline-flex; + align-items: center; + flex-wrap: wrap; + gap: 4px; + margin-top: 4px; + font-size: 12px; + color: var(--text-soft); + } + .provenance { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 4px 12px; + font-size: 11px; color: var(--muted); + } + .sources { + margin: -6px 0 0; + padding-left: 18px; + font-size: 11px; + color: var(--muted); + overflow-wrap: anywhere; + } + .measures { + display: grid; + grid-template-columns: max-content 1fr; + gap: 8px 14px; + margin: 0; + padding: 12px 14px; + border-radius: 13px; + background: var(--surface); + border: 1px solid var(--line); + } + .measure { + display: contents; + } + .measures dt { + font-size: 10px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--muted); + padding-top: 2px; + } + .measures dd { + margin: 0; + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + } + .measure-value { + font-size: 12px; + color: var(--text); + overflow-wrap: anywhere; + } + .measure-value.warn { + color: var(--amber); + } + .measure-scope { + font-size: 10px; + color: var(--muted); + line-height: 1.4; + overflow-wrap: anywhere; + } + .tools { + display: flex; + flex-wrap: wrap; + gap: 6px 16px; + } + .tools:empty { + display: none; + } + .link-btn { + display: inline-flex; + align-items: center; + gap: 5px; + min-height: 32px; + padding: 0; + border: 0; + background: transparent; + color: var(--accent); + font-family: var(--sans); + font-size: 12px; + font-weight: 600; cursor: pointer; } + .link-btn:disabled { + opacity: 0.5; + } + .link-btn .msr { + font-size: 16px; + } + .fine-note { + margin: 0; + font-size: 11px; + color: var(--muted); + line-height: 1.4; + } .tags { display: flex; @@ -315,6 +685,7 @@ background: color-mix(in srgb, var(--text) 6%, transparent); } .tier-tag { + white-space: nowrap; font-size: 10px; font-weight: 700; padding: 4px 9px; @@ -354,9 +725,13 @@ font-size: 11px; color: var(--text-soft); } - .meta-tag .msr { - font-size: 14px; - color: var(--muted); + .meta-tag.recognised { + font-size: 10px; + font-weight: 700; + padding: 4px 9px; + border-radius: 7px; + color: var(--text-soft); + background: color-mix(in srgb, var(--text) 6%, transparent); } .reason-block { @@ -408,17 +783,6 @@ line-height: 1.45; } - .usage-row { - display: flex; - align-items: center; - gap: 8px; - font-size: 12px; - color: var(--muted); - } - .usage-row .msr { - font-size: 16px; - } - .reversible { display: flex; align-items: flex-start; diff --git a/v2/mobile/src/components/AppReportSheet.svelte b/v2/mobile/src/components/AppReportSheet.svelte new file mode 100644 index 00000000..a3412e01 --- /dev/null +++ b/v2/mobile/src/components/AppReportSheet.svelte @@ -0,0 +1,480 @@ + + + +
+ + +
+ + diff --git a/v2/mobile/src/components/AppSheetHost.svelte b/v2/mobile/src/components/AppSheetHost.svelte new file mode 100644 index 00000000..e5eaff07 --- /dev/null +++ b/v2/mobile/src/components/AppSheetHost.svelte @@ -0,0 +1,309 @@ + + + target && current(target) && void measure(target)} + {busy} + {uninstalled} + onClose={closeSelection} + onToggle={handleToggle} + onForceStop={handleForceStop} + onUninstall={(a) => void requestRemoval(a, "uninstall")} + onPlayStore={handlePlayStore} + onReinstall={handleReinstall} +/> + + (intent = null)} +/> + + (showPaywall = false)} /> diff --git a/v2/mobile/src/components/BottomTabs.svelte b/v2/mobile/src/components/BottomTabs.svelte index 0d7e5956..d0bca215 100644 --- a/v2/mobile/src/components/BottomTabs.svelte +++ b/v2/mobile/src/components/BottomTabs.svelte @@ -10,8 +10,8 @@ const tabs: { id: Screen; icon: string; label: string }[] = [ { id: "dashboard", icon: "grid_view", label: "Home" }, - { id: "optimize", icon: "auto_fix_high", label: "Optimize" }, { id: "apps", icon: "apps", label: "Apps" }, + { id: "tweaks", icon: "tune", label: "Tweaks" }, { id: "remote", icon: "stadia_controller", label: "Remote" }, { id: "more", icon: "more_horiz", label: "More" }, ]; diff --git a/v2/mobile/src/components/ConfirmDialog.svelte b/v2/mobile/src/components/ConfirmDialog.svelte index 70921ca5..83c13e15 100644 --- a/v2/mobile/src/components/ConfirmDialog.svelte +++ b/v2/mobile/src/components/ConfirmDialog.svelte @@ -1,4 +1,5 @@ + +
+
+
+

Playback report

+ +
+

Advertised configuration, not a playback test. Actual decoding and passthrough depend on the app, content and connected display or receiver.

+ {#if playbackError}{/if} + {#if playback} +
+

Video decoders

+
+ {#each playback.video as format (format.mime)} +
{format.label}
{formatSupport(format).label}
+ {/each} +
+
+
+

Display

+
+
Advertised HDR
{playback.hdr_types.length ? playback.hdr_types.join(", ") : "Unknown / not reported"}
+
Display modes
+ {#if playback.modes.length} +
    + {#each playback.modes as mode} +
  • {mode.width}×{mode.height} @ {mode.fps} Hz{#if mode.active}(active){/if}
  • + {/each} +
+ {:else}Unknown / not reported{/if} +
+
+
+
+

Audio & frame rate

+
+
Surround policy
{playback.audio.mode === "unset" ? "Default / unset" : playback.audio.mode}
+
Manual formats
{playback.audio.enabled_formats.join(", ") || "None / unset"}
+
Frame-rate policy
{playback.match_content_frame_rate == null ? "Default / unset" : (matchContentLabel[playback.match_content_frame_rate] ?? `Unknown (${playback.match_content_frame_rate})`)}
+
+
+ {#if playback.verdicts.length} +
+

Interpretation

+
+ {#each playback.verdicts as verdict} +
+
{verdict.title}

{verdict.detail}

+
+ {/each} +
+
+ {/if} + {/if} +
+ +
+
+

CPU & network

+ +
+

One device-side sample per tap. Interfaces are shown separately because VPN traffic can also appear on the physical interface.

+ {#if resourceError}{/if} + {#if resources} +
+
CPU use
{resources.cpu_percent == null ? "Unavailable" : `${resources.cpu_percent.toFixed(1)}%`}
+
Sample duration
{resources.interval_ms == null ? "Unavailable" : `${(resources.interval_ms / 1000).toFixed(2)} s`}
+
+ {#if resources.interfaces.length} +
+

Network interfaces

+
    + {#each resources.interfaces as network (network.name)} +
  • +
    {network.name}
    +
    +
    Receive
    {rate(network.rx_bytes_per_s)}
    +
    Send
    {rate(network.tx_bytes_per_s)}
    +
    +
  • + {/each} +
+
+ {:else}

No network counters available.

{/if} + {/if} +
+
+ + diff --git a/v2/mobile/src/components/EntitlementPreview.svelte b/v2/mobile/src/components/EntitlementPreview.svelte new file mode 100644 index 00000000..25c5bc21 --- /dev/null +++ b/v2/mobile/src/components/EntitlementPreview.svelte @@ -0,0 +1,14 @@ + +
+

Simulated entitlement preview

+

No real purchase or session unlock occurs here.

+

{previewMessage(state)}

+ + + + +
diff --git a/v2/mobile/src/components/HomeAppPicker.svelte b/v2/mobile/src/components/HomeAppPicker.svelte new file mode 100644 index 00000000..476738d1 --- /dev/null +++ b/v2/mobile/src/components/HomeAppPicker.svelte @@ -0,0 +1,173 @@ + + +
+ + + {#if loading} +
+ Reading installed apps… +
+ {:else if error} +

{error}

+ + {:else if rows.length === 0} +

No matching apps.

+ {:else} + + {rows.length} shown · {packages.length} installed app{packages.length === 1 ? "" : "s"} + +
+ {#each rows as p (p.package)} + + {/each} +
+ {/if} + +
+ + diff --git a/v2/mobile/src/components/LauncherDiagnostics.svelte b/v2/mobile/src/components/LauncherDiagnostics.svelte new file mode 100644 index 00000000..aa2971a5 --- /dev/null +++ b/v2/mobile/src/components/LauncherDiagnostics.svelte @@ -0,0 +1,87 @@ + + +{#if lines.length > 0} +
+ +

+ Every command this attempt ran and what your TV replied. Paste it into a GitHub issue; it is + what makes a launcher report fixable. +

+ {#if shown} +
{lines.join("\n")}
+ {/if} +
+{/if} + + diff --git a/v2/mobile/src/components/PaywallSheet.svelte b/v2/mobile/src/components/PaywallSheet.svelte index 391bf64b..39eca9c7 100644 --- a/v2/mobile/src/components/PaywallSheet.svelte +++ b/v2/mobile/src/components/PaywallSheet.svelte @@ -1,4 +1,5 @@ + +
+
+ {state === "risk" ? "warning" : "info"} + Setup Wraith + {state === "off" ? "OFF" : "ON"} +
+

+ {#if state === "off"} + Setup Wraith is off. Turn this back on if Google asks you to sign in again or you need to + pair a remote; turn it off again after. + {:else if state === "on"} + Google TV's setup helper (Setup Wraith) is on. It can take the Home button back after you + switch launchers. Disable stock launcher turns it off too. + {:else} + Google TV's setup helper (Setup Wraith) is on while the stock launcher is off. It will likely + grab the Home button. + {/if} +

+ {pkg} + {#if progress} + {progress}… + {:else if state === "off"} + + {:else if state === "risk"} + + {/if} +
+ + diff --git a/v2/mobile/src/components/StockTakeoverDialog.svelte b/v2/mobile/src/components/StockTakeoverDialog.svelte new file mode 100644 index 00000000..c2736fce --- /dev/null +++ b/v2/mobile/src/components/StockTakeoverDialog.svelte @@ -0,0 +1,142 @@ + + +{#if open} + +
+ +
e.stopPropagation()} + > + home +

Disable the stock launcher?

+

+ Disable the stock launcher and hand Home to {target}? If Home + doesn't land on it, the stock launcher is re-enabled straight away. You can re-enable it + from the launcher list at any time. +

+ {#if turnsOffSetupHelper} +

+ warning + + Also turns off Google TV's setup helper (Setup Wraith), or it takes the Home button + back. You may need to turn it back on briefly to sign in to Google again or pair a + remote. + +

+ {/if} +
+ + + +
+
+
+{/if} + + diff --git a/v2/mobile/src/fonts/README.md b/v2/mobile/src/fonts/README.md index 440e34d9..69ced734 100644 --- a/v2/mobile/src/fonts/README.md +++ b/v2/mobile/src/fonts/README.md @@ -4,6 +4,7 @@ Apache-2.0 `material-symbols` npm package (currently 0.45.5). It is generated from the original package font by `scripts/subset-material-symbols.py`; do not replace it with a hand-edited binary. -Geist and Geist Mono come from the npm `geist` package and are distributed under the SIL Open Font -License 1.1. The full dependency license texts remain available in the installed npm packages; a -consolidated in-app third-party notices surface is still required before release. +Geist and Geist Mono are the checked-in, unmodified WOFF2 assets from `geist` 1.7.2, under +SIL Open Font License 1.1. The full license and attribution remain in `THIRD-PARTY-NOTICES.md` +and the in-app notices. CSS loads these files directly; no `geist` or Next.js npm installation +is required to build them. Removing the unused npm package does not change the font bytes. diff --git a/v2/mobile/src/fonts/material-symbols-rounded.woff2 b/v2/mobile/src/fonts/material-symbols-rounded.woff2 index 8d201845..5b39520a 100644 Binary files a/v2/mobile/src/fonts/material-symbols-rounded.woff2 and b/v2/mobile/src/fonts/material-symbols-rounded.woff2 differ diff --git a/v2/mobile/src/lib/api.ts b/v2/mobile/src/lib/api.ts index a90d6620..854d8df2 100644 --- a/v2/mobile/src/lib/api.ts +++ b/v2/mobile/src/lib/api.ts @@ -15,6 +15,7 @@ import { connectionGeneration, emitConnectionLost, isConnectionLostError } from import type { ActionResult, AppEntry, + AppStorage, AppUsage, ApplyResult, BackupEntry, @@ -39,6 +40,7 @@ import type { PerformanceProfile, PerformanceResult, OtherPackage, + PermissionState, PrivateDnsResult, PrivateDnsState, RebootMode, @@ -48,12 +50,14 @@ import type { Safety, ScreenshotResult, SendTextResult, + SetHomeAnyResult, SetLauncherResult, SnapshotApplyPlan, SnapshotFile, TweaksState, WirelessStatus, WriteResult, + FindResult, } from "./types"; type PackageState = "enabled" | "disabled" | "missing"; @@ -121,6 +125,22 @@ export const api = { /// Backend-audited protected / caution / unknown verdict for one package. safetyInfo: async (pkg: string): Promise => parseSafety(await call("safety_info", { package: pkg })), + /// Classify a *process* name from a memory report. Catalog-free on purpose — + /// see the Rust doc on `process_safety_info`. + processSafetyInfo: async (process: string): Promise => + parseSafety(await call("process_safety_info", { process })), + /// Runtime permission state. Read-only, free. + appPermissionState: (serial: string, pkg: string, permission: string) => + call("app_permission_state", { serial, package: pkg, permission }), + /// Grant/revoke a runtime permission. Pro (AppPermissionWrite). + setAppPermission: (serial: string, pkg: string, permission: string, grant: boolean) => + call("set_app_permission", { serial, package: pkg, permission, grant }), + /// Set an appop to allow/ignore. Pro (AppPermissionWrite). + setAppOp: (serial: string, pkg: string, op: string, allow: boolean) => + call("set_app_op", { serial, package: pkg, op, allow }), + /// Raw `appops get` mode string for one op. Read-only, free. + getAppOp: (serial: string, pkg: string, op: string) => + call("get_app_op", { serial, package: pkg, op }), // ---- Maintenance ---- trimCaches: (serial: string) => @@ -161,6 +181,13 @@ export const api = { call>("app_memory_map", { serial }), appUsageMap: (serial: string) => call>("app_usage_map", { serial }), + /// Installed storage per package from one `dumpsys diskstats`. Read-only, free. + appStorageMap: (serial: string) => + call>("app_storage_map", { serial }), + /// One package's APK size (`pm path` + `stat`), for a package diskstats has + /// no row for. Data and cache come back null. Read-only, free. + appApkSize: (serial: string, pkg: string) => + call("app_apk_size", { serial, package: pkg }), openPlayStore: (serial: string, pkg: string) => call("open_play_store", { serial, package: pkg }), reinstallExisting: (serial: string, pkg: string) => @@ -192,6 +219,15 @@ export const api = { }, disableLauncher: (serial: string, pkg: string) => call("disable_launcher", { serial, package: pkg }), + /// Turn off an enabled Setup Wraith while stock is already disabled. Pro. + disableSetupHelper: (serial: string, pkg: string) => + call("disable_setup_helper", { serial, package: pkg }), + /// Advanced picker: try any app as Home. Never disables anything. Pro. + setHomeAny: (serial: string, pkg: string, activity: string | null = null) => + call("set_home_any", { serial, package: pkg, activity }), + /// The explicit, confirmed step that hands Home from stock to `target`. Pro. + disableStockLauncher: (serial: string, target: string) => + call("disable_stock_launcher", { serial, target }), // ---- Tweaks (5.1) — writes are Pro (TweaksWrite) ---- getTweaks: (serial: string) => call("get_tweaks", { serial }), @@ -255,4 +291,28 @@ export const api = { /// the transport the next press will actually use. remoteWarm: (serial: string) => call("remote_warm", { serial }), + + /// App-files catalog search under /sdcard. Directories that could not be + /// searched come back in `unsearched`, distinct from no matches. + findFiles: (serial: string, dirs: string[], pattern: string) => + call("find_files", { serial, dirs, pattern }), + /// Delete a file or folder on the TV. /sdcard-confined backend-side. + deletePath: (serial: string, path: string) => + call("delete_path", { serial, path }), + + /// Hand a web link to the phone's browser. Only http(s) is accepted here as + /// well as in the capability scope, so a catalog or report URL can never + /// launch some other scheme handler. + openUrl: (url: string) => { + let protocol = ""; + try { + protocol = new URL(url).protocol; + } catch { + // Falls through to the rejection below. + } + if (protocol !== "https:" && protocol !== "http:") { + return Promise.reject(new Error("Only web links can be opened.")); + } + return call("plugin:opener|open_url", { url }); + }, }; diff --git a/v2/mobile/src/lib/app-details.ts b/v2/mobile/src/lib/app-details.ts new file mode 100644 index 00000000..4048b96f --- /dev/null +++ b/v2/mobile/src/lib/app-details.ts @@ -0,0 +1,113 @@ +// What the app detail sheet needs to say about each figure: whether it was +// read, when, and why not. A figure is only ever shown next to the time it was +// measured, and a failed read is "unavailable", never zero. Ported from +// desktop's v2/src/lib/app-details.ts; keep the two in step. + +import type { AppStorage, AppUsage } from "./types"; +import type { AppReportDevice } from "./app-report"; + +export type MeasureStatus = + | { status: "idle" } + | { status: "loading" } + | { status: "ready"; at: number } + | { status: "unavailable"; at: number; reason: string }; + +export interface AppMeasurements { + /// `dumpsys meminfo` — Total PSS by process. + memory: MeasureStatus; + /// `dumpsys usagestats` — last foreground use. + usage: MeasureStatus; + /// `dumpsys diskstats` — installed storage per package. + storage: MeasureStatus; +} + +// Internal receipt only: identity and timing fields never enter report JSON. +export interface ApkReportMeasurement { + package: string; + serial: string; + generation: number; + measurementId: number; + appBytes: number; + at: number; +} + +/// One row's slice of the page's measurements, as the detail panel reads it. +export interface AppDetailInputs { + serial: string; + measures: AppMeasurements; + memoryMb?: number; + usage?: AppUsage; + storage?: AppStorage; + onRemeasure?: () => void; + /// The page's current package state, resynced after every action. A row + /// can carry an older one (the Optimize plan is not reloaded after a run), + /// so a report uses this for a catalog package: null means the page has no + /// current state, and undefined (a non-catalog row) defers to the row. + liveState?: "enabled" | "disabled" | "missing" | null; + /// The device context "Report this app" may share. Absent, the panel + /// offers no report. + report?: AppReportDevice; +} + +/// The state a report may carry for a row. A catalog row takes the page's +/// current state, and none while that state is being re-read: an Optimize or +/// bulk change has just run, so the cached value may predate it. A row outside +/// the catalog (undefined) defers to its own state, unless a run has changed +/// the device since that row's list was read (`othersStale`). When a run +/// changed the device while the page had no inventory to re-read +/// (`unreadAfterRun`), every row's state may predate it, and none is reported +/// until the inventory is read. +export function reportLiveState( + inCatalog: boolean, + state: "enabled" | "disabled" | "missing" | undefined, + resyncing: boolean, + unreadAfterRun = false, + othersStale = false, +): "enabled" | "disabled" | "missing" | null | undefined { + if (unreadAfterRun) return null; + if (!inCatalog) return othersStale ? null : undefined; + return resyncing ? null : (state ?? null); +} + +export function idleMeasurements(): AppMeasurements { + return { memory: { status: "idle" }, usage: { status: "idle" }, storage: { status: "idle" } }; +} + +export function loadingMeasurements(): AppMeasurements { + return { + memory: { status: "loading" }, + usage: { status: "loading" }, + storage: { status: "loading" }, + }; +} + +/// Settle one read into a status, stamped with when the answer came back. +export function settled(result: PromiseSettledResult, at: number): MeasureStatus { + return result.status === "fulfilled" + ? { status: "ready", at } + : { status: "unavailable", at, reason: String(result.reason) }; +} + +export function measuredAt(at: number): string { + return new Date(at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" }); +} + +/// Bytes as a short size, or null when the device did not report it. Null is +/// deliberately not "0 B": an unreported figure is unknown, not empty. +export function formatBytes(bytes: number | null | undefined): string | null { + if (bytes === null || bytes === undefined || !Number.isFinite(bytes) || bytes < 0) return null; + if (bytes < 1024) return `${bytes} B`; + const units = ["KB", "MB", "GB", "TB"]; + let value = bytes / 1024; + let unit = 0; + while (value >= 1024 && unit < units.length - 1) { + value /= 1024; + unit += 1; + } + return `${value >= 100 ? value.toFixed(0) : value.toFixed(1)} ${units[unit]}`; +} + +/// True when at least one storage figure was actually reported. +export function hasStorage(s: AppStorage | undefined): s is AppStorage { + return !!s && (s.app_bytes !== null || s.data_bytes !== null || s.cache_bytes !== null); +} diff --git a/v2/mobile/src/lib/app-report.ts b/v2/mobile/src/lib/app-report.ts new file mode 100644 index 00000000..1b56397e --- /dev/null +++ b/v2/mobile/src/lib/app-report.ts @@ -0,0 +1,236 @@ +// "Report this app": one package, written out as a record the catalog +// reviewer's tool (tools/registry-triage) reads, in the same shape as the +// unknown-package export (unknownDiagnostics.ts). Ported from desktop's +// v2/src/lib/app-report.ts; keep the two in step. Nothing here sends anything. +// The user sees the exact JSON, then copies it, shares it, or opens a GitHub +// form they still have to submit. +// +// A report is a claim by one user, not a verdict. It never carries a +// suggested classification, and it is never read back into the app. + +import type { DeviceType, Safety, TvEvidence } from "./types"; +import { normalizeHardwareId } from "./identity"; + +export type AppReportReason = "not_listed" | "wrong_verdict" | "wrong_description" | "other"; + +/// The `reason` values a report writes. registry-triage accepts these beside +/// the unknown-diagnostics collector's own reasons. +export const APP_REPORT_REASONS: { id: AppReportReason; label: string; record: string }[] = [ + { id: "not_listed", label: "Not in the list", record: "user_report_not_listed" }, + { id: "wrong_verdict", label: "Wrong verdict", record: "user_report_wrong_verdict" }, + { id: "wrong_description", label: "Wrong description", record: "user_report_wrong_description" }, + { id: "other", label: "Other", record: "user_report_other" }, +]; + +export type ReportDeviceFamily = "shield" | "google_tv" | "android_tv" | "unknown"; + +/// What the report needs about the device. Deliberately no serial, no address +/// and no properties beyond the Android version. +export interface AppReportDevice { + family: ReportDeviceFamily; + androidVersion: string | null; + /// The device's adb serial and ro.serialno. Never written into a report: + /// they are only used to strip themselves out of the user's note. + redact: string[]; +} + +export interface AppReportState { + status: "enabled" | "disabled" | "missing" | null; + /// Whether meminfo showed a process for the package: null when RAM was not + /// read or the read failed, false for a successful read with no process. + running: boolean | null; + /// PSS when running; null otherwise. + ramMb: number | null; + /// `diskstats` is the full app/data/cache row; `apk_files` is the fallback + /// that only knows the APK size. + storage: { + source: "diskstats" | "apk_files"; + app_bytes: number | null; + data_bytes: number | null; + cache_bytes: number | null; + } | null; +} + +export interface AppReportInput { + package: string; + appName: string | null; + reason: AppReportReason; + note: string; + appVersion: string | null; + device: AppReportDevice; + /// null when the safety lookup did not complete. + verdict: Safety | null; + includeState: boolean; + state: AppReportState; + now: Date; +} + +export const APP_REPORT_NOTE_MAX = 1000; +const NAME_MAX = 120; + +/// A device that reported nothing about being a TV is "unknown", never +/// promoted to a family it did not claim. A missing `tv_evidence` (an older +/// core) reads as unknown too. +export function reportFamily(type: DeviceType, tv: TvEvidence | undefined): ReportDeviceFamily { + if (type === "shield") return "shield"; + if (type === "google_tv") return "google_tv"; + return tv === "tv" ? "android_tv" : "unknown"; +} + +/// The registry-triage / unknown-diagnostics rules for an installed_package +/// token. A package id that fails them cannot go into a report. +export function isReportablePackage(pkg: string): boolean { + return ( + pkg.length > 0 && + pkg.length <= 255 && + /^[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*)+$/.test(pkg) + ); +} + +function validVersion(value: string | null): value is string { + return !!value && value.length <= 32 && /^[0-9A-Za-z][0-9A-Za-z.+_-]*$/.test(value); +} + +function validDeviceOs(value: string | null): value is string { + return !!value && value.length <= 32 && /^[0-9A-Za-z][0-9A-Za-z._ -]*$/.test(value); +} + +function clean(text: string, max: number, keepNewlines: boolean): string | null { + const pattern = keepNewlines ? /[\u0000-\u0009\u000b-\u001f\u007f]/g : /[\u0000-\u001f\u007f]/g; + const out = text.replace(pattern, " ").trim().slice(0, max).trim(); + return out.length > 0 ? out : null; +} + +const REDACTED = "[redacted]"; + +/// Shorter than this, a serial is only redacted where it stands as a whole +/// token: "A1" must not eat the middle of "A1B2", let alone every word that +/// happens to contain those letters. +const SERIAL_SUBSTRING_MIN = 4; + +function escapeRegExp(text: string): string { + return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +/// Anything shaped like an Android package id (two or more dot-joined +/// segments, each starting with a letter). The leading boundary is captured +/// rather than a lookbehind, which older WebViews cannot parse. +const PACKAGE_SHAPED = /(^|[^A-Za-z0-9_.])([A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*)+)(?![A-Za-z0-9_])/g; + +/// The note is the user's own text, but a pasted log line or address must not +/// carry the identifiers the rest of the report leaves out. Addresses, MACs, +/// this device's own serials, and package ids other than the reported one are +/// replaced, visibly, in the preview. See desktop's copy for the full +/// rationale; the rules are identical. +export function redactNote(text: string, redact: string[], reportedPackage?: string): string { + let out = text; + for (const id of redact) { + // A ro.serialno of "unknown" is no id, and scrubbing it would eat the word. + const v = normalizeHardwareId(id); + if (!v) continue; + const body = escapeRegExp(v); + out = + v.length >= SERIAL_SUBSTRING_MIN + ? out.replace(new RegExp(body, "gi"), REDACTED) + : out.replace(new RegExp(`(^|[^A-Za-z0-9])${body}(?![A-Za-z0-9])`, "gi"), `$1${REDACTED}`); + } + const keep = reportedPackage?.toLowerCase(); + return out + .replace(/\b\d{1,3}(?:\.\d{1,3}){3}(?::\d+)?\b/g, REDACTED) + .replace(/\b(?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}\b/g, REDACTED) + .replace(/\b(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}\b/g, REDACTED) + .replace(/(?:\b[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*\b)?/g, (m) => + m === "::" ? m : REDACTED, + ) + .replace(PACKAGE_SHAPED, (_m, before: string, token: string) => { + if (token.toLowerCase() === keep) return before + token; + if (token.split(".").every((segment) => segment.length === 1)) return before + token; + return before + REDACTED; + }); +} + +function finiteOrNull(n: number | null | undefined): number | null { + return typeof n === "number" && Number.isFinite(n) && n >= 0 ? n : null; +} + +/// The report: a `schema_version: 1` export with one record. Returns null +/// when the package id cannot be reported. +export function buildAppReport(input: AppReportInput): Record | null { + if (!isReportablePackage(input.package)) return null; + const at = input.now.toISOString(); + const reason = APP_REPORT_REASONS.find((r) => r.id === input.reason) ?? APP_REPORT_REASONS[3]; + const record: Record = { + kind: "installed_package", + token: input.package, + reason: reason.record, + app_version: validVersion(input.appVersion) ? input.appVersion : "unknown", + registry_version: null, + device_family: input.device.family, + device_os: validDeviceOs(input.device.androidVersion) ? input.device.androidVersion : null, + first_seen: at, + last_seen: at, + count: 1, + app_name: input.appName ? clean(input.appName, NAME_MAX, false) : null, + current_verdict: input.verdict ? input.verdict.kind : "unavailable", + verdict_source: input.verdict ? input.verdict.source : null, + note: clean(redactNote(input.note, input.device.redact, input.package), APP_REPORT_NOTE_MAX, true), + }; + if (input.includeState) { + const s = input.state; + record.state = { + installed: s.status === null ? null : s.status !== "missing", + enabled: s.status === "enabled" ? true : s.status === "disabled" ? false : null, + running: s.running, + ram_mb: s.running ? finiteOrNull(s.ramMb) : null, + storage: s.storage + ? { + source: s.storage.source, + app_bytes: finiteOrNull(s.storage.app_bytes), + data_bytes: finiteOrNull(s.storage.data_bytes), + cache_bytes: finiteOrNull(s.storage.cache_bytes), + } + : null, + }; + } + return { + schema_version: 1, + generated_at: at, + truncated: false, + source: "mobile_app_report", + records: [record], + }; +} + +export function reportText(report: Record): string { + return JSON.stringify(report, null, 2); +} + +export function reportFileName(pkg: string, now: Date): string { + return `app-report-${pkg}-${now.toISOString().slice(0, 10)}.json`; +} + +const ISSUE_BASE = "https://github.com/bryanroscoe/shield_optimizer/issues/new"; +/// Same ceiling the bug form uses, well under what GitHub accepts in a URL. +export const APP_REPORT_PREFILL_LIMIT = 6000; + +/// The app_report.yml issue form, its fields prefilled by query parameters of +/// the same ids. When the report would not fit, the form opens with the +/// package and reason only and the caller asks the user to paste (copy the +/// report to the clipboard first). +export function appReportIssueUrl( + pkg: string, + reason: AppReportReason, + text: string, +): { url: string; prefilled: boolean } { + const url = new URL(ISSUE_BASE); + url.searchParams.set("template", "app_report.yml"); + url.searchParams.set("title", `App report: ${pkg}`); + url.searchParams.set("package", pkg); + url.searchParams.set("reason", APP_REPORT_REASONS.find((r) => r.id === reason)?.label ?? "Other"); + const short = url.toString(); + url.searchParams.set("report", text); + const full = url.toString(); + return full.length <= APP_REPORT_PREFILL_LIMIT + ? { url: full, prefilled: true } + : { url: short, prefilled: false }; +} diff --git a/v2/mobile/src/lib/appFiles.ts b/v2/mobile/src/lib/appFiles.ts new file mode 100644 index 00000000..d959e19a --- /dev/null +++ b/v2/mobile/src/lib/appFiles.ts @@ -0,0 +1,68 @@ +// App-files finder and TV-side delete rules for the Files screen. Pure, so the +// #86 distinction (a search that could not run is not "no matches") and the +// delete confinement are testable without a device. + +import type { FindResult } from "./types"; +// Single source with desktop's Files tab: read the catalog, don't copy it. +import catalog from "../../../src/lib/app-files-catalog.json"; + +export interface FileConnection { + serial: string; + generation: number; +} + +export function fileConnectionMatches( + target: FileConnection, + current: { serial: string | null; generation: number; isConnected: boolean }, +): boolean { + return current.isConnected && target.serial === current.serial && target.generation === current.generation; +} + +export interface AppFilesEntry { + id: string; + package: string; + name: string; + hint: string; + search_dirs: string[]; + pattern: string; +} + +export const appFilesCatalog: AppFilesEntry[] = catalog; + +export type FindSummary = + | { kind: "found"; hits: string[]; unsearched: string[] } + | { kind: "none" } + | { kind: "unsearched"; unsearched: string[] }; + +/// "none" only when every directory was actually searched and nothing matched. +export function summarizeFind(result: FindResult): FindSummary { + if (result.hits.length > 0) { + return { kind: "found", hits: result.hits, unsearched: result.unsearched }; + } + if (result.unsearched.length > 0) return { kind: "unsearched", unsearched: result.unsearched }; + return { kind: "none" }; +} + +export function unsearchedMessage(dirs: string[]): string { + return `Couldn't search ${dirs.join(", ")} — the TV didn't answer. Check the connection and try again.`; +} + +export const NO_MATCHES_MESSAGE = "No matches — export from the app first, then search again."; + +export function parentDir(path: string): string { + const cut = path.lastIndexOf("/"); + return cut <= 0 ? "/" : path.slice(0, cut); +} + +export function baseName(path: string): string { + return path.slice(path.lastIndexOf("/") + 1); +} + +/// Mirrors the backend guard (crates/core commands::files): a delete target +/// must be strictly inside /sdcard. The backend refuses anything else anyway; +/// this keeps the button off rows it would refuse. +export function canDeleteOnTv(path: string): boolean { + if (!path.startsWith("/sdcard/") || path.length <= "/sdcard/".length) return false; + if (/[\u0000-\u001f\u007f]/.test(path)) return false; + return !path.split("/").some((segment) => segment === ".."); +} diff --git a/v2/mobile/src/lib/appsList.ts b/v2/mobile/src/lib/appsList.ts new file mode 100644 index 00000000..e25d4fcb --- /dev/null +++ b/v2/mobile/src/lib/appsList.ts @@ -0,0 +1,167 @@ +// The Apps screen's two sections, merged and filtered in one place: the +// catalogued ("Recognised") apps from `app_list_for_device` + `package_states`, +// and everything else from `list_other_packages`. Pure, so the rules that +// decide whether a user can find an app are tested without a webview. +// +// Mirrors desktop's App List (v2/src/routes/devices/[serial]/+page.svelte): +// one search over name and package id across both sections, "Hide not +// installed" for catalogue rows, "Show system apps" for everything else. + +import type { AppEntry, OtherPackage } from "./types"; +import type { PackageState } from "./recommendation"; + +export type StatusFilter = "all" | "enabled" | "disabled"; + +/// One row as the screen, the detail sheet and the action menu see it. +export interface AppItem { + package: string; + /// Friendly name when one is known; null shows the package id instead. + name: string | null; + /// Null for catalogue rows: the catalogue does not say, and guessing would + /// put a "system" label on something we never read. + system: boolean | null; + /// Null when the state read failed or did not cover this package. Null is + /// unknown, never "enabled". + state: PackageState | null; + /// The catalogue entry, for recognised apps only. + entry: AppEntry | null; + description: string | null; +} + +/// One entry in the long-press menu. `run` is the screen's own handler. +export interface AppMenuItem { + id: string; + label: string; + icon: string; + disabled?: boolean; + danger?: boolean; + run: () => void; +} + +export interface ListFilters { + query: string; + status: StatusFilter; + showSystem: boolean; + hideNotInstalled: boolean; +} + +export function normalizeQuery(query: string): string { + return query.trim().toLowerCase(); +} + +/// Name or package id, case-insensitive. An empty query matches everything. +export function matchesQuery(item: Pick, query: string): boolean { + const q = normalizeQuery(query); + if (!q) return true; + return (item.name ?? "").toLowerCase().includes(q) || item.package.toLowerCase().includes(q); +} + +/// The state map with anything that is not one of the three known states +/// dropped, so a malformed reply reads as "unknown" rather than as a state. +export function validatedStates( + packages: string[], + raw: unknown, +): Record { + const out: Record = {}; + if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return out; + const record = raw as Record; + for (const pkg of packages) { + if (!Object.prototype.hasOwnProperty.call(record, pkg)) continue; + const s = record[pkg]; + if (s === "enabled" || s === "disabled" || s === "missing") out[pkg] = s; + } + return out; +} + +export function catalogItem(entry: AppEntry, state: PackageState | null): AppItem { + return { + package: entry.package, + name: entry.name || null, + system: null, + state, + entry, + description: entry.optimize_description || null, + }; +} + +export function otherItem(app: OtherPackage): AppItem { + return { + package: app.package, + name: app.name || null, + system: app.system, + state: app.enabled ? "enabled" : "disabled", + entry: null, + description: app.description || null, + }; +} + +/// Catalogue rows in catalogue order. A package that also comes back from the +/// other-packages list is kept only here, so no app is listed twice. +export function catalogItems( + entries: AppEntry[], + states: Record, +): AppItem[] { + const seen = new Set(); + const out: AppItem[] = []; + for (const entry of entries) { + if (seen.has(entry.package)) continue; + seen.add(entry.package); + out.push(catalogItem(entry, states[entry.package] ?? null)); + } + return out; +} + +export function otherItems(others: OtherPackage[], catalogPackages: Set): AppItem[] { + return others.filter((o) => !catalogPackages.has(o.package)).map(otherItem); +} + +function matchesStatus(item: AppItem, status: StatusFilter): boolean { + if (status === "enabled") return item.state === "enabled"; + if (status === "disabled") return item.state === "disabled"; + return true; +} + +/// Catalogue rows passing every filter. "Show system apps" does not apply: +/// the catalogue does not record which entries are system packages, and most +/// of what it covers is preinstalled, so hiding them would hide the section. +export function filterCatalog(items: AppItem[], f: ListFilters): AppItem[] { + return items.filter( + (item) => + !(f.hideNotInstalled && item.state === "missing") && + matchesStatus(item, f.status) && + matchesQuery(item, f.query), + ); +} + +export function filterOthers(items: AppItem[], f: ListFilters): AppItem[] { + return items.filter( + (item) => + (f.showSystem || item.system !== true) && + matchesStatus(item, f.status) && + matchesQuery(item, f.query), + ); +} + +/// Rows a filter toggle is hiding from the current search, so the empty state +/// can offer to reveal them rather than claim nothing matched. +export function hiddenMatches( + catalog: AppItem[], + others: AppItem[], + f: ListFilters, +): { notInstalled: number; system: number } { + return { + notInstalled: f.hideNotInstalled + ? filterCatalog(catalog, { ...f, hideNotInstalled: false }).length - + filterCatalog(catalog, f).length + : 0, + system: f.showSystem + ? 0 + : filterOthers(others, { ...f, showSystem: true }).length - filterOthers(others, f).length, + }; +} + +/// Rows that are (or may be) on the TV: everything except catalogue rows +/// known to be missing. Used for both sides of "N of M visible". +export function onDeviceCount(items: AppItem[]): number { + return items.filter((i) => i.state !== "missing").length; +} diff --git a/v2/mobile/src/lib/demo-mock.ts b/v2/mobile/src/lib/demo-mock.ts new file mode 100644 index 00000000..90450b2e --- /dev/null +++ b/v2/mobile/src/lib/demo-mock.ts @@ -0,0 +1,98 @@ +// Demo fixture layer for the mobile screenshot pipeline and browser-only dev. +// +// With VITE_DEMO=1 there is no Tauri host, so this installs a stand-in for +// `window.__TAURI_INTERNALS__`. Commands the phone shares with desktop are +// answered by the desktop fixtures (one Nvidia Shield, the real merged app +// list), so both galleries describe the same TV; only the mobile-only +// commands (wireless transport, licensing, files, backups) are answered here. +// +// Wired in main.ts behind the VITE_DEMO flag and never ships in a real build. + +import { handle as sharedHandle } from "../../../src/lib/demo-mock"; +import type { BackupEntry, Device, FileEntry } from "./types"; + +const HOST = "192.168.1.42"; +const PORT = 5555; +const SERIAL = `${HOST}:${PORT}`; + +const backups: BackupEntry[] = [ + { + package: "com.teamsmart.videomanager.tv", + path: "/data/user/0/com.atvoptimizer.app/files/backups/com.teamsmart.videomanager.tv.apks", + size_bytes: 83_800_064, + apk_count: 3, + complete: true, + saved_at: "2026-10-02T19:14:08Z", + }, + { + package: "com.spocky.projengmenu", + path: "/data/user/0/com.atvoptimizer.app/files/backups/com.spocky.projengmenu.apks", + size_bytes: 24_862_720, + apk_count: 1, + complete: true, + saved_at: "2026-09-21T08:40:51Z", + }, +]; + +function handle(cmd: string, args: Record): unknown { + switch (cmd) { + case "get_entitlement": + case "activate_license": + return "pro"; + case "license_info": + return { plan: "pro", licensee: "demo@example.com", issued: "2026-09-01", expires: null, key_id: 1 }; + case "wireless_discover": + return { + devices: [ + { name: "adb-0323220012345", host: HOST, port: PORT, service: "_adb._tcp" }, + { name: "adb-9AB4C21D7E03-x7Kq2b", host: "192.168.1.77", port: 37215, service: "_adb-tls-connect._tcp" }, + ], + message: "Found 2 TVs on this network.", + }; + case "wireless_pair": + return { ok: true, message: "Paired." }; + case "wireless_connect": + return { ok: true, message: `Connected to ${String(args.host)}:${String(args.port)}.` }; + case "wireless_disconnect": + return { ok: true, message: "Disconnected." }; + case "wireless_cancel_connect": + return null; + case "wireless_status": + return { connected: true, serial: SERIAL, host: HOST }; + // The phone holds exactly one connection; its serial is the dialed address. + case "list_devices": + return [{ ...(sharedHandle("device_profile", { serial: SERIAL }) as Device), serial: SERIAL }]; + case "list_remote_dir": + return sharedHandle("list_dir", { path: args.path }) as FileEntry[]; + case "pull_file": { + const remote = String(args.remotePath ?? ""); + return { name: remote.split("/").pop() ?? remote, path: `/storage/emulated/0/Download/${remote.split("/").pop()}`, size_bytes: 18432 }; + } + case "list_backups": + return backups; + case "backup_apk": + return backups[0]; + case "remote_warm": + return { transport: "channel", message: "Fast control channel ready." }; + case "send_key": + case "send_text": + case "open_settings": + return { ok: true, message: "", transport: "channel" }; + case "read_debug_log": + return "INFO atv_optimizer: wireless_connect 192.168.1.42:5555 ok\nINFO atv_optimizer: list_devices 1 device"; + case "plugin:opener|open_url": + return null; + default: + return sharedHandle(cmd, args); + } +} + +export function installDemoMock(): void { + const w = window as unknown as { __TAURI_INTERNALS__?: unknown }; + w.__TAURI_INTERNALS__ = { + invoke: (cmd: string, args: Record = {}) => Promise.resolve(handle(cmd, args)), + transformCallback: (cb: unknown) => cb, + unregisterCallback: () => {}, + convertFileSrc: (path: string) => path, + }; +} diff --git a/v2/mobile/src/lib/entitlementPreview.ts b/v2/mobile/src/lib/entitlementPreview.ts new file mode 100644 index 00000000..96c670c4 --- /dev/null +++ b/v2/mobile/src/lib/entitlementPreview.ts @@ -0,0 +1,28 @@ +// Isolated simulation: never connected to real session entitlement or payment APIs. +export type Phase = "idle" | "buying" | "restoring" | "pending" | "verifying" | "verified" | "canceled" | "error" | "revoked"; +export type Preview = { request: number; phase: Phase; legacyPro: boolean; playPro: boolean; acknowledged: boolean }; +export type Event = { type: "begin"; request: number; operation: "buy" | "restore" } | { type: "pending" | "purchased" | "verified" | "acknowledged" | "canceled" | "error"; request: number } | { type: "revoked" } | { type: "legacy"; valid: boolean }; +export const initialPreview = (): Preview => ({ request: 0, phase: "idle", legacyPro: false, playPro: false, acknowledged: false }); +export const previewIsPro = (s: Preview): boolean => s.legacyPro || s.playPro; +export function reducePreview(s: Preview, e: Event): Preview { + if (e.type === "legacy") return { ...s, legacyPro: e.valid }; + // A future authenticated verifier, not a store/client callback, owns these verdicts. + if (e.type === "revoked") return { ...s, request: s.request + 1, phase: "revoked", playPro: false, acknowledged: false }; + if (e.type === "begin") return e.request <= s.request ? s : { ...s, request: e.request, phase: e.operation === "buy" ? "buying" : "restoring" }; + if (e.request !== s.request) return s; + if (e.type === "pending" && ["buying", "restoring"].includes(s.phase)) return { ...s, phase: "pending" }; + if (e.type === "purchased" && ["buying", "restoring", "pending"].includes(s.phase)) return { ...s, phase: "verifying" }; + if (e.type === "verified" && s.phase === "verifying") return { ...s, phase: "verified", playPro: true, acknowledged: false }; + if (e.type === "acknowledged" && s.phase === "verified" && s.playPro) return { ...s, acknowledged: true }; + if (["canceled", "error"].includes(e.type) && ["buying", "restoring", "pending", "verifying"].includes(s.phase)) return { ...s, phase: e.type as Phase }; + return s; +} +export function previewMessage(s: Preview): string { + if (s.phase === "pending") return "Payment pending — no new access granted."; + if (s.phase === "verifying") return "Verifying purchase — client success alone does not unlock Pro."; + if (s.phase === "error") return "Request failed. Existing verified access is unchanged."; + if (s.phase === "canceled") return "Purchase canceled. No new access granted."; + if (s.phase === "revoked") return "Play access revoked. Free recovery remains available."; + if (previewIsPro(s)) return s.playPro && !s.acknowledged ? "Pro verified — acknowledgment pending." : "Pro verified."; + return "Free compatibility checks and recovery are available."; +} diff --git a/v2/mobile/src/lib/healthPresentation.ts b/v2/mobile/src/lib/healthPresentation.ts new file mode 100644 index 00000000..83b04fb2 --- /dev/null +++ b/v2/mobile/src/lib/healthPresentation.ts @@ -0,0 +1,70 @@ +import type { HealthReport, RamInfo, StorageInfo } from './types'; +const finite = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value); +export const metricNumber = (value: number) => String(Math.round(value * 10) / 10); + +export function memoryUsage(ram: Partial | null | undefined) { + const total = ram?.total_mb; + if (!finite(total) || total <= 0) return null; + const free = ram?.free_mb; + const used = ram?.used_mb; + if (free != null && (!finite(free) || free < 0 || free > total)) return null; + if (used != null && (!finite(used) || used < 0 || used > total)) return null; + const amount = used ?? (free != null ? total - free : null); + // Android reports Used and Free RAM with separate cache/swap accounting. + if (amount == null) return null; + return { used: amount, total, free: free ?? null, percent: Math.round(amount / total * 100) }; +} + +export function storageUsage(storage: Partial | null | undefined) { + const percent = storage?.used_percent; + return finite(percent) && percent >= 0 && percent <= 100 ? percent : null; +} + +const statusNames = ['No throttling reported', 'Light throttling', 'Moderate throttling', 'Severe throttling', 'Critical thermal status', 'Emergency thermal status', 'Shutdown thermal status']; +const statusHelp = [ + 'No thermal throttling is reported in this snapshot; this is not a temperature safety rating.', + 'The device reports light thermal limiting.', + 'The device reports thermal limiting. Reduce heavy workloads and recheck.', + 'Thermal limits can noticeably reduce performance. Pause heavy workloads and recheck.', + 'The device reports strong thermal limiting. Pause heavy workloads and let it cool.', + 'The device reports an emergency condition. Stop heavy workloads and follow its warnings.', + 'The device reports a shutdown condition. Stop heavy workloads and follow its warnings.', +]; +const validStatus = (value: unknown): value is number => finite(value) && Number.isInteger(value) && value >= 0 && value <= 6; + +export function temperatureContext(report: Partial | null | undefined) { + const value = finite(report?.temperature_c) ? report.temperature_c : null; + const candidate = report?.temperature_reading; + // Never attach another value's sensor/threshold context to a legacy scalar. + const reading = candidate && value != null && candidate.celsius === value ? candidate : null; + const deviceStatus = report?.thermal_status; + const sensorStatus = validStatus(reading?.status) ? reading.status : null; + const status = validStatus(deviceStatus) ? deviceStatus : sensorStatus; + const rawName = reading?.sensor_name; + const indexed = rawName?.match(/^(CPU|GPU) temperatures\[(\d+)\]$/); + const name = indexed ? `${indexed[1]} reading ${Number(indexed[2]) + 1}` : rawName; + const kind = reading?.sensor_type; + const sensor = indexed ? name! : name && kind && name.toLowerCase() !== kind.toLowerCase() ? `${kind} · ${name}` : name || kind || 'Sensor not reported'; + const cached = reading?.source === 'thermalservice_cached'; + const source = reading?.source === 'thermalservice_current' ? 'Current Android thermal sensors' + : cached ? 'Cached Android thermal sensors' + : reading?.source === 'thermalservice' ? 'Android thermal sensors' + : reading?.source === 'hardware_properties' ? 'CPU/GPU hardware report' : null; + const throttle = finite(reading?.throttling_c) && reading.throttling_c > 0 ? reading.throttling_c : null; + const shutdown = finite(reading?.shutdown_c) && reading.shutdown_c > 0 ? reading.shutdown_c : null; + let comparison: string | null = null; + if (value != null && throttle != null) { + const difference = value - throttle; + comparison = difference === 0 ? 'At reported sensor threshold' + : `${metricNumber(Math.abs(difference))}°C ${difference > 0 ? 'above' : 'below'} reported sensor threshold`; + } + return { + value, sensor, source, throttle, shutdown, comparison, cached, + thresholdHelp: throttle == null ? null : "Throttling may already be active below this value. The report does not identify this threshold's severity.", + deviceStatus: validStatus(deviceStatus) ? deviceStatus : null, sensorStatus, + sensorStatusLabel: sensorStatus == null ? null : statusNames[sensorStatus], + status, statusScope: validStatus(deviceStatus) ? 'Device status' : cached ? 'Cached sensor status' : 'Selected sensor status', + statusLabel: status == null ? 'Thermal status not reported' : statusNames[status], + statusHelp: cached && !validStatus(deviceStatus) ? 'This cached sensor status may not reflect current thermal conditions.' : status == null ? (throttle != null ? 'No severity level was supplied; the limits below belong to this reported sensor.' : 'A temperature alone does not establish overheating. No device-specific severity was supplied.') : statusHelp[status], + }; +} diff --git a/v2/mobile/src/lib/launcherView.ts b/v2/mobile/src/lib/launcherView.ts new file mode 100644 index 00000000..beb5f30e --- /dev/null +++ b/v2/mobile/src/lib/launcherView.ts @@ -0,0 +1,139 @@ +// Pure view logic for the Launcher screen. Mirrors the desktop Launcher tab +// (src/routes/devices/[serial]/+page.svelte) so the two apps reach the same +// conclusions from the same launcher rows. + +import type { LauncherStatus, OtherPackage } from "./types"; + +/// "off": disabled. "on": enabled while an enabled stock launcher can still +/// hold Home. "risk": enabled while stock is off, so it will likely take Home. +/// The role can name a custom launcher while the helper's higher-priority +/// filter wins the Home button, so the current launcher never clears "risk". +export type SetupHelperState = "off" | "on" | "risk"; + +export interface SetupHelperView { + row: LauncherStatus; + state: SetupHelperState; +} + +/// Google TV's setup helper (Setup Wraith) whenever it is installed. A +/// disabled one no longer answers the Home query, so the backend reads it from +/// the package lists and it stays a row either way. +export function setupHelperView(launchers: LauncherStatus[]): SetupHelperView | null { + const row = launchers.find((l) => l.setup_helper && l.installed); + if (!row) return null; + if (!row.enabled) return { row, state: "off" }; + return { row, state: launchers.some((l) => l.stock && l.enabled) ? "on" : "risk" }; +} + +/// A stock takeover also turns the setup helper off: it is enabled and an +/// enabled stock launcher names it in the catalog's `disable_with`. +export function takeoverTurnsOffSetupHelper(launchers: LauncherStatus[]): boolean { + const helper = launchers.find((l) => l.setup_helper && l.installed); + if (!helper?.enabled) return false; + return launchers.some( + (l) => l.stock && l.enabled && (l.entry.disable_with ?? []).includes(helper.entry.package), + ); +} + +export interface StockDisableGate { + allowed: boolean; + /// Why the button is off. Empty when allowed. + reason: string; +} + +/// Whether "Disable stock launcher" may be offered for the picked app. It stays +/// off until the picked app holds Home, or until `set_home_any` reported that +/// the stock launcher is what's in the way for exactly this app. The backend +/// re-checks all of it (and the last-Home-handler guard); this only keeps the +/// button from inviting a request it will refuse. +export function stockDisableGate(input: { + launchers: LauncherStatus[]; + choice: string; + currentPkg: string | null; + stockHoldsHomeFor: string | null; +}): StockDisableGate { + const { launchers, choice, currentPkg, stockHoldsHomeFor } = input; + if (!choice) return { allowed: false, reason: "Choose the app that should take over Home first." }; + if (!launchers.some((l) => l.stock && l.enabled)) { + return { allowed: false, reason: "No enabled stock launcher on this TV." }; + } + if (launchers.some((l) => l.stock && l.entry.package === choice)) { + return { allowed: false, reason: "Pick the app that should take over, not the stock launcher." }; + } + if (launchers.some((l) => l.setup_helper && l.entry.package === choice)) { + return { + allowed: false, + reason: "That is Google TV's setup helper, not a launcher. Pick a real launcher.", + }; + } + if (currentPkg === choice || stockHoldsHomeFor === choice) return { allowed: true, reason: "" }; + return { + allowed: false, + reason: + "Set the app as Home first. This stays off until it's Home, or until Android says the stock launcher is what's in the way.", + }; +} + +/// The picker's rows: user apps unless system apps are revealed, filtered by a +/// case-insensitive name/package search, one row per package, sorted by name. +export function filterHomeCandidates( + packages: OtherPackage[], + query: string, + showSystem: boolean, +): OtherPackage[] { + const q = query.trim().toLowerCase(); + const unique = new Map(); + for (const p of packages) { + if (!showSystem && p.system) continue; + if ( + q && + !p.package.toLowerCase().includes(q) && + !(p.name ?? "").toLowerCase().includes(q) + ) { + continue; + } + if (!unique.has(p.package)) unique.set(p.package, p); + } + return [...unique.values()].sort((a, b) => + (a.name ?? a.package).localeCompare(b.name ?? b.package), + ); +} + +/// The host to show for a launcher's source site, or null when the catalog +/// value is not an http(s) URL (nothing is offered for anything else). +export function sourceSiteHost(url: string | null | undefined): string | null { + if (!url) return null; + try { + const parsed = new URL(url); + if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null; + return parsed.host.replace(/^www\./, "") || null; + } catch { + return null; + } +} + +/// True once the launcher list reports `pkg` installed. +export function isInstalled(launchers: LauncherStatus[], pkg: string): boolean { + return launchers.some((l) => l.entry.package === pkg && l.installed); +} + +/// The message for a finished `disable_stock_launcher`. Success is only +/// claimed when the backend verified it; otherwise its own sentence is shown. +/// The Setup Wraith note comes from the re-read after the call, not from the +/// prediction before it: `helperAfter` is null when there is no helper row or +/// the re-read failed, and then nothing is said about it. +export function stockDisableMessage( + result: { ok: boolean; last_error: string | null }, + target: string, + helperExpectedOff: boolean, + helperAfter: SetupHelperState | null, +): string { + if (!result.ok) return result.last_error ?? "The stock launcher was left alone."; + let wraith = ""; + if (helperExpectedOff && helperAfter === "off") { + wraith = " Google TV's setup helper (Setup Wraith) was turned off too."; + } else if (helperAfter === "risk") { + wraith = " Setup Wraith still reads as on, so it may take the Home button back; see below."; + } + return `The stock launcher is disabled and ${target} is Home.${wraith} Re-enable stock from the list above any time.`; +} diff --git a/v2/mobile/src/lib/memorySuggestion.ts b/v2/mobile/src/lib/memorySuggestion.ts new file mode 100644 index 00000000..cdbfc4e1 --- /dev/null +++ b/v2/mobile/src/lib/memorySuggestion.ts @@ -0,0 +1,106 @@ +// Health's Suggestion for one memory row — the mobile port of desktop's +// `memorySuggestion` (routes/devices/[serial]/+page.svelte). +// +// A row is an app only once the TV confirms its package candidate is +// installed; then it gets the same `recommendation()` the App List and +// Optimize use. Any other name is a process: it is classified catalog-free +// (`process_safety_info`) and never offered as something to remove. When the +// installed list could not be read nothing is confirmed, and a name with +// package shape says so instead of being called "Not an app". + +import type { AppEntry, MemoryEntry } from "./types"; +import { recommendation, type PackageState, type Recommendation } from "./recommendation"; +import { CHECKING_LABEL, safetyLabel, type SafetyStatus } from "./safety"; + +/// Installed packages and their state, or why we don't have them. +export type InstalledState = + | { status: "loading" } + | { status: "failed" } + | { status: "ready"; packages: Map }; + +export type CatalogState = + | { status: "loading" } + | { status: "failed" } + | { status: "ready"; entries: Map }; + +export type MemorySuggestion = + | { kind: "checking" } + | { kind: "unconfirmed" } + | { kind: "recommendation"; pkg: string; rec: Recommendation } + | { kind: "verdict"; pkg: string; status: SafetyStatus | undefined } + | { kind: "process"; status: SafetyStatus | undefined }; + +/// The row's package, only once the device has confirmed it is installed. +export function confirmedPackage(m: MemoryEntry, installed: InstalledState): string | null { + if (installed.status !== "ready" || m.package === null) return null; + return installed.packages.has(m.package) ? m.package : null; +} + +/// What to ask about this row: the package verdict for a confirmed app, the +/// catalog-free process verdict for anything else. Null while it can't be +/// decided yet. +export function safetyQuery( + m: MemoryEntry, + installed: InstalledState, +): { kind: "package" | "process"; name: string } | null { + if (installed.status === "loading") return null; + const pkg = confirmedPackage(m, installed); + return pkg ? { kind: "package", name: pkg } : { kind: "process", name: m.process }; +} + +export function memorySuggestion( + m: MemoryEntry, + installed: InstalledState, + catalog: CatalogState, + safety: SafetyStatus | undefined, +): MemorySuggestion { + if (installed.status === "loading") return { kind: "checking" }; + const pkg = confirmedPackage(m, installed); + if (pkg === null) { + if (installed.status === "failed" && m.package !== null) return { kind: "unconfirmed" }; + return { kind: "process", status: safety }; + } + // A verdict that flips to a recommendation a second later is a column that + // cannot be trusted at a glance, so wait for the catalog. + if (catalog.status === "loading") return { kind: "checking" }; + const entry = catalog.status === "ready" ? catalog.entries.get(pkg) : undefined; + if (entry && installed.status === "ready") { + const state = installed.packages.get(pkg) ?? null; + return { kind: "recommendation", pkg, rec: recommendation(entry, state, safety) }; + } + return { kind: "verdict", pkg, status: safety }; +} + +/// Label plus a style hint. `act` reads as the accent, `review` / +/// `unavailable` as amber ("look at this first"), and anything that needs +/// nothing recedes — the same scheme as desktop's Suggestion column. +export function suggestionDisplay(s: MemorySuggestion): { + label: string; + tone: "act" | "warn" | "muted" | "safe" | "caution" | "blocked" | "unknown"; +} { + switch (s.kind) { + case "checking": + return { label: CHECKING_LABEL, tone: "muted" }; + case "unconfirmed": + return { label: "Not checked", tone: "warn" }; + case "process": + return { label: "Not an app", tone: "muted" }; + case "recommendation": + if (s.rec.kind === "act") return { label: s.rec.label, tone: "act" }; + if (s.rec.kind === "review" || s.rec.kind === "unavailable") { + return { label: s.rec.label, tone: "warn" }; + } + return { label: s.rec.label, tone: "muted" }; + case "verdict": { + const label = safetyLabel(s.status); + if (s.status?.status !== "ready") { + return { label, tone: s.status?.status === "checking" ? "muted" : "warn" }; + } + const kind = s.status.verdict.kind; + return { + label, + tone: kind === "never_disable" ? "blocked" : kind, + }; + } + } +} diff --git a/v2/mobile/src/lib/modal.ts b/v2/mobile/src/lib/modal.ts new file mode 100644 index 00000000..2c3b2133 --- /dev/null +++ b/v2/mobile/src/lib/modal.ts @@ -0,0 +1,145 @@ +// One document lock shared by nested sheets/dialogs. Each action owns only its +// own stack entry, so removing an underlying sheet cannot unlock a child modal. +type Options = { onClose: () => void }; +type Entry = { node: HTMLElement; options: Options; returnFocus: HTMLElement | null; lastFocus: HTMLElement | null }; +const stack: Entry[] = []; +let unlock: (() => void) | undefined; +let documentReturnFocus: HTMLElement | null = null; +let revision = 0; +const focusable = 'button:not(:disabled), a[href], input:not(:disabled), textarea:not(:disabled), select:not(:disabled), [tabindex]:not([tabindex="-1"])'; + +function controls(node: HTMLElement): HTMLElement[] { + return Array.from(node.querySelectorAll(focusable)) + .filter((element) => element.getClientRects().length > 0 && !element.closest('[inert]')); +} +function focusControl(element: HTMLElement, entry: Entry) { + element.focus({ preventScroll: true }); + // Keep the document lock intact while revealing focus inside a long sheet. + // Walking only modal ancestors avoids scrollIntoView moving the page behind + // the overlay (and works without newer WebView container:"nearest" support). + for (let parent = element.parentElement; parent && entry.node.contains(parent); parent = parent.parentElement) { + if (!/^(auto|scroll)$/.test(getComputedStyle(parent).overflowY) || parent.scrollHeight <= parent.clientHeight) continue; + const target = element.getBoundingClientRect(); + const bounds = parent.getBoundingClientRect(); + const top = bounds.top + parent.clientTop; + const bottom = top + parent.clientHeight; + if (target.top < top) parent.scrollTop += target.top - top; + else if (target.bottom > bottom) parent.scrollTop += target.bottom - bottom; + } +} +function focusInside(entry: Entry) { + focusControl(controls(entry.node)[0] ?? entry.node.querySelector('[role="dialog"], [role="alertdialog"], [role="menu"]') ?? entry.node, entry); +} +function holdDocument() { + const x = window.scrollX; + const y = window.scrollY; + const saved: Array<() => void> = []; + const set = (element: HTMLElement, property: string, value: string) => { + const before = element.style.getPropertyValue(property); + const priority = element.style.getPropertyPriority(property); + saved.push(() => { + if (before) element.style.setProperty(property, before, priority); + else element.style.removeProperty(property); + }); + element.style.setProperty(property, value); + }; + // A fixed body also covers mobile WebViews where overflow:hidden alone does + // not stop touch scrolling. Do not disable touch or pinch zoom globally. + set(document.documentElement, 'overflow', 'hidden'); + set(document.documentElement, 'overscroll-behavior', 'none'); + set(document.body, 'position', 'fixed'); + set(document.body, 'top', `${-y}px`); + set(document.body, 'left', `${-x}px`); + set(document.body, 'width', '100%'); + set(document.body, 'overflow', 'hidden'); + return () => { + saved.reverse().forEach((restore) => restore()); + window.scrollTo({ left: x, top: y, behavior: 'instant' }); + }; +} + +export function modal(node: HTMLElement, options: Options) { + const previous = stack.at(-1); + const active = document.activeElement instanceof HTMLElement ? document.activeElement : null; + // A triggering action may briefly disable its button while awaiting a safety + // read, causing the browser to drop focus before its confirmation mounts. + let returnFocus = active; + if (!previous && unlock) returnFocus = documentReturnFocus; + else if (previous && (!active || !previous.node.contains(active))) returnFocus = previous.lastFocus; + const entry: Entry = { node, options, returnFocus, lastFocus: null }; + if (!stack.length && !unlock) { + documentReturnFocus = entry.returnFocus; + unlock = holdDocument(); + } + stack.push(entry); + revision++; + const initialFocus = requestAnimationFrame(() => { + if (stack.at(-1) === entry && node.isConnected) focusInside(entry); + }); + const keydown = (event: KeyboardEvent) => { + if (stack.at(-1) !== entry) return; + if (event.key === 'Escape') { + event.preventDefault(); + event.stopImmediatePropagation(); + entry.options.onClose(); + } else if (event.key === 'Tab') { + const items = controls(node); + const first = items[0]; + const last = items.at(-1); + if (!first) { + event.preventDefault(); + focusInside(entry); + } else if (event.shiftKey && (document.activeElement === first || !node.contains(document.activeElement))) { + event.preventDefault(); + if (last) focusControl(last, entry); + } else if (!event.shiftKey && (document.activeElement === last || !node.contains(document.activeElement))) { + event.preventDefault(); + focusControl(first, entry); + } + } + }; + const focusin = (event: FocusEvent) => { + if (stack.at(-1) === entry && event.target instanceof HTMLElement && node.contains(event.target)) entry.lastFocus = event.target; + }; + document.addEventListener('focusin', focusin); + document.addEventListener('keydown', keydown, true); + return { + update(next: Options) { entry.options = next; }, + destroy() { + cancelAnimationFrame(initialFocus); + document.removeEventListener('keydown', keydown, true); + document.removeEventListener('focusin', focusin); + const wasTop = stack.at(-1) === entry; + const index = stack.indexOf(entry); + if (index < 0) return; + stack.splice(index, 1); + const closedAt = ++revision; + // Svelte can destroy a menu before mounting its replacement in the same + // update. Retain the document lock/opener until that update settles. + queueMicrotask(() => { + if (closedAt !== revision) return; + const top = stack.at(-1); + const fallbackFocus = documentReturnFocus; + if (!top) { + documentReturnFocus = null; + unlock?.(); + unlock = undefined; + } + if (!wasTop) { + // An ancestor removal can supersede a just-closed child's cleanup. + // Repair focus only if it fell outside the surviving top modal. + if (top?.node.isConnected && !top.node.contains(document.activeElement)) { + if (top.lastFocus && controls(top.node).includes(top.lastFocus)) focusControl(top.lastFocus, top); + else focusInside(top); + } + return; + } + if (entry.returnFocus?.isConnected && (!top || top.node.contains(entry.returnFocus))) { + if (top) focusControl(entry.returnFocus, top); + else entry.returnFocus.focus({ preventScroll: true }); + } else if (top && top.node.isConnected) focusInside(top); + else if (!top && fallbackFocus?.isConnected) fallbackFocus.focus({ preventScroll: true }); + }); + }, + }; +} diff --git a/v2/mobile/src/lib/notices.generated.ts b/v2/mobile/src/lib/notices.generated.ts index b5bd63bd..de41c4c8 100644 --- a/v2/mobile/src/lib/notices.generated.ts +++ b/v2/mobile/src/lib/notices.generated.ts @@ -3,4 +3,4 @@ // Large (hundreds of KB) — import it dynamically so it lands in its own chunk. export const THIRD_PARTY_NOTICES_VERSION = "0.1.0"; -export const THIRD_PARTY_NOTICES: string = "\n# Third-party notices\n\nATV Optimizer for Android bundles the third-party software listed below. Each component is used\nunder the license named with it; the full license texts follow.\n\nRegenerate this document with `mobile/scripts/gen-notices.sh` after any dependency change.\n\n## Components not managed by Cargo\n\n### Geist and Geist Mono \u2014 SIL Open Font License 1.1\n\n`src/fonts/Geist-Variable.woff2` and `src/fonts/GeistMono-Variable.woff2` are the variable-weight\nWOFF2 builds shipped in the npm `geist` package (1.7.2), unmodified.\n\nCopyright (c) 2023 Vercel, in collaboration with basement.studio\n\n
\nSIL Open Font License 1.1 (full text)\n\n```\nCopyright (c) 2023 Vercel, in collaboration with basement.studio\n\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also available with a FAQ at:\nhttp://scripts.sil.org/OFL\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded,\nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION AND CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE.\n```\n\n
\n\n### Material Symbols Rounded \u2014 Apache License 2.0\n\n`src/fonts/material-symbols-rounded.woff2` is a subset of Material Symbols Rounded from the npm\n`material-symbols` package (0.45.5), reduced to the glyphs the app actually references by\n`mobile/scripts/subset-material-symbols.py`. Subsetting removes glyphs; it does not alter the\noutlines that remain.\n\nCopyright Google LLC. Licensed under the Apache License, Version 2.0 \u2014 full text reproduced in the\nApache-2.0 section below.\n\n### scrcpy server 3.1 \u2014 Apache License 2.0\n\n`scrcpy-server-v3.1` is embedded verbatim in the app binary (`src-tauri/src/scrcpy_resource.rs`,\nSHA-256 `958f0944a62f23b1f33a16e9eb14844c1a04b882ca175a738c16d23cb22b86c0`) and pushed to the\nconnected TV to provide the screen-mirroring stream. It is the unmodified release artifact from\nthe scrcpy project.\n\nCopyright (C) 2018 Genymobile, Copyright (C) 2018-2024 Romain Vimont. Licensed under the Apache\nLicense, Version 2.0 \u2014 full text reproduced in the Apache-2.0 section below.\nSource: \n\n### adb_client \u2014 MIT (vendored with modifications)\n\nThe wireless-ADB transport is `adb_client` 3.2.2 by Corentin Liaud, vendored at\n`v2/vendor/adb_client` and pinned through `[patch.crates-io]`. Shield Optimizer's changes are\ndescribed in `v2/vendor/adb_client/SHIELD-OPTIMIZER-PATCH.md`; the crate remains MIT-licensed and\nappears in the Cargo inventory below with its upstream license text.\n\nSource: \n\n### npm runtime dependencies\n\nOnly these npm packages contribute code or assets to the shipped app; everything else in\n`package.json` is build-time tooling.\n\n| Package | Version | License |\n| --- | --- | --- |\n| `svelte` | 5.56.4 | MIT |\n| `@tauri-apps/api` | 2.11.1 | Apache-2.0 OR MIT |\n| `geist` | 1.7.2 | SIL OFL 1.1 (see above) |\n| `material-symbols` | 0.45.5 | Apache-2.0 (see above) |\n\nSvelte compiles into the app bundle; `@tauri-apps/api` ships as JavaScript. Both are used under\nthe MIT License, whose text is reproduced in the MIT section below.\n\n## Rust crate dependencies\n\n308 crates are linked into the Android build (build-time and dev-only crates excluded).\n\n| License | Crates |\n| --- | --- |\n| MIT | 271 |\n| Apache-2.0 | 211 |\n| Unicode-3.0 | 19 |\n| BSD-3-Clause | 11 |\n| Unlicense | 6 |\n| MPL-2.0 | 5 |\n| ISC | 4 |\n| Zlib | 4 |\n| LLVM-exception | 2 |\n| 0BSD | 1 |\n| BSD-2-Clause | 1 |\n| CC0-1.0 | 1 |\n| MIT-0 | 1 |\n\nA crate under `A OR B` is counted under both; we distribute under whichever term applies, and the texts below cover every option offered.\n\n| Crate | Version | License |\n| --- | --- | --- |\n| [adb_client](https://github.com/cocool97/adb_client) | 3.2.2 | MIT |\n| [adler2](https://github.com/oyvindln/adler2) | 2.0.1 | 0BSD OR MIT OR Apache-2.0 |\n| [aead](https://github.com/RustCrypto/traits) | 0.5.2 | MIT OR Apache-2.0 |\n| [aes](https://github.com/RustCrypto/block-ciphers) | 0.8.4 | MIT OR Apache-2.0 |\n| [aes-gcm](https://github.com/RustCrypto/AEADs) | 0.10.3 | Apache-2.0 OR MIT |\n| [aho-corasick](https://github.com/BurntSushi/aho-corasick) | 1.1.4 | Unlicense OR MIT |\n| [alloc-no-stdlib](https://github.com/dropbox/rust-alloc-no-stdlib) | 2.0.4 | BSD-3-Clause |\n| [alloc-stdlib](https://github.com/dropbox/rust-alloc-no-stdlib) | 0.2.2 | BSD-3-Clause |\n| [android_system_properties](https://github.com/nical/android_system_properties) | 0.1.5 | MIT OR Apache-2.0 |\n| [anyhow](https://github.com/dtolnay/anyhow) | 1.0.102 | MIT OR Apache-2.0 |\n| [async-trait](https://github.com/dtolnay/async-trait) | 0.1.89 | MIT OR Apache-2.0 |\n| [atomic-waker](https://github.com/smol-rs/atomic-waker) | 1.1.2 | Apache-2.0 OR MIT |\n| [base64](https://github.com/marshallpierce/rust-base64) | 0.22.1 | MIT OR Apache-2.0 |\n| [base64ct](https://github.com/RustCrypto/formats) | 1.8.3 | Apache-2.0 OR MIT |\n| [bit-set](https://github.com/contain-rs/bit-set) | 0.8.0 | Apache-2.0 OR MIT |\n| [bit-vec](https://github.com/contain-rs/bit-vec) | 0.8.0 | Apache-2.0 OR MIT |\n| [bitflags](https://github.com/bitflags/bitflags) | 1.3.2 | MIT OR Apache-2.0 |\n| [bitflags](https://github.com/bitflags/bitflags) | 2.11.1 | MIT OR Apache-2.0 |\n| [block-buffer](https://github.com/RustCrypto/utils) | 0.10.4 | MIT OR Apache-2.0 |\n| [brotli](https://github.com/dropbox/rust-brotli) | 8.0.2 | BSD-3-Clause AND MIT |\n| [brotli-decompressor](https://github.com/dropbox/rust-brotli-decompressor) | 5.0.0 | BSD-3-Clause OR MIT |\n| [bytemuck](https://github.com/Lokathor/bytemuck) | 1.25.0 | Zlib OR Apache-2.0 OR MIT |\n| [byteorder](https://github.com/BurntSushi/byteorder) | 1.5.0 | Unlicense OR MIT |\n| [byteorder-lite](https://github.com/image-rs/byteorder-lite) | 0.1.0 | Unlicense OR MIT |\n| [bytes](https://github.com/tokio-rs/bytes) | 1.11.1 | MIT |\n| [camino](https://github.com/camino-rs/camino) | 1.2.2 | MIT OR Apache-2.0 |\n| [cargo-platform](https://github.com/rust-lang/cargo) | 0.1.9 | MIT OR Apache-2.0 |\n| [cargo_metadata](https://github.com/oli-obk/cargo_metadata) | 0.19.2 | MIT |\n| [cesu8](https://github.com/emk/cesu8-rs) | 1.1.0 | Apache-2.0 OR MIT |\n| [cfb](https://github.com/mdsteele/rust-cfb) | 0.7.3 | MIT |\n| [cfg-if](https://github.com/rust-lang/cfg-if) | 1.0.4 | MIT OR Apache-2.0 |\n| [chacha20](https://github.com/RustCrypto/stream-ciphers) | 0.10.1 | MIT OR Apache-2.0 |\n| [chrono](https://github.com/chronotope/chrono) | 0.4.44 | MIT OR Apache-2.0 |\n| [cipher](https://github.com/RustCrypto/traits) | 0.4.4 | MIT OR Apache-2.0 |\n| [combine](https://github.com/Marwes/combine) | 4.6.7 | MIT |\n| [const-oid](https://github.com/RustCrypto/formats/tree/master/const-oid) | 0.9.6 | Apache-2.0 OR MIT |\n| [cookie](https://github.com/SergioBenitez/cookie-rs) | 0.18.1 | MIT OR Apache-2.0 |\n| [cpufeatures](https://github.com/RustCrypto/utils) | 0.2.17 | MIT OR Apache-2.0 |\n| [cpufeatures](https://github.com/RustCrypto/utils) | 0.3.0 | MIT OR Apache-2.0 |\n| [crc32fast](https://github.com/srijs/rust-crc32fast) | 1.5.0 | MIT OR Apache-2.0 |\n| [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) | 0.5.15 | MIT OR Apache-2.0 |\n| [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) | 0.8.21 | MIT OR Apache-2.0 |\n| [crypto-common](https://github.com/RustCrypto/traits) | 0.1.7 | MIT OR Apache-2.0 |\n| [cssparser](https://github.com/servo/rust-cssparser) | 0.36.0 | MPL-2.0 |\n| [cssparser-macros](https://github.com/servo/rust-cssparser) | 0.6.1 | MPL-2.0 |\n| [ctor](https://github.com/mmastrac/rust-ctor) | 0.8.0 | Apache-2.0 OR MIT |\n| [ctor-proc-macro](https://github.com/mmastrac/rust-ctor) | 0.0.7 | Apache-2.0 OR MIT |\n| [ctr](https://github.com/RustCrypto/block-modes) | 0.9.2 | MIT OR Apache-2.0 |\n| [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek) | 4.1.3 | BSD-3-Clause |\n| [curve25519-dalek-derive](https://github.com/dalek-cryptography/curve25519-dalek) | 0.1.1 | MIT OR Apache-2.0 |\n| [darling](https://github.com/TedDriggs/darling) | 0.23.0 | MIT |\n| [darling_core](https://github.com/TedDriggs/darling) | 0.23.0 | MIT |\n| [darling_macro](https://github.com/TedDriggs/darling) | 0.23.0 | MIT |\n| [der](https://github.com/RustCrypto/formats/tree/master/der) | 0.7.10 | Apache-2.0 OR MIT |\n| [deranged](https://github.com/jhpratt/deranged) | 0.5.8 | MIT OR Apache-2.0 |\n| [derive_more](https://github.com/JelteF/derive_more) | 2.1.1 | MIT |\n| [derive_more-impl](https://github.com/JelteF/derive_more) | 2.1.1 | MIT |\n| [digest](https://github.com/RustCrypto/traits) | 0.10.7 | MIT OR Apache-2.0 |\n| [dirs](https://github.com/soc/dirs-rs) | 5.0.1 | MIT OR Apache-2.0 |\n| [dirs](https://github.com/soc/dirs-rs) | 6.0.0 | MIT OR Apache-2.0 |\n| [dirs-sys](https://github.com/dirs-dev/dirs-sys-rs) | 0.4.1 | MIT OR Apache-2.0 |\n| [dirs-sys](https://github.com/dirs-dev/dirs-sys-rs) | 0.5.0 | MIT OR Apache-2.0 |\n| [displaydoc](https://github.com/yaahc/displaydoc) | 0.2.6 | MIT OR Apache-2.0 |\n| [dom_query](https://github.com/niklak/dom_query) | 0.27.0 | MIT |\n| [dpi](https://github.com/rust-windowing/winit) | 0.1.2 | Apache-2.0 AND MIT |\n| [dtoa](https://github.com/dtolnay/dtoa) | 1.0.11 | MIT OR Apache-2.0 |\n| [dtoa-short](https://github.com/upsuper/dtoa-short) | 0.3.5 | MPL-2.0 |\n| [dunce](https://gitlab.com/kornelski/dunce) | 1.0.5 | CC0-1.0 OR MIT-0 OR Apache-2.0 |\n| [dyn-clone](https://github.com/dtolnay/dyn-clone) | 1.0.20 | MIT OR Apache-2.0 |\n| [ed25519](https://github.com/RustCrypto/signatures/tree/master/ed25519) | 2.2.3 | Apache-2.0 OR MIT |\n| [ed25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek) | 2.2.0 | BSD-3-Clause |\n| [equivalent](https://github.com/indexmap-rs/equivalent) | 1.0.2 | Apache-2.0 OR MIT |\n| [erased-serde](https://github.com/dtolnay/erased-serde) | 0.4.10 | MIT OR Apache-2.0 |\n| [errno](https://github.com/lambda-fairy/rust-errno) | 0.3.14 | MIT OR Apache-2.0 |\n| [fastrand](https://github.com/smol-rs/fastrand) | 2.4.1 | Apache-2.0 OR MIT |\n| [fdeflate](https://github.com/image-rs/fdeflate) | 0.3.7 | MIT OR Apache-2.0 |\n| [flate2](https://github.com/rust-lang/flate2-rs) | 1.1.9 | MIT OR Apache-2.0 |\n| [fnv](https://github.com/servo/rust-fnv) | 1.0.7 | Apache-2.0 OR MIT |\n| [foldhash](https://github.com/orlp/foldhash) | 0.2.0 | Zlib |\n| [form_urlencoded](https://github.com/servo/rust-url) | 1.2.2 | MIT OR Apache-2.0 |\n| [futures-channel](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [futures-core](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [futures-sink](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [futures-task](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [futures-util](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [generic-array](https://github.com/fizyk20/generic-array.git) | 0.14.7 | MIT |\n| [getrandom](https://github.com/rust-random/getrandom) | 0.2.17 | MIT OR Apache-2.0 |\n| [getrandom](https://github.com/rust-random/getrandom) | 0.3.4 | MIT OR Apache-2.0 |\n| [getrandom](https://github.com/rust-random/getrandom) | 0.4.2 | MIT OR Apache-2.0 |\n| [ghash](https://github.com/RustCrypto/universal-hashes) | 0.5.1 | Apache-2.0 OR MIT |\n| [glob](https://github.com/rust-lang/glob) | 0.3.3 | MIT OR Apache-2.0 |\n| [hashbrown](https://github.com/rust-lang/hashbrown) | 0.17.1 | MIT OR Apache-2.0 |\n| [heck](https://github.com/withoutboats/heck) | 0.5.0 | MIT OR Apache-2.0 |\n| [hkdf](https://github.com/RustCrypto/KDFs/) | 0.12.4 | MIT OR Apache-2.0 |\n| [hmac](https://github.com/RustCrypto/MACs) | 0.12.1 | MIT OR Apache-2.0 |\n| [html5ever](https://github.com/servo/html5ever) | 0.38.0 | MIT OR Apache-2.0 |\n| [http](https://github.com/hyperium/http) | 1.4.1 | MIT OR Apache-2.0 |\n| [http-body](https://github.com/hyperium/http-body) | 1.0.1 | MIT |\n| [http-body-util](https://github.com/hyperium/http-body) | 0.1.3 | MIT |\n| [httparse](https://github.com/seanmonstar/httparse) | 1.10.1 | MIT OR Apache-2.0 |\n| [hyper](https://github.com/hyperium/hyper) | 1.10.0 | MIT |\n| [hyper-util](https://github.com/hyperium/hyper-util) | 0.1.20 | MIT |\n| [iana-time-zone](https://github.com/strawlab/iana-time-zone) | 0.1.65 | MIT OR Apache-2.0 |\n| [ico](https://github.com/mdsteele/rust-ico) | 0.5.0 | MIT |\n| [icu_collections](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_locale_core](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_normalizer](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_normalizer_data](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_properties](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_properties_data](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_provider](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [ident_case](https://github.com/TedDriggs/ident_case) | 1.0.1 | MIT OR Apache-2.0 |\n| [idna](https://github.com/servo/rust-url/) | 1.1.0 | MIT OR Apache-2.0 |\n| [idna_adapter](https://github.com/hsivonen/idna_adapter) | 1.2.2 | Apache-2.0 OR MIT |\n| [image](https://github.com/image-rs/image) | 0.25.10 | MIT OR Apache-2.0 |\n| [indexmap](https://github.com/indexmap-rs/indexmap) | 2.14.0 | Apache-2.0 OR MIT |\n| [infer](https://github.com/bojand/infer) | 0.19.0 | MIT |\n| [inout](https://github.com/RustCrypto/utils) | 0.1.4 | MIT OR Apache-2.0 |\n| [ipnet](https://github.com/krisprice/ipnet) | 2.12.0 | MIT OR Apache-2.0 |\n| [itoa](https://github.com/dtolnay/itoa) | 1.0.18 | MIT OR Apache-2.0 |\n| [jni](https://github.com/jni-rs/jni-rs) | 0.21.1 | MIT OR Apache-2.0 |\n| [jni-sys](https://github.com/jni-rs/jni-sys) | 0.3.1 | MIT OR Apache-2.0 |\n| [jni-sys](https://github.com/jni-rs/jni-sys) | 0.4.1 | MIT OR Apache-2.0 |\n| [jni-sys-macros](https://github.com/jni-rs/jni-sys) | 0.4.1 | MIT OR Apache-2.0 |\n| [json-patch](https://github.com/idubrov/json-patch) | 3.0.1 | MIT OR Apache-2.0 |\n| [jsonptr](https://github.com/chanced/jsonptr) | 0.6.3 | MIT OR Apache-2.0 |\n| [lazy_static](https://github.com/rust-lang-nursery/lazy-static.rs) | 1.5.0 | MIT OR Apache-2.0 |\n| [libc](https://github.com/rust-lang/libc) | 0.2.186 | MIT OR Apache-2.0 |\n| [libm](https://github.com/rust-lang/compiler-builtins) | 0.2.16 | MIT |\n| [linux-raw-sys](https://github.com/sunfishcode/linux-raw-sys) | 0.12.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT |\n| [litemap](https://github.com/unicode-org/icu4x) | 0.8.2 | Unicode-3.0 |\n| [lock_api](https://github.com/Amanieu/parking_lot) | 0.4.14 | MIT OR Apache-2.0 |\n| [log](https://github.com/rust-lang/log) | 0.4.30 | MIT OR Apache-2.0 |\n| [markup5ever](https://github.com/servo/html5ever) | 0.38.0 | MIT OR Apache-2.0 |\n| [matchers](https://github.com/hawkw/matchers) | 0.2.0 | MIT |\n| [memchr](https://github.com/BurntSushi/memchr) | 2.8.1 | Unlicense OR MIT |\n| [mime](https://github.com/hyperium/mime) | 0.3.17 | MIT OR Apache-2.0 |\n| [miniz_oxide](https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide) | 0.8.9 | MIT OR Zlib OR Apache-2.0 |\n| [mio](https://github.com/tokio-rs/mio) | 1.2.0 | MIT |\n| [moxcms](https://github.com/awxkee/moxcms.git) | 0.8.1 | BSD-3-Clause OR Apache-2.0 |\n| [ndk](https://github.com/rust-mobile/ndk) | 0.9.0 | MIT OR Apache-2.0 |\n| [ndk-sys](https://github.com/rust-mobile/ndk) | 0.6.0+11769913 | MIT OR Apache-2.0 |\n| [new_debug_unreachable](https://github.com/mbrubeck/rust-debug-unreachable) | 1.0.6 | MIT |\n| [nu-ansi-term](https://github.com/nushell/nu-ansi-term) | 0.50.3 | MIT |\n| [num-bigint-dig](https://github.com/dignifiedquire/num-bigint) | 0.8.6 | MIT OR Apache-2.0 |\n| [num-conv](https://github.com/jhpratt/num-conv) | 0.2.2 | MIT OR Apache-2.0 |\n| [num-integer](https://github.com/rust-num/num-integer) | 0.1.46 | MIT OR Apache-2.0 |\n| [num-iter](https://github.com/rust-num/num-iter) | 0.1.45 | MIT OR Apache-2.0 |\n| [num-traits](https://github.com/rust-num/num-traits) | 0.2.19 | MIT OR Apache-2.0 |\n| [num_enum](https://github.com/illicitonion/num_enum) | 0.7.6 | BSD-3-Clause OR MIT OR Apache-2.0 |\n| [num_enum_derive](https://github.com/illicitonion/num_enum) | 0.7.6 | BSD-3-Clause OR MIT OR Apache-2.0 |\n| [once_cell](https://github.com/matklad/once_cell) | 1.21.4 | MIT OR Apache-2.0 |\n| [opaque-debug](https://github.com/RustCrypto/utils) | 0.3.1 | MIT OR Apache-2.0 |\n| [option-ext](https://github.com/soc/option-ext.git) | 0.2.0 | MPL-2.0 |\n| [parking_lot](https://github.com/Amanieu/parking_lot) | 0.12.5 | MIT OR Apache-2.0 |\n| [parking_lot_core](https://github.com/Amanieu/parking_lot) | 0.9.12 | MIT OR Apache-2.0 |\n| [pem](https://github.com/jcreekmore/pem-rs.git) | 3.0.6 | MIT |\n| [pem-rfc7468](https://github.com/RustCrypto/formats/tree/master/pem-rfc7468) | 0.7.0 | Apache-2.0 OR MIT |\n| [percent-encoding](https://github.com/servo/rust-url/) | 2.3.2 | MIT OR Apache-2.0 |\n| [phf](https://github.com/rust-phf/rust-phf) | 0.13.1 | MIT |\n| [phf_generator](https://github.com/rust-phf/rust-phf) | 0.13.1 | MIT |\n| [phf_macros](https://github.com/rust-phf/rust-phf) | 0.13.1 | MIT |\n| [phf_shared](https://github.com/rust-phf/rust-phf) | 0.13.1 | MIT |\n| [pin-project-lite](https://github.com/taiki-e/pin-project-lite) | 0.2.17 | Apache-2.0 OR MIT |\n| [pkcs1](https://github.com/RustCrypto/formats/tree/master/pkcs1) | 0.7.5 | Apache-2.0 OR MIT |\n| [pkcs8](https://github.com/RustCrypto/formats/tree/master/pkcs8) | 0.10.2 | Apache-2.0 OR MIT |\n| [plist](https://github.com/ebarnard/rust-plist/) | 1.9.0 | MIT |\n| [png](https://github.com/image-rs/image-png) | 0.17.16 | MIT OR Apache-2.0 |\n| [png](https://github.com/image-rs/image-png) | 0.18.1 | MIT OR Apache-2.0 |\n| [polyval](https://github.com/RustCrypto/universal-hashes) | 0.6.2 | Apache-2.0 OR MIT |\n| [potential_utf](https://github.com/unicode-org/icu4x) | 0.1.5 | Unicode-3.0 |\n| [powerfmt](https://github.com/jhpratt/powerfmt) | 0.2.0 | MIT OR Apache-2.0 |\n| [ppv-lite86](https://github.com/cryptocorrosion/cryptocorrosion) | 0.2.21 | MIT OR Apache-2.0 |\n| [precomputed-hash](https://github.com/emilio/precomputed-hash) | 0.1.1 | MIT |\n| [proc-macro-crate](https://github.com/bkchr/proc-macro-crate) | 3.5.0 | MIT OR Apache-2.0 |\n| [proc-macro2](https://github.com/dtolnay/proc-macro2) | 1.0.106 | MIT OR Apache-2.0 |\n| [pxfm](https://github.com/awxkee/pxfm) | 0.1.29 | BSD-3-Clause OR Apache-2.0 |\n| [quick-protobuf](https://github.com/tafia/quick-protobuf) | 0.8.1 | MIT |\n| [quick-xml](https://github.com/tafia/quick-xml) | 0.39.4 | MIT |\n| [quote](https://github.com/dtolnay/quote) | 1.0.45 | MIT OR Apache-2.0 |\n| [rand](https://github.com/rust-random/rand) | 0.10.2 | MIT OR Apache-2.0 |\n| [rand](https://github.com/rust-random/rand) | 0.8.6 | MIT OR Apache-2.0 |\n| [rand_chacha](https://github.com/rust-random/rand) | 0.3.1 | MIT OR Apache-2.0 |\n| [rand_core](https://github.com/rust-random/rand_core) | 0.10.1 | MIT OR Apache-2.0 |\n| [rand_core](https://github.com/rust-random/rand) | 0.6.4 | MIT OR Apache-2.0 |\n| [raw-window-handle](https://github.com/rust-windowing/raw-window-handle) | 0.6.2 | MIT OR Apache-2.0 OR Zlib |\n| [rcgen](https://github.com/rustls/rcgen) | 0.14.8 | MIT OR Apache-2.0 |\n| [regex](https://github.com/rust-lang/regex) | 1.12.3 | MIT OR Apache-2.0 |\n| [regex-automata](https://github.com/rust-lang/regex) | 0.4.14 | MIT OR Apache-2.0 |\n| [regex-syntax](https://github.com/rust-lang/regex) | 0.8.10 | MIT OR Apache-2.0 |\n| [reqwest](https://github.com/seanmonstar/reqwest) | 0.13.4 | MIT OR Apache-2.0 |\n| [ring](https://github.com/briansmith/ring) | 0.17.14 | Apache-2.0 AND ISC |\n| [rsa](https://github.com/RustCrypto/RSA) | 0.9.10 | MIT OR Apache-2.0 |\n| [rustc-hash](https://github.com/rust-lang/rustc-hash) | 2.1.2 | Apache-2.0 OR MIT |\n| [rustix](https://github.com/bytecodealliance/rustix) | 1.1.4 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT |\n| [rustls](https://github.com/rustls/rustls) | 0.23.40 | Apache-2.0 OR ISC OR MIT |\n| [rustls-pki-types](https://github.com/rustls/pki-types) | 1.14.1 | MIT OR Apache-2.0 |\n| [rustls-webpki](https://github.com/rustls/webpki) | 0.103.13 | ISC |\n| [rustversion](https://github.com/dtolnay/rustversion) | 1.0.22 | MIT OR Apache-2.0 |\n| [same-file](https://github.com/BurntSushi/same-file) | 1.0.6 | Unlicense OR MIT |\n| [schemars](https://github.com/GREsau/schemars) | 0.8.22 | MIT |\n| [schemars_derive](https://github.com/GREsau/schemars) | 0.8.22 | MIT |\n| [scopeguard](https://github.com/bluss/scopeguard) | 1.2.0 | MIT OR Apache-2.0 |\n| [selectors](https://github.com/servo/stylo) | 0.36.1 | MPL-2.0 |\n| [semver](https://github.com/dtolnay/semver) | 1.0.28 | MIT OR Apache-2.0 |\n| [serde](https://github.com/serde-rs/serde) | 1.0.228 | MIT OR Apache-2.0 |\n| [serde-untagged](https://github.com/dtolnay/serde-untagged) | 0.1.9 | MIT OR Apache-2.0 |\n| [serde_core](https://github.com/serde-rs/serde) | 1.0.228 | MIT OR Apache-2.0 |\n| [serde_derive](https://github.com/serde-rs/serde) | 1.0.228 | MIT OR Apache-2.0 |\n| [serde_derive_internals](https://github.com/serde-rs/serde) | 0.29.1 | MIT OR Apache-2.0 |\n| [serde_json](https://github.com/serde-rs/json) | 1.0.150 | MIT OR Apache-2.0 |\n| [serde_repr](https://github.com/dtolnay/serde-repr) | 0.1.20 | MIT OR Apache-2.0 |\n| [serde_spanned](https://github.com/toml-rs/toml) | 1.1.1 | MIT OR Apache-2.0 |\n| [serde_with](https://github.com/jonasbb/serde_with/) | 3.20.0 | MIT OR Apache-2.0 |\n| [serde_with_macros](https://github.com/jonasbb/serde_with/) | 3.20.0 | MIT OR Apache-2.0 |\n| [serialize-to-javascript](https://github.com/chippers/serialize-to-javascript) | 0.1.2 | MIT OR Apache-2.0 |\n| [serialize-to-javascript-impl](https://github.com/chippers/serialize-to-javascript) | 0.1.2 | MIT OR Apache-2.0 |\n| [servo_arc](https://github.com/servo/stylo) | 0.4.3 | MIT OR Apache-2.0 |\n| [sha1](https://github.com/RustCrypto/hashes) | 0.10.6 | MIT OR Apache-2.0 |\n| [sha2](https://github.com/RustCrypto/hashes) | 0.10.9 | MIT OR Apache-2.0 |\n| [sharded-slab](https://github.com/hawkw/sharded-slab) | 0.1.7 | MIT |\n| [signal-hook-registry](https://github.com/vorner/signal-hook) | 1.4.8 | MIT OR Apache-2.0 |\n| [signature](https://github.com/RustCrypto/traits/tree/master/signature) | 2.2.0 | Apache-2.0 OR MIT |\n| [simd-adler32](https://github.com/mcountryman/simd-adler32) | 0.3.9 | MIT |\n| [siphasher](https://github.com/jedisct1/rust-siphash) | 1.0.3 | MIT OR Apache-2.0 |\n| [slab](https://github.com/tokio-rs/slab) | 0.4.12 | MIT |\n| [smallvec](https://github.com/servo/rust-smallvec) | 1.15.1 | MIT OR Apache-2.0 |\n| [socket2](https://github.com/rust-lang/socket2) | 0.6.3 | MIT OR Apache-2.0 |\n| [spin](https://github.com/mvdnes/spin-rs.git) | 0.9.8 | MIT |\n| [spki](https://github.com/RustCrypto/formats/tree/master/spki) | 0.7.3 | Apache-2.0 OR MIT |\n| [stable_deref_trait](https://github.com/storyyeller/stable_deref_trait) | 1.2.1 | MIT OR Apache-2.0 |\n| [string_cache](https://github.com/servo/string-cache) | 0.9.0 | MIT OR Apache-2.0 |\n| [strsim](https://github.com/rapidfuzz/strsim-rs) | 0.11.1 | MIT |\n| [subtle](https://github.com/dalek-cryptography/subtle) | 2.6.1 | BSD-3-Clause |\n| [symlink](https://gitlab.com/chris-morgan/symlink) | 0.1.0 | MIT OR Apache-2.0 |\n| [syn](https://github.com/dtolnay/syn) | 2.0.117 | MIT OR Apache-2.0 |\n| [sync_wrapper](https://github.com/Actyx/sync_wrapper) | 1.0.2 | Apache-2.0 |\n| [synstructure](https://github.com/mystor/synstructure) | 0.13.2 | MIT |\n| [tao](https://github.com/tauri-apps/tao) | 0.35.3 | Apache-2.0 |\n| [tao-macros](https://github.com/tauri-apps/tao) | 0.1.3 | MIT OR Apache-2.0 |\n| [tauri](https://github.com/tauri-apps/tauri) | 2.11.2 | Apache-2.0 OR MIT |\n| [tauri-codegen](https://github.com/tauri-apps/tauri) | 2.6.2 | Apache-2.0 OR MIT |\n| [tauri-macros](https://github.com/tauri-apps/tauri) | 2.6.2 | Apache-2.0 OR MIT |\n| [tauri-runtime](https://github.com/tauri-apps/tauri) | 2.11.2 | Apache-2.0 OR MIT |\n| [tauri-runtime-wry](https://github.com/tauri-apps/tauri) | 2.11.2 | Apache-2.0 OR MIT |\n| [tauri-utils](https://github.com/tauri-apps/tauri) | 2.9.2 | Apache-2.0 OR MIT |\n| [tempfile](https://github.com/Stebalien/tempfile) | 3.27.0 | MIT OR Apache-2.0 |\n| [tendril](https://github.com/servo/html5ever) | 0.5.0 | MIT OR Apache-2.0 |\n| [thiserror](https://github.com/dtolnay/thiserror) | 1.0.69 | MIT OR Apache-2.0 |\n| [thiserror](https://github.com/dtolnay/thiserror) | 2.0.18 | MIT OR Apache-2.0 |\n| [thiserror-impl](https://github.com/dtolnay/thiserror) | 1.0.69 | MIT OR Apache-2.0 |\n| [thiserror-impl](https://github.com/dtolnay/thiserror) | 2.0.18 | MIT OR Apache-2.0 |\n| [thread_local](https://github.com/Amanieu/thread_local-rs) | 1.1.9 | MIT OR Apache-2.0 |\n| [time](https://github.com/time-rs/time) | 0.3.47 | MIT OR Apache-2.0 |\n| [time-core](https://github.com/time-rs/time) | 0.1.8 | MIT OR Apache-2.0 |\n| [time-macros](https://github.com/time-rs/time) | 0.2.27 | MIT OR Apache-2.0 |\n| [tinystr](https://github.com/unicode-org/icu4x) | 0.8.3 | Unicode-3.0 |\n| [tokio](https://github.com/tokio-rs/tokio) | 1.52.3 | MIT |\n| [tokio-macros](https://github.com/tokio-rs/tokio) | 2.7.0 | MIT |\n| [tokio-util](https://github.com/tokio-rs/tokio) | 0.7.18 | MIT |\n| [toml](https://github.com/toml-rs/toml) | 1.1.2+spec-1.1.0 | MIT OR Apache-2.0 |\n| [toml_datetime](https://github.com/toml-rs/toml) | 1.1.1+spec-1.1.0 | MIT OR Apache-2.0 |\n| [toml_edit](https://github.com/toml-rs/toml) | 0.25.12+spec-1.1.0 | MIT OR Apache-2.0 |\n| [toml_parser](https://github.com/toml-rs/toml) | 1.1.2+spec-1.1.0 | MIT OR Apache-2.0 |\n| [toml_writer](https://github.com/toml-rs/toml) | 1.1.1+spec-1.1.0 | MIT OR Apache-2.0 |\n| [tower](https://github.com/tower-rs/tower) | 0.5.3 | MIT |\n| [tower-http](https://github.com/tower-rs/tower-http) | 0.6.11 | MIT |\n| [tower-layer](https://github.com/tower-rs/tower) | 0.3.3 | MIT |\n| [tower-service](https://github.com/tower-rs/tower) | 0.3.3 | MIT |\n| [tracing](https://github.com/tokio-rs/tracing) | 0.1.44 | MIT |\n| [tracing-appender](https://github.com/tokio-rs/tracing) | 0.2.5 | MIT |\n| [tracing-attributes](https://github.com/tokio-rs/tracing) | 0.1.31 | MIT |\n| [tracing-core](https://github.com/tokio-rs/tracing) | 0.1.36 | MIT |\n| [tracing-log](https://github.com/tokio-rs/tracing) | 0.2.0 | MIT |\n| [tracing-subscriber](https://github.com/tokio-rs/tracing) | 0.3.23 | MIT |\n| [try-lock](https://github.com/seanmonstar/try-lock) | 0.2.5 | MIT |\n| [typeid](https://github.com/dtolnay/typeid) | 1.0.3 | MIT OR Apache-2.0 |\n| [typenum](https://github.com/paholg/typenum) | 1.20.0 | MIT OR Apache-2.0 |\n| [unic-char-property](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unic-char-range](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unic-common](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unic-ucd-ident](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unic-ucd-version](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unicode-ident](https://github.com/dtolnay/unicode-ident) | 1.0.24 | (MIT OR Apache-2.0) AND Unicode-3.0 |\n| [universal-hash](https://github.com/RustCrypto/traits) | 0.5.1 | MIT OR Apache-2.0 |\n| [untrusted](https://github.com/briansmith/untrusted) | 0.9.0 | ISC |\n| [url](https://github.com/servo/rust-url) | 2.5.8 | MIT OR Apache-2.0 |\n| [urlpattern](https://github.com/denoland/rust-urlpattern) | 0.3.0 | MIT |\n| [utf-8](https://github.com/SimonSapin/rust-utf8) | 0.7.6 | MIT OR Apache-2.0 |\n| [utf8_iter](https://github.com/hsivonen/utf8_iter) | 1.0.4 | Apache-2.0 OR MIT |\n| [uuid](https://github.com/uuid-rs/uuid) | 1.23.1 | Apache-2.0 OR MIT |\n| [walkdir](https://github.com/BurntSushi/walkdir) | 2.5.0 | Unlicense OR MIT |\n| [want](https://github.com/seanmonstar/want) | 0.3.1 | MIT |\n| [web_atoms](https://github.com/servo/html5ever) | 0.2.4 | MIT OR Apache-2.0 |\n| [winnow](https://github.com/winnow-rs/winnow) | 1.0.3 | MIT |\n| [writeable](https://github.com/unicode-org/icu4x) | 0.6.3 | Unicode-3.0 |\n| [wry](https://github.com/tauri-apps/wry) | 0.55.1 | Apache-2.0 OR MIT |\n| [yasna](https://github.com/qnighy/yasna.rs) | 0.6.0 | MIT OR Apache-2.0 |\n| [yoke](https://github.com/unicode-org/icu4x) | 0.8.2 | Unicode-3.0 |\n| [yoke-derive](https://github.com/unicode-org/icu4x) | 0.8.2 | Unicode-3.0 |\n| [zerocopy](https://github.com/google/zerocopy) | 0.8.49 | BSD-2-Clause OR Apache-2.0 OR MIT |\n| [zerofrom](https://github.com/unicode-org/icu4x) | 0.1.8 | Unicode-3.0 |\n| [zerofrom-derive](https://github.com/unicode-org/icu4x) | 0.1.7 | Unicode-3.0 |\n| [zeroize](https://github.com/RustCrypto/utils) | 1.8.2 | Apache-2.0 OR MIT |\n| [zerotrie](https://github.com/unicode-org/icu4x) | 0.2.4 | Unicode-3.0 |\n| [zerovec](https://github.com/unicode-org/icu4x) | 0.11.6 | Unicode-3.0 |\n| [zerovec-derive](https://github.com/unicode-org/icu4x) | 0.11.3 | Unicode-3.0 |\n| [zmij](https://github.com/dtolnay/zmij) | 1.0.21 | MIT |\n\n## License texts\n\n### Apache-2.0\n\nUsed by: ring 0.17.14\n\n```\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n\nLicenses for support code\n-------------------------\n\nParts of the TLS test suite are under the Go license. This code is not included\nin BoringSSL (i.e. libcrypto and libssl) when compiled, however, so\ndistributing code linked against BoringSSL does not trigger this license:\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\nBoringSSL uses the Chromium test infrastructure to run a continuous build,\ntrybots etc. The scripts which manage this, and the script for generating build\nmetadata, are under the Chromium license. Distributing code linked against\nBoringSSL does not trigger this license.\n\nCopyright 2015 The Chromium Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n```\n\nUsed by: moxcms 0.8.1, pxfm 0.1.29\n\n```\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2024 Radzivon Bartoshyk\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n```\n\nUsed by: dpi 0.1.2, tao 0.35.3\n\n```\nApache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n```\n\nUsed by: ring 0.17.14\n\n```\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\nUsed by: dunce 1.0.5, sync_wrapper 1.0.2\n\n```\nApache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\n (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.\n\n You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\n### BSD-3-Clause\n\nUsed by: subtle 2.6.1\n\n```\nCopyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved.\nCopyright (c) 2016-2024 Isis Agora Lovecruft. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n1. Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n```\n\nUsed by: ed25519-dalek 2.2.0\n\n```\nCopyright (c) 2017-2019 isis agora lovecruft. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n1. Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n```\n\nUsed by: alloc-no-stdlib 2.0.4\n\n```\nCopyright (c) 2016 Dropbox, Inc.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n```\n\nUsed by: alloc-stdlib 0.2.2, brotli 8.0.2, curve25519-dalek 4.1.3\n\n```\nCopyright (c) . \n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n```\n\n### ISC\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2015-2022 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nuse crate::limb::Limb;\n\n#[derive(Clone, Copy)]\n#[repr(transparent)]\npub struct N0([Limb; 2]);\n\nimpl N0 {\n #[cfg(feature = \"alloc\")]\n pub(super) const LIMBS_USED: usize = 64 / crate::limb::LIMB_BITS;\n\n #[inline]\n pub const fn precalculated(n0: u64) -> Self {\n #[cfg(target_pointer_width = \"64\")]\n {\n Self([n0, 0])\n }\n\n #[cfg(target_pointer_width = \"32\")]\n {\n Self([n0 as Limb, (n0 >> crate::limb::LIMB_BITS) as Limb])\n }\n }\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2016-2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nuse crate::error::{KeyRejected, Unspecified};\n\nimpl From for Unspecified {\n fn from(source: untrusted::EndOfInput) -> Self {\n super::erase(source)\n }\n}\n\nimpl From for Unspecified {\n fn from(source: core::array::TryFromSliceError) -> Self {\n super::erase(source)\n }\n}\n\nimpl From for Unspecified {\n fn from(source: KeyRejected) -> Self {\n super::erase(source)\n }\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2019-2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nuse super::BlockLen;\n\npub(super) use self::{\n sha2_32::{block_data_order_32, State32, SHA256_BLOCK_LEN},\n sha2_64::{block_data_order_64, State64, SHA512_BLOCK_LEN},\n};\n\npub(super) const CHAINING_WORDS: usize = 8;\n\n#[cfg(any(\n all(target_arch = \"aarch64\", target_endian = \"little\"),\n all(target_arch = \"arm\", target_endian = \"little\"),\n target_arch = \"x86_64\"\n))]\n#[macro_use]\nmod ffi;\n\npub(super) mod fallback;\nmod sha2_32;\nmod sha2_64;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2015-2016 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n//! EdDSA Signatures.\n\nuse super::ops::ELEM_LEN;\nuse crate::digest;\n\npub mod signing;\npub mod verification;\n\n/// The length of an Ed25519 public key.\npub const ED25519_PUBLIC_KEY_LEN: usize = ELEM_LEN;\n\npub fn eddsa_digest(signature_r: &[u8], public_key: &[u8], msg: &[u8]) -> digest::Digest {\n let mut ctx = digest::Context::new(&digest::SHA512);\n ctx.update(signature_r);\n ctx.update(public_key);\n ctx.update(msg);\n ctx.finish()\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n/* Copyright (c) 2014, Intel Corporation.\n *\n * Permission to use, copy, modify, and/or distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */\n\n#ifndef OPENSSL_HEADER_EC_ECP_NISTZ384_H\n#define OPENSSL_HEADER_EC_ECP_NISTZ384_H\n\n#include \"../../limbs/limbs.h\"\n\n#define P384_LIMBS (384u / LIMB_BITS)\n\ntypedef struct {\n Limb X[P384_LIMBS];\n Limb Y[P384_LIMBS];\n Limb Z[P384_LIMBS];\n} P384_POINT;\n\ntypedef struct {\n Limb X[P384_LIMBS];\n Limb Y[P384_LIMBS];\n} P384_POINT_AFFINE;\n\n\n#endif // OPENSSL_HEADER_EC_ECP_NISTZ384_H\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n// TODO(MSRV 1.76): Replace with `core::ptr::from_mut`.\n#[allow(dead_code)]\n#[inline(always)]\npub fn from_mut(r: &mut T) -> *mut T {\n r\n}\n\n// TODO(MSRV 1.76): Replace with `core::ptr::from_ref`.\n#[allow(dead_code)]\n#[inline(always)]\npub const fn from_ref(r: &T) -> *const T {\n r\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2015-2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nuse crate::{bb, error};\n\n#[deprecated(\n note = \"To be removed. Internal function not intended for external use with no promises regarding side channels.\"\n)]\npub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<(), error::Unspecified> {\n bb::verify_slices_are_equal(a, b)\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2018 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n//! Serialization and deserialization.\n\n#[doc(hidden)]\npub mod der;\n\n#[cfg(feature = \"alloc\")]\nmod writer;\n\n#[cfg(feature = \"alloc\")]\npub(crate) mod der_writer;\n\npub(crate) mod positive;\n\npub use self::positive::Positive;\n\n#[cfg(feature = \"alloc\")]\npub(crate) use self::writer::TooLongError;\n```\n\nUsed by: rustls-webpki 0.103.13\n\n```\nExcept as otherwise noted, this project is licensed under the following\n(ISC-style) terms:\n\nCopyright 2015 Brian Smith.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHORS DISCLAIM ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nThe files under third-party/chromium are licensed as described in\nthird-party/chromium/LICENSE.\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\npub use self::{\n array::Array,\n base::{IndexError, Overlapping},\n partial_block::PartialBlock,\n};\n\nmod array;\nmod base;\nmod partial_block;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2016 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n//! Elliptic curve operations and schemes using Curve25519.\n\npub mod ed25519;\n\npub mod x25519;\n\nmod ops;\nmod scalar;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n#![cfg(all(target_arch = \"aarch64\", target_endian = \"little\"))]\n\npub(in super::super) mod mont;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nmod storage;\n\npub(super) use self::storage::{AlignedStorage, LIMBS_PER_CHUNK};\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n#![cfg(target_arch = \"x86_64\")]\n\npub(in super::super::super) mod mont;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n//! Integration tests for non-public APIs.\n\nmod bits_tests;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\npub(super) mod aarch64;\npub(super) mod x86_64;\n```\n\nUsed by: untrusted 0.9.0\n\n```\n// Copyright 2015-2016 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHORS DISCLAIM ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR\n// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n```\n\nUsed by: ring 0.17.14\n\n```\nCopyright 2015-2025 Brian Smith.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\nSPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\nOF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\nCONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n```\n\n### MIT\n\nUsed by: schemars_derive 0.8.22\n\n```\n#![allow(clippy::all)]\n// Copied from regex_syntax crate to avoid pulling in the whole crate just for a utility function\n// https://github.com/rust-lang/regex/blob/431c4e4867e1eb33eb39b23ed47c9934b2672f8f/regex-syntax/src/lib.rs\n//\n// Copyright (c) 2014 The Rust Project Developers\n//\n// Permission is hereby granted, free of charge, to any\n// person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the\n// Software without restriction, including without\n// limitation the rights to use, copy, modify, merge,\n// publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software\n// is furnished to do so, subject to the following\n// conditions:\n//\n// The above copyright notice and this permission notice\n// shall be included in all copies or substantial portions\n// of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\n// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\n// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\n// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\n// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n// DEALINGS IN THE SOFTWARE.\n\npub fn escape(text: &str) -> String {\n let mut quoted = String::new();\n escape_into(text, &mut quoted);\n quoted\n}\n\nfn escape_into(text: &str, buf: &mut String) {\n buf.reserve(text.len());\n for c in text.chars() {\n if is_meta_character(c) {\n buf.push('\\\\');\n }\n buf.push(c);\n }\n}\n\nfn is_meta_character(c: char) -> bool {\n match c {\n '\\\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$'\n | '#' | '&' | '-' | '~' => true,\n _ => false,\n }\n}\n```\n\nUsed by: atomic-waker 1.1.2\n\n```\n===============================================================================\n\nCopyright (c) 2016 Alex Crichton\nCopyright (c) 2017 The Tokio Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n===============================================================================\n\nCopyright (c) 2016 Alex Crichton\nCopyright (c) 2017 The Tokio Authors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: dom_query 0.27.0\n\n```\nMIT License\n\nCopyright (c) 2023 Mykola Humanov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n---\n\nThis project contains portions of code and architectural concepts originally \nderived from the \"nipper\" project (https://github.com/importcjj/nipper), \ndeveloped by Chen Jiaju, licensed under the MIT License and the Apache License 2.0 (dual licensed).\n```\n\nUsed by: miniz_oxide 0.8.9\n\n```\nMIT License\n\nCopyright 2013-2014 RAD Game Tools and Valve Software\nCopyright 2010-2014 Rich Geldreich and Tenacious Software LLC\nCopyright (c) 2017 Frommi\nCopyright (c) 2017-2024 oyvindln\n\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: miniz_oxide 0.8.9\n\n```\nMIT License\n\nCopyright 2013-2014 RAD Game Tools and Valve Software\nCopyright 2010-2014 Rich Geldreich and Tenacious Software LLC\nCopyright (c) 2017 Frommi\nCopyright (c) 2017-2024 oyvindln\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: strsim 0.11.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2015 Danny Guo\nCopyright (c) 2016 Titus Wormer \nCopyright (c) 2018 Akash Kurdekar\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: uuid 1.23.1\n\n```\nCopyright (c) 2014 The Rust Project Developers\nCopyright (c) 2018 Ashley Mannix, Christopher Armstrong, Dylan DPC, Hunar Roop Kahlon\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: nu-ansi-term 0.50.3\n\n```\nThe MIT License (MIT)\r\n\r\nCopyright (c) 2014 Benjamin Sago\r\nCopyright (c) 2021-2022 The Nushell Project Developers\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.\r\n```\n\nUsed by: base64ct 1.8.3\n\n```\nCopyright (c) 2014 Steve \"Sc00bz\" Thomas (steve at tobtu dot com)\nCopyright (c) 2021-2025 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: sha1 0.10.6, sha2 0.10.9\n\n```\nCopyright (c) 2006-2009 Graydon Hoare\nCopyright (c) 2009-2013 Mozilla Foundation\nCopyright (c) 2016 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: getrandom 0.2.17\n\n```\nCopyright (c) 2018-2024 The rust-random Project Developers\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: getrandom 0.3.4\n\n```\nCopyright (c) 2018-2025 The rust-random Project Developers\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: getrandom 0.4.2\n\n```\nCopyright (c) 2018-2026 The rust-random Project Developers\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: wry 0.55.1\n\n```\nMIT License\n\nCopyright (c) 2020-2023 Ngo Iok Ui & Tauri Programme within The Commons Conservancy\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: rand 0.10.2, rand 0.8.6, rand_chacha 0.3.1, rand_core 0.6.4\n\n```\nCopyright 2018 Developers of the Rand project\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: aead 0.5.2\n\n```\nCopyright (c) 2019 The RustCrypto Project Developers\nCopyright (c) 2019 MobileCoin, LLC\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: inout 0.1.4\n\n```\nCopyright (c) 2022 The RustCrypto Project Developers\nCopyright (c) 2022 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: bytemuck 1.25.0\n\n```\nMIT License\n\nCopyright (c) 2019 Daniel \"Lokathor\" Gee.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: hkdf 0.12.4\n\n```\nCopyright (c) 2015-2018 Vlad Filippov\nCopyright (c) 2018-2021 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: generic-array 0.14.7\n\n```\nThe MIT License (MIT)\r\n\r\nCopyright (c) 2015 Bart\u0142omiej Kami\u0144ski\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.\n```\n\nUsed by: quick-xml 0.39.4\n\n```\nThe MIT License (MIT)\r\n\r\nCopyright (c) 2016 Johann Tuffe\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\n\r\nThe above copyright notice and this permission notice shall be included in\r\nall copies or substantial portions of the Software.\r\n\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\nTHE SOFTWARE.\r\n```\n\nUsed by: jni 0.21.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2016 Prevoty, Inc. and jni-rs contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: ctr 0.9.2\n\n```\nCopyright (c) 2018-2022 RustCrypto Developers\nCopyright (c) 2018 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: tokio-macros 2.7.0\n\n```\nMIT License\n\nCopyright (c) 2019 Yoshua Wuyts\nCopyright (c) Tokio Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: phf 0.13.1, phf_generator 0.13.1, phf_macros 0.13.1, phf_shared 0.13.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2014-2022 Steven Fackler, Yuki Okushi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: crossbeam-channel 0.5.15, crossbeam-utils 0.8.21\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2019 The Crossbeam Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: scopeguard 1.2.0\n\n```\nCopyright (c) 2016-2019 Ulrik Sverdrup \"bluss\" and scopeguard developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: crc32fast 1.5.0\n\n```\nMIT License\n\nCopyright (c) 2018 Sam Rijs, Alex Crichton and contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: try-lock 0.2.5\n\n```\nCopyright (c) 2018-2023 Sean McArthur\nCopyright (c) 2016 Alex Crichton\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: zeroize 1.8.2\n\n```\nMIT License\n\nCopyright (c) 2018-2021 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: futures-channel 0.3.32, futures-core 0.3.32, futures-sink 0.3.32, futures-task 0.3.32, futures-util 0.3.32\n\n```\nCopyright (c) 2016 Alex Crichton\nCopyright (c) 2017 The Tokio Authors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: cookie 0.18.1\n\n```\nCopyright (c) 2017 Sergio Benitez\nCopyright (c) 2014 Alex Crichton\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: symlink 0.1.0\n\n```\nCopyright (c) 2014 Chris Morgan and the Teepee project developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: spin 0.9.8, tracing-core 0.1.36\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2014 Mathijs van de Nes\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: pem 3.0.6\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2016 Jonathan Creekmore\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: brotli 8.0.2\n\n```\nCopyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: combine 4.6.7\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2015 Markus Westerlind\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: http-body 1.0.1\n\n```\nCopyright (c) 2019-2024 Sean McArthur & Hyper Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: http-body-util 0.1.3\n\n```\nCopyright (c) 2019-2025 Sean McArthur & Hyper Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: typenum 1.20.0\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2014 Paho Lurie-Gregg\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: mio 1.2.0\n\n```\nCopyright (c) 2014 Carl Lerche and other MIO contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: rustls 0.23.40\n\n```\nCopyright (c) 2016 Joseph Birr-Pixton \n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: block-buffer 0.10.4\n\n```\nCopyright (c) 2018-2019 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: opaque-debug 0.3.1\n\n```\nCopyright (c) 2018-2024 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: chacha20 0.10.1\n\n```\nCopyright (c) 2019-2026 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: const-oid 0.9.6\n\n```\nCopyright (c) 2020-2022 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: der 0.7.10, pkcs8 0.10.2\n\n```\nCopyright (c) 2020-2023 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: cpufeatures 0.2.17, cpufeatures 0.3.0\n\n```\nCopyright (c) 2020-2025 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: pkcs1 0.7.5, spki 0.7.3\n\n```\nCopyright (c) 2021-2023 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: aho-corasick 1.1.4, byteorder 1.5.0, byteorder-lite 0.1.0, memchr 2.8.1, walkdir 2.5.0\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2015 Andrew Gallant\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: same-file 1.0.6\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2017 Andrew Gallant\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: rustls-pki-types 1.14.1\n\n```\nCopyright (c) 2023 Dirkjan Ochtman \n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: android_system_properties 0.1.5\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2013 Nicolas Silva\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: derive_more 2.1.1, derive_more-impl 2.1.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2016 Jelte Fennema\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: brotli-decompressor 5.0.0, cesu8 1.1.0, chrono 0.4.44, dpi 0.1.2, jni-sys-macros 0.4.1, libm 0.2.16, ndk 0.9.0, ndk-sys 0.6.0+11769913, quick-protobuf 0.8.1, rcgen 0.14.8, siphasher 1.0.3, tao-macros 0.1.3, tauri 2.11.2, tauri-codegen 2.6.2, tauri-macros 2.6.2, tauri-runtime 2.11.2, tauri-runtime-wry 2.11.2, tauri-utils 2.9.2, unic-char-property 0.9.0, unic-char-range 0.9.0, unic-common 0.9.0, unic-ucd-ident 0.9.0, unic-ucd-version 0.9.0\n\n```\nMIT License\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and\nassociated documentation files (the \"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial\nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO\nEVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: simd-adler32 0.3.9\n\n```\nMIT License\n\nCopyright (c) [2021] [Marvin Countryman]\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: aes-gcm 0.10.3\n\n```\nCopyright (c) 2019 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: pem-rfc7468 0.7.0\n\n```\nCopyright (c) 2021 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: precomputed-hash 0.1.1\n\n```\nMIT License\n\nCopyright (c) 2017 Emilio Cobos \u00c1lvarez\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: html5ever 0.38.0, markup5ever 0.38.0, web_atoms 0.2.4\n\n```\nCopyright (c) 2014 The html5ever Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: rand_core 0.10.1\n\n```\nCopyright (c) 2018-2026 The Rand Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ppv-lite86 0.2.21\n\n```\nCopyright (c) 2019 The CryptoCorrosion Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: adb_client 3.2.2\n\n```\nMIT License\n\nCopyright (c) 2023-2024 Corentin LIAUD\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: base64 0.22.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2015 Alice Maz\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: cfb 0.7.3\n\n```\nMIT License\n\nCopyright (c) 2017 Matthew D. Steele\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: ico 0.5.0\n\n```\nMIT License\n\nCopyright (c) 2018 Matthew D. Steele\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: urlpattern 0.3.0\n\n```\nMIT License\n\nCopyright (c) 2021 the Deno authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: form_urlencoded 1.2.2\n\n```\nCopyright (c) 2013-2016 The rust-url developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: idna 1.1.0, percent-encoding 2.3.2, url 2.5.8\n\n```\nCopyright (c) 2013-2025 The rust-url developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: smallvec 1.15.1\n\n```\nCopyright (c) 2018 The Servo Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: lazy_static 1.5.0\n\n```\nCopyright (c) 2010 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: bitflags 1.3.2, bitflags 2.11.1, glob 0.3.3, log 0.4.30, num-bigint-dig 0.8.6, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, regex 1.12.3, regex-automata 0.4.14, regex-syntax 0.8.10\n\n```\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: heck 0.5.0\n\n```\nCopyright (c) 2015 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: jni-sys 0.3.1, jni-sys 0.4.1\n\n```\nCopyright (c) 2015 The rust-jni-sys Developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: lock_api 0.4.14, parking_lot 0.12.5, parking_lot_core 0.9.12, thread_local 1.1.9\n\n```\nCopyright (c) 2016 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: bit-set 0.8.0, bit-vec 0.8.0\n\n```\nCopyright (c) 2023 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: jsonptr 0.6.3\n\n```\nMIT License\n\nCopyright (c) 2022 Chance Dinkins\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: cipher 0.4.4\n\n```\nCopyright (c) 2016-2020 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ed25519 2.2.3, signature 2.2.0\n\n```\nCopyright (c) 2018-2023 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: universal-hash 0.5.1\n\n```\nCopyright (c) 2019-2020 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: polyval 0.6.2\n\n```\nCopyright (c) 2019-2023 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: tokio 1.52.3, tokio-util 0.7.18\n\n```\nMIT License\n\nCopyright (c) Tokio Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: dirs 5.0.1, dirs 6.0.0, dirs-sys 0.4.1, dirs-sys 0.5.0\n\n```\nCopyright (c) 2018-2019 dirs-rs contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: signal-hook-registry 1.4.8\n\n```\nCopyright (c) 2017 tokio-jsonrpc developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: json-patch 3.0.1\n\n```\nMIT License\n\nCopyright (c) 2017 Ivan Dubrov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: schemars 0.8.22, schemars_derive 0.8.22\n\n```\nMIT License\n\nCopyright (c) 2019 Graham Esau\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: string_cache 0.9.0\n\n```\nCopyright (c) 2012-2013 Mozilla Foundation\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: tower-http 0.6.11\n\n```\nCopyright (c) 2019-2021 Tower Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: darling 0.23.0, darling_core 0.23.0, darling_macro 0.23.0\n\n```\nMIT License\n\nCopyright (c) 2017 Ted Driggs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: libc 0.2.186\n\n```\nCopyright (c) The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: serialize-to-javascript 0.1.2, serialize-to-javascript-impl 0.1.2\n\n```\nMIT License\n\nCopyright (c) 2021 Chip Reed\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: ghash 0.5.1\n\n```\nCopyright (c) 2019 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: crypto-common 0.1.7\n\n```\nCopyright (c) 2021 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: raw-window-handle 0.6.2\n\n```\nMIT License\n\nCopyright (c) 2019 Osspial\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: flate2 1.1.9\n\n```\nCopyright (c) 2014-2026 Alex Crichton\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: hyper 1.10.0\n\n```\nCopyright (c) 2014-2026 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: httparse 1.10.1\n\n```\nCopyright (c) 2015-2025 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: reqwest 0.13.4\n\n```\nCopyright (c) 2016-2026 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: want 0.3.1\n\n```\nCopyright (c) 2018-2019 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: tracing 0.1.44, tracing-appender 0.2.5, tracing-attributes 0.1.31, tracing-core 0.1.36, tracing-log 0.2.0, tracing-subscriber 0.3.23\n\n```\nCopyright (c) 2019 Tokio Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: tower 0.5.3, tower-layer 0.3.3, tower-service 0.3.3\n\n```\nCopyright (c) 2019 Tower Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: powerfmt 0.2.0\n\n```\nCopyright (c) 2023 Jacob Pratt et al.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: hyper-util 0.1.20\n\n```\nCopyright (c) 2023-2025 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: deranged 0.5.8\n\n```\nCopyright (c) 2024 Jacob Pratt et al.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: serde_spanned 1.1.1, toml 1.1.2+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_writer 1.1.1+spec-1.1.0\n\n```\nCopyright (c) Individual contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: idna_adapter 1.2.2\n\n```\nCopyright (c) The rust-url developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ipnet 2.12.0\n\n```\nCopyright 2017 Juniper Networks, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: infer 0.19.0\n\n```\nMIT License\n\nCopyright (c) 2019 Bojan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: tendril 0.5.0\n\n```\nCopyright (c) 2015 Keegan McAllister\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: hashbrown 0.17.1\n\n```\nCopyright (c) 2016 Amanieu d'Antras\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: http 1.4.1\n\n```\nCopyright (c) 2017 http-rs authors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: iana-time-zone 0.1.65\n\n```\nCopyright (c) 2020 Andrew D. Straw\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: zerocopy 0.8.49\n\n```\nCopyright 2023 The Fuchsia Authors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: plist 1.9.0\n\n```\nCopyright (c) 2015 Edward Barnard\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: cfg-if 1.0.4, socket2 0.6.3\n\n```\nCopyright (c) 2014 Alex Crichton\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: mime 0.3.17\n\n```\nCopyright (c) 2014 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: new_debug_unreachable 1.0.6\n\n```\nCopyright (c) 2015 Jonathan Reem\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: digest 0.10.7, hmac 0.12.1\n\n```\nCopyright (c) 2017 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: stable_deref_trait 1.2.1\n\n```\nCopyright (c) 2017 Robert Grosse\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: aes 0.8.4\n\n```\nCopyright (c) 2018 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: sharded-slab 0.1.7\n\n```\nCopyright (c) 2019 Eliza Weisman\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: matchers 0.2.0\n\n```\nCopyright (c) 2019 Eliza Weisman\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: time 0.3.47, time-core 0.1.8, time-macros 0.2.27\n\n```\nCopyright (c) Jacob Pratt et al.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: tempfile 3.27.0\n\n```\nCopyright (c) 2015 Steven Allen\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: fnv 1.0.7\n\n```\nCopyright (c) 2017 Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: yasna 0.6.0\n\n```\nCopyright (c) 2016 Masaki Hara\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: bytes 1.11.1\n\n```\nCopyright (c) 2018 Carl Lerche\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: slab 0.4.12\n\n```\nCopyright (c) 2019 Carl Lerche\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: errno 0.3.14\n\n```\nCopyright (c) 2014 Chris Wong\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: utf8_iter 1.0.4\n\n```\nCopyright Mozilla Foundation\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: synstructure 0.13.2\n\n```\nCopyright 2016 Nika Layzell\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: num-conv 0.2.2\n\n```\nCopyright (c) Jacob Pratt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: indexmap 2.14.0\n\n```\nCopyright (c) 2016--2017\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: equivalent 1.0.2\n\n```\nCopyright (c) 2016--2023\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: png 0.17.16, png 0.18.1\n\n```\nCopyright (c) 2015 nwin\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: serde_with 3.20.0, serde_with_macros 3.20.0\n\n```\nCopyright (c) 2015\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: fdeflate 0.3.7, image 0.25.10\n\n```\nMIT License\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ident_case 1.0.1\n\n```\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: adler2 2.0.1, anyhow 1.0.102, async-trait 0.1.89, atomic-waker 1.1.2, camino 1.2.2, cargo-platform 0.1.9, cargo_metadata 0.19.2, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, dtoa 1.0.11, dyn-clone 1.0.20, erased-serde 0.4.10, fastrand 2.4.1, itoa 1.0.18, linux-raw-sys 0.12.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, pin-project-lite 0.2.17, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.45, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde-untagged 0.1.9, serde_core 1.0.228, serde_derive 1.0.228, serde_derive_internals 0.29.1, serde_json 1.0.150, serde_repr 0.1.20, servo_arc 0.4.3, syn 2.0.117, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, typeid 1.0.3, unicode-ident 1.0.24, utf-8 0.7.6, zmij 1.0.21\n\n```\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: winnow 1.0.3\n\n```\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ctor 0.8.0, ctor-proc-macro 0.0.7\n\n```\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\n### MPL-2.0\n\nUsed by: option-ext 0.2.0, selectors 0.36.1\n\n```\nMozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in \n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party's negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n```\n\nUsed by: cssparser 0.36.0, cssparser-macros 0.6.1\n\n```\nMozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in \n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party's negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n```\n\nUsed by: dtoa-short 0.3.5\n\n```\nMozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in\n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party's negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n```\n\n### Unicode-3.0\n\nUsed by: icu_collections 2.2.0, icu_locale_core 2.2.0, icu_normalizer 2.2.0, icu_normalizer_data 2.2.0, icu_properties 2.2.0, icu_properties_data 2.2.0, icu_provider 2.2.0, litemap 0.8.2, potential_utf 0.1.5, tinystr 0.8.3, writeable 0.6.3, yoke 0.8.2, yoke-derive 0.8.2, zerofrom 0.1.8, zerofrom-derive 0.1.7, zerotrie 0.2.4, zerovec 0.11.6, zerovec-derive 0.11.3\n\n```\nUNICODE LICENSE V3\n\nCOPYRIGHT AND PERMISSION NOTICE\n\nCopyright \u00a9 2020-2024 Unicode, Inc.\n\nNOTICE TO USER: Carefully read the following legal agreement. BY\nDOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR\nSOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE\nTERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT\nDOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of data files and any associated documentation (the \"Data Files\") or\nsoftware and any associated documentation (the \"Software\") to deal in the\nData Files or Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, and/or sell\ncopies of the Data Files or Software, and to permit persons to whom the\nData Files or Software are furnished to do so, provided that either (a)\nthis copyright and permission notice appear with all copies of the Data\nFiles or Software, or (b) this copyright and permission notice appear in\nassociated Documentation.\n\nTHE DATA FILES AND SOFTWARE ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF\nTHIRD PARTY RIGHTS.\n\nIN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE\nBE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,\nOR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA\nFILES OR SOFTWARE.\n\nExcept as contained in this notice, the name of a copyright holder shall\nnot be used in advertising or otherwise to promote the sale, use or other\ndealings in these Data Files or Software without prior written\nauthorization of the copyright holder.\n\nSPDX-License-Identifier: Unicode-3.0\n\n\u2014\n\nPortions of ICU4X may have been adapted from ICU4C and/or ICU4J.\nICU 1.8.1 to ICU 57.1 \u00a9 1995-2016 International Business Machines Corporation and others.\n```\n\nUsed by: unicode-ident 1.0.24\n\n```\nUNICODE LICENSE V3\n\nCOPYRIGHT AND PERMISSION NOTICE\n\nCopyright \u00a9 1991-2023 Unicode, Inc.\n\nNOTICE TO USER: Carefully read the following legal agreement. BY\nDOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR\nSOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE\nTERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT\nDOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of data files and any associated documentation (the \"Data Files\") or\nsoftware and any associated documentation (the \"Software\") to deal in the\nData Files or Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, and/or sell\ncopies of the Data Files or Software, and to permit persons to whom the\nData Files or Software are furnished to do so, provided that either (a)\nthis copyright and permission notice appear with all copies of the Data\nFiles or Software, or (b) this copyright and permission notice appear in\nassociated Documentation.\n\nTHE DATA FILES AND SOFTWARE ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF\nTHIRD PARTY RIGHTS.\n\nIN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE\nBE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,\nOR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA\nFILES OR SOFTWARE.\n\nExcept as contained in this notice, the name of a copyright holder shall\nnot be used in advertising or otherwise to promote the sale, use or other\ndealings in these Data Files or Software without prior written\nauthorization of the copyright holder.\n```\n\n### Zlib\n\nUsed by: foldhash 0.2.0\n\n```\nCopyright (c) 2024 Orson Peters\n\nThis software is provided 'as-is', without any express or implied warranty. In\nno event will the authors be held liable for any damages arising from the use of\nthis software.\n\nPermission is granted to anyone to use this software for any purpose, including\ncommercial applications, and to alter it and redistribute it freely, subject to\nthe following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not claim\n that you wrote the original software. If you use this software in a product,\n an acknowledgment in the product documentation would be appreciated but is\n not required.\n\n2. Altered source versions must be plainly marked as such, and must not be\n misrepresented as being the original software.\n\n3. This notice may not be removed or altered from any source distribution.\n```\n"; +export const THIRD_PARTY_NOTICES: string = "\n# Third-party notices\n\nATV Optimizer for Android bundles the third-party software listed below. Each component is used\nunder the license named with it; the full license texts follow.\n\nRegenerate this document with `mobile/scripts/gen-notices.sh` after any dependency change.\n\n## Components not managed by Cargo\n\n### Android/JVM runtime dependencies\n\nThe application declares AndroidX WebKit 1.14.0, AppCompat 1.7.1, Activity KTX 1.10.1,\nLifecycle Process 2.10.0 and Material Components 1.12.0. The ADB plugin declares AndroidX\nCore KTX 1.13.1 and kotlinx-coroutines-android 1.10.2. Tauri Android declares older Core KTX,\nAppCompat and Material versions plus Jackson Databind 2.15.3.\nThe opener plugin 2.5.4 declares **AndroidX Browser 1.8.0**, Core KTX 1.9.0 and Jackson Databind\n2.15.3. These components use Apache License 2.0 (AndroidX, Material, Jackson) or Apache License\n2.0 (kotlinx.coroutines); retain upstream notices and applicable license texts with the resolved\nrelease inventory. The Apache License 2.0 text appears below.\n\nThese are actual dependency declarations, not a claim of final Gradle resolution. Transitive\nJVM dependencies, conflict-selected versions and their per-artifact NOTICE files must be captured\nfrom the final release runtime classpath before distribution. See `ANDROID-DEPENDENCIES.md`.\n\n\n\n### Geist and Geist Mono \u2014 SIL Open Font License 1.1\n\n`src/fonts/Geist-Variable.woff2` and `src/fonts/GeistMono-Variable.woff2` are the variable-weight\nWOFF2 builds shipped in the npm `geist` package (1.7.2), unmodified.\n\nCopyright (c) 2023 Vercel, in collaboration with basement.studio\n\n
\nSIL Open Font License 1.1 (full text)\n\n```\nCopyright (c) 2023 Vercel, in collaboration with basement.studio\n\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also available with a FAQ at:\nhttp://scripts.sil.org/OFL\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded,\nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION AND CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE.\n```\n\n
\n\n### Material Symbols Rounded \u2014 Apache License 2.0\n\n`src/fonts/material-symbols-rounded.woff2` is a subset of Material Symbols Rounded from the npm\n`material-symbols` package (0.45.5), reduced to the glyphs the app actually references by\n`mobile/scripts/subset-material-symbols.py`. Subsetting removes glyphs; it does not alter the\noutlines that remain.\n\nCopyright Google LLC. Licensed under the Apache License, Version 2.0 \u2014 full text reproduced in the\nApache-2.0 section below.\n\n### scrcpy server 3.1 \u2014 Apache License 2.0\n\n`scrcpy-server-v3.1` is embedded verbatim in the app binary (`src-tauri/src/scrcpy_resource.rs`,\nSHA-256 `958f0944a62f23b1f33a16e9eb14844c1a04b882ca175a738c16d23cb22b86c0`) and pushed to the\nconnected TV to provide the screen-mirroring stream. It is the unmodified release artifact from\nthe scrcpy project.\n\nCopyright (C) 2018 Genymobile, Copyright (C) 2018-2024 Romain Vimont. Licensed under the Apache\nLicense, Version 2.0 \u2014 full text reproduced in the Apache-2.0 section below.\nSource: \n\n### adb_client \u2014 MIT (vendored with modifications)\n\nThe wireless-ADB transport is `adb_client` 3.2.2 by Corentin Liaud, vendored at\n`v2/vendor/adb_client` and pinned through `[patch.crates-io]`. Shield Optimizer's changes are\ndescribed in `v2/vendor/adb_client/SHIELD-OPTIMIZER-PATCH.md`; the crate remains MIT-licensed and\nappears in the Cargo inventory below with its upstream license text.\n\nSource: \n\n### Bundled npm-origin code and assets\n\nThese packages are the sources of bundled code/assets. Geist 1.7.2 is retained as checked-in\nWOFF2 assets with its license; the unused npm package is not needed to build the app.\nOther entries in `package.json` provide tooling, which can also contribute compiled runtime code.\n\n| Package | Version | License |\n| --- | --- | --- |\n| `svelte` | 5.56.4 | MIT |\n| `@tauri-apps/api` | 2.11.1 | Apache-2.0 OR MIT |\n| `geist` (checked-in WOFF2 assets only) | 1.7.2 | SIL OFL 1.1 (see above) |\n| `material-symbols` | 0.45.5 | Apache-2.0 (see above) |\n\nSvelte compiles into the app bundle; `@tauri-apps/api` ships as JavaScript. Both are used under\nthe MIT License, whose text is reproduced in the MIT section below.\n\n## Rust crate dependencies\n\n313 crates are linked into the Android build (build-time and dev-only crates excluded).\n\n| License | Crates |\n| --- | --- |\n| MIT | 276 |\n| Apache-2.0 | 213 |\n| Unicode-3.0 | 19 |\n| BSD-3-Clause | 11 |\n| Unlicense | 6 |\n| MPL-2.0 | 5 |\n| ISC | 4 |\n| Zlib | 4 |\n| LLVM-exception | 2 |\n| 0BSD | 1 |\n| BSD-2-Clause | 1 |\n| CC0-1.0 | 1 |\n| MIT-0 | 1 |\n\nA crate under `A OR B` is counted under both; we distribute under whichever term applies, and the texts below cover every option offered.\n\n| Crate | Version | License |\n| --- | --- | --- |\n| [adb_client](https://github.com/cocool97/adb_client) | 3.2.2 | MIT |\n| [adler2](https://github.com/oyvindln/adler2) | 2.0.1 | 0BSD OR MIT OR Apache-2.0 |\n| [aead](https://github.com/RustCrypto/traits) | 0.5.2 | MIT OR Apache-2.0 |\n| [aes](https://github.com/RustCrypto/block-ciphers) | 0.8.4 | MIT OR Apache-2.0 |\n| [aes-gcm](https://github.com/RustCrypto/AEADs) | 0.10.3 | Apache-2.0 OR MIT |\n| [aho-corasick](https://github.com/BurntSushi/aho-corasick) | 1.1.4 | Unlicense OR MIT |\n| [alloc-no-stdlib](https://github.com/dropbox/rust-alloc-no-stdlib) | 2.0.4 | BSD-3-Clause |\n| [alloc-stdlib](https://github.com/dropbox/rust-alloc-no-stdlib) | 0.2.2 | BSD-3-Clause |\n| [android_system_properties](https://github.com/nical/android_system_properties) | 0.1.5 | MIT OR Apache-2.0 |\n| [anyhow](https://github.com/dtolnay/anyhow) | 1.0.102 | MIT OR Apache-2.0 |\n| [async-trait](https://github.com/dtolnay/async-trait) | 0.1.89 | MIT OR Apache-2.0 |\n| [atomic-waker](https://github.com/smol-rs/atomic-waker) | 1.1.2 | Apache-2.0 OR MIT |\n| [base64](https://github.com/marshallpierce/rust-base64) | 0.22.1 | MIT OR Apache-2.0 |\n| [base64ct](https://github.com/RustCrypto/formats) | 1.8.3 | Apache-2.0 OR MIT |\n| [bit-set](https://github.com/contain-rs/bit-set) | 0.8.0 | Apache-2.0 OR MIT |\n| [bit-vec](https://github.com/contain-rs/bit-vec) | 0.8.0 | Apache-2.0 OR MIT |\n| [bitflags](https://github.com/bitflags/bitflags) | 1.3.2 | MIT OR Apache-2.0 |\n| [bitflags](https://github.com/bitflags/bitflags) | 2.11.1 | MIT OR Apache-2.0 |\n| [block-buffer](https://github.com/RustCrypto/utils) | 0.10.4 | MIT OR Apache-2.0 |\n| [brotli](https://github.com/dropbox/rust-brotli) | 8.0.2 | BSD-3-Clause AND MIT |\n| [brotli-decompressor](https://github.com/dropbox/rust-brotli-decompressor) | 5.0.0 | BSD-3-Clause OR MIT |\n| [bytemuck](https://github.com/Lokathor/bytemuck) | 1.25.0 | Zlib OR Apache-2.0 OR MIT |\n| [byteorder](https://github.com/BurntSushi/byteorder) | 1.5.0 | Unlicense OR MIT |\n| [byteorder-lite](https://github.com/image-rs/byteorder-lite) | 0.1.0 | Unlicense OR MIT |\n| [bytes](https://github.com/tokio-rs/bytes) | 1.11.1 | MIT |\n| [camino](https://github.com/camino-rs/camino) | 1.2.2 | MIT OR Apache-2.0 |\n| [cargo-platform](https://github.com/rust-lang/cargo) | 0.1.9 | MIT OR Apache-2.0 |\n| [cargo_metadata](https://github.com/oli-obk/cargo_metadata) | 0.19.2 | MIT |\n| [cesu8](https://github.com/emk/cesu8-rs) | 1.1.0 | Apache-2.0 OR MIT |\n| [cfb](https://github.com/mdsteele/rust-cfb) | 0.7.3 | MIT |\n| [cfg-if](https://github.com/rust-lang/cfg-if) | 1.0.4 | MIT OR Apache-2.0 |\n| [chacha20](https://github.com/RustCrypto/stream-ciphers) | 0.10.1 | MIT OR Apache-2.0 |\n| [chrono](https://github.com/chronotope/chrono) | 0.4.44 | MIT OR Apache-2.0 |\n| [cipher](https://github.com/RustCrypto/traits) | 0.4.4 | MIT OR Apache-2.0 |\n| [combine](https://github.com/Marwes/combine) | 4.6.7 | MIT |\n| [const-oid](https://github.com/RustCrypto/formats/tree/master/const-oid) | 0.9.6 | Apache-2.0 OR MIT |\n| [cookie](https://github.com/SergioBenitez/cookie-rs) | 0.18.1 | MIT OR Apache-2.0 |\n| [cpufeatures](https://github.com/RustCrypto/utils) | 0.2.17 | MIT OR Apache-2.0 |\n| [cpufeatures](https://github.com/RustCrypto/utils) | 0.3.0 | MIT OR Apache-2.0 |\n| [crc32fast](https://github.com/srijs/rust-crc32fast) | 1.5.0 | MIT OR Apache-2.0 |\n| [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) | 0.5.15 | MIT OR Apache-2.0 |\n| [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) | 0.8.21 | MIT OR Apache-2.0 |\n| [crypto-common](https://github.com/RustCrypto/traits) | 0.1.7 | MIT OR Apache-2.0 |\n| [cssparser](https://github.com/servo/rust-cssparser) | 0.36.0 | MPL-2.0 |\n| [cssparser-macros](https://github.com/servo/rust-cssparser) | 0.6.1 | MPL-2.0 |\n| [ctor](https://github.com/mmastrac/rust-ctor) | 0.8.0 | Apache-2.0 OR MIT |\n| [ctor-proc-macro](https://github.com/mmastrac/rust-ctor) | 0.0.7 | Apache-2.0 OR MIT |\n| [ctr](https://github.com/RustCrypto/block-modes) | 0.9.2 | MIT OR Apache-2.0 |\n| [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek) | 4.1.3 | BSD-3-Clause |\n| [curve25519-dalek-derive](https://github.com/dalek-cryptography/curve25519-dalek) | 0.1.1 | MIT OR Apache-2.0 |\n| [darling](https://github.com/TedDriggs/darling) | 0.23.0 | MIT |\n| [darling_core](https://github.com/TedDriggs/darling) | 0.23.0 | MIT |\n| [darling_macro](https://github.com/TedDriggs/darling) | 0.23.0 | MIT |\n| [der](https://github.com/RustCrypto/formats/tree/master/der) | 0.7.10 | Apache-2.0 OR MIT |\n| [deranged](https://github.com/jhpratt/deranged) | 0.5.8 | MIT OR Apache-2.0 |\n| [derive_more](https://github.com/JelteF/derive_more) | 2.1.1 | MIT |\n| [derive_more-impl](https://github.com/JelteF/derive_more) | 2.1.1 | MIT |\n| [digest](https://github.com/RustCrypto/traits) | 0.10.7 | MIT OR Apache-2.0 |\n| [dirs](https://github.com/soc/dirs-rs) | 5.0.1 | MIT OR Apache-2.0 |\n| [dirs](https://github.com/soc/dirs-rs) | 6.0.0 | MIT OR Apache-2.0 |\n| [dirs-sys](https://github.com/dirs-dev/dirs-sys-rs) | 0.4.1 | MIT OR Apache-2.0 |\n| [dirs-sys](https://github.com/dirs-dev/dirs-sys-rs) | 0.5.0 | MIT OR Apache-2.0 |\n| [displaydoc](https://github.com/yaahc/displaydoc) | 0.2.6 | MIT OR Apache-2.0 |\n| [dom_query](https://github.com/niklak/dom_query) | 0.27.0 | MIT |\n| [dpi](https://github.com/rust-windowing/winit) | 0.1.2 | Apache-2.0 AND MIT |\n| [dtoa](https://github.com/dtolnay/dtoa) | 1.0.11 | MIT OR Apache-2.0 |\n| [dtoa-short](https://github.com/upsuper/dtoa-short) | 0.3.5 | MPL-2.0 |\n| [dunce](https://gitlab.com/kornelski/dunce) | 1.0.5 | CC0-1.0 OR MIT-0 OR Apache-2.0 |\n| [dyn-clone](https://github.com/dtolnay/dyn-clone) | 1.0.20 | MIT OR Apache-2.0 |\n| [ed25519](https://github.com/RustCrypto/signatures/tree/master/ed25519) | 2.2.3 | Apache-2.0 OR MIT |\n| [ed25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek) | 2.2.0 | BSD-3-Clause |\n| [equivalent](https://github.com/indexmap-rs/equivalent) | 1.0.2 | Apache-2.0 OR MIT |\n| [erased-serde](https://github.com/dtolnay/erased-serde) | 0.4.10 | MIT OR Apache-2.0 |\n| [errno](https://github.com/lambda-fairy/rust-errno) | 0.3.14 | MIT OR Apache-2.0 |\n| [fastrand](https://github.com/smol-rs/fastrand) | 2.4.1 | Apache-2.0 OR MIT |\n| [fdeflate](https://github.com/image-rs/fdeflate) | 0.3.7 | MIT OR Apache-2.0 |\n| [flate2](https://github.com/rust-lang/flate2-rs) | 1.1.9 | MIT OR Apache-2.0 |\n| [fnv](https://github.com/servo/rust-fnv) | 1.0.7 | Apache-2.0 OR MIT |\n| [foldhash](https://github.com/orlp/foldhash) | 0.2.0 | Zlib |\n| [form_urlencoded](https://github.com/servo/rust-url) | 1.2.2 | MIT OR Apache-2.0 |\n| [futures-channel](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [futures-core](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [futures-sink](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [futures-task](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [futures-util](https://github.com/rust-lang/futures-rs) | 0.3.32 | MIT OR Apache-2.0 |\n| [generic-array](https://github.com/fizyk20/generic-array.git) | 0.14.7 | MIT |\n| [getrandom](https://github.com/rust-random/getrandom) | 0.2.17 | MIT OR Apache-2.0 |\n| [getrandom](https://github.com/rust-random/getrandom) | 0.3.4 | MIT OR Apache-2.0 |\n| [getrandom](https://github.com/rust-random/getrandom) | 0.4.2 | MIT OR Apache-2.0 |\n| [ghash](https://github.com/RustCrypto/universal-hashes) | 0.5.1 | Apache-2.0 OR MIT |\n| [glob](https://github.com/rust-lang/glob) | 0.3.3 | MIT OR Apache-2.0 |\n| [hashbrown](https://github.com/rust-lang/hashbrown) | 0.17.1 | MIT OR Apache-2.0 |\n| [heck](https://github.com/withoutboats/heck) | 0.5.0 | MIT OR Apache-2.0 |\n| [hkdf](https://github.com/RustCrypto/KDFs/) | 0.12.4 | MIT OR Apache-2.0 |\n| [hmac](https://github.com/RustCrypto/MACs) | 0.12.1 | MIT OR Apache-2.0 |\n| [html5ever](https://github.com/servo/html5ever) | 0.38.0 | MIT OR Apache-2.0 |\n| [http](https://github.com/hyperium/http) | 1.4.1 | MIT OR Apache-2.0 |\n| [http-body](https://github.com/hyperium/http-body) | 1.0.1 | MIT |\n| [http-body-util](https://github.com/hyperium/http-body) | 0.1.3 | MIT |\n| [httparse](https://github.com/seanmonstar/httparse) | 1.10.1 | MIT OR Apache-2.0 |\n| [hyper](https://github.com/hyperium/hyper) | 1.10.0 | MIT |\n| [hyper-util](https://github.com/hyperium/hyper-util) | 0.1.20 | MIT |\n| [iana-time-zone](https://github.com/strawlab/iana-time-zone) | 0.1.65 | MIT OR Apache-2.0 |\n| [ico](https://github.com/mdsteele/rust-ico) | 0.5.0 | MIT |\n| [icu_collections](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_locale_core](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_normalizer](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_normalizer_data](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_properties](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_properties_data](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [icu_provider](https://github.com/unicode-org/icu4x) | 2.2.0 | Unicode-3.0 |\n| [ident_case](https://github.com/TedDriggs/ident_case) | 1.0.1 | MIT OR Apache-2.0 |\n| [idna](https://github.com/servo/rust-url/) | 1.1.0 | MIT OR Apache-2.0 |\n| [idna_adapter](https://github.com/hsivonen/idna_adapter) | 1.2.2 | Apache-2.0 OR MIT |\n| [image](https://github.com/image-rs/image) | 0.25.10 | MIT OR Apache-2.0 |\n| [indexmap](https://github.com/indexmap-rs/indexmap) | 2.14.0 | Apache-2.0 OR MIT |\n| [infer](https://github.com/bojand/infer) | 0.19.0 | MIT |\n| [inout](https://github.com/RustCrypto/utils) | 0.1.4 | MIT OR Apache-2.0 |\n| [ipnet](https://github.com/krisprice/ipnet) | 2.12.0 | MIT OR Apache-2.0 |\n| [is-docker](https://github.com/TheLarkInn/is-docker) | 0.2.0 | MIT |\n| [is-wsl](https://github.com/TheLarkInn/is-wsl) | 0.4.0 | MIT |\n| [itoa](https://github.com/dtolnay/itoa) | 1.0.18 | MIT OR Apache-2.0 |\n| [jni](https://github.com/jni-rs/jni-rs) | 0.21.1 | MIT OR Apache-2.0 |\n| [jni-sys](https://github.com/jni-rs/jni-sys) | 0.3.1 | MIT OR Apache-2.0 |\n| [jni-sys](https://github.com/jni-rs/jni-sys) | 0.4.1 | MIT OR Apache-2.0 |\n| [jni-sys-macros](https://github.com/jni-rs/jni-sys) | 0.4.1 | MIT OR Apache-2.0 |\n| [json-patch](https://github.com/idubrov/json-patch) | 3.0.1 | MIT OR Apache-2.0 |\n| [jsonptr](https://github.com/chanced/jsonptr) | 0.6.3 | MIT OR Apache-2.0 |\n| [lazy_static](https://github.com/rust-lang-nursery/lazy-static.rs) | 1.5.0 | MIT OR Apache-2.0 |\n| [libc](https://github.com/rust-lang/libc) | 0.2.186 | MIT OR Apache-2.0 |\n| [libm](https://github.com/rust-lang/compiler-builtins) | 0.2.16 | MIT |\n| [linux-raw-sys](https://github.com/sunfishcode/linux-raw-sys) | 0.12.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT |\n| [litemap](https://github.com/unicode-org/icu4x) | 0.8.2 | Unicode-3.0 |\n| [lock_api](https://github.com/Amanieu/parking_lot) | 0.4.14 | MIT OR Apache-2.0 |\n| [log](https://github.com/rust-lang/log) | 0.4.30 | MIT OR Apache-2.0 |\n| [markup5ever](https://github.com/servo/html5ever) | 0.38.0 | MIT OR Apache-2.0 |\n| [matchers](https://github.com/hawkw/matchers) | 0.2.0 | MIT |\n| [memchr](https://github.com/BurntSushi/memchr) | 2.8.1 | Unlicense OR MIT |\n| [mime](https://github.com/hyperium/mime) | 0.3.17 | MIT OR Apache-2.0 |\n| [miniz_oxide](https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide) | 0.8.9 | MIT OR Zlib OR Apache-2.0 |\n| [mio](https://github.com/tokio-rs/mio) | 1.2.0 | MIT |\n| [moxcms](https://github.com/awxkee/moxcms.git) | 0.8.1 | BSD-3-Clause OR Apache-2.0 |\n| [ndk](https://github.com/rust-mobile/ndk) | 0.9.0 | MIT OR Apache-2.0 |\n| [ndk-sys](https://github.com/rust-mobile/ndk) | 0.6.0+11769913 | MIT OR Apache-2.0 |\n| [new_debug_unreachable](https://github.com/mbrubeck/rust-debug-unreachable) | 1.0.6 | MIT |\n| [nu-ansi-term](https://github.com/nushell/nu-ansi-term) | 0.50.3 | MIT |\n| [num-bigint-dig](https://github.com/dignifiedquire/num-bigint) | 0.8.6 | MIT OR Apache-2.0 |\n| [num-conv](https://github.com/jhpratt/num-conv) | 0.2.2 | MIT OR Apache-2.0 |\n| [num-integer](https://github.com/rust-num/num-integer) | 0.1.46 | MIT OR Apache-2.0 |\n| [num-iter](https://github.com/rust-num/num-iter) | 0.1.45 | MIT OR Apache-2.0 |\n| [num-traits](https://github.com/rust-num/num-traits) | 0.2.19 | MIT OR Apache-2.0 |\n| [num_enum](https://github.com/illicitonion/num_enum) | 0.7.6 | BSD-3-Clause OR MIT OR Apache-2.0 |\n| [num_enum_derive](https://github.com/illicitonion/num_enum) | 0.7.6 | BSD-3-Clause OR MIT OR Apache-2.0 |\n| [once_cell](https://github.com/matklad/once_cell) | 1.21.4 | MIT OR Apache-2.0 |\n| [opaque-debug](https://github.com/RustCrypto/utils) | 0.3.1 | MIT OR Apache-2.0 |\n| [open](https://github.com/Byron/open-rs) | 5.3.5 | MIT |\n| [option-ext](https://github.com/soc/option-ext.git) | 0.2.0 | MPL-2.0 |\n| [parking_lot](https://github.com/Amanieu/parking_lot) | 0.12.5 | MIT OR Apache-2.0 |\n| [parking_lot_core](https://github.com/Amanieu/parking_lot) | 0.9.12 | MIT OR Apache-2.0 |\n| [pathdiff](https://github.com/Manishearth/pathdiff) | 0.2.3 | MIT OR Apache-2.0 |\n| [pem](https://github.com/jcreekmore/pem-rs.git) | 3.0.6 | MIT |\n| [pem-rfc7468](https://github.com/RustCrypto/formats/tree/master/pem-rfc7468) | 0.7.0 | Apache-2.0 OR MIT |\n| [percent-encoding](https://github.com/servo/rust-url/) | 2.3.2 | MIT OR Apache-2.0 |\n| [phf](https://github.com/rust-phf/rust-phf) | 0.13.1 | MIT |\n| [phf_generator](https://github.com/rust-phf/rust-phf) | 0.13.1 | MIT |\n| [phf_macros](https://github.com/rust-phf/rust-phf) | 0.13.1 | MIT |\n| [phf_shared](https://github.com/rust-phf/rust-phf) | 0.13.1 | MIT |\n| [pin-project-lite](https://github.com/taiki-e/pin-project-lite) | 0.2.17 | Apache-2.0 OR MIT |\n| [pkcs1](https://github.com/RustCrypto/formats/tree/master/pkcs1) | 0.7.5 | Apache-2.0 OR MIT |\n| [pkcs8](https://github.com/RustCrypto/formats/tree/master/pkcs8) | 0.10.2 | Apache-2.0 OR MIT |\n| [plist](https://github.com/ebarnard/rust-plist/) | 1.9.0 | MIT |\n| [png](https://github.com/image-rs/image-png) | 0.17.16 | MIT OR Apache-2.0 |\n| [png](https://github.com/image-rs/image-png) | 0.18.1 | MIT OR Apache-2.0 |\n| [polyval](https://github.com/RustCrypto/universal-hashes) | 0.6.2 | Apache-2.0 OR MIT |\n| [potential_utf](https://github.com/unicode-org/icu4x) | 0.1.5 | Unicode-3.0 |\n| [powerfmt](https://github.com/jhpratt/powerfmt) | 0.2.0 | MIT OR Apache-2.0 |\n| [ppv-lite86](https://github.com/cryptocorrosion/cryptocorrosion) | 0.2.21 | MIT OR Apache-2.0 |\n| [precomputed-hash](https://github.com/emilio/precomputed-hash) | 0.1.1 | MIT |\n| [proc-macro-crate](https://github.com/bkchr/proc-macro-crate) | 3.5.0 | MIT OR Apache-2.0 |\n| [proc-macro2](https://github.com/dtolnay/proc-macro2) | 1.0.106 | MIT OR Apache-2.0 |\n| [pxfm](https://github.com/awxkee/pxfm) | 0.1.29 | BSD-3-Clause OR Apache-2.0 |\n| [quick-protobuf](https://github.com/tafia/quick-protobuf) | 0.8.1 | MIT |\n| [quick-xml](https://github.com/tafia/quick-xml) | 0.39.4 | MIT |\n| [quote](https://github.com/dtolnay/quote) | 1.0.45 | MIT OR Apache-2.0 |\n| [rand](https://github.com/rust-random/rand) | 0.10.2 | MIT OR Apache-2.0 |\n| [rand](https://github.com/rust-random/rand) | 0.8.6 | MIT OR Apache-2.0 |\n| [rand_chacha](https://github.com/rust-random/rand) | 0.3.1 | MIT OR Apache-2.0 |\n| [rand_core](https://github.com/rust-random/rand_core) | 0.10.1 | MIT OR Apache-2.0 |\n| [rand_core](https://github.com/rust-random/rand) | 0.6.4 | MIT OR Apache-2.0 |\n| [raw-window-handle](https://github.com/rust-windowing/raw-window-handle) | 0.6.2 | MIT OR Apache-2.0 OR Zlib |\n| [rcgen](https://github.com/rustls/rcgen) | 0.14.8 | MIT OR Apache-2.0 |\n| [regex](https://github.com/rust-lang/regex) | 1.12.3 | MIT OR Apache-2.0 |\n| [regex-automata](https://github.com/rust-lang/regex) | 0.4.14 | MIT OR Apache-2.0 |\n| [regex-syntax](https://github.com/rust-lang/regex) | 0.8.10 | MIT OR Apache-2.0 |\n| [reqwest](https://github.com/seanmonstar/reqwest) | 0.13.4 | MIT OR Apache-2.0 |\n| [ring](https://github.com/briansmith/ring) | 0.17.14 | Apache-2.0 AND ISC |\n| [rsa](https://github.com/RustCrypto/RSA) | 0.9.10 | MIT OR Apache-2.0 |\n| [rustc-hash](https://github.com/rust-lang/rustc-hash) | 2.1.2 | Apache-2.0 OR MIT |\n| [rustix](https://github.com/bytecodealliance/rustix) | 1.1.4 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT |\n| [rustls](https://github.com/rustls/rustls) | 0.23.40 | Apache-2.0 OR ISC OR MIT |\n| [rustls-pki-types](https://github.com/rustls/pki-types) | 1.14.1 | MIT OR Apache-2.0 |\n| [rustls-webpki](https://github.com/rustls/webpki) | 0.103.13 | ISC |\n| [rustversion](https://github.com/dtolnay/rustversion) | 1.0.22 | MIT OR Apache-2.0 |\n| [same-file](https://github.com/BurntSushi/same-file) | 1.0.6 | Unlicense OR MIT |\n| [schemars](https://github.com/GREsau/schemars) | 0.8.22 | MIT |\n| [schemars_derive](https://github.com/GREsau/schemars) | 0.8.22 | MIT |\n| [scopeguard](https://github.com/bluss/scopeguard) | 1.2.0 | MIT OR Apache-2.0 |\n| [selectors](https://github.com/servo/stylo) | 0.36.1 | MPL-2.0 |\n| [semver](https://github.com/dtolnay/semver) | 1.0.28 | MIT OR Apache-2.0 |\n| [serde](https://github.com/serde-rs/serde) | 1.0.228 | MIT OR Apache-2.0 |\n| [serde-untagged](https://github.com/dtolnay/serde-untagged) | 0.1.9 | MIT OR Apache-2.0 |\n| [serde_core](https://github.com/serde-rs/serde) | 1.0.228 | MIT OR Apache-2.0 |\n| [serde_derive](https://github.com/serde-rs/serde) | 1.0.228 | MIT OR Apache-2.0 |\n| [serde_derive_internals](https://github.com/serde-rs/serde) | 0.29.1 | MIT OR Apache-2.0 |\n| [serde_json](https://github.com/serde-rs/json) | 1.0.150 | MIT OR Apache-2.0 |\n| [serde_repr](https://github.com/dtolnay/serde-repr) | 0.1.20 | MIT OR Apache-2.0 |\n| [serde_spanned](https://github.com/toml-rs/toml) | 1.1.1 | MIT OR Apache-2.0 |\n| [serde_with](https://github.com/jonasbb/serde_with/) | 3.20.0 | MIT OR Apache-2.0 |\n| [serde_with_macros](https://github.com/jonasbb/serde_with/) | 3.20.0 | MIT OR Apache-2.0 |\n| [serialize-to-javascript](https://github.com/chippers/serialize-to-javascript) | 0.1.2 | MIT OR Apache-2.0 |\n| [serialize-to-javascript-impl](https://github.com/chippers/serialize-to-javascript) | 0.1.2 | MIT OR Apache-2.0 |\n| [servo_arc](https://github.com/servo/stylo) | 0.4.3 | MIT OR Apache-2.0 |\n| [sha1](https://github.com/RustCrypto/hashes) | 0.10.6 | MIT OR Apache-2.0 |\n| [sha2](https://github.com/RustCrypto/hashes) | 0.10.9 | MIT OR Apache-2.0 |\n| [sharded-slab](https://github.com/hawkw/sharded-slab) | 0.1.7 | MIT |\n| [signal-hook-registry](https://github.com/vorner/signal-hook) | 1.4.8 | MIT OR Apache-2.0 |\n| [signature](https://github.com/RustCrypto/traits/tree/master/signature) | 2.2.0 | Apache-2.0 OR MIT |\n| [simd-adler32](https://github.com/mcountryman/simd-adler32) | 0.3.9 | MIT |\n| [siphasher](https://github.com/jedisct1/rust-siphash) | 1.0.3 | MIT OR Apache-2.0 |\n| [slab](https://github.com/tokio-rs/slab) | 0.4.12 | MIT |\n| [smallvec](https://github.com/servo/rust-smallvec) | 1.15.1 | MIT OR Apache-2.0 |\n| [socket2](https://github.com/rust-lang/socket2) | 0.6.3 | MIT OR Apache-2.0 |\n| [spin](https://github.com/mvdnes/spin-rs.git) | 0.9.8 | MIT |\n| [spki](https://github.com/RustCrypto/formats/tree/master/spki) | 0.7.3 | Apache-2.0 OR MIT |\n| [stable_deref_trait](https://github.com/storyyeller/stable_deref_trait) | 1.2.1 | MIT OR Apache-2.0 |\n| [string_cache](https://github.com/servo/string-cache) | 0.9.0 | MIT OR Apache-2.0 |\n| [strsim](https://github.com/rapidfuzz/strsim-rs) | 0.11.1 | MIT |\n| [subtle](https://github.com/dalek-cryptography/subtle) | 2.6.1 | BSD-3-Clause |\n| [symlink](https://gitlab.com/chris-morgan/symlink) | 0.1.0 | MIT OR Apache-2.0 |\n| [syn](https://github.com/dtolnay/syn) | 2.0.117 | MIT OR Apache-2.0 |\n| [sync_wrapper](https://github.com/Actyx/sync_wrapper) | 1.0.2 | Apache-2.0 |\n| [synstructure](https://github.com/mystor/synstructure) | 0.13.2 | MIT |\n| [tao](https://github.com/tauri-apps/tao) | 0.35.3 | Apache-2.0 |\n| [tao-macros](https://github.com/tauri-apps/tao) | 0.1.3 | MIT OR Apache-2.0 |\n| [tauri](https://github.com/tauri-apps/tauri) | 2.11.2 | Apache-2.0 OR MIT |\n| [tauri-codegen](https://github.com/tauri-apps/tauri) | 2.6.2 | Apache-2.0 OR MIT |\n| [tauri-macros](https://github.com/tauri-apps/tauri) | 2.6.2 | Apache-2.0 OR MIT |\n| [tauri-plugin-opener](https://github.com/tauri-apps/plugins-workspace) | 2.5.4 | Apache-2.0 OR MIT |\n| [tauri-runtime](https://github.com/tauri-apps/tauri) | 2.11.2 | Apache-2.0 OR MIT |\n| [tauri-runtime-wry](https://github.com/tauri-apps/tauri) | 2.11.2 | Apache-2.0 OR MIT |\n| [tauri-utils](https://github.com/tauri-apps/tauri) | 2.9.2 | Apache-2.0 OR MIT |\n| [tempfile](https://github.com/Stebalien/tempfile) | 3.27.0 | MIT OR Apache-2.0 |\n| [tendril](https://github.com/servo/html5ever) | 0.5.0 | MIT OR Apache-2.0 |\n| [thiserror](https://github.com/dtolnay/thiserror) | 1.0.69 | MIT OR Apache-2.0 |\n| [thiserror](https://github.com/dtolnay/thiserror) | 2.0.18 | MIT OR Apache-2.0 |\n| [thiserror-impl](https://github.com/dtolnay/thiserror) | 1.0.69 | MIT OR Apache-2.0 |\n| [thiserror-impl](https://github.com/dtolnay/thiserror) | 2.0.18 | MIT OR Apache-2.0 |\n| [thread_local](https://github.com/Amanieu/thread_local-rs) | 1.1.9 | MIT OR Apache-2.0 |\n| [time](https://github.com/time-rs/time) | 0.3.47 | MIT OR Apache-2.0 |\n| [time-core](https://github.com/time-rs/time) | 0.1.8 | MIT OR Apache-2.0 |\n| [time-macros](https://github.com/time-rs/time) | 0.2.27 | MIT OR Apache-2.0 |\n| [tinystr](https://github.com/unicode-org/icu4x) | 0.8.3 | Unicode-3.0 |\n| [tokio](https://github.com/tokio-rs/tokio) | 1.52.3 | MIT |\n| [tokio-macros](https://github.com/tokio-rs/tokio) | 2.7.0 | MIT |\n| [tokio-util](https://github.com/tokio-rs/tokio) | 0.7.18 | MIT |\n| [toml](https://github.com/toml-rs/toml) | 1.1.2+spec-1.1.0 | MIT OR Apache-2.0 |\n| [toml_datetime](https://github.com/toml-rs/toml) | 1.1.1+spec-1.1.0 | MIT OR Apache-2.0 |\n| [toml_edit](https://github.com/toml-rs/toml) | 0.25.12+spec-1.1.0 | MIT OR Apache-2.0 |\n| [toml_parser](https://github.com/toml-rs/toml) | 1.1.2+spec-1.1.0 | MIT OR Apache-2.0 |\n| [toml_writer](https://github.com/toml-rs/toml) | 1.1.1+spec-1.1.0 | MIT OR Apache-2.0 |\n| [tower](https://github.com/tower-rs/tower) | 0.5.3 | MIT |\n| [tower-http](https://github.com/tower-rs/tower-http) | 0.6.11 | MIT |\n| [tower-layer](https://github.com/tower-rs/tower) | 0.3.3 | MIT |\n| [tower-service](https://github.com/tower-rs/tower) | 0.3.3 | MIT |\n| [tracing](https://github.com/tokio-rs/tracing) | 0.1.44 | MIT |\n| [tracing-appender](https://github.com/tokio-rs/tracing) | 0.2.5 | MIT |\n| [tracing-attributes](https://github.com/tokio-rs/tracing) | 0.1.31 | MIT |\n| [tracing-core](https://github.com/tokio-rs/tracing) | 0.1.36 | MIT |\n| [tracing-log](https://github.com/tokio-rs/tracing) | 0.2.0 | MIT |\n| [tracing-subscriber](https://github.com/tokio-rs/tracing) | 0.3.23 | MIT |\n| [try-lock](https://github.com/seanmonstar/try-lock) | 0.2.5 | MIT |\n| [typeid](https://github.com/dtolnay/typeid) | 1.0.3 | MIT OR Apache-2.0 |\n| [typenum](https://github.com/paholg/typenum) | 1.20.0 | MIT OR Apache-2.0 |\n| [unic-char-property](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unic-char-range](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unic-common](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unic-ucd-ident](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unic-ucd-version](https://github.com/open-i18n/rust-unic/) | 0.9.0 | MIT OR Apache-2.0 |\n| [unicode-ident](https://github.com/dtolnay/unicode-ident) | 1.0.24 | (MIT OR Apache-2.0) AND Unicode-3.0 |\n| [universal-hash](https://github.com/RustCrypto/traits) | 0.5.1 | MIT OR Apache-2.0 |\n| [untrusted](https://github.com/briansmith/untrusted) | 0.9.0 | ISC |\n| [url](https://github.com/servo/rust-url) | 2.5.8 | MIT OR Apache-2.0 |\n| [urlpattern](https://github.com/denoland/rust-urlpattern) | 0.3.0 | MIT |\n| [utf-8](https://github.com/SimonSapin/rust-utf8) | 0.7.6 | MIT OR Apache-2.0 |\n| [utf8_iter](https://github.com/hsivonen/utf8_iter) | 1.0.4 | Apache-2.0 OR MIT |\n| [uuid](https://github.com/uuid-rs/uuid) | 1.23.1 | Apache-2.0 OR MIT |\n| [walkdir](https://github.com/BurntSushi/walkdir) | 2.5.0 | Unlicense OR MIT |\n| [want](https://github.com/seanmonstar/want) | 0.3.1 | MIT |\n| [web_atoms](https://github.com/servo/html5ever) | 0.2.4 | MIT OR Apache-2.0 |\n| [winnow](https://github.com/winnow-rs/winnow) | 1.0.3 | MIT |\n| [writeable](https://github.com/unicode-org/icu4x) | 0.6.3 | Unicode-3.0 |\n| [wry](https://github.com/tauri-apps/wry) | 0.55.1 | Apache-2.0 OR MIT |\n| [yasna](https://github.com/qnighy/yasna.rs) | 0.6.0 | MIT OR Apache-2.0 |\n| [yoke](https://github.com/unicode-org/icu4x) | 0.8.2 | Unicode-3.0 |\n| [yoke-derive](https://github.com/unicode-org/icu4x) | 0.8.2 | Unicode-3.0 |\n| [zerocopy](https://github.com/google/zerocopy) | 0.8.49 | BSD-2-Clause OR Apache-2.0 OR MIT |\n| [zerofrom](https://github.com/unicode-org/icu4x) | 0.1.8 | Unicode-3.0 |\n| [zerofrom-derive](https://github.com/unicode-org/icu4x) | 0.1.7 | Unicode-3.0 |\n| [zeroize](https://github.com/RustCrypto/utils) | 1.8.2 | Apache-2.0 OR MIT |\n| [zerotrie](https://github.com/unicode-org/icu4x) | 0.2.4 | Unicode-3.0 |\n| [zerovec](https://github.com/unicode-org/icu4x) | 0.11.6 | Unicode-3.0 |\n| [zerovec-derive](https://github.com/unicode-org/icu4x) | 0.11.3 | Unicode-3.0 |\n| [zmij](https://github.com/dtolnay/zmij) | 1.0.21 | MIT |\n\n## License texts\n\n### Apache-2.0\n\nUsed by: ring 0.17.14\n\n```\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n\nLicenses for support code\n-------------------------\n\nParts of the TLS test suite are under the Go license. This code is not included\nin BoringSSL (i.e. libcrypto and libssl) when compiled, however, so\ndistributing code linked against BoringSSL does not trigger this license:\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\nBoringSSL uses the Chromium test infrastructure to run a continuous build,\ntrybots etc. The scripts which manage this, and the script for generating build\nmetadata, are under the Chromium license. Distributing code linked against\nBoringSSL does not trigger this license.\n\nCopyright 2015 The Chromium Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n```\n\nUsed by: moxcms 0.8.1, pxfm 0.1.29\n\n```\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2024 Radzivon Bartoshyk\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n```\n\nUsed by: dpi 0.1.2, tao 0.35.3\n\n```\nApache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n```\n\nUsed by: ring 0.17.14\n\n```\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\nUsed by: dunce 1.0.5, sync_wrapper 1.0.2\n\n```\nApache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\n (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.\n\n You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\n### BSD-3-Clause\n\nUsed by: subtle 2.6.1\n\n```\nCopyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved.\nCopyright (c) 2016-2024 Isis Agora Lovecruft. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n1. Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n```\n\nUsed by: ed25519-dalek 2.2.0\n\n```\nCopyright (c) 2017-2019 isis agora lovecruft. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n1. Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n```\n\nUsed by: alloc-no-stdlib 2.0.4\n\n```\nCopyright (c) 2016 Dropbox, Inc.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n```\n\nUsed by: alloc-stdlib 0.2.2, brotli 8.0.2, curve25519-dalek 4.1.3\n\n```\nCopyright (c) . \n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n```\n\n### ISC\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2015-2022 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nuse crate::limb::Limb;\n\n#[derive(Clone, Copy)]\n#[repr(transparent)]\npub struct N0([Limb; 2]);\n\nimpl N0 {\n #[cfg(feature = \"alloc\")]\n pub(super) const LIMBS_USED: usize = 64 / crate::limb::LIMB_BITS;\n\n #[inline]\n pub const fn precalculated(n0: u64) -> Self {\n #[cfg(target_pointer_width = \"64\")]\n {\n Self([n0, 0])\n }\n\n #[cfg(target_pointer_width = \"32\")]\n {\n Self([n0 as Limb, (n0 >> crate::limb::LIMB_BITS) as Limb])\n }\n }\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2016-2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nuse crate::error::{KeyRejected, Unspecified};\n\nimpl From for Unspecified {\n fn from(source: untrusted::EndOfInput) -> Self {\n super::erase(source)\n }\n}\n\nimpl From for Unspecified {\n fn from(source: core::array::TryFromSliceError) -> Self {\n super::erase(source)\n }\n}\n\nimpl From for Unspecified {\n fn from(source: KeyRejected) -> Self {\n super::erase(source)\n }\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2019-2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nuse super::BlockLen;\n\npub(super) use self::{\n sha2_32::{block_data_order_32, State32, SHA256_BLOCK_LEN},\n sha2_64::{block_data_order_64, State64, SHA512_BLOCK_LEN},\n};\n\npub(super) const CHAINING_WORDS: usize = 8;\n\n#[cfg(any(\n all(target_arch = \"aarch64\", target_endian = \"little\"),\n all(target_arch = \"arm\", target_endian = \"little\"),\n target_arch = \"x86_64\"\n))]\n#[macro_use]\nmod ffi;\n\npub(super) mod fallback;\nmod sha2_32;\nmod sha2_64;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2015-2016 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n//! EdDSA Signatures.\n\nuse super::ops::ELEM_LEN;\nuse crate::digest;\n\npub mod signing;\npub mod verification;\n\n/// The length of an Ed25519 public key.\npub const ED25519_PUBLIC_KEY_LEN: usize = ELEM_LEN;\n\npub fn eddsa_digest(signature_r: &[u8], public_key: &[u8], msg: &[u8]) -> digest::Digest {\n let mut ctx = digest::Context::new(&digest::SHA512);\n ctx.update(signature_r);\n ctx.update(public_key);\n ctx.update(msg);\n ctx.finish()\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n/* Copyright (c) 2014, Intel Corporation.\n *\n * Permission to use, copy, modify, and/or distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */\n\n#ifndef OPENSSL_HEADER_EC_ECP_NISTZ384_H\n#define OPENSSL_HEADER_EC_ECP_NISTZ384_H\n\n#include \"../../limbs/limbs.h\"\n\n#define P384_LIMBS (384u / LIMB_BITS)\n\ntypedef struct {\n Limb X[P384_LIMBS];\n Limb Y[P384_LIMBS];\n Limb Z[P384_LIMBS];\n} P384_POINT;\n\ntypedef struct {\n Limb X[P384_LIMBS];\n Limb Y[P384_LIMBS];\n} P384_POINT_AFFINE;\n\n\n#endif // OPENSSL_HEADER_EC_ECP_NISTZ384_H\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n// TODO(MSRV 1.76): Replace with `core::ptr::from_mut`.\n#[allow(dead_code)]\n#[inline(always)]\npub fn from_mut(r: &mut T) -> *mut T {\n r\n}\n\n// TODO(MSRV 1.76): Replace with `core::ptr::from_ref`.\n#[allow(dead_code)]\n#[inline(always)]\npub const fn from_ref(r: &T) -> *const T {\n r\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2015-2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nuse crate::{bb, error};\n\n#[deprecated(\n note = \"To be removed. Internal function not intended for external use with no promises regarding side channels.\"\n)]\npub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<(), error::Unspecified> {\n bb::verify_slices_are_equal(a, b)\n}\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2018 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n//! Serialization and deserialization.\n\n#[doc(hidden)]\npub mod der;\n\n#[cfg(feature = \"alloc\")]\nmod writer;\n\n#[cfg(feature = \"alloc\")]\npub(crate) mod der_writer;\n\npub(crate) mod positive;\n\npub use self::positive::Positive;\n\n#[cfg(feature = \"alloc\")]\npub(crate) use self::writer::TooLongError;\n```\n\nUsed by: rustls-webpki 0.103.13\n\n```\nExcept as otherwise noted, this project is licensed under the following\n(ISC-style) terms:\n\nCopyright 2015 Brian Smith.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHORS DISCLAIM ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nThe files under third-party/chromium are licensed as described in\nthird-party/chromium/LICENSE.\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\npub use self::{\n array::Array,\n base::{IndexError, Overlapping},\n partial_block::PartialBlock,\n};\n\nmod array;\nmod base;\nmod partial_block;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2016 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n//! Elliptic curve operations and schemes using Curve25519.\n\npub mod ed25519;\n\npub mod x25519;\n\nmod ops;\nmod scalar;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n#![cfg(all(target_arch = \"aarch64\", target_endian = \"little\"))]\n\npub(in super::super) mod mont;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nmod storage;\n\npub(super) use self::storage::{AlignedStorage, LIMBS_PER_CHUNK};\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n#![cfg(target_arch = \"x86_64\")]\n\npub(in super::super::super) mod mont;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2024 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n//! Integration tests for non-public APIs.\n\nmod bits_tests;\n```\n\nUsed by: ring 0.17.14\n\n```\n// Copyright 2025 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\npub(super) mod aarch64;\npub(super) mod x86_64;\n```\n\nUsed by: untrusted 0.9.0\n\n```\n// Copyright 2015-2016 Brian Smith.\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHORS DISCLAIM ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR\n// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n```\n\nUsed by: ring 0.17.14\n\n```\nCopyright 2015-2025 Brian Smith.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\nSPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\nOF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\nCONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n```\n\n### MIT\n\nUsed by: schemars_derive 0.8.22\n\n```\n#![allow(clippy::all)]\n// Copied from regex_syntax crate to avoid pulling in the whole crate just for a utility function\n// https://github.com/rust-lang/regex/blob/431c4e4867e1eb33eb39b23ed47c9934b2672f8f/regex-syntax/src/lib.rs\n//\n// Copyright (c) 2014 The Rust Project Developers\n//\n// Permission is hereby granted, free of charge, to any\n// person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the\n// Software without restriction, including without\n// limitation the rights to use, copy, modify, merge,\n// publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software\n// is furnished to do so, subject to the following\n// conditions:\n//\n// The above copyright notice and this permission notice\n// shall be included in all copies or substantial portions\n// of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\n// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\n// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\n// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\n// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n// DEALINGS IN THE SOFTWARE.\n\npub fn escape(text: &str) -> String {\n let mut quoted = String::new();\n escape_into(text, &mut quoted);\n quoted\n}\n\nfn escape_into(text: &str, buf: &mut String) {\n buf.reserve(text.len());\n for c in text.chars() {\n if is_meta_character(c) {\n buf.push('\\\\');\n }\n buf.push(c);\n }\n}\n\nfn is_meta_character(c: char) -> bool {\n match c {\n '\\\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$'\n | '#' | '&' | '-' | '~' => true,\n _ => false,\n }\n}\n```\n\nUsed by: atomic-waker 1.1.2\n\n```\n===============================================================================\n\nCopyright (c) 2016 Alex Crichton\nCopyright (c) 2017 The Tokio Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n===============================================================================\n\nCopyright (c) 2016 Alex Crichton\nCopyright (c) 2017 The Tokio Authors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: dom_query 0.27.0\n\n```\nMIT License\n\nCopyright (c) 2023 Mykola Humanov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n---\n\nThis project contains portions of code and architectural concepts originally \nderived from the \"nipper\" project (https://github.com/importcjj/nipper), \ndeveloped by Chen Jiaju, licensed under the MIT License and the Apache License 2.0 (dual licensed).\n```\n\nUsed by: miniz_oxide 0.8.9\n\n```\nMIT License\n\nCopyright 2013-2014 RAD Game Tools and Valve Software\nCopyright 2010-2014 Rich Geldreich and Tenacious Software LLC\nCopyright (c) 2017 Frommi\nCopyright (c) 2017-2024 oyvindln\n\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: miniz_oxide 0.8.9\n\n```\nMIT License\n\nCopyright 2013-2014 RAD Game Tools and Valve Software\nCopyright 2010-2014 Rich Geldreich and Tenacious Software LLC\nCopyright (c) 2017 Frommi\nCopyright (c) 2017-2024 oyvindln\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: strsim 0.11.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2015 Danny Guo\nCopyright (c) 2016 Titus Wormer \nCopyright (c) 2018 Akash Kurdekar\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: uuid 1.23.1\n\n```\nCopyright (c) 2014 The Rust Project Developers\nCopyright (c) 2018 Ashley Mannix, Christopher Armstrong, Dylan DPC, Hunar Roop Kahlon\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: nu-ansi-term 0.50.3\n\n```\nThe MIT License (MIT)\r\n\r\nCopyright (c) 2014 Benjamin Sago\r\nCopyright (c) 2021-2022 The Nushell Project Developers\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.\r\n```\n\nUsed by: base64ct 1.8.3\n\n```\nCopyright (c) 2014 Steve \"Sc00bz\" Thomas (steve at tobtu dot com)\nCopyright (c) 2021-2025 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: sha1 0.10.6, sha2 0.10.9\n\n```\nCopyright (c) 2006-2009 Graydon Hoare\nCopyright (c) 2009-2013 Mozilla Foundation\nCopyright (c) 2016 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: getrandom 0.2.17\n\n```\nCopyright (c) 2018-2024 The rust-random Project Developers\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: getrandom 0.3.4\n\n```\nCopyright (c) 2018-2025 The rust-random Project Developers\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: getrandom 0.4.2\n\n```\nCopyright (c) 2018-2026 The rust-random Project Developers\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: wry 0.55.1\n\n```\nMIT License\n\nCopyright (c) 2020-2023 Ngo Iok Ui & Tauri Programme within The Commons Conservancy\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: rand 0.10.2, rand 0.8.6, rand_chacha 0.3.1, rand_core 0.6.4\n\n```\nCopyright 2018 Developers of the Rand project\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: aead 0.5.2\n\n```\nCopyright (c) 2019 The RustCrypto Project Developers\nCopyright (c) 2019 MobileCoin, LLC\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: inout 0.1.4\n\n```\nCopyright (c) 2022 The RustCrypto Project Developers\nCopyright (c) 2022 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: bytemuck 1.25.0\n\n```\nMIT License\n\nCopyright (c) 2019 Daniel \"Lokathor\" Gee.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: hkdf 0.12.4\n\n```\nCopyright (c) 2015-2018 Vlad Filippov\nCopyright (c) 2018-2021 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: open 5.3.5\n\n```\nThe MIT License (MIT)\n=====================\n\nCopyright \u00a9 `2015` `Sebastian Thiel`\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \u201cSoftware\u201d), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \u201cAS IS\u201d, WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: generic-array 0.14.7\n\n```\nThe MIT License (MIT)\r\n\r\nCopyright (c) 2015 Bart\u0142omiej Kami\u0144ski\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.\n```\n\nUsed by: quick-xml 0.39.4\n\n```\nThe MIT License (MIT)\r\n\r\nCopyright (c) 2016 Johann Tuffe\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\n\r\nThe above copyright notice and this permission notice shall be included in\r\nall copies or substantial portions of the Software.\r\n\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\nTHE SOFTWARE.\r\n```\n\nUsed by: jni 0.21.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2016 Prevoty, Inc. and jni-rs contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: ctr 0.9.2\n\n```\nCopyright (c) 2018-2022 RustCrypto Developers\nCopyright (c) 2018 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: tokio-macros 2.7.0\n\n```\nMIT License\n\nCopyright (c) 2019 Yoshua Wuyts\nCopyright (c) Tokio Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: phf 0.13.1, phf_generator 0.13.1, phf_macros 0.13.1, phf_shared 0.13.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2014-2022 Steven Fackler, Yuki Okushi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: crossbeam-channel 0.5.15, crossbeam-utils 0.8.21\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2019 The Crossbeam Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: scopeguard 1.2.0\n\n```\nCopyright (c) 2016-2019 Ulrik Sverdrup \"bluss\" and scopeguard developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: crc32fast 1.5.0\n\n```\nMIT License\n\nCopyright (c) 2018 Sam Rijs, Alex Crichton and contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: try-lock 0.2.5\n\n```\nCopyright (c) 2018-2023 Sean McArthur\nCopyright (c) 2016 Alex Crichton\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: zeroize 1.8.2\n\n```\nMIT License\n\nCopyright (c) 2018-2021 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: futures-channel 0.3.32, futures-core 0.3.32, futures-sink 0.3.32, futures-task 0.3.32, futures-util 0.3.32\n\n```\nCopyright (c) 2016 Alex Crichton\nCopyright (c) 2017 The Tokio Authors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: cookie 0.18.1\n\n```\nCopyright (c) 2017 Sergio Benitez\nCopyright (c) 2014 Alex Crichton\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: symlink 0.1.0\n\n```\nCopyright (c) 2014 Chris Morgan and the Teepee project developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: spin 0.9.8, tracing-core 0.1.36\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2014 Mathijs van de Nes\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: pem 3.0.6\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2016 Jonathan Creekmore\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: brotli 8.0.2\n\n```\nCopyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: combine 4.6.7\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2015 Markus Westerlind\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: http-body 1.0.1\n\n```\nCopyright (c) 2019-2024 Sean McArthur & Hyper Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: http-body-util 0.1.3\n\n```\nCopyright (c) 2019-2025 Sean McArthur & Hyper Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: typenum 1.20.0\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2014 Paho Lurie-Gregg\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: mio 1.2.0\n\n```\nCopyright (c) 2014 Carl Lerche and other MIO contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: rustls 0.23.40\n\n```\nCopyright (c) 2016 Joseph Birr-Pixton \n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: block-buffer 0.10.4\n\n```\nCopyright (c) 2018-2019 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: opaque-debug 0.3.1\n\n```\nCopyright (c) 2018-2024 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: chacha20 0.10.1\n\n```\nCopyright (c) 2019-2026 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: const-oid 0.9.6\n\n```\nCopyright (c) 2020-2022 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: der 0.7.10, pkcs8 0.10.2\n\n```\nCopyright (c) 2020-2023 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: cpufeatures 0.2.17, cpufeatures 0.3.0\n\n```\nCopyright (c) 2020-2025 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: pkcs1 0.7.5, spki 0.7.3\n\n```\nCopyright (c) 2021-2023 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: aho-corasick 1.1.4, byteorder 1.5.0, byteorder-lite 0.1.0, memchr 2.8.1, walkdir 2.5.0\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2015 Andrew Gallant\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: same-file 1.0.6\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2017 Andrew Gallant\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: rustls-pki-types 1.14.1\n\n```\nCopyright (c) 2023 Dirkjan Ochtman \n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: android_system_properties 0.1.5\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2013 Nicolas Silva\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: derive_more 2.1.1, derive_more-impl 2.1.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2016 Jelte Fennema\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: brotli-decompressor 5.0.0, cesu8 1.1.0, chrono 0.4.44, dpi 0.1.2, jni-sys-macros 0.4.1, libm 0.2.16, ndk 0.9.0, ndk-sys 0.6.0+11769913, quick-protobuf 0.8.1, rcgen 0.14.8, siphasher 1.0.3, tao-macros 0.1.3, tauri 2.11.2, tauri-codegen 2.6.2, tauri-macros 2.6.2, tauri-plugin-opener 2.5.4, tauri-runtime 2.11.2, tauri-runtime-wry 2.11.2, tauri-utils 2.9.2, unic-char-property 0.9.0, unic-char-range 0.9.0, unic-common 0.9.0, unic-ucd-ident 0.9.0, unic-ucd-version 0.9.0\n\n```\nMIT License\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and\nassociated documentation files (the \"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial\nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO\nEVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: simd-adler32 0.3.9\n\n```\nMIT License\n\nCopyright (c) [2021] [Marvin Countryman]\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: aes-gcm 0.10.3\n\n```\nCopyright (c) 2019 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: pem-rfc7468 0.7.0\n\n```\nCopyright (c) 2021 The RustCrypto Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: precomputed-hash 0.1.1\n\n```\nMIT License\n\nCopyright (c) 2017 Emilio Cobos \u00c1lvarez\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: html5ever 0.38.0, markup5ever 0.38.0, web_atoms 0.2.4\n\n```\nCopyright (c) 2014 The html5ever Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: rand_core 0.10.1\n\n```\nCopyright (c) 2018-2026 The Rand Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ppv-lite86 0.2.21\n\n```\nCopyright (c) 2019 The CryptoCorrosion Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: adb_client 3.2.2\n\n```\nMIT License\n\nCopyright (c) 2023-2024 Corentin LIAUD\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: base64 0.22.1\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2015 Alice Maz\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: cfb 0.7.3\n\n```\nMIT License\n\nCopyright (c) 2017 Matthew D. Steele\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: ico 0.5.0\n\n```\nMIT License\n\nCopyright (c) 2018 Matthew D. Steele\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: urlpattern 0.3.0\n\n```\nMIT License\n\nCopyright (c) 2021 the Deno authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: form_urlencoded 1.2.2\n\n```\nCopyright (c) 2013-2016 The rust-url developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: idna 1.1.0, percent-encoding 2.3.2, url 2.5.8\n\n```\nCopyright (c) 2013-2025 The rust-url developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: smallvec 1.15.1\n\n```\nCopyright (c) 2018 The Servo Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: lazy_static 1.5.0\n\n```\nCopyright (c) 2010 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: bitflags 1.3.2, bitflags 2.11.1, glob 0.3.3, log 0.4.30, num-bigint-dig 0.8.6, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, regex 1.12.3, regex-automata 0.4.14, regex-syntax 0.8.10\n\n```\nCopyright (c) 2014 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: heck 0.5.0\n\n```\nCopyright (c) 2015 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: jni-sys 0.3.1, jni-sys 0.4.1\n\n```\nCopyright (c) 2015 The rust-jni-sys Developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: lock_api 0.4.14, parking_lot 0.12.5, parking_lot_core 0.9.12, thread_local 1.1.9\n\n```\nCopyright (c) 2016 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: bit-set 0.8.0, bit-vec 0.8.0\n\n```\nCopyright (c) 2023 The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: jsonptr 0.6.3\n\n```\nMIT License\n\nCopyright (c) 2022 Chance Dinkins\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: cipher 0.4.4\n\n```\nCopyright (c) 2016-2020 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ed25519 2.2.3, signature 2.2.0\n\n```\nCopyright (c) 2018-2023 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: universal-hash 0.5.1\n\n```\nCopyright (c) 2019-2020 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: polyval 0.6.2\n\n```\nCopyright (c) 2019-2023 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: tokio 1.52.3, tokio-util 0.7.18\n\n```\nMIT License\n\nCopyright (c) Tokio Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: dirs 5.0.1, dirs 6.0.0, dirs-sys 0.4.1, dirs-sys 0.5.0\n\n```\nCopyright (c) 2018-2019 dirs-rs contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: signal-hook-registry 1.4.8\n\n```\nCopyright (c) 2017 tokio-jsonrpc developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: json-patch 3.0.1\n\n```\nMIT License\n\nCopyright (c) 2017 Ivan Dubrov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: schemars 0.8.22, schemars_derive 0.8.22\n\n```\nMIT License\n\nCopyright (c) 2019 Graham Esau\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: is-docker 0.2.0, is-wsl 0.4.0\n\n```\nMIT License\n\nCopyright (c) 2023 Sean Larkin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: string_cache 0.9.0\n\n```\nCopyright (c) 2012-2013 Mozilla Foundation\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: tower-http 0.6.11\n\n```\nCopyright (c) 2019-2021 Tower Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: darling 0.23.0, darling_core 0.23.0, darling_macro 0.23.0\n\n```\nMIT License\n\nCopyright (c) 2017 Ted Driggs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: libc 0.2.186\n\n```\nCopyright (c) The Rust Project Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: serialize-to-javascript 0.1.2, serialize-to-javascript-impl 0.1.2\n\n```\nMIT License\n\nCopyright (c) 2021 Chip Reed\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: ghash 0.5.1\n\n```\nCopyright (c) 2019 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: crypto-common 0.1.7\n\n```\nCopyright (c) 2021 RustCrypto Developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: raw-window-handle 0.6.2\n\n```\nMIT License\n\nCopyright (c) 2019 Osspial\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: flate2 1.1.9\n\n```\nCopyright (c) 2014-2026 Alex Crichton\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: hyper 1.10.0\n\n```\nCopyright (c) 2014-2026 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: httparse 1.10.1\n\n```\nCopyright (c) 2015-2025 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: reqwest 0.13.4\n\n```\nCopyright (c) 2016-2026 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: want 0.3.1\n\n```\nCopyright (c) 2018-2019 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: tracing 0.1.44, tracing-appender 0.2.5, tracing-attributes 0.1.31, tracing-core 0.1.36, tracing-log 0.2.0, tracing-subscriber 0.3.23\n\n```\nCopyright (c) 2019 Tokio Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: tower 0.5.3, tower-layer 0.3.3, tower-service 0.3.3\n\n```\nCopyright (c) 2019 Tower Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: powerfmt 0.2.0\n\n```\nCopyright (c) 2023 Jacob Pratt et al.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: hyper-util 0.1.20\n\n```\nCopyright (c) 2023-2025 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: deranged 0.5.8\n\n```\nCopyright (c) 2024 Jacob Pratt et al.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: serde_spanned 1.1.1, toml 1.1.2+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_writer 1.1.1+spec-1.1.0\n\n```\nCopyright (c) Individual contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: idna_adapter 1.2.2\n\n```\nCopyright (c) The rust-url developers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ipnet 2.12.0\n\n```\nCopyright 2017 Juniper Networks, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: infer 0.19.0\n\n```\nMIT License\n\nCopyright (c) 2019 Bojan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: tendril 0.5.0\n\n```\nCopyright (c) 2015 Keegan McAllister\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: hashbrown 0.17.1\n\n```\nCopyright (c) 2016 Amanieu d'Antras\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: http 1.4.1\n\n```\nCopyright (c) 2017 http-rs authors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: iana-time-zone 0.1.65\n\n```\nCopyright (c) 2020 Andrew D. Straw\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: zerocopy 0.8.49\n\n```\nCopyright 2023 The Fuchsia Authors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: plist 1.9.0\n\n```\nCopyright (c) 2015 Edward Barnard\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: cfg-if 1.0.4, socket2 0.6.3\n\n```\nCopyright (c) 2014 Alex Crichton\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: mime 0.3.17\n\n```\nCopyright (c) 2014 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: new_debug_unreachable 1.0.6\n\n```\nCopyright (c) 2015 Jonathan Reem\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: digest 0.10.7, hmac 0.12.1\n\n```\nCopyright (c) 2017 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: stable_deref_trait 1.2.1\n\n```\nCopyright (c) 2017 Robert Grosse\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: aes 0.8.4\n\n```\nCopyright (c) 2018 Artyom Pavlov\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: sharded-slab 0.1.7\n\n```\nCopyright (c) 2019 Eliza Weisman\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n\nUsed by: matchers 0.2.0\n\n```\nCopyright (c) 2019 Eliza Weisman\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: time 0.3.47, time-core 0.1.8, time-macros 0.2.27\n\n```\nCopyright (c) Jacob Pratt et al.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: tempfile 3.27.0\n\n```\nCopyright (c) 2015 Steven Allen\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: fnv 1.0.7\n\n```\nCopyright (c) 2017 Contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: yasna 0.6.0\n\n```\nCopyright (c) 2016 Masaki Hara\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: bytes 1.11.1\n\n```\nCopyright (c) 2018 Carl Lerche\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: slab 0.4.12\n\n```\nCopyright (c) 2019 Carl Lerche\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: errno 0.3.14\n\n```\nCopyright (c) 2014 Chris Wong\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: utf8_iter 1.0.4\n\n```\nCopyright Mozilla Foundation\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: synstructure 0.13.2\n\n```\nCopyright 2016 Nika Layzell\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: num-conv 0.2.2\n\n```\nCopyright (c) Jacob Pratt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: indexmap 2.14.0\n\n```\nCopyright (c) 2016--2017\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: equivalent 1.0.2\n\n```\nCopyright (c) 2016--2023\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: png 0.17.16, png 0.18.1\n\n```\nCopyright (c) 2015 nwin\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: serde_with 3.20.0, serde_with_macros 3.20.0\n\n```\nCopyright (c) 2015\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: fdeflate 0.3.7, image 0.25.10\n\n```\nMIT License\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ident_case 1.0.1\n\n```\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\nUsed by: adler2 2.0.1, anyhow 1.0.102, async-trait 0.1.89, atomic-waker 1.1.2, camino 1.2.2, cargo-platform 0.1.9, cargo_metadata 0.19.2, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, dtoa 1.0.11, dyn-clone 1.0.20, erased-serde 0.4.10, fastrand 2.4.1, itoa 1.0.18, linux-raw-sys 0.12.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, pathdiff 0.2.3, pin-project-lite 0.2.17, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.45, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde-untagged 0.1.9, serde_core 1.0.228, serde_derive 1.0.228, serde_derive_internals 0.29.1, serde_json 1.0.150, serde_repr 0.1.20, servo_arc 0.4.3, syn 2.0.117, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, typeid 1.0.3, unicode-ident 1.0.24, utf-8 0.7.6, zmij 1.0.21\n\n```\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n```\n\nUsed by: winnow 1.0.3\n\n```\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\nUsed by: ctor 0.8.0, ctor-proc-macro 0.0.7\n\n```\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n\n### MPL-2.0\n\nUsed by: option-ext 0.2.0, selectors 0.36.1\n\n```\nMozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in \n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party's negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n```\n\nUsed by: cssparser 0.36.0, cssparser-macros 0.6.1\n\n```\nMozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in \n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party's negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n```\n\nUsed by: dtoa-short 0.3.5\n\n```\nMozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in\n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party's negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n```\n\n### Unicode-3.0\n\nUsed by: icu_collections 2.2.0, icu_locale_core 2.2.0, icu_normalizer 2.2.0, icu_normalizer_data 2.2.0, icu_properties 2.2.0, icu_properties_data 2.2.0, icu_provider 2.2.0, litemap 0.8.2, potential_utf 0.1.5, tinystr 0.8.3, writeable 0.6.3, yoke 0.8.2, yoke-derive 0.8.2, zerofrom 0.1.8, zerofrom-derive 0.1.7, zerotrie 0.2.4, zerovec 0.11.6, zerovec-derive 0.11.3\n\n```\nUNICODE LICENSE V3\n\nCOPYRIGHT AND PERMISSION NOTICE\n\nCopyright \u00a9 2020-2024 Unicode, Inc.\n\nNOTICE TO USER: Carefully read the following legal agreement. BY\nDOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR\nSOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE\nTERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT\nDOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of data files and any associated documentation (the \"Data Files\") or\nsoftware and any associated documentation (the \"Software\") to deal in the\nData Files or Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, and/or sell\ncopies of the Data Files or Software, and to permit persons to whom the\nData Files or Software are furnished to do so, provided that either (a)\nthis copyright and permission notice appear with all copies of the Data\nFiles or Software, or (b) this copyright and permission notice appear in\nassociated Documentation.\n\nTHE DATA FILES AND SOFTWARE ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF\nTHIRD PARTY RIGHTS.\n\nIN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE\nBE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,\nOR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA\nFILES OR SOFTWARE.\n\nExcept as contained in this notice, the name of a copyright holder shall\nnot be used in advertising or otherwise to promote the sale, use or other\ndealings in these Data Files or Software without prior written\nauthorization of the copyright holder.\n\nSPDX-License-Identifier: Unicode-3.0\n\n\u2014\n\nPortions of ICU4X may have been adapted from ICU4C and/or ICU4J.\nICU 1.8.1 to ICU 57.1 \u00a9 1995-2016 International Business Machines Corporation and others.\n```\n\nUsed by: unicode-ident 1.0.24\n\n```\nUNICODE LICENSE V3\n\nCOPYRIGHT AND PERMISSION NOTICE\n\nCopyright \u00a9 1991-2023 Unicode, Inc.\n\nNOTICE TO USER: Carefully read the following legal agreement. BY\nDOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR\nSOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE\nTERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT\nDOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of data files and any associated documentation (the \"Data Files\") or\nsoftware and any associated documentation (the \"Software\") to deal in the\nData Files or Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, and/or sell\ncopies of the Data Files or Software, and to permit persons to whom the\nData Files or Software are furnished to do so, provided that either (a)\nthis copyright and permission notice appear with all copies of the Data\nFiles or Software, or (b) this copyright and permission notice appear in\nassociated Documentation.\n\nTHE DATA FILES AND SOFTWARE ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\nKIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF\nTHIRD PARTY RIGHTS.\n\nIN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE\nBE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,\nOR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA\nFILES OR SOFTWARE.\n\nExcept as contained in this notice, the name of a copyright holder shall\nnot be used in advertising or otherwise to promote the sale, use or other\ndealings in these Data Files or Software without prior written\nauthorization of the copyright holder.\n```\n\n### Zlib\n\nUsed by: foldhash 0.2.0\n\n```\nCopyright (c) 2024 Orson Peters\n\nThis software is provided 'as-is', without any express or implied warranty. In\nno event will the authors be held liable for any damages arising from the use of\nthis software.\n\nPermission is granted to anyone to use this software for any purpose, including\ncommercial applications, and to alter it and redistribute it freely, subject to\nthe following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not claim\n that you wrote the original software. If you use this software in a product,\n an acknowledgment in the product documentation would be appreciated but is\n not required.\n\n2. Altered source versions must be plainly marked as such, and must not be\n misrepresented as being the original software.\n\n3. This notice may not be removed or altered from any source distribution.\n```\n"; diff --git a/v2/mobile/src/lib/optimizeRows.ts b/v2/mobile/src/lib/optimizeRows.ts new file mode 100644 index 00000000..98cb0705 --- /dev/null +++ b/v2/mobile/src/lib/optimizeRows.ts @@ -0,0 +1,86 @@ +// Pure row logic for the Optimize screen. The defaults come from +// recommendation.ts — the same function the App List and Health call — so +// mobile's Recommended tab and desktop's Optimize wizard pre-select the same +// apps for the same TV. + +import type { OptimizeMode, OptimizePlanItem, Safety } from "./types"; +import { catalogItem, type AppItem } from "./appsList"; +import { isBlocked, type SafetyStatus } from "./safety"; +import { isReinstallable, recommendation, type PackageState, type RemovalMethod } from "./recommendation"; + +export type RowAction = RemovalMethod | "enable"; + +/// Enable plans come only from installed, disabled apps. Skip reasons retain +/// the state the planner observed; other actionable plans start enabled. +export function planRowState(item: OptimizePlanItem): PackageState { + if (item.action.kind === "enable") return "disabled"; + if (item.action.kind === "skip") { + if (item.action.reason === "not_installed") return "missing"; + if (item.action.reason === "already_disabled") return "disabled"; + } + return "enabled"; +} + +/// The action an actionable row runs, or null for a skip row. The engine +/// already downgrades an uninstall the store can't give back; this repeats it +/// so no plan can make an unrecoverable uninstall the action. +export function naturalAction(item: OptimizePlanItem): RowAction | null { + if (item.action.kind === "skip") return null; + if (item.action.kind === "uninstall" && !isReinstallable(item.entry)) return "disable"; + return item.action.kind; +} + +/// Lookup state for one row from the screen's verdict map and load flags. +export function rowSafety( + verdict: Safety | undefined, + loading: boolean, + failed: boolean, +): SafetyStatus { + if (verdict) return { status: "ready", verdict }; + if (loading) return { status: "checking" }; + return { status: "unavailable", reason: failed ? "lookup failed" : "no verdict" }; +} + +/// Whether a row belongs on the Recommended tab and starts selected. +/// Optimize: exactly the rows desktop pre-selects (`recommendation().kind === +/// "act"`). Restore: the catalog's `default_restore` enables. +export function isPlanRecommended( + item: OptimizePlanItem, + mode: OptimizeMode, + safety: SafetyStatus | undefined, +): boolean { + const action = naturalAction(item); + if (mode === "restore") return action === "enable" && item.entry.default_restore; + if (action !== "disable" && action !== "uninstall") return false; + return recommendation(item.entry, planRowState(item), safety).kind === "act"; +} + +/// A "remove if unused" row whose safety is resolved and not protected — +/// desktop's `isReviewRow`, which earns the amber "Review: … if unused" pill. +export function isReviewRow(item: OptimizePlanItem, safety: SafetyStatus | undefined): boolean { + const action = naturalAction(item); + return ( + !!item.entry.review && + planRowState(item) === "enabled" && + (action === "disable" || action === "uninstall") && + safety?.status === "ready" && + !isBlocked(safety.verdict) + ); +} + +/// Case-insensitive match on the app's name or package id. An empty query +/// matches everything. +export function matchesPlanQuery(item: OptimizePlanItem, query: string): boolean { + const q = query.trim().toLowerCase(); + if (!q) return true; + return ( + item.entry.name.toLowerCase().includes(q) || item.entry.package.toLowerCase().includes(q) + ); +} + +/// The row as AppDetailSheet reads it. The plan's state is what the TV said +/// when the plan was built; the catalog does not say whether it is a system +/// app, so that stays unknown. +export function planItemApp(item: OptimizePlanItem): AppItem { + return catalogItem(item.entry, planRowState(item)); +} diff --git a/v2/mobile/src/lib/recommendation.ts b/v2/mobile/src/lib/recommendation.ts new file mode 100644 index 00000000..d0bb31c4 --- /dev/null +++ b/v2/mobile/src/lib/recommendation.ts @@ -0,0 +1,119 @@ +// What we suggest doing with one catalog app, in one place. Ported from +// desktop's v2/src/lib/recommendation.ts; keep the two in step so the same app +// reads the same suggestion on phone and desktop. +// +// Pure: catalog entry, on-device state and safety verdict in, suggestion out. +// Uninstall is only ever recommended when the store can give the app back +// (`play_store || defunct`); otherwise it is downgraded to the reversible +// Disable. Nothing here is ever labelled "Remove". + +import type { AppEntry } from "./types"; +import { isBlocked, type SafetyStatus } from "./safety"; +// Single source with desktop: the sideload catalog lives beside desktop's +// recommendation.ts and is read here rather than copied. +import sideloadCatalog from "../../../src/lib/sideload-catalog.json"; + +export type PackageState = "enabled" | "disabled" | "missing"; +export type RemovalMethod = "disable" | "uninstall"; + +export type Recommendation = + | { kind: "done"; label: string } + | { kind: "act"; label: string; action: RemovalMethod } + | { kind: "review"; label: string; action: RemovalMethod } + | { kind: "restore"; label: string } + | { kind: "unavailable"; label: string } + | { kind: "keep"; label: string }; + +export const NO_CHANGE_LABEL = "No change needed"; + +export interface SideloadSource { + name: string; + url: string; +} + +const SIDELOAD_SOURCES: Record = Object.fromEntries( + (sideloadCatalog as { name: string; package: string; url: string }[]).map((s) => [ + s.package, + { name: s.name, url: s.url }, + ]), +); + +/// Where a sideloaded app comes from, when we know. Null means we don't — which +/// is not the same as "it's on the Play Store". +export function sideloadSource(pkg: string): SideloadSource | null { + return SIDELOAD_SOURCES[pkg] ?? null; +} + +/// Can the user get this back from the store after uninstalling it? Mirrors +/// the engine's `AppEntry::reinstallable` (crates/core/src/engine/types.rs). +export function isReinstallable(a: Pick): boolean { + return a.play_store || !!a.defunct; +} + +/// The method it is safe to *recommend* — mirrors the engine's `safe_method`: +/// an uninstall you cannot undo from the store is downgraded to the +/// reversible disable. +export function effectiveMethod(a: AppEntry): RemovalMethod { + return a.method === "uninstall" && !isReinstallable(a) ? "disable" : a.method; +} + +/// Whether Uninstall may be *offered* at all. Store apps, defunct services, +/// and sideloads whose source we can name — the confirm prompt names it. A +/// preinstalled app with no store listing gets Disable only. +export function canOfferUninstall(a: AppEntry): boolean { + return isReinstallable(a) || sideloadSource(a.package) !== null; +} + +export function reviewLabel(method: RemovalMethod): string { + return method === "uninstall" ? "Review: uninstall if unused" : "Review: disable if unused"; +} + +/// What we'd suggest for this row given its current on-device state. +/// `act` = recommended by default. `review` = worth removing if you don't use +/// it, never a default. `restore` = gone, and worth bringing back. +/// `done` / `keep` = nothing to do. `unavailable` = we can't say yet. +export function recommendation( + a: AppEntry, + state: PackageState | null, + safety: SafetyStatus | undefined, +): Recommendation { + if (state === null) return { kind: "unavailable", label: "State unavailable" }; + if (state === "missing") { + if (a.default_restore) return { kind: "restore", label: "Reinstall" }; + if (a.default_optimize && a.method === "uninstall") { + return { kind: "done", label: "Already uninstalled" }; + } + return { kind: "keep", label: NO_CHANGE_LABEL }; + } + const method = effectiveMethod(a); + if (safety?.status !== "ready") return { kind: "unavailable", label: "Safety unavailable" }; + if (isBlocked(safety.verdict)) return { kind: "done", label: "Protected" }; + if (safety.verdict.kind === "unknown") { + return state === "enabled" + ? { kind: "review", label: reviewLabel(method), action: method } + : { kind: "keep", label: NO_CHANGE_LABEL }; + } + if (a.default_optimize) { + if (method === "disable") { + return state === "disabled" + ? { kind: "done", label: "Already disabled" } + : { kind: "act", label: "Disable", action: "disable" }; + } + return { kind: "act", label: "Uninstall", action: "uninstall" }; + } + if (a.review && state === "enabled") { + return { kind: "review", label: reviewLabel(method), action: method }; + } + return { kind: "keep", label: NO_CHANGE_LABEL }; +} + +/// The extra paragraph an uninstall confirm carries when the store can't give +/// the app back. Null when it can, so store apps keep the short prompt. +export function uninstallNote(pkg: string, entry: AppEntry | null): string | null { + if (entry && isReinstallable(entry)) return null; + const source = sideloadSource(pkg); + if (source) { + return `Not on the Play Store. To get it back you would reinstall ${source.name} from ${source.url} — back up its APK first if you might want this exact version.`; + } + return "We can't confirm this is on the Play Store. If it isn't, getting it back needs its APK — back it up first."; +} diff --git a/v2/mobile/src/lib/router.svelte.ts b/v2/mobile/src/lib/router.svelte.ts index a67734ff..31f7fd02 100644 --- a/v2/mobile/src/lib/router.svelte.ts +++ b/v2/mobile/src/lib/router.svelte.ts @@ -9,15 +9,18 @@ export type Screen = | "onboarding" // Onboarding pushed from Devices to add a TV; never auto-dials. | "addtv" + // Bottom tabs. | "dashboard" - | "diagnostics" - | "optimize" | "apps" + | "tweaks" | "remote" | "more" - // Detail screens reached from More / Dashboard (not bottom tabs). + // Detail screens reached from Dashboard / More (not bottom tabs). Open any + // of them with `router.navigate(screen)` (or a screen's `navigate` prop), + // which pushes it so Back returns to where the user came from. + | "diagnostics" + | "optimize" | "launcher" - | "tweaks" | "snapshots" | "devices" | "riskguide" @@ -25,8 +28,13 @@ export type Screen = | "backups"; /// Screens that live behind the bottom tab bar (top-level). Everything else -/// (e.g. diagnostics) is a detail screen reached from the dashboard. -const TABS = new Set(["dashboard", "optimize", "apps", "remote", "more"]); +/// (Optimize, Diagnostics, Launcher, …) is a detail screen pushed on top. +export const TAB_SCREENS: readonly Screen[] = ["dashboard", "apps", "tweaks", "remote", "more"]; +const TABS = new Set(TAB_SCREENS); + +export function isTabScreen(screen: Screen): boolean { + return TABS.has(screen); +} // The Android back gesture reaches us as `popstate` only while the webview // has a history entry to pop. We keep exactly ONE spare entry while the stack diff --git a/v2/mobile/src/lib/safety.ts b/v2/mobile/src/lib/safety.ts index 99bf4131..8beca4f7 100644 --- a/v2/mobile/src/lib/safety.ts +++ b/v2/mobile/src/lib/safety.ts @@ -2,7 +2,8 @@ // exactly four kinds (crates/core/src/engine/safety.rs: NeverDisable / // Caution / Safe / Unknown); this maps each to the label, blurb and CSS class // every screen renders. Screens must not invent tiers and must not classify -// packages themselves. +// packages themselves. Labels and descriptions match desktop's +// v2/src/lib/safety.ts word for word, so one TV reads the same on both apps. // // `Safe` only ever comes from the reviewed app list, never as a fallback — // that is what separates "we looked at this and rated it" from "we have no @@ -10,8 +11,15 @@ import type { Safety } from "./types"; +export type { Safety }; export type SafetyKind = Safety["kind"]; +/// Resolution state of an async `safety_info` lookup — same shape as desktop. +export type SafetyStatus = + | { status: "checking" } + | { status: "ready"; verdict: Safety } + | { status: "unavailable"; reason: string }; + export interface SafetyTier { kind: SafetyKind; label: string; @@ -25,9 +33,9 @@ export interface SafetyTier { export const SAFETY_TIERS: Record = { safe: { kind: "safe", - label: "Safe", + label: "Safe to remove", description: - "Reviewed for Android TV and rated safe to remove. The reason says what it is and what you lose.", + "The reason says what the app is and what you lose. Only ever comes from the reviewed list, never as a fallback.", cls: "safe", icon: "check_circle", }, @@ -43,7 +51,7 @@ export const SAFETY_TIERS: Record = { kind: "caution", label: "Caution", description: - "Removing it has a known consequence — the reason says exactly what can stop working. You must review that warning first.", + "Removing it has a known consequence — the reason says exactly what can stop working. Review that warning first.", cls: "caution", icon: "warning", }, @@ -51,7 +59,7 @@ export const SAFETY_TIERS: Record = { kind: "never_disable", label: "Protected", description: - "Disabling would brick the TV or cut this app's connection to it. Every disable and uninstall path refuses these.", + "Disabling would break the TV or cut this app's connection to it. Every disable and uninstall path refuses these.", cls: "blocked", icon: "shield", }, @@ -73,6 +81,35 @@ export function tierOf(safety: Safety | null | undefined): SafetyTier | null { return safety ? SAFETY_TIERS[safety.kind] : null; } +/// The verdict inside a lookup state, or null while it is unresolved. +export function verdictOf(status: SafetyStatus | undefined): Safety | null { + return status?.status === "ready" ? status.verdict : null; +} + +export const CHECKING_LABEL = "Checking…"; +export const UNAVAILABLE_LABEL = "Safety unavailable"; + +/// Label for a resolved verdict. +export function verdictLabel(safety: Safety): string { + return SAFETY_TIERS[safety.kind].label; +} + +/// Short label for a lookup state — never falls back to Unknown while the +/// lookup is unresolved or failed. +export function safetyLabel(status: SafetyStatus | undefined): string { + if (!status || status.status === "unavailable") return UNAVAILABLE_LABEL; + if (status.status === "checking") return CHECKING_LABEL; + return verdictLabel(status.verdict); +} + +/// The core-supplied reason, or why there isn't one yet. +export function safetyReason(status: SafetyStatus | undefined): string { + if (!status) return "Safety lookup has not completed."; + if (status.status === "checking") return "Safety lookup is in progress."; + if (status.status === "unavailable") return `Safety lookup failed: ${status.reason}`; + return status.verdict.reason; +} + /// The core-supplied reason for every canonical verdict. export function reasonOf(safety: Safety | null | undefined): string { return safety?.reason ?? ""; @@ -87,7 +124,7 @@ export function reasonOf(safety: Safety | null | undefined): string { export function verdictSummary(safety: Safety | null | undefined): string { switch (safety?.kind) { case "safe": - return "The safety engine rated this Safe to remove."; + return `The safety engine rated this ${SAFETY_TIERS.safe.label}.`; case "caution": return "The safety engine marked this Caution."; case "never_disable": @@ -106,3 +143,8 @@ export function isBlocked(safety: Safety | null | undefined): boolean { export function needsConfirm(safety: Safety | null | undefined): boolean { return safety?.kind === "caution" || safety?.kind === "unknown"; } + +/// The Safety/Reason lines shared by every removal confirm prompt. +export function confirmVerdictLine(safety: Safety): string { + return `Safety: ${verdictLabel(safety)}\nReason: ${safety.reason}`; +} diff --git a/v2/mobile/src/lib/session.svelte.ts b/v2/mobile/src/lib/session.svelte.ts index b0b86620..9b4aaee7 100644 --- a/v2/mobile/src/lib/session.svelte.ts +++ b/v2/mobile/src/lib/session.svelte.ts @@ -21,7 +21,7 @@ import type { export type Liveness = "idle" | "connecting" | "live" | "reconnecting" | "lost"; class Session { - private connectionGeneration = Date.now() * 1000; + private connectionGeneration = $state(Date.now() * 1000); private healthGeneration = 0; private healthLoadingSerial = ""; private bloatGeneration = 0; diff --git a/v2/mobile/src/lib/snapshotPreview.ts b/v2/mobile/src/lib/snapshotPreview.ts new file mode 100644 index 00000000..f3997fb0 --- /dev/null +++ b/v2/mobile/src/lib/snapshotPreview.ts @@ -0,0 +1,87 @@ +// Rows for the snapshot "Preview restore" sheet — the mobile port of +// desktop's plan table (routes/snapshots/+page.svelte): Item, Now, What +// happens. "Now" is what the TV reported when the plan was computed; a +// setting already at the snapshot's value is listed as "Already set" rather +// than counted as work. + +import type { SnapshotApplyPlan } from "./types"; + +export type PreviewRowKind = "disable" | "launcher" | "setting" | "reset"; + +export interface PreviewRow { + item: string; + now: string; + /// What happens — the action for acting rows, the reason for the rest. + result: string; + kind: PreviewRowKind; +} + +export interface PreviewRows { + acting: PreviewRow[]; + unchanged: PreviewRow[]; +} + +export function previewRows(plan: SnapshotApplyPlan, snapshotLauncher: string | null): PreviewRows { + const now = plan.current_values ?? {}; + const currentLauncher = plan.current_launcher ?? "—"; + const acting: PreviewRow[] = []; + const unchanged: PreviewRow[] = []; + + for (const pkg of plan.packages_to_disable) { + acting.push({ item: pkg, now: "enabled", result: "Disable", kind: "disable" }); + } + if (plan.launcher_to_set) { + acting.push({ + item: "Home app", + now: currentLauncher, + result: `Set Home → ${plan.launcher_to_set}`, + kind: "launcher", + }); + } + for (const [key, value] of Object.entries(plan.settings_to_write)) { + acting.push({ item: key, now: now[key] ?? "unset", result: `Set → ${value}`, kind: "setting" }); + } + for (const key of plan.settings_to_delete) { + acting.push({ + item: key, + now: now[key] ?? "—", + result: "Reset → device default", + kind: "reset", + }); + } + + if (plan.launcher_not_installed) { + unchanged.push({ + item: "Home app", + now: currentLauncher, + result: `${plan.launcher_not_installed} isn't installed; skipped`, + kind: "launcher", + }); + } else if (!plan.launcher_to_set && snapshotLauncher !== null) { + unchanged.push({ item: "Home app", now: currentLauncher, result: "Already Home", kind: "launcher" }); + } + for (const key of plan.settings_already_set) { + unchanged.push({ item: key, now: now[key] ?? "unset", result: "Already set", kind: "setting" }); + } + for (const pkg of plan.packages_already_disabled) { + unchanged.push({ item: pkg, now: "disabled", result: "Already disabled", kind: "disable" }); + } + for (const pkg of plan.packages_not_installed) { + unchanged.push({ item: pkg, now: "not installed", result: "Not on device", kind: "disable" }); + } + return { acting, unchanged }; +} + +/// Desktop's one-line plan summary. +export function previewSummary(plan: SnapshotApplyPlan): string { + const settings = Object.keys(plan.settings_to_write).length; + const parts = [ + `${plan.packages_to_disable.length} to disable`, + `${plan.packages_already_disabled.length} already disabled`, + `${plan.packages_not_installed.length} not on device`, + `${settings} setting${settings === 1 ? "" : "s"} to write`, + `${plan.settings_to_delete.length} to reset`, + ]; + if (plan.launcher_to_set) parts.push("Home app"); + return parts.join(" · "); +} diff --git a/v2/mobile/src/lib/types.ts b/v2/mobile/src/lib/types.ts index 883423c7..7bfbb604 100644 --- a/v2/mobile/src/lib/types.ts +++ b/v2/mobile/src/lib/types.ts @@ -7,6 +7,9 @@ export type ConnectionType = "network" | "usb"; export type DeviceStatus = "device" | "unauthorized" | "offline"; export type DeviceType = "shield" | "google_tv" | "unknown"; +/// What the device said about being a TV. Distinct from DeviceType, whose +/// "unknown" only means "no catalog match" — see engine/detection.rs. +export type TvEvidence = "tv" | "not_tv" | "unknown"; export type ActionMethod = "disable" | "uninstall"; export type RiskTier = "safe" | "medium" | "high" | "advanced"; @@ -23,6 +26,9 @@ export interface DeviceProperties { characteristics?: string; /// `ro.serialno` — stable hardware identity; empty/missing when unreadable. serial_number?: string; + /// `pm has-feature android.software.leanback`. null when the device gave no + /// readable answer — which is not the same as "no". + leanback?: boolean | null; } export interface Device { @@ -31,6 +37,7 @@ export interface Device { name: string; model: string; device_type: DeviceType; + tv_evidence: TvEvidence; status: DeviceStatus; connection: ConnectionType; properties: DeviceProperties | null; @@ -45,9 +52,19 @@ export interface AppEntry { restore_description: string; default_optimize: boolean; default_restore: boolean; + /// Whether this package has a real Google Play listing at this exact id + /// (audited). Controls whether the "Play Store" button shows. play_store: boolean; + /// Discontinued service — safe to uninstall despite no Play Store listing. defunct?: boolean; + /// "Remove if unused" tier — surfaced as a candidate with a usage signal. review?: boolean; + /// When a person last reviewed this classification, `YYYY-MM-DD`. + reviewed_at?: string; + /// Short evidence notes or URLs behind the classification. + sources?: string[]; + /// Device families the entry applies to; absent means the list decides. + device_scope?: string[]; } export interface DisplayMode { @@ -81,11 +98,23 @@ export interface StorageInfo { used_percent: number | null; } +export interface TemperatureReading { + celsius: number; + source: string; + sensor_name: string | null; + sensor_type: string | null; + status: number | null; + throttling_c: number | null; + shutdown_c: number | null; +} + export interface HealthReport { display: DisplayMode; ram: RamInfo; storage: StorageInfo; temperature_c: number | null; + temperature_reading?: TemperatureReading | null; + thermal_status?: number | null; audio_device: string | null; top_memory: MemoryEntry[]; } @@ -106,6 +135,9 @@ export interface OtherPackage { enabled: boolean; /// Friendly name for recognized sideloads; null otherwise. name?: string | null; + /// One line on what the app is, from known-names.json. Display only: the + /// verdict for these packages stays Unknown. + description?: string | null; } export interface ScreenshotResult { @@ -198,6 +230,12 @@ export type Entitlement = "free" | "pro"; export interface LauncherEntry { name: string; package: string; + /// The launcher's official page, for the "Get" link on a row that isn't + /// installed. Null for stock launchers and for HOME handlers found on the + /// device rather than in the catalog. + source_url: string | null; + /// Setup helpers (Setup Wraith) a takeover turns off together with this + /// stock launcher. Present only on a stock entry that has one. disable_with?: string[]; } @@ -229,6 +267,21 @@ export interface SetLauncherResult { /// True when the only working switch is to disable the active stock launcher; /// the UI must confirm and retry with allow_stock_disable. stock_takeover_available: boolean; + /// Every command the attempt issued and what the device replied, in order. + /// Offered as copyable detail on failure. + diagnostics: string[]; +} + +/// `set_home_any` — the Advanced picker. It never disables anything. +export interface SetHomeAnyResult { + ok: boolean; + current_launcher: string | null; + /// null when the device can't answer (query-activities is Android 9+). + declares_home: boolean | null; + /// Stock still holds Home; only the separate "Disable stock launcher" step hands it over. + stock_holds_home: boolean; + message: string; + diagnostics: string[]; } // ---- Tweaks (crates/core/src/commands/tuning.rs) ---- @@ -256,6 +309,11 @@ export interface TweaksState { encoded_surround_output: string | null; /// Comma-separated AudioFormat encodings; applies only in Manual mode. encoded_surround_output_enabled_formats: string | null; + /// `secure.screensaver_components`: the active Daydream's ComponentName, or + /// null when no screensaver is configured. + screensaver_components: string | null; + /// `secure.screensaver_enabled`: whether Daydream runs at all. + screensaver_enabled: string | null; } export interface WriteResult { @@ -274,7 +332,7 @@ export interface ResourceSample { } /// Must stay in lockstep with the Rust `DisplayScalePreset` serde renames. -export type DisplayScalePreset = "uhd_4k" | "fhd_1080p" | "reset"; +export type DisplayScalePreset = "uhd_4k" | "fhd_1080p" | "hd_720p" | "reset"; export interface CurrentDisplayScaling { size: string; @@ -320,9 +378,16 @@ export interface SnapshotApplyPlan { packages_already_disabled: string[]; packages_not_installed: string[]; launcher_to_set: string | null; + /// The TV's Home app when the plan was computed; null means it couldn't say. + current_launcher: string | null; + /// The snapshot's launcher when it isn't installed on this TV. + launcher_not_installed: string | null; settings_to_write: Record; settings_to_delete: string[]; settings_already_set: string[]; + /// The TV's current value for every setting the snapshot mentions; a key + /// missing here is unset on the TV. + current_values: Record; } export interface ApplyResult { @@ -344,6 +409,18 @@ export interface AppUsage { launch_count: number; } +/// Installed storage for one package, in bytes. Disk, never memory. A field +/// the device did not report is null, which renders as unavailable — never 0. +/// `data_bytes` includes the cache, so the two are never added together. +export interface AppStorage { + app_bytes: number | null; + data_bytes: number | null; + cache_bytes: number | null; +} + +/// `app_permission_state` — a runtime permission's grant state for one package. +export type PermissionState = "granted" | "revoked" | "missing"; + // ---- Devices hub (crates/core/src/commands/health.rs) ---- export interface DeviceReport { @@ -462,3 +539,11 @@ export interface LicenseInfo { expires: string | null; key_id: number; } + +/// Result of `find_files` (crates/core commands::files::FindResult). +/// `unsearched` lists directories the search could not run against at all — +/// never to be shown as "no matches" (GitHub #86). +export interface FindResult { + hits: string[]; + unsearched: string[]; +} diff --git a/v2/mobile/src/main.ts b/v2/mobile/src/main.ts index d47b9308..72da0216 100644 --- a/v2/mobile/src/main.ts +++ b/v2/mobile/src/main.ts @@ -2,8 +2,11 @@ import { mount } from "svelte"; import "./app.css"; import App from "./App.svelte"; -const app = mount(App, { - target: document.getElementById("app")!, -}); +async function start() { + if (import.meta.env.VITE_DEMO === "1") { + (await import("./lib/demo-mock")).installDemoMock(); + } + mount(App, { target: document.getElementById("app")! }); +} -export default app; +void start(); diff --git a/v2/mobile/src/screens/Apps.svelte b/v2/mobile/src/screens/Apps.svelte index c75eec3d..0ae9c025 100644 --- a/v2/mobile/src/screens/Apps.svelte +++ b/v2/mobile/src/screens/Apps.svelte @@ -3,13 +3,33 @@ import { api } from "../lib/api"; import { session } from "../lib/session.svelte"; import { recordUnknownDiagnostics } from "../lib/unknownDiagnostics"; - import { verdictSummary } from "../lib/safety"; + import { isBlocked, safetyLabel, tierOf, verdictOf, verdictSummary, type SafetyStatus } from "../lib/safety"; + import { canOfferUninstall, recommendation, uninstallNote, type PackageState, type Recommendation } from "../lib/recommendation"; + import { isReportablePackage, reportFamily, type AppReportDevice, type AppReportState } from "../lib/app-report"; + import { hasStorage, idleMeasurements, type AppMeasurements, type ApkReportMeasurement, type MeasureStatus } from "../lib/app-details"; + import { normalizeHardwareId } from "../lib/identity"; + import { + catalogItems, + filterCatalog, + filterOthers, + hiddenMatches, + normalizeQuery, + onDeviceCount, + otherItems, + validatedStates, + type AppItem, + type AppMenuItem, + type ListFilters, + type StatusFilter, + } from "../lib/appsList"; import packageMetadata from "../../package.json"; import type { Screen } from "../lib/router.svelte"; - import type { AppUsage, OtherPackage, Safety } from "../lib/types"; + import type { AppEntry, AppStorage, AppUsage, DeviceType, OtherPackage, Safety } from "../lib/types"; import BottomTabs from "../components/BottomTabs.svelte"; import FindRemoteButton from "../components/FindRemoteButton.svelte"; import AppDetailSheet from "../components/AppDetailSheet.svelte"; + import AppActionMenu from "../components/AppActionMenu.svelte"; + import AppReportSheet from "../components/AppReportSheet.svelte"; import ConfirmDialog from "../components/ConfirmDialog.svelte"; import PaywallSheet from "../components/PaywallSheet.svelte"; import Toast from "../components/Toast.svelte"; @@ -24,9 +44,12 @@ // frames on a phone webview. let debouncedQuery = $state(""); let searchTimer: ReturnType | undefined; - let activeFilter = $state<"all" | "enabled" | "disabled">("all"); + let activeFilter = $state("all"); let showSystemApps = $state(false); - let selectedApp = $state(null); + // Desktop's default: a catalogue entry this TV doesn't have is noise until + // the user asks for it. + let hideNotInstalled = $state(true); + let selectedPkg = $state(null); // The sheet stays open after a successful uninstall so Reinstall is offered. let sheetUninstalled = $state(false); let showPaywall = $state(false); @@ -35,27 +58,44 @@ type RemovalAction = "disable" | "uninstall"; type RemovalIntent = ListIdentity & { action: RemovalAction; - app: OtherPackage; + app: AppItem; verdict: Safety; }; let listIdentity = $state(null); - let removedIdentity = $state<(ListIdentity & { app: OtherPackage }) | null>(null); + let removedIdentity = $state<(ListIdentity & { app: AppItem }) | null>(null); let removalIntent = $state(null); let loadRequest = 0; - let enrichmentRequest = 0; + let enrichmentRequest = $state(0); + let safetyRequest = 0; let removalRequest = 0; let actionRequest = 0; + let reportRequest = 0; let destroyed = false; let observedSession = ""; - let apps = $state([]); - // Lazily-loaded, best-effort enrichment maps for the detail sheet. + let others = $state([]); + let othersError = $state(""); + let catalog = $state([]); + let catalogStates = $state>({}); + let catalogSafety = $state>({}); + let catalogError = $state(""); + // Lazily-loaded, best-effort enrichment maps for the detail sheet, each + // stamped with when it was read. let memoryMap = $state>({}); let usageMap = $state>({}); + let storageMap = $state>({}); + let measures = $state(idleMeasurements()); + /// Verdicts the detail sheet resolved, so a report carries what was shown. + let sheetVerdicts = $state>({}); let lookupBusy = $state(""); let busyAction = $state(""); + let menuApp = $state(null); + let reportApp = $state(null); + let reportVerdict = $state(null); + let reportApk = $state(null); + function isLocked(e: unknown): boolean { return String(e).includes("LOCKED:"); } @@ -90,12 +130,22 @@ ); } - function currentTarget(input: OtherPackage): OtherPackage | null { + const catalogRows = $derived(catalogItems(catalog, catalogStates)); + const catalogPackages = $derived(new Set(catalogRows.map((i) => i.package))); + const otherRows = $derived(otherItems(others, catalogPackages)); + const itemByPackage = $derived.by(() => { + const map = new Map(); + for (const item of otherRows) map.set(item.package, item); + for (const item of catalogRows) map.set(item.package, item); + return map; + }); + + function currentTarget(input: AppItem): AppItem | null { if (!listCurrent()) return null; - return apps.find((candidate) => candidate.package === input.package) ?? null; + return itemByPackage.get(input.package) ?? null; } - function removedTargetCurrent(input: OtherPackage): boolean { + function removedTargetCurrent(input: AppItem): boolean { return ( removedIdentity !== null && removedIdentity.app.package === input.package && @@ -106,16 +156,30 @@ function invalidateActions() { ++removalRequest; ++actionRequest; + ++reportRequest; removalIntent = null; lookupBusy = ""; busyAction = ""; - selectedApp = null; + selectedPkg = null; sheetUninstalled = false; removedIdentity = null; + menuApp = null; + reportApp = null; + reportApk = null; + } + + async function readCatalog(serial: string, deviceType: DeviceType) { + const entries = await api.appListForDevice(deviceType); + if (!Array.isArray(entries)) throw new Error("The recognised-app list is unavailable."); + const packages = entries.map((entry) => entry.package); + const raw = packages.length > 0 ? await api.packageStates(serial, packages) : {}; + return { entries, states: validatedStates(packages, raw) }; } // Keep stale rows visible while refreshing, but make them inert until a - // successful list load is bound to this exact connection generation. + // successful list load is bound to this exact connection generation. The + // recognised list and everything else load together; either one failing + // clears only its own section, so no stale row in it stays actionable. async function loadApps(force = false) { const device = session.connectedDevice; const serial = session.serial; @@ -135,55 +199,125 @@ const request = ++loadRequest; listIdentity = null; invalidateActions(); - if (apps.length === 0) loading = true; + if (others.length === 0 && catalog.length === 0) loading = true; error = ""; try { - const result = await api.listOtherPackages(serial); + const [otherResult, catalogResult] = await Promise.allSettled([ + api.listOtherPackages(serial), + readCatalog(serial, device.device_type), + ]); if (request !== loadRequest || !sessionCurrent(serial, generation)) return; - apps = result; - loaded = true; const identity = { serial, generation, token: request }; + + if (otherResult.status === "fulfilled") { + others = otherResult.value; + othersError = ""; + } else { + others = []; + othersError = String(otherResult.reason); + } + if (catalogResult.status === "fulfilled") { + catalog = catalogResult.value.entries; + catalogStates = catalogResult.value.states; + const unread = catalog.length - Object.keys(catalogStates).length; + catalogError = + unread > 0 + ? `State unavailable for ${unread} recognised ${unread === 1 ? "app" : "apps"}. Refresh before changing them.` + : ""; + } else { + catalog = []; + catalogStates = {}; + catalogError = String(catalogResult.reason); + } + catalogSafety = {}; + + if (otherResult.status === "rejected" && catalogResult.status === "rejected") { + error = othersError; + loaded = false; + return; + } + loaded = true; listIdentity = identity; - recordUnknownDiagnostics( - result.map((app) => ({ - kind: "installed_package", - token: app.package, - reason: "uncatalogued_package", - appVersion: packageMetadata.version, - registryVersion: null, - deviceFamily: device?.device_type ?? "unknown", - deviceOs: device?.properties?.android_release || null, - })), - () => listCurrent(identity), - ); + if (otherResult.status === "fulfilled") { + const uncatalogued = otherResult.value.filter((app) => !catalogPackages.has(app.package)); + recordUnknownDiagnostics( + uncatalogued.map((app) => ({ + kind: "installed_package", + token: app.package, + reason: "uncatalogued_package", + appVersion: packageMetadata.version, + registryVersion: null, + deviceFamily: device?.device_type ?? "unknown", + deviceOs: device?.properties?.android_release || null, + })), + () => listCurrent(identity), + ); + } + void loadCatalogSafety(identity); void loadEnrichment(identity); - } catch (e) { - if (request !== loadRequest || !sessionCurrent(serial, generation)) return; - error = String(e); - loaded = false; } finally { if (request === loadRequest && sessionCurrent(serial, generation)) loading = false; } } - // Memory + usage power the detail sheet's "142 MB" / "never opened" signals. - // Best-effort: on failure the sheet simply omits those rows (no fabrication). + // Verdicts for the recognised rows' labels and suggestions. Display only: + // every removal re-reads safety at the moment of the action. + async function loadCatalogSafety(identity: ListIdentity) { + const request = ++safetyRequest; + const packages = catalog.map((entry) => entry.package); + catalogSafety = Object.fromEntries(packages.map((pkg) => [pkg, { status: "checking" }])); + const results = await Promise.allSettled(packages.map((pkg) => api.safetyInfo(pkg))); + if (request !== safetyRequest || !listCurrent(identity)) return; + catalogSafety = Object.fromEntries( + packages.map((pkg, index) => { + const result = results[index]; + return [ + pkg, + result.status === "fulfilled" + ? ({ status: "ready", verdict: result.value } satisfies SafetyStatus) + : ({ status: "unavailable", reason: String(result.reason) } satisfies SafetyStatus), + ]; + }), + ); + } + + // Memory, usage and storage power the detail sheet's figures. Best-effort: + // a failed read is shown as unavailable with when it failed, never as zero. async function loadEnrichment(identity: ListIdentity) { const request = ++enrichmentRequest; + const current = () => request === enrichmentRequest && listCurrent(identity); + measures = { memory: { status: "loading" }, usage: { status: "loading" }, storage: { status: "loading" } }; + const settle = (key: keyof AppMeasurements, status: MeasureStatus) => { + if (current()) measures = { ...measures, [key]: status }; + }; try { const memory = await api.appMemoryMap(identity.serial); - if (request === enrichmentRequest && listCurrent(identity)) memoryMap = memory; - } catch { - // leave empty + if (current()) memoryMap = memory; + settle("memory", { status: "ready", at: Date.now() }); + } catch (e) { + settle("memory", { status: "unavailable", at: Date.now(), reason: String(e) }); } try { const usage = await api.appUsageMap(identity.serial); - if (request === enrichmentRequest && listCurrent(identity)) usageMap = usage; - } catch { - // leave empty + if (current()) usageMap = usage; + settle("usage", { status: "ready", at: Date.now() }); + } catch (e) { + settle("usage", { status: "unavailable", at: Date.now(), reason: String(e) }); + } + try { + const storage = await api.appStorageMap(identity.serial); + if (current()) storageMap = storage; + settle("storage", { status: "ready", at: Date.now() }); + } catch (e) { + settle("storage", { status: "unavailable", at: Date.now(), reason: String(e) }); } } + function remeasure() { + const identity = listIdentity; + if (listCurrent(identity)) void loadEnrichment(identity); + } + // generation is a plain getter, so the reactive device and liveness reads // are intentional: same-serial reconnects still invalidate the list. $effect(() => { @@ -196,11 +330,16 @@ untrack(() => { ++loadRequest; ++enrichmentRequest; + ++safetyRequest; listIdentity = null; loaded = false; invalidateActions(); memoryMap = {}; usageMap = {}; + storageMap = {}; + measures = idleMeasurements(); + sheetVerdicts = {}; + catalogSafety = {}; if (device && liveness === "live") void loadApps(true); else { loading = false; @@ -212,7 +351,7 @@ $effect(() => { const q = searchQuery; clearTimeout(searchTimer); - searchTimer = setTimeout(() => (debouncedQuery = q.trim().toLowerCase()), 120); + searchTimer = setTimeout(() => (debouncedQuery = normalizeQuery(q)), 120); return () => clearTimeout(searchTimer); }); @@ -220,78 +359,81 @@ destroyed = true; ++loadRequest; ++enrichmentRequest; + ++safetyRequest; invalidateActions(); clearTimeout(searchTimer); clearTimeout(toastTimer); + clearTimeout(pressTimer); }); - // One lowercase haystack per app, rebuilt only when the list itself changes. - const haystacks = $derived.by(() => { - const map = new Map(); - for (const app of apps) { - map.set(app.package, `${app.name ?? ""} ${app.package}`.toLowerCase()); - } - return map; - }); - - function matchesStatus(app: OtherPackage): boolean { - if (activeFilter === "enabled") return app.enabled; - if (activeFilter === "disabled") return !app.enabled; - return true; - } - - const visibleApps = $derived.by(() => - apps.filter((app) => (showSystemApps || !app.system) && matchesStatus(app)), - ); - - const filteredApps = $derived.by(() => { - const q = debouncedQuery; - return visibleApps.filter( - (app) => !q || (haystacks.get(app.package) ?? "").includes(q), - ); - }); - - const systemAppCount = $derived(apps.filter((app) => app.system).length); - const hiddenSystemMatches = $derived.by(() => { - if (showSystemApps) return 0; - const q = debouncedQuery; - return apps.filter( - (app) => - app.system && - matchesStatus(app) && - (!q || (haystacks.get(app.package) ?? "").includes(q)), - ).length; + const filters = $derived({ + query: debouncedQuery, + status: activeFilter, + showSystem: showSystemApps, + hideNotInstalled, }); + const visibleCatalog = $derived(filterCatalog(catalogRows, filters)); + const visibleOthers = $derived(filterOthers(otherRows, filters)); + const visibleCount = $derived(visibleCatalog.length + visibleOthers.length); + const hidden = $derived(hiddenMatches(catalogRows, otherRows, filters)); + const systemAppCount = $derived(otherRows.filter((app) => app.system === true).length); + const notInstalledCount = $derived(catalogRows.filter((app) => app.state === "missing").length); const countText = $derived( debouncedQuery - ? `${filteredApps.length} ${filteredApps.length === 1 ? "result" : "results"}` - : `${visibleApps.length} of ${apps.length} visible`, + ? `${visibleCount} ${visibleCount === 1 ? "result" : "results"}` + : `${onDeviceCount(visibleCatalog) + onDeviceCount(visibleOthers)} of ${onDeviceCount(catalogRows) + onDeviceCount(otherRows)} visible`, ); + const sheetApp = $derived.by((): AppItem | null => { + if (!selectedPkg) return null; + if (sheetUninstalled && removedIdentity?.app.package === selectedPkg) return removedIdentity.app; + return itemByPackage.get(selectedPkg) ?? null; + }); + /// Friendly name when the backend actually has one, otherwise the package. /// Never the package's last segment — that invents names like "Tv". - function label(app: OtherPackage): string { + function label(app: AppItem): string { return app.name || app.package; } - function patch(pkg: string, enabled: boolean, identity: ListIdentity) { + function rowRecommendation(app: AppItem): Recommendation | null { + if (!app.entry) return null; + const rec = recommendation(app.entry, app.state, catalogSafety[app.package]); + // "No change needed" and "unavailable" on every row would drown out the + // rows that do have a suggestion; the verdict chip already says the rest. + // "Protected" is already the verdict chip. + if (rec.kind === "keep" || rec.kind === "unavailable") return null; + return rec.kind === "done" && isBlocked(verdictOf(catalogSafety[app.package])) ? null : rec; + } + + function recText(rec: Recommendation): string { + return rec.kind === "act" || rec.kind === "restore" ? `Suggested: ${rec.label}` : rec.label; + } + + function patch(pkg: string, state: PackageState, identity: ListIdentity) { if (!listCurrent(identity)) return; - apps = apps.map((a) => (a.package === pkg ? { ...a, enabled } : a)); + if (catalogPackages.has(pkg)) { + catalogStates = { ...catalogStates, [pkg]: state }; + } else if (state === "missing") { + others = others.filter((a) => a.package !== pkg); + } else { + others = others.map((a) => (a.package === pkg ? { ...a, enabled: state === "enabled" } : a)); + } } function actionCurrent(request: number, identity: ListIdentity): boolean { return request === actionRequest && listCurrent(identity); } - async function enableCurrent(input: OtherPackage) { + async function enableCurrent(input: AppItem) { if (busyAction) return; const app = currentTarget(input); const identity = listIdentity; - if (!app || !identity || app.enabled) return; + if (!app || !identity || app.state !== "disabled") return; const request = ++actionRequest; busyAction = app.package; - patch(app.package, true, identity); - selectedApp = null; + patch(app.package, "enabled", identity); + selectedPkg = null; try { const r = await api.enablePackage(identity.serial, app.package); if (!actionCurrent(request, identity)) return; @@ -307,7 +449,7 @@ } } catch (e) { if (!actionCurrent(request, identity)) return; - patch(app.package, false, identity); + patch(app.package, "disabled", identity); if (isLocked(e)) showPaywall = true; else showToast(String(e), "error"); } finally { @@ -315,11 +457,17 @@ } } - async function requestRemoval(input: OtherPackage, action: RemovalAction) { + function removalAllowed(app: AppItem, action: RemovalAction): boolean { + if (action === "disable") return app.state === "enabled"; + if (app.state !== "enabled" && app.state !== "disabled") return false; + return !app.entry || canOfferUninstall(app.entry); + } + + async function requestRemoval(input: AppItem, action: RemovalAction) { if (busyAction || lookupBusy) return; const app = currentTarget(input); const identity = listIdentity; - if (!app || !identity || (action === "disable" && !app.enabled)) { + if (!app || !identity || !removalAllowed(app, action)) { showToast("This app list is no longer current. Refresh and try again.", "error"); return; } @@ -334,7 +482,7 @@ return; } removalIntent = { ...identity, action, app, verdict }; - selectedApp = null; + selectedPkg = null; } catch { if (request !== removalRequest || !listCurrent(identity)) return; showToast("Safety unavailable. Retry before removing this app.", "error"); @@ -343,16 +491,16 @@ } } - function handleToggle(app: OtherPackage) { - if (app.enabled) void requestRemoval(app, "disable"); - else void enableCurrent(app); + function handleToggle(app: AppItem) { + if (app.state === "enabled") void requestRemoval(app, "disable"); + else if (app.state === "disabled") void enableCurrent(app); } - function requestUninstall(app: OtherPackage) { + function requestUninstall(app: AppItem) { void requestRemoval(app, "uninstall"); } - async function handleForceStop(input: OtherPackage) { + async function handleForceStop(input: AppItem) { if (busyAction) return; const app = currentTarget(input); const identity = listIdentity; @@ -372,7 +520,7 @@ } } - async function handlePlayStore(input: OtherPackage) { + async function handlePlayStore(input: AppItem) { if (busyAction) return; const app = sheetUninstalled && removedTargetCurrent(input) ? input : currentTarget(input); const serial = sheetUninstalled ? removedIdentity?.serial : listIdentity?.serial; @@ -420,8 +568,8 @@ } if (intent.action === "disable") { - patch(intent.app.package, false, intent); - selectedApp = null; + patch(intent.app.package, "disabled", intent); + selectedPkg = null; } const r = intent.action === "disable" @@ -430,10 +578,16 @@ if (!actionCurrent(request, intent)) return; if (r.ok) { if (intent.action === "uninstall") { - apps = apps.filter((a) => a.package !== intent.app.package); - removedIdentity = { ...intent, app: intent.app }; - selectedApp = intent.app; - sheetUninstalled = true; + const recognised = catalogPackages.has(intent.app.package); + patch(intent.app.package, "missing", intent); + selectedPkg = intent.app.package; + // A recognised row stays in the list as "not installed", which + // already offers Reinstall; anything else leaves the list, so the + // sheet keeps it to offer Reinstall here. + if (!recognised) { + removedIdentity = { ...intent, app: { ...intent.app, state: "missing" } }; + sheetUninstalled = true; + } } showToast(`${intent.action === "disable" ? "Disabled" : "Uninstalled"} ${label(intent.app)}.`, "success"); session.invalidateAll(); @@ -447,7 +601,7 @@ } } catch (e) { if (!actionCurrent(request, intent)) return; - if (intent.action === "disable") patch(intent.app.package, true, intent); + if (intent.action === "disable") patch(intent.app.package, "enabled", intent); if (isLocked(e)) showPaywall = true; else showToast(String(e), "error"); } finally { @@ -458,17 +612,21 @@ // `install-existing` restores an APK that is still on the TV (the usual case // for a system app removed with `pm uninstall --user 0`). It cannot conjure // an app that was never installed — that's what the Play Store button is for. - async function handleReinstall(app: OtherPackage) { - if (busyAction || !removedTargetCurrent(app) || !removedIdentity) return; - const identity = removedIdentity; + async function handleReinstall(app: AppItem) { + if (busyAction) return; + let identity: ListIdentity | null = null; + if (sheetUninstalled && removedTargetCurrent(app) && removedIdentity) identity = removedIdentity; + else if (currentTarget(app)?.state === "missing" && listIdentity) identity = listIdentity; + if (!identity) return; + const target = identity; const request = ++actionRequest; busyAction = app.package; try { - const r = await api.reinstallExisting(identity.serial, app.package); - if (request !== actionRequest || !sessionCurrent(identity.serial, identity.generation)) return; + const r = await api.reinstallExisting(target.serial, app.package); + if (request !== actionRequest || !sessionCurrent(target.serial, target.generation)) return; if (r.ok) { showToast(`Reinstalled ${label(app)}.`, "success"); - selectedApp = null; + selectedPkg = null; sheetUninstalled = false; removedIdentity = null; session.invalidateAll(); @@ -477,7 +635,7 @@ showToast(r.message || "Reinstall failed. Try the Play Store.", "error"); } } catch (e) { - if (request !== actionRequest || !sessionCurrent(identity.serial, identity.generation)) return; + if (request !== actionRequest || !sessionCurrent(target.serial, target.generation)) return; if (isLocked(e)) showPaywall = true; else showToast(String(e), "error"); } finally { @@ -485,27 +643,217 @@ } } + function openDetail(app: AppItem) { + if (!listCurrent()) return; + sheetUninstalled = false; + removedIdentity = null; + selectedPkg = app.package; + } + function closeDetail() { ++removalRequest; lookupBusy = ""; - selectedApp = null; + selectedPkg = null; sheetUninstalled = false; removedIdentity = null; } - function iconFor(app: OtherPackage): string { + function iconFor(app: AppItem): string { if (app.system) return "system_update"; const n = (app.name ?? "").toLowerCase(); return n.includes("video") || n.includes("tv") ? "smart_display" : "apps"; } + + // ---- Report this app (#100) ---- + + const reportDevice = $derived.by((): AppReportDevice | null => { + const d = session.connectedDevice; + if (!d) return null; + return { + family: reportFamily(d.device_type, d.tv_evidence), + androidVersion: d.properties?.android_release || null, + // A placeholder ro.serialno ("unknown") identifies nothing, and + // scrubbing it would eat that word from the user's note. + redact: [session.serial, d.serial, d.properties?.serial_number].filter( + (id): id is string => !!normalizeHardwareId(id), + ), + }; + }); + + function canReport(app: AppItem | null): boolean { + return !!app && !!reportDevice && isReportablePackage(app.package); + } + + function knownVerdict(pkg: string): Safety | null { + // A failed fresh sheet lookup supersedes an older catalog verdict too. + return Object.prototype.hasOwnProperty.call(sheetVerdicts, pkg) + ? sheetVerdicts[pkg] + : verdictOf(catalogSafety[pkg]) ?? null; + } + + function openReport(app: AppItem, apk?: ApkReportMeasurement) { + if (!canReport(app)) return; + const request = ++reportRequest; + reportApp = app; + reportApk = apk ?? null; + reportVerdict = knownVerdict(app.package); + if (reportVerdict) return; + const serial = session.serial; + const generation = session.generation; + api.safetyInfo(app.package).then( + (verdict) => { + if (request === reportRequest && reportApp?.package === app.package && sessionCurrent(serial, generation)) { + reportVerdict = verdict; + } + }, + () => {}, + ); + } + + function reportState(app: AppItem): AppReportState { + const pkg = app.package; + const memRead = measures.memory.status === "ready"; + const mb = memoryMap[pkg]; + const running = memRead ? mb !== undefined && mb > 0 : null; + const stored = storageMap[pkg]; + const apk = reportApk; + const apkCurrent = apk && listCurrent() && sessionCurrent(apk.serial, apk.generation) + && apk.package === pkg && apk.measurementId === enrichmentRequest + && (measures.storage.status === "ready" || measures.storage.status === "unavailable") + && !sheetUninstalled && app.state !== "missing" + && Number.isFinite(apk.appBytes) && apk.appBytes >= 0 && Number.isFinite(apk.at); + return { + status: app.state, + running, + ramMb: running ? (mb ?? null) : null, + storage: + measures.storage.status === "ready" && hasStorage(stored) + ? { source: "diskstats", ...stored } + : apkCurrent + ? { source: "apk_files", app_bytes: apk.appBytes, data_bytes: null, cache_bytes: null } + : null, + }; + } + + // ---- Long-press menu (#129's phone counterpart) ---- + + let pressTimer: ReturnType | undefined; + let pressOrigin: { x: number; y: number } | null = null; + // The tap that ends a long-press must not also open the detail sheet. + let suppressClickFor = ""; + + function startPress(e: PointerEvent, app: AppItem) { + suppressClickFor = ""; + if (e.pointerType === "mouse" && e.button !== 0) return; + clearTimeout(pressTimer); + pressOrigin = { x: e.clientX, y: e.clientY }; + pressTimer = setTimeout(() => { + pressOrigin = null; + suppressClickFor = app.package; + openMenu(app); + }, 480); + } + + function movePress(e: PointerEvent) { + if (pressOrigin && Math.hypot(e.clientX - pressOrigin.x, e.clientY - pressOrigin.y) > 10) cancelPress(); + } + + function cancelPress() { + clearTimeout(pressTimer); + pressOrigin = null; + } + + function onRowContextMenu(e: MouseEvent, app: AppItem) { + // The WebView's own long-press menu (and text selection) means nothing on + // a row; ours replaces it. + e.preventDefault(); + cancelPress(); + suppressClickFor = app.package; + openMenu(app); + } + + function onRowClick(app: AppItem) { + if (suppressClickFor === app.package) { + suppressClickFor = ""; + return; + } + openDetail(app); + } + + function openMenu(app: AppItem) { + if (menuApp?.package === app.package) return; + try { + navigator.vibrate?.(12); + } catch { + // Haptics are a nicety. + } + menuApp = app; + } + + async function copyPackage(app: AppItem) { + try { + await navigator.clipboard.writeText(app.package); + showToast("Copied package name.", "success"); + } catch { + showToast("Couldn't reach the clipboard.", "error"); + } + } + + const menuItems = $derived.by((): AppMenuItem[] => { + const app = menuApp ? (itemByPackage.get(menuApp.package) ?? menuApp) : null; + if (!app) return []; + const live = listIdentity !== null && itemByPackage.has(app.package); + const busy = busyAction !== "" || lookupBusy !== ""; + const verdict = verdictOf(catalogSafety[app.package]); + const items: AppMenuItem[] = [ + { id: "details", label: "Details", icon: "info", disabled: !live, run: () => openDetail(app) }, + ]; + if (app.state === "enabled") { + items.push({ + id: "disable", + label: "Disable", + icon: "block", + danger: true, + disabled: !live || busy || isBlocked(verdict), + run: () => void requestRemoval(app, "disable"), + }); + } else if (app.state === "disabled") { + items.push({ + id: "enable", + label: "Enable", + icon: "check_circle", + disabled: !live || busy, + run: () => void enableCurrent(app), + }); + } + items.push({ id: "copy", label: "Copy package name", icon: "content_copy", run: () => void copyPackage(app) }); + if (canReport(app)) { + items.push({ id: "report", label: "Report this app", icon: "warning", run: () => openReport(app) }); + } + return items; + }); + + function rowSafety(app: AppItem): SafetyStatus | undefined { + return catalogSafety[app.package]; + } + + function stateBadge(app: AppItem): { text: string; off: boolean } | null { + if (app.state === "enabled") return { text: "ON", off: false }; + if (app.state === "disabled") return { text: "OFF", off: true }; + if (app.state === null) return { text: "?", off: true }; + return null; + } + + const removalNote = $derived.by(() => { + const intent = removalIntent; + if (!intent || intent.action !== "uninstall") return ""; + return uninstallNote(intent.app.package, intent.app.entry) ?? ""; + });
-

Apps

@@ -519,13 +867,13 @@ placeholder="Search apps or packages" bind:value={searchQuery} class="search-input" + autocomplete="off" + autocapitalize="off" + spellcheck="false" />
-

- Everything else installed on the TV. The bloat this app recognizes lives on the - Optimize tab, so searching for it here finds nothing. -

+

Search covers every app on the TV, by name or package. Long-press an app for quick actions.

@@ -533,40 +881,64 @@
- + + + +
- {#if loading && apps.length === 0} + {#if loading && others.length === 0 && catalog.length === 0}
Loading packages…
- {:else if error && apps.length === 0} + {:else if error && others.length === 0 && catalog.length === 0}

{error}

{:else}
- {#each filteredApps as app (app.package)} + {#snippet row(app: AppItem)} + {@const safety = rowSafety(app)} + {@const rec = rowRecommendation(app)} + {@const badge = stateBadge(app)} - {/each} - {#if filteredApps.length === 0} + {/snippet} + + {#if catalogError} + + {/if} + {#if visibleCatalog.length > 0} +
+

Recognised apps {visibleCatalog.length}

+

Apps we have notes on, with what removing each one costs.

+
+ {#each visibleCatalog as app (app.package)} + {@render row(app)} + {/each} + {/if} + + {#if othersError} + + {/if} + {#if visibleOthers.length > 0} +
+

Everything else {visibleOthers.length}

+

Installed apps we have no notes on. Removals still go through the same safety checks.

+
+ {#each visibleOthers as app (app.package)} + {@render row(app)} + {/each} + {/if} + + {#if visibleCount === 0}

{debouncedQuery ? "No matching apps" : "No apps to show"}

{#if debouncedQuery} No {activeFilter === "all" ? "" : `${activeFilter} `}apps match “{searchQuery.trim()}” in this view. - {:else if showSystemApps && activeFilter === "all"} - No apps are available in this non-curated list. - {:else if showSystemApps} - No {activeFilter} apps are available in this non-curated list. {:else if activeFilter === "all"} - No user-installed apps are available in this non-curated list. + No apps are available in this view. {:else} - No {activeFilter} user-installed apps are available in this non-curated list. + No {activeFilter} apps are available in this view. {/if}

@@ -601,11 +1020,16 @@ {#if debouncedQuery} {/if} - {#if hiddenSystemMatches > 0} + {#if hidden.system > 0} {/if} + {#if hidden.notInstalled > 0} + + {/if}
{/if} @@ -615,19 +1039,44 @@
(sheetVerdicts = { ...sheetVerdicts, [pkg]: verdict })} /> + (menuApp = null)} /> + + {#if reportApp && reportDevice} + { + ++reportRequest; + reportApp = null; + reportApk = null; + }} + /> + {/if} + { @@ -706,8 +1155,8 @@ .filters-row { display: flex; + flex-wrap: wrap; gap: 8px; - overflow-x: auto; margin-bottom: 10px; padding-bottom: 2px; } @@ -730,16 +1179,27 @@ font-weight: 600; } - .system-toggle { + .toggles { display: flex; - align-items: center; - gap: 12px; - min-height: 48px; - padding: 8px 12px; + flex-direction: column; margin-bottom: 14px; border: 1px solid var(--line); border-radius: 13px; background: var(--surface); + overflow: hidden; + } + .system-toggle + .system-toggle { + border-top: 1px solid var(--line); + } + .system-toggle { + display: flex; + align-items: center; + gap: 12px; + min-height: 46px; + padding: 6px 12px; + border: 0; + border-radius: 0; + background: transparent; color: var(--text); font-family: var(--sans); text-align: left; @@ -858,9 +1318,118 @@ cursor: pointer; width: 100%; } + .app-row { + /* A long-press opens the action menu; it must not start a text + selection or the WebView's own callout. */ + user-select: none; + -webkit-user-select: none; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: transparent; + } .app-row.selected { border-color: var(--accent); } + .app-row.missing .app-avatar, + .app-row.missing .app-name-text { + opacity: 0.6; + } + .app-desc { + font-size: 11px; + line-height: 1.35; + color: var(--text-soft); + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + margin-top: 2px; + } + .row-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 4px 6px; + margin-top: 4px; + min-width: 0; + } + .chip { + font-size: 10px; + font-weight: 700; + padding: 2px 7px; + border-radius: 6px; + white-space: nowrap; + color: var(--muted); + background: color-mix(in srgb, var(--text) 6%, transparent); + } + .verdict-chip.safe { + color: var(--teal); + background: color-mix(in srgb, var(--teal) 14%, transparent); + } + .verdict-chip.caution, + .verdict-chip.unavailable { + color: var(--amber); + background: color-mix(in srgb, var(--amber) 14%, transparent); + } + .verdict-chip.blocked { + color: var(--danger); + background: color-mix(in srgb, var(--danger) 14%, transparent); + } + .verdict-chip.checking { + font-weight: 500; + } + .rec-text { + font-size: 11px; + color: var(--muted); + min-width: 0; + } + .rec-text.act { + color: var(--accent); + font-weight: 600; + } + .section-head { + display: flex; + flex-direction: column; + gap: 2px; + margin: 6px 2px 0; + } + .section-head h4 { + margin: 0; + font-size: 13px; + font-weight: 700; + display: flex; + align-items: baseline; + gap: 6px; + } + .section-head h4 .mono { + font-size: 11px; + font-weight: 500; + color: var(--muted); + } + .section-head p { + margin: 0; + font-size: 11px; + color: var(--muted); + line-height: 1.4; + } + .section-error { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; + border-radius: 12px; + border: 1px solid color-mix(in srgb, var(--amber) 40%, transparent); + font-size: 12px; + color: var(--amber); + } + .section-error span { + flex: 1; + min-width: 0; + overflow-wrap: anywhere; + } + .section-error button { + width: auto; + flex: none; + } .app-avatar { width: 40px; height: 40px; diff --git a/v2/mobile/src/screens/Dashboard.svelte b/v2/mobile/src/screens/Dashboard.svelte index 17d4beb5..c6338999 100644 --- a/v2/mobile/src/screens/Dashboard.svelte +++ b/v2/mobile/src/screens/Dashboard.svelte @@ -212,59 +212,57 @@ Loading stats…
{:else} - {#if session.healthError} -
- error -
- Couldn't read device health - {session.healthError} -
- -
- {:else} - -
+ +
+
{session.bloatLoading ? "…" : bloatKnown ? session.bloatCount : "—"} Enabled
+ Optimize Recommended app review - {#if session.bloatLoading} - Checking installed recommended apps… - {:else if !bloatKnown} - {session.bloatError || "App status unavailable."} - - {:else if session.bloatCount > 0} - + + {#if session.bloatLoading} + Checking installed recommended apps… + {:else if !bloatKnown} + {session.bloatError || "App status unavailable."} + {:else if session.bloatCount > 0} {session.bloatCount} of {session.bloatTotal} installed recommended apps are enabled. - - - {:else if session.bloatTotal > 0} - No recommended apps are enabled. You can still review optional apps. - - {:else} - No apps from the recommended list are installed. You can still review optional apps. - {/if}
+ +
+ + {#if session.healthError} +
+ error +
+ Couldn't read device health + {session.healthError} +
+ +
+ {:else}
{/if} + +
+ + +
+
- -
Current TV screen -

{screenshot.path}

-

Saved in the app's private storage on this phone. Sharing and export are not available yet.

+

Private app location: {screenshot.path}

+

Saved in ATV Optimizer's private storage on this phone, not in Gallery or Downloads. Sharing and export are not available yet.

+

Close screenshot preview hides this view. The saved image stays in the app.

{/if} @@ -466,15 +477,17 @@ } .shot-img { width: 100%; + box-sizing: border-box; border-radius: 12px; border: 1px solid var(--line); background: #000; } .shot-note { margin: 0; - font-size: 11px; + font-size: .875rem; + line-height: 1.5; color: var(--muted); - word-break: break-all; + overflow-wrap: anywhere; } .dropdown-item { width: 100%; @@ -580,20 +593,25 @@ word-break: break-word; } - /* Health Card */ + /* Optimize CTA card */ .health-card { display: flex; - gap: 18px; - align-items: center; - padding: 20px; + flex-direction: column; + gap: 16px; + padding: 18px; border-radius: 22px; background: linear-gradient(160deg, color-mix(in srgb, var(--accent) 10%, #141519), #141519); border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent); margin-bottom: 8px; } + .cta-top { + display: flex; + gap: 16px; + align-items: center; + } .review-count { - width: 86px; - min-height: 86px; + width: 80px; + min-height: 80px; flex: none; display: flex; flex-direction: column; @@ -619,9 +637,17 @@ .health-details { display: flex; flex-direction: column; - gap: 8px; + gap: 5px; min-width: 0; } + .cta-eyebrow { + font-family: var(--mono); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--accent); + } .health-title { font-size: 17px; font-weight: 700; @@ -650,15 +676,87 @@ .optimize-link .msr { font-size: 15px; } + .cta-btn { + display: flex; + align-items: center; + gap: 9px; + width: 100%; + min-height: 50px; + padding: 0 16px; + border: none; + border-radius: 14px; + background: var(--accent); + color: var(--accent-ink); + font-family: var(--sans); + font-size: 15px; + font-weight: 700; + cursor: pointer; + text-align: left; + } + .cta-btn:active { + opacity: 0.9; + } + .cta-btn .msr { + font-size: 20px; + } + .cta-label { + flex: 1; + } + .cta-btn .pro-marker { + background: color-mix(in srgb, var(--accent-ink) 14%, transparent); + color: var(--accent-ink); + } .pro-marker { padding: 2px 5px; border-radius: 5px; background: color-mix(in srgb, var(--accent) 18%, transparent); + color: var(--accent); font-family: var(--mono); font-size: 8px; + font-weight: 700; letter-spacing: 0.06em; } + /* Tune tiles */ + .tune-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + } + .tune-tile { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 6px; + padding: 14px; + border-radius: 16px; + background: var(--surface); + border: 1px solid var(--line); + cursor: pointer; + text-align: left; + font-family: var(--sans); + color: var(--text); + } + .tune-tile:active { + background: var(--surface-2); + } + .tune-icon { + font-size: 22px; + color: var(--accent); + } + .tune-title { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 14px; + font-weight: 600; + } + .tune-desc { + font-size: 11px; + line-height: 1.35; + color: var(--muted); + } + /* Stats Grid */ .stats-grid { display: flex; @@ -700,24 +798,28 @@ .quick-actions-section { display: flex; flex-direction: column; - gap: 11px; - margin-top: 12px; + } + .quick-actions-section .section-label { + margin-bottom: 12px; } .actions-grid { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: repeat(3, 1fr); gap: 10px; } .action-btn { display: flex; + flex-direction: column; align-items: center; - gap: 11px; - padding: 14px; + justify-content: center; + gap: 7px; + min-height: 74px; + padding: 12px 6px; + text-align: center; border-radius: 15px; background: var(--surface); border: 1px solid var(--line); cursor: pointer; - text-align: left; font-family: var(--sans); font-size: 13px; font-weight: 600; @@ -734,11 +836,8 @@ font-size: 22px; color: var(--text-soft); } - .action-icon.primary-color { - color: var(--accent); - } .action-text { - font-size: 13px; + font-size: 12px; font-weight: 600; } .bottom-callout { diff --git a/v2/mobile/src/screens/Diagnostics.svelte b/v2/mobile/src/screens/Diagnostics.svelte index 47b0ec1f..a5531c26 100644 --- a/v2/mobile/src/screens/Diagnostics.svelte +++ b/v2/mobile/src/screens/Diagnostics.svelte @@ -1,11 +1,24 @@ @@ -189,7 +664,9 @@

Launcher

- +
{#if loading} @@ -198,7 +675,7 @@ {:else if error}

{error}

- +
{:else}
@@ -216,12 +693,15 @@ {:else} {activeLabel} + {#if current?.note} + {current.note} + {/if} {/if}
{#if activeLabel !== ""} DEFAULT {:else} - + {/if} @@ -245,11 +725,21 @@ {#each launchers as l (l.entry.package)} {@const isActive = l.entry.package === currentPkg} {@const busy = busyPkg === l.entry.package} + {@const host = !l.installed && !l.stock ? sourceSiteHost(l.entry.source_url) : null}
{iconFor(l)}
{l.entry.name} {subtitle(l)} + {#if host} + + {/if}
{#if busy} @@ -257,18 +747,23 @@ {:else if isActive} Active {:else if !l.installed && !l.stock} - {:else if l.setup_helper} - + + {#if l.enabled} + + {/if} {:else if l.stock && l.enabled} - {:else} - {/if} @@ -277,14 +772,165 @@
{/if} + {#if wraith} + + {/if} + + {#if storeOpened} +
+ check_circle + + Opened the Play Store on the TV. Confirm the install there. + + {storeOpened.checking + ? `Checking for ${storeOpened.name} every few seconds.` + : `Stopped checking for ${storeOpened.name}. Tap Refresh once the install finishes.`} + + + +
+ {/if} + + {#if linkShown} +
+ info + + Couldn't copy the link for {linkShown.name}. Select it and open it in your phone's + browser: + {linkShown.url} + + +
+ {/if} + + {#if actionNote} +
+

{actionNote}

+ +
+ {/if} +
info - Changing the launcher is reversible — set stock back as default any time. A snapshot - records which launcher was active so you can re-apply it later; it never re-enables or - reinstalls anything on its own. + Changing the launcher is reversible — set stock back as default any time. Disabling the + current home app without setting a replacement first leaves the TV with no home screen, + so save a snapshot before you change this. A snapshot records which launcher was active; + it never re-enables or reinstalls anything on its own.
+ + + + {#if advancedOpen} +
+

+ Any installed app can be tried. Android only accepts an app that declares a Home + screen, and this says so when it doesn't. Setting an app never disables anything; + taking over from the stock launcher is the separate step below. +

+ + {#if pickerOpen} + loadPicker(true)} + onCancel={() => (pickerOpen = false)} + /> + {:else} +
+ {choice ? "android" : "apps"} + + {#if choice} + {choice.name ?? choice.package} + {choice.package} + {:else} + No app chosen + {/if} + + +
+ + + + + + + {#if !gate.allowed} +

{gate.reason}

+ {/if} + + {#if homeResult} +

{homeResult.text}

+ {/if} + + {/if} +
+ {/if}
{/if} @@ -294,6 +940,9 @@ icon="home" title="Disable the stock launcher?" message={`On this TV the only way to hand Home to ${takeover?.name ?? "this launcher"} is to disable the stock launcher. Your other launchers are left alone, and stock can be re-enabled here any time.`} + warning={takeoverTurnsOffWraith + ? "Also turns off Google TV's setup helper (Setup Wraith), or it takes the Home button back. You may need to turn it back on briefly to sign in to Google again or pair a remote." + : ""} confirmLabel="Disable stock & switch" onConfirm={confirmTakeover} onCancel={() => (takeover = null)} @@ -303,13 +952,26 @@ open={disableConfirm !== null} danger icon="block" - title={`Disable ${disableConfirm?.entry.name ?? "launcher"}?`} - message="The TV will fall back to another enabled launcher for its home screen. You can re-enable this one from the Apps list." + title={disableConfirm?.setup_helper + ? "Disable Setup Wraith?" + : `Disable ${disableConfirm?.entry.name ?? "launcher"}?`} + message={disableConfirm?.setup_helper + ? "This is Google TV's setup wizard, not a launcher. You don't need to disable it: the app already shows your real launcher as current. Disabling it is only useful if the Home button keeps landing on a setup screen. It stays disabled until you re-enable it here, and Android TV needs it to run first-time setup. Re-enable it before a factory reset." + : "The TV will fall back to another enabled launcher for its home screen. You can re-enable this one here or from the Apps list."} confirmLabel="Disable" onConfirm={() => disableConfirm && doDisable(disableConfirm)} onCancel={() => (disableConfirm = null)} /> + disableStock(true)} + onConfirm={() => disableStock(false)} + onCancel={() => (stockConfirmOpen = false)} + /> + (showPaywall = false)} /> @@ -520,4 +1182,192 @@ .callout.amber .msr { color: var(--amber); } + .iconbtn:disabled { + opacity: 0.5; + cursor: default; + } + .src-link { + align-self: flex-start; + margin-top: 4px; + padding: 4px 0; + min-height: 28px; + border: 0; + background: transparent; + color: var(--accent); + font-family: var(--sans); + font-size: 11px; + font-weight: 600; + display: inline-flex; + align-items: center; + gap: 5px; + cursor: pointer; + } + .src-link .msr { + font-size: 14px; + } + .muted-line { + display: block; + margin-top: 3px; + color: var(--muted); + } + .link-text { + display: block; + margin-top: 6px; + overflow-wrap: anywhere; + user-select: text; + } + .callout-dismiss { + width: 32px; + height: 32px; + flex: none; + border: 0; + background: transparent; + display: grid; + place-items: center; + cursor: pointer; + } + .callout-dismiss .msr { + font-size: 18px; + color: var(--muted); + } + .action-note { + display: flex; + flex-direction: column; + gap: 8px; + padding: 12px 14px; + border-radius: 14px; + background: color-mix(in srgb, var(--danger) 8%, transparent); + border: 1px solid color-mix(in srgb, var(--danger) 24%, transparent); + } + .action-text { + margin: 0; + font-size: 12px; + line-height: 1.45; + color: var(--text-soft); + } + + .adv-toggle { + display: flex; + align-items: center; + gap: 10px; + min-height: 48px; + padding: 0 14px; + border-radius: 14px; + border: 1px solid var(--line); + background: var(--surface); + color: var(--text); + font-family: var(--sans); + cursor: pointer; + text-align: left; + } + .adv-title { + flex: 1; + font-size: 13px; + font-weight: 600; + } + .adv-toggle .msr { + font-size: 22px; + color: var(--muted); + } + .adv { + display: flex; + flex-direction: column; + gap: 12px; + padding: 14px; + border-radius: 16px; + border: 1px solid var(--line); + background: color-mix(in srgb, var(--surface) 60%, transparent); + } + .adv-note { + margin: 0; + font-size: 12px; + line-height: 1.45; + color: var(--muted); + } + .adv-choice { + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + border-radius: 13px; + background: var(--surface); + border: 1px solid var(--line); + } + .adv-choice-icon { + font-size: 22px; + color: var(--text-soft); + flex: none; + } + .adv-choice-body { + flex: 1; + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + } + .adv-choice-name { + font-size: 14px; + font-weight: 600; + } + .adv-choice-pkg { + font-family: var(--mono); + font-size: 10px; + color: var(--muted); + overflow-wrap: anywhere; + } + .adv-choice-empty { + font-size: 13px; + color: var(--muted); + } + .adv-field { + display: flex; + flex-direction: column; + gap: 6px; + } + .adv-label { + font-family: var(--mono); + font-size: 10px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--dim); + } + .adv-input { + min-height: 44px; + padding: 0 12px; + border-radius: 11px; + border: 1px solid var(--line); + background: var(--surface); + color: var(--text); + font-family: var(--mono); + font-size: 13px; + } + .adv-action { + flex: none; + width: 100%; + gap: 8px; + } + .danger-ghost { + color: var(--danger); + border-color: color-mix(in srgb, var(--danger) 35%, transparent); + } + .adv-reason { + margin: -4px 0 0; + font-size: 11px; + line-height: 1.45; + color: var(--muted); + } + .adv-result { + margin: 0; + padding: 10px 12px; + border-radius: 12px; + font-size: 12px; + line-height: 1.45; + color: var(--text-soft); + background: color-mix(in srgb, var(--amber) 8%, transparent); + border: 1px solid color-mix(in srgb, var(--amber) 24%, transparent); + } + .adv-result.ok { + background: color-mix(in srgb, var(--teal) 8%, transparent); + border-color: color-mix(in srgb, var(--teal) 25%, transparent); + } diff --git a/v2/mobile/src/screens/More.svelte b/v2/mobile/src/screens/More.svelte index 3b607d0e..2b286f00 100644 --- a/v2/mobile/src/screens/More.svelte +++ b/v2/mobile/src/screens/More.svelte @@ -109,8 +109,8 @@ { label: "Tuning", tools: [ + { screen: "optimize", icon: "auto_fix_high", title: "Optimize", desc: "Review and turn off apps you don't use", pro: true }, { screen: "launcher", icon: "home", title: "Launcher", desc: "Set a custom home screen", pro: true }, - { screen: "tweaks", icon: "tune", title: "Tweaks", desc: "CEC, frame rate, DNS, animations", pro: true }, { screen: "snapshots", icon: "photo_camera_back", title: "Snapshots", desc: "Record a setup and re-apply it later", pro: true }, ], }, @@ -296,12 +296,9 @@
-
-

Settings

+

More

@@ -439,7 +436,7 @@

Includes app or process identifiers, observation times and version information. Review the contents before sharing. No network addresses, device serials, license keys or full - debug logs are included. + debug logs are included. Copy diagnostic JSON puts text on your clipboard; it does not save a file.

+ {/if} +
+ {/if} +
{/if} - {#if visibleItems.length === 0} + {#if visibleItems.length === 0 && tabItems.length > 0} +

No apps on this tab match “{searchQuery.trim()}”.

+ + {:else if visibleItems.length === 0} {#if mode === "optimize" && activeTab === "recommended" && safetyLoading}

Checking safety before showing recommendations…

{:else if mode === "optimize" && activeTab === "recommended"} @@ -782,7 +813,12 @@ {@const tier = tierOf(safetyMap[item.entry.package])} {@const hardBlocked = isHardBlocked(item)}
-
+
+ {#if mode === "optimize" && activeTab === "optional"}
+ >{actionLabel(item)}
{:else}

Launcher optical alignment · simulated masks

64 CSS px; candidate moves artwork down 1.8 px. No launcher/device test.${['circle','square','squircle'].map(mask => `
${mask}
${current}
${shifted}
`).join('')}

Current ← → proposed optical shift (source assets unchanged)

`); + await icons.screenshot({ path: `${dir}/icon-optical-comparison.png` }); +}); + +test("DNS confirmed changes survive navigation and reconnect, while unset stays explicit", async (t) => { + const page = await open(t, 'tweaks', { pro: true }); + await page.evaluate(() => { + window.dnsState = { mode: 'opportunistic', hostname: null }; + handlers.get_private_dns = () => ({ ...dnsState }); + handlers.set_private_dns = args => { dnsState = { mode: args.mode, hostname: args.hostname ?? null }; return { ok: true, message: 'done' }; }; + }); + const card = page.locator('.tweak-row').filter({ has: page.getByText('Private DNS', { exact: true }) }); + await card.getByRole('button', { name: 'Off', exact: true }).click(); + await page.waitForFunction(() => [...document.querySelectorAll('button')].some(b => b.textContent.trim() === 'Off' && b.getAttribute('aria-pressed') === 'true')); + await page.evaluate(() => router.navigate('more')); + await page.evaluate(() => router.navigate('tweaks')); + await card.getByRole('button', { name: 'Off', exact: true }).waitFor(); + assert.equal(await card.getByRole('button', { name: 'Off', exact: true }).getAttribute('aria-pressed'), 'true'); + await page.evaluate(async () => { await session.connect('B', 5555); dnsState = { mode: null, hostname: null }; }); + await page.getByRole('button', { name: 'Re-read settings' }).click(); + await page.getByText('Not explicitly set (TV default)', { exact: true }).waitFor(); + assert.equal(await card.locator('[aria-pressed="true"]').count(), 0); +}); + +for (const component of [VENDOR, null]) { + for (const outcome of ["refuse", "reject"]) { + test(`Daydream component ${outcome} never enables an off screensaver with ${component ? "previous" : "unset"} component`, async t => { + const page = await open(t, "tweaks", { pro: true }); + await page.locator('[data-tweak="screensaver"]').waitFor(); + await page.evaluate(({ component, outcome }) => { + tweaks.screensaver_enabled = "0"; tweaks.screensaver_components = component; + handlers.write_setting = args => { + if (args.key === "screensaver_components") { + if (outcome === "reject") throw "Component write failed"; + return { ok: false, message: "Component write refused" }; + } + tweaks[args.key] = args.value || null; return { ok: true, message: "done" }; + }; + }, { component, outcome }); + await page.locator('[data-tweak="screensaver"]').getByRole("button", { name: "Basic Daydream", exact: true }).click(); + await page.getByText(/Component write (failed|refused)/).waitFor(); + assert.equal(await page.evaluate(() => tweaks.screensaver_enabled), "0"); + assert.equal(await page.evaluate(() => tweaks.screensaver_components), component); + assert.deepEqual((await writes(page, "write_setting")).map(a => a.key), ["screensaver_components"]); + }); + } +} + +for (const action of ["Basic Daydream", "Restore previous"]) { + test(`${action} does not enable after a refused component; failed enable retains off with configured component`, async t => { + const page = await open(t, "tweaks", { pro: true }); + const card = page.locator('[data-tweak="screensaver"]'); await card.waitFor(); + if (action === "Restore previous") { + await card.getByRole("button", { name: "Off", exact: true }).click(); + await page.waitForFunction(() => document.querySelector('[data-tweak="screensaver"] .state')?.textContent === "Off"); + } + await page.evaluate(() => { + tweaks.screensaver_enabled = "0"; + calls.length = 0; + handlers.write_setting = args => { + if (args.key === "screensaver_components") return { ok: false, message: "Component refused" }; + tweaks[args.key] = args.value || null; return { ok: true, message: "done" }; + }; + }); + const button = () => card.getByRole("button", { name: action === "Restore previous" ? /Restore previous/ : action, exact: action !== "Restore previous" }); + await button().click(); await page.getByText(/Component refused/).waitFor(); + assert.equal(await page.evaluate(() => tweaks.screensaver_enabled), "0"); + assert.deepEqual((await writes(page, "write_setting")).map(a => a.key), ["screensaver_components"]); + await page.evaluate(() => { + calls.length = 0; + handlers.write_setting = args => { + if (args.key === "screensaver_enabled") return { ok: false, message: "Enable refused" }; + tweaks[args.key] = args.value || null; return { ok: true, message: "done" }; + }; + }); + await button().click(); await page.getByText(/switch was not confirmed/).waitFor(); + assert.equal(await page.evaluate(() => tweaks.screensaver_enabled), "0"); + assert.equal(await page.evaluate(() => tweaks.screensaver_components), action === "Restore previous" ? VENDOR : BASIC); + assert.deepEqual((await writes(page, "write_setting")).map(a => a.key), ["screensaver_components", "screensaver_enabled"]); + }); +} + +test("Off keeps the switch disabled if component clearing fails", async t => { + const page = await open(t, "tweaks", { pro: true }); + await page.locator('[data-tweak="screensaver"]').waitFor(); + await page.evaluate(() => { + handlers.write_setting = args => { + if (args.key === "screensaver_components") return { ok: false, message: "Clear refused" }; + tweaks[args.key] = args.value || null; return { ok: true, message: "done" }; + }; + }); + await page.locator('[data-tweak="screensaver"]').getByRole("button", { name: "Off", exact: true }).click(); + await page.getByText(/component was not confirmed/).waitFor(); + assert.equal(await page.evaluate(() => tweaks.screensaver_enabled), "0"); + assert.equal(await page.evaluate(() => tweaks.screensaver_components), VENDOR); + assert.deepEqual((await writes(page, "write_setting")).map(a => a.key), ["screensaver_enabled", "screensaver_components"]); +}); + +if (process.env.DAYDREAM_EVIDENCE === "1") test("capture simulated Daydream partial failure and recovery", async t => { + const dir = "evidence/daydream-order-2026-10-17"; await mkdir(dir, { recursive: true }); + const page = await open(t, "tweaks", { pro: true, daydreamEvidence: true }); + const card = page.locator('[data-tweak="screensaver"]'); await card.waitFor(); + await card.getByRole("button", { name: "Off", exact: true }).click(); + await page.waitForFunction(() => document.querySelector('[data-tweak="screensaver"] .state')?.textContent === "Off"); + await card.scrollIntoViewIfNeeded(); + await page.evaluate(() => { handlers.write_setting = args => args.key === "screensaver_components" ? { ok: false, message: "Component write refused (simulated)" } : { ok: true, message: "done" }; }); + await card.getByRole("button", { name: "Basic Daydream", exact: true }).click(); + await page.getByText(/Component write refused \(simulated\)/).waitFor(); + await page.screenshot({ path: `${dir}/component-refused-off-simulated.png`, animations: "disabled" }); await page.waitForTimeout(800); + assert.equal(await page.evaluate(() => tweaks.screensaver_enabled), "0"); + await page.evaluate(() => { delete handlers.write_setting; }); + await card.getByRole("button", { name: "Basic Daydream", exact: true }).click(); + await page.waitForFunction(() => document.querySelector('[data-tweak="screensaver"] .state')?.textContent === "Basic Daydream"); + await page.screenshot({ path: `${dir}/basic-daydream-readback-simulated.png`, animations: "disabled" }); await page.waitForTimeout(800); + const video = page.video(); await page.close(); await video.saveAs(`${dir}/daydream-failure-recovery-simulated.webm`); +}); + +test("a reconnect after the Daydream component write prevents enabling", async t => { + const page = await open(t, "tweaks", { pro: true }); + const card = page.locator('[data-tweak="screensaver"]'); await card.waitFor(); + await page.evaluate(() => { + tweaks.screensaver_enabled = "0"; + handlers.write_setting = args => new Promise(resolve => { window.finishComponent = () => { tweaks[args.key] = args.value; resolve({ ok: true, message: "done" }); }; }); + }); + await card.getByRole("button", { name: "Basic Daydream", exact: true }).click(); + await page.waitForFunction(() => !!window.finishComponent); + await page.evaluate(async () => { await session.connect("A", 5555); finishComponent(); }); + assert.deepEqual((await writes(page, "write_setting")).map(a => a.key), ["screensaver_components"]); + assert.equal(await page.evaluate(() => tweaks.screensaver_enabled), "0"); +}); + + +test("screensaver names preserve raw known, custom, disabled and missing component states", async t => { + for (const fixture of [ + { component: VENDOR, enabled: '1', label: 'Ambient Mode', raw: VENDOR }, + { component: BASIC, enabled: '1', label: 'Basic Daydream', raw: BASIC }, + { component: 'vendor.unknown/.Dream', enabled: '1', label: 'Custom screensaver', raw: 'vendor.unknown/.Dream' }, + { component: 'com.google.android.backdrop.clone/.Backdrop', enabled: '1', label: 'Custom screensaver', raw: 'com.google.android.backdrop.clone/.Backdrop' }, + { component: `${VENDOR},vendor.unknown/.Dream`, enabled: '1', label: 'Custom screensaver', raw: `${VENDOR},vendor.unknown/.Dream` }, + { component: VENDOR, enabled: '0', label: 'Off', raw: VENDOR }, + { component: null, enabled: null, label: 'None set', raw: 'Not reported' }, + { component: '', enabled: '1', label: 'None set', raw: 'None set' }, + ]) { + const page = await open(t, 'tweaks', { screensaver: fixture.component, screensaverEnabled: fixture.enabled }); + const card = page.locator('[data-tweak="screensaver"]'); + await card.locator('.state').getByText(fixture.label, { exact: true }).waitFor(); + assert.match(await card.getByRole('button', { name: /Restore previous/ }).innerText(), new RegExp(fixture.label)); + const summary = card.locator('summary'); await summary.focus(); await page.keyboard.press('Enter'); + assert.equal(await card.locator('details').getAttribute('open'), ''); + assert.deepEqual(await card.locator('details p .mono').allTextContents(), [fixture.raw, fixture.raw]); + assert.equal(await summary.evaluate(el => getComputedStyle(el).outlineStyle), 'solid'); + assert.deepEqual(await writes(page, 'write_setting'), []); + await page.close(); + } +}); + +test("catalog failure stays custom and late old catalog names cannot cross connections", async t => { + const page = await open(t, 'tweaks'); const card = page.locator('[data-tweak="screensaver"]'); + await card.locator('.state').getByText('Ambient Mode', { exact: true }).waitFor(); + await page.evaluate(() => { handlers.app_list_for_device = () => Promise.reject('catalog unavailable'); }); + await page.getByRole('button', { name: 'Re-read settings' }).click(); + await card.locator('.state').getByText('Custom screensaver', { exact: true }).waitFor(); + await page.evaluate(() => { handlers.app_list_for_device = () => new Promise(resolve => { window.finishOldCatalog = resolve; }); }); + await page.getByRole('button', { name: 'Re-read settings' }).click(); await page.waitForFunction(() => !!window.finishOldCatalog); + await page.evaluate(async () => { + handlers.app_list_for_device = () => []; + tweaks.screensaver_components = 'vendor.other/.Dream'; + await session.connect('B', 5555); + }); + await card.locator('.state').getByText('Custom screensaver', { exact: true }).waitFor(); + await page.evaluate(() => finishOldCatalog([{ package: 'vendor.other', name: 'Old connection name' }])); + await page.waitForTimeout(50); + assert.equal(await card.locator('.state').innerText(), 'Custom screensaver'); + await card.locator('summary').click(); + assert.deepEqual(await card.locator('details p .mono').allTextContents(), ['vendor.other/.Dream', 'vendor.other/.Dream']); + assert.deepEqual(await writes(page, 'write_setting'), []); +}); + +if (process.env.SCREENSAVER_LABEL_EVIDENCE === '1') test("capture named and custom screensavers with raw details", async t => { + const dir = 'evidence/screensaver-labels-2026-10-17'; await mkdir(dir, { recursive: true }); + for (const [name, component, enabled] of [['known', VENDOR, '1'], ['custom', 'vendor.example.really_long_component_owner/.ScreensaverWithALongServiceName', '1'], ['off', VENDOR, '0'], ['missing', null, null]]) { + const page = await open(t, 'tweaks', { screensaver: component, screensaverEnabled: enabled }); + const card = page.locator('[data-tweak="screensaver"]'); await card.locator('summary').waitFor(); + await page.setViewportSize({ width: 320, height: 812 }); await card.locator('summary').click(); + assert.equal(await card.evaluate(el => el.scrollWidth <= el.clientWidth), true); + await card.screenshot({ path: `${dir}/${name}-320-simulated.png` }); + await page.close(); + } +}); + + +test("density-only readback shows the override instead of physical density or device default", async t => { + const page = await open(t, "tweaks"); + const card = page.locator('.tweak-row').filter({ has: page.getByText('Display scaling', { exact: true }) }); + await page.evaluate(() => { handlers.get_display_scaling = () => ({ size: 'Physical size: 3840x2160', density: 'Physical density: 640\nOverride density: 300' }); }); + await page.getByRole('button', { name: 'Re-read settings' }).click(); + await card.getByText(/Custom size \/ density/).waitFor(); + if (process.env.DENSITY_BEFORE === '1') { await mkdir('evidence/density-only-label-2026-10-17', { recursive: true }); await card.screenshot({ path: 'evidence/density-only-label-2026-10-17/before-simulated.png' }); } + assert.equal(await card.locator('.t-info .t-desc').innerText(), '3840x2160 @ 300 · density override · Custom size / density'); + assert.equal(await card.locator('[aria-pressed="true"]').count(), 0); + assert.equal(await card.getByText(/device default/).count(), 0); + assert.deepEqual(await writes(page, 'set_display_scaling'), []); + assert.deepEqual(await writes(page, 'write_setting'), []); +}); + + +test("density-only matching preset, absent size and reset readbacks retain their distinct states", async t => { + const page = await open(t, "tweaks"); + const card = page.locator('.tweak-row').filter({ has: page.getByText('Display scaling', { exact: true }) }); + for (const fixture of [ + { size: 'Physical size: 1920x1080', density: 'Physical density: 640\nOverride density: 320', text: '1920x1080 @ 320 · density override', selected: '1080p' }, + { size: '', density: 'Physical density: 640\nOverride density: 300', text: 'Size not reported @ 300 · density override · Custom size / density', selected: null }, + { size: 'Physical size: 3840x2160', density: 'Physical density: 640', text: '3840x2160 @ 640 · device default', selected: 'Reset' }, + { size: 'Physical size: 3840x2160\nOverride size: 1920x1080', density: 'Physical density: 320', text: '1920x1080 @ 320 · panel 3840x2160', selected: '1080p' }, + { size: '', density: '', text: '—', selected: null }, + ]) { + await page.evaluate(fixture => { handlers.get_display_scaling = () => ({ size: fixture.size, density: fixture.density }); }, fixture); + await page.getByRole('button', { name: 'Re-read settings' }).click(); + await card.getByText(fixture.text, { exact: true }).waitFor(); + assert.equal(await card.locator('[aria-pressed="true"]').count(), fixture.selected ? 1 : 0); + if (fixture.selected) assert.equal(await card.getByRole('button', { name: fixture.selected, exact: true }).getAttribute('aria-pressed'), 'true'); + } + assert.deepEqual(await writes(page, 'set_display_scaling'), []); + assert.deepEqual(await writes(page, 'write_setting'), []); +}); + +if (process.env.DENSITY_EVIDENCE === '1') test("capture density-only label at narrow width and enlarged summary text", async t => { + const page = await open(t, "tweaks"); + const card = page.locator('.tweak-row').filter({ has: page.getByText('Display scaling', { exact: true }) }); + await page.evaluate(() => { handlers.get_display_scaling = () => ({ size: 'Physical size: 3840x2160', density: 'Physical density: 640\nOverride density: 300' }); }); + await page.getByRole('button', { name: 'Re-read settings' }).click(); + await card.getByText('3840x2160 @ 300 · density override · Custom size / density', { exact: true }).waitFor(); + const dir = 'evidence/density-only-label-2026-10-17'; await mkdir(dir, { recursive: true }); + await card.screenshot({ path: `${dir}/after-384-simulated.png` }); + await page.setViewportSize({ width: 320, height: 812 }); + await card.evaluate(el => { + const nodes = [el, ...el.querySelectorAll('*')]; + const sizes = nodes.map(node => parseFloat(getComputedStyle(node).fontSize)); + nodes.forEach((node, i) => { if (!node.classList.contains('msr')) node.style.fontSize = `${sizes[i] * 2}px`; }); + }); + await card.screenshot({ path: `${dir}/known-full-card-text-200-simulated.png` }); + const overflow = await card.evaluate(el => [...el.querySelectorAll('*')].filter(node => node.scrollWidth > node.clientWidth + 1).map(node => ({ text: node.textContent.trim(), width: node.clientWidth, content: node.scrollWidth }))); + await writeFile(`${dir}/full-card-text-200-observations.json`, JSON.stringify(overflow, null, 2) + '\n'); + await card.evaluate(el => { + [el, ...el.querySelectorAll('*')].forEach(node => node.style.fontSize = ''); + const summary = el.querySelector('.t-info .t-desc'); + summary.style.fontSize = `${parseFloat(getComputedStyle(summary).fontSize) * 2}px`; + }); + assert.equal(await card.evaluate(el => el.scrollWidth <= el.clientWidth), true); + await card.screenshot({ path: `${dir}/after-320-summary-text-200-simulated.png` }); + assert.deepEqual(await writes(page, 'set_display_scaling'), []); + assert.deepEqual(await writes(page, 'write_setting'), []); +}); + + +for (const [width, scale] of [[320, 1], [384, 1], [320, 2], [384, 2]]) test(`scaling preset row fits ${width}px at ${scale * 100}% text and preserves controls`, async t => { + const page = await open(t, 'tweaks', { pro: true }); + const card = page.locator('.tweak-row').filter({ has: page.getByText('Display scaling', { exact: true }) }); + await page.evaluate(() => { handlers.get_display_scaling = () => ({ size: 'Physical size: 3840x2160\nOverride size: 1920x1080', density: 'Physical density: 640\nOverride density: 320' }); }); + await page.getByRole('button', { name: 'Re-read settings' }).click(); + const row = card.locator('.segmented'); + await page.waitForFunction(() => [...document.querySelectorAll('button')].some(el => el.textContent.trim() === '1080p' && el.getAttribute('aria-pressed') === 'true')); + await page.setViewportSize({ width, height: 812 }); + if (scale !== 1) await card.evaluate((el, scale) => { + const nodes = [el, ...el.querySelectorAll('*')]; + const sizes = nodes.map(node => parseFloat(getComputedStyle(node).fontSize)); + nodes.forEach((node, i) => { if (!node.classList.contains('msr')) node.style.fontSize = `${sizes[i] * scale}px`; }); + }, scale); + await row.evaluate(el => el.scrollIntoView({ block: 'center' })); + const dir = 'evidence/scaling-preset-wrap-2026-10-17'; + if (process.env.SCALING_WRAP_BEFORE === '1') { await mkdir(dir, { recursive: true }); await row.screenshot({ path: `${dir}/before-${width}-${scale * 100}-simulated.png` }); } + const dimensions = await row.evaluate(el => ({ width: el.clientWidth, content: el.scrollWidth })); + assert.ok(dimensions.content <= dimensions.width, JSON.stringify(dimensions)); + assert.equal(await card.evaluate(el => el.scrollWidth <= el.clientWidth), true); + assert.deepEqual((await row.getByRole('button').allTextContents()).map(x => x.trim()), ['4K', '1080p', '720p', 'Reset']); + const buttons = row.getByRole('button'); + await buttons.first().focus(); + for (let i = 0; i < 4; i++) { + const button = buttons.nth(i); + assert.equal(await button.evaluate(el => document.activeElement === el), true); + assert.equal(await button.evaluate(el => { + const r = el.getBoundingClientRect(); + return r.width >= 44 && r.height >= 44 && r.left >= 0 && r.right <= innerWidth && r.top >= 0 && r.bottom <= innerHeight && el.scrollWidth <= el.clientWidth; + }), true); + await page.keyboard.press('Enter'); + const dialog = page.getByRole('dialog', { name: i === 3 ? 'Reset display scaling?' : 'Change display scaling?', exact: true }); + await dialog.waitFor(); await dialog.getByRole('button', { name: 'Cancel', exact: true }).click(); await dialog.waitFor({ state: 'detached' }); + assert.equal(await button.evaluate(el => document.activeElement === el), true); + if (i < 3) await page.keyboard.press('Tab'); + } + assert.equal(await row.getByRole('button', { name: '1080p', exact: true }).getAttribute('aria-pressed'), 'true'); + assert.deepEqual(await writes(page, 'set_display_scaling'), []); + assert.deepEqual(await writes(page, 'write_setting'), []); + if (process.env.SCALING_WRAP_EVIDENCE === '1') { + await mkdir(dir, { recursive: true }); await row.evaluate(el => el.scrollIntoView({ block: 'center' })); + await row.screenshot({ path: `${dir}/after-${width}-${scale * 100}-simulated.png` }); + } +}); diff --git a/v2/mobile/vite.config.ts b/v2/mobile/vite.config.ts index aac53e80..c10cbe8d 100644 --- a/v2/mobile/vite.config.ts +++ b/v2/mobile/vite.config.ts @@ -1,8 +1,12 @@ import { svelte } from "@sveltejs/vite-plugin-svelte"; import { defineConfig } from "vite"; +import mobileTsconfig from "./tsconfig.json"; export default defineConfig({ plugins: [svelte()], + // A string bypasses per-file tsconfig discovery for shared desktop fixtures, + // which must not require generated desktop SvelteKit files in a mobile build. + esbuild: { tsconfigRaw: JSON.stringify({ compilerOptions: mobileTsconfig.compilerOptions }) }, clearScreen: false, build: { outDir: "build", diff --git a/v2/package-lock.json b/v2/package-lock.json index 1eebe47f..3b3bfcf4 100644 --- a/v2/package-lock.json +++ b/v2/package-lock.json @@ -1,12 +1,12 @@ { "name": "atv-optimizer-v2", - "version": "2.1.0", + "version": "2.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "atv-optimizer-v2", - "version": "2.1.0", + "version": "2.3.0", "license": "UNLICENSED", "dependencies": { "@tauri-apps/api": "^2", @@ -906,9 +906,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.61.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.61.1.tgz", - "integrity": "sha512-Ny8s1SR1TyQS2hD2Rvw0XKzU2Nw1eUF52dTb6T2bdcgz7wSC+Nyb5IwjWYlR4b2dvbbR5NJDiQwHg3rnNseghg==", + "version": "2.70.3", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.70.3.tgz", + "integrity": "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg==", "dev": true, "license": "MIT", "dependencies": { @@ -1379,9 +1379,9 @@ } }, "node_modules/devalue": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", - "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.4.tgz", + "integrity": "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==", "dev": true, "license": "MIT" }, @@ -1557,9 +1557,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -1643,9 +1643,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.30.tgz", + "integrity": "sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA==", "dev": true, "funding": [ { @@ -1663,9 +1663,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1773,9 +1773,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1883,9 +1883,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", - "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.4.tgz", + "integrity": "sha512-xGjU/SyjeNyb0E4KxXBvYz/1C+3JXuujvf2hXNOtnlcE+58o5ajd9N2/n2VhkiJvpCP2qQasnUpx5PXNeI+p1Q==", "dev": true, "license": "MIT", "dependencies": { diff --git a/v2/src-tauri/src/commands/files.rs b/v2/src-tauri/src/commands/files.rs index d64aa35f..5c203b5f 100644 --- a/v2/src-tauri/src/commands/files.rs +++ b/v2/src-tauri/src/commands/files.rs @@ -7,38 +7,14 @@ use std::path::{Path, PathBuf}; use serde::Serialize; use tauri::State; +use shield_optimizer_core::commands::files::{ + delete_path_with, find_files_with, validate_device_path, validate_sdcard_path, FindResult, +}; + use crate::adb::{parse_ls_output, FileEntry}; use super::AppState; -/// Validate a device path for file-manager use. Always absolute, no `..` -/// traversal, no control characters (they'd corrupt the shell line even -/// quoted). When `allow_system` is false the path must also live under -/// `/sdcard`; power-user mode lifts only that boundary — the injection guards -/// stay. Returns the trimmed path. -fn validate_device_path(path: &str, allow_system: bool) -> Result { - let p = path.trim(); - if !p.starts_with('/') { - return Err(format!("Path must be absolute: {p:?}")); - } - if p.split('/').any(|seg| seg == "..") { - return Err("Path traversal (`..`) is not allowed.".to_string()); - } - if p.chars().any(|c| c.is_control()) { - return Err("Path contains control characters.".to_string()); - } - if !allow_system && p != "/sdcard" && !p.starts_with("/sdcard/") { - return Err(format!("Path must be under /sdcard: {p:?}")); - } - Ok(p.to_string()) -} - -/// `/sdcard`-confined validation — for the paths that are user-storage only by -/// design (the device-to-device copy and the backup finder). -fn validate_sdcard_path(path: &str) -> Result { - validate_device_path(path, false) -} - /// Single-quote a validated device path for the device-side shell. fn quote_path(p: &str) -> String { format!("'{}'", p.replace('\'', r"'\''")) @@ -212,30 +188,10 @@ fn sanitize_temp(name: &str) -> String { .collect() } -/// Refuse deleting the filesystem root, `/sdcard` itself, or a critical system -/// mount even in power-user mode — a recursive delete there could brick the -/// device. Subpaths are the user's call (and mostly permission-denied without -/// root). Returns the refusal message, or `None` if the path is deletable. -fn protected_delete_reason(path: &str) -> Option { - let trimmed = path.trim_end_matches('/'); - if trimmed == "/sdcard" { - return Some("Refusing to delete /sdcard itself.".to_string()); - } - const PROTECTED: &[&str] = &[ - "", "/system", "/data", "/vendor", "/proc", "/sys", "/dev", "/boot", "/init", "/sbin", - "/bin", "/etc", - ]; - if PROTECTED.contains(&trimmed) { - return Some(format!( - "Refusing to delete a protected system path: {path}" - )); - } - None -} - /// `delete_path` — remove a file or directory (recursively). Confined to /// `/sdcard` unless `allow_system` (power-user) is set; the UI confirms before -/// calling, and `protected_delete_reason` still blocks catastrophic targets. +/// calling, and core's `protected_delete_reason` still blocks catastrophic +/// targets. #[tauri::command] pub async fn delete_path( state: State<'_, AppState>, @@ -243,61 +199,17 @@ pub async fn delete_path( path: String, allow_system: bool, ) -> Result { - let path = validate_device_path(&path, allow_system)?; - if let Some(reason) = protected_delete_reason(&path) { - return Err(reason); - } let adb = state.adb_snapshot().await; - let out = adb - .shell(&serial, &format!("rm -rf {}", quote_path(&path))) - .await - .map_err(|e| format!("rm: {e}"))?; - let noise = out.combined().trim().to_string(); - if noise.is_empty() { - Ok(FileTransferResult { - ok: true, - message: format!("Deleted {path}."), - local_path: None, - }) - } else { - Ok(FileTransferResult { - ok: false, - message: noise, - local_path: None, - }) - } -} - -/// Filename patterns for `find -name`: glob stars and dots only — no slashes, -/// quotes, or anything the shell could reinterpret. -fn validate_find_pattern(pattern: &str) -> Result<(), String> { - let ok = !pattern.is_empty() - && pattern.len() <= 64 - && pattern - .chars() - .all(|c| c.is_ascii_alphanumeric() || matches!(c, '*' | '.' | '_' | '-')); - if ok { - Ok(()) - } else { - Err(format!("Invalid search pattern: {pattern:?}")) - } -} - -#[derive(Serialize)] -pub struct FindResult { - /// Matching file paths, capped at 100. - pub hits: Vec, - /// Directories the search could not be run against at all, because the ADB - /// call itself failed. A directory that simply does not exist is *not* - /// listed here — that is a real "no matches". Without this split, a - /// dropped connection rendered as "export from the app first", telling the - /// user to redo something that had worked (GitHub #86). - pub unsearched: Vec, + let r = delete_path_with(adb.as_ref(), &serial, &path, allow_system).await?; + Ok(FileTransferResult { + ok: r.ok, + message: r.message, + local_path: None, + }) } /// `find_files` — locate files matching a name pattern under one or more -/// `/sdcard` directories. Powers the app-backup finder (e.g. Projectivy's -/// `*.plbackup` exports land wherever the user's file picker put them). +/// `/sdcard` directories. Powers the app-backup finder. #[tauri::command] pub async fn find_files( state: State<'_, AppState>, @@ -305,36 +217,8 @@ pub async fn find_files( dirs: Vec, pattern: String, ) -> Result { - validate_find_pattern(&pattern)?; let adb = state.adb_snapshot().await; - let mut hits = Vec::new(); - let mut unsearched = Vec::new(); - for dir in dirs { - let dir = validate_sdcard_path(&dir)?; - // A missing directory or a permission denial is expected for some - // candidates, and `find` reports those on stderr — suppress them, since - // an empty result is the honest answer. A failure of the ADB call - // itself is different: nothing was searched, so it must not be - // reported as "nothing found". - let cmd = format!( - "find {} -maxdepth 4 -type f -name '{pattern}' 2>/dev/null", - quote_path(&dir) - ); - let Ok(out) = adb.shell(&serial, &cmd).await else { - unsearched.push(dir); - continue; - }; - for line in out.stdout.lines() { - let line = line.trim(); - if line.starts_with("/sdcard") && !hits.iter().any(|h| h == line) { - hits.push(line.to_string()); - } - if hits.len() >= 100 { - break; - } - } - } - Ok(FindResult { hits, unsearched }) + find_files_with(adb.as_ref(), &serial, &dirs, &pattern).await } #[cfg(test)] @@ -342,59 +226,6 @@ mod tests { use super::*; use pretty_assertions::assert_eq; - #[test] - fn find_patterns_validated() { - assert!(validate_find_pattern("*.plbackup").is_ok()); - assert!(validate_find_pattern("backup_*.zip").is_ok()); - assert!(validate_find_pattern("a/b").is_err()); - assert!(validate_find_pattern("x'y").is_err()); - assert!(validate_find_pattern("").is_err()); - assert!(validate_find_pattern(&"x".repeat(65)).is_err()); - } - - #[test] - fn accepts_sdcard_paths() { - assert_eq!(validate_sdcard_path("/sdcard").unwrap(), "/sdcard"); - assert_eq!( - validate_sdcard_path("/sdcard/Download/file 1.mp4").unwrap(), - "/sdcard/Download/file 1.mp4" - ); - } - - #[test] - fn rejects_escapes_and_system_paths() { - assert!(validate_sdcard_path("/data/data/com.x").is_err()); - assert!(validate_sdcard_path("/sdcard/../data").is_err()); - assert!(validate_sdcard_path("/sdcardX/evil").is_err()); - assert!(validate_sdcard_path("/sdcard/a\nb").is_err()); - assert!(validate_sdcard_path("").is_err()); - } - - #[test] - fn power_user_mode_allows_system_paths_but_keeps_injection_guards() { - // System paths are reachable only with allow_system. - assert!(validate_device_path("/system/app", false).is_err()); - assert_eq!( - validate_device_path("/system/app", true).unwrap(), - "/system/app" - ); - assert_eq!(validate_device_path("/", true).unwrap(), "/"); - // The shell-safety guards never relax. - assert!(validate_device_path("/system/../x", true).is_err()); - assert!(validate_device_path("/system/a\nb", true).is_err()); - assert!(validate_device_path("relative/path", true).is_err()); - } - - #[test] - fn protected_paths_are_never_deletable() { - for p in ["/", "/system", "/data", "/vendor", "/sdcard", "/system/"] { - assert!(protected_delete_reason(p).is_some(), "{p} must be refused"); - } - // Real targets are allowed through the guard. - assert!(protected_delete_reason("/sdcard/Download/old.zip").is_none()); - assert!(protected_delete_reason("/system/app/Bloat/Bloat.apk").is_none()); - } - #[test] fn quotes_single_quotes_in_paths() { assert_eq!(quote_path("/sdcard/it's here"), r"'/sdcard/it'\''s here'"); diff --git a/v2/src/lib/demo-mock.ts b/v2/src/lib/demo-mock.ts index bd12077d..9de184d5 100644 --- a/v2/src/lib/demo-mock.ts +++ b/v2/src/lib/demo-mock.ts @@ -515,8 +515,9 @@ function demoShellOutput(command: string): string { } // Map of command name → handler. Unlisted commands fall through to a benign -// success so a stray click during capture never throws. -function handle(cmd: string, args: Record): unknown { +// success so a stray click during capture never throws. Exported so the mobile +// app's demo layer answers the commands both apps share from the same fixtures. +export function handle(cmd: string, args: Record): unknown { switch (cmd) { case "adb_status": return { available: true, path: "/opt/homebrew/bin/adb", last_probe: "2026-06-02T14:40:00Z" };