diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d6eee66f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +apps/dashboard/android/gradlew text eol=lf diff --git a/.github/workflows/wallet-release.yml b/.github/workflows/wallet-release.yml new file mode 100644 index 00000000..6c406b74 --- /dev/null +++ b/.github/workflows/wallet-release.yml @@ -0,0 +1,157 @@ +name: Wallet release + +on: + workflow_dispatch: + push: + tags: + - "wallet-v*" + +permissions: + contents: write + +jobs: + desktop: + name: Desktop wallet (${{ matrix.name }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - name: windows + os: windows-latest + command: npm run desktop:installer:win + artifact: apps/dashboard/release/* + - name: macos + os: macos-latest + command: npm run desktop:installer:mac + artifact: apps/dashboard/release/* + - name: linux + os: ubuntu-latest + command: npm run desktop:installer:linux + artifact: apps/dashboard/release/* + + defaults: + run: + working-directory: apps/dashboard + + env: + CSC_IDENTITY_AUTO_DISCOVERY: "false" + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "24" + cache: npm + cache-dependency-path: apps/dashboard/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Build desktop app + run: ${{ matrix.command }} + + - name: Upload desktop artifact + uses: actions/upload-artifact@v4 + with: + name: flowchain-wallet-desktop-${{ matrix.name }} + path: ${{ matrix.artifact }} + if-no-files-found: error + + android: + name: Android wallet APK + runs-on: ubuntu-latest + defaults: + run: + working-directory: apps/dashboard + env: + HAS_ANDROID_RELEASE_KEYSTORE: ${{ secrets.FLOWCHAIN_ANDROID_KEYSTORE_BASE64 != '' }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "24" + cache: npm + cache-dependency-path: apps/dashboard/package-lock.json + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + + - name: Install dependencies + run: npm ci + + - name: Sync Android project + run: npm run mobile:android:sync + + - name: Make Gradle wrapper executable + run: chmod +x ./gradlew + working-directory: apps/dashboard/android + + - name: Build debug APK + run: ./gradlew assembleDebug + working-directory: apps/dashboard/android + + - name: Decode Android release keystore + if: env.HAS_ANDROID_RELEASE_KEYSTORE == 'true' + shell: bash + run: | + echo "$FLOWCHAIN_ANDROID_KEYSTORE_BASE64" | base64 -d > flowchain-release.keystore + env: + FLOWCHAIN_ANDROID_KEYSTORE_BASE64: ${{ secrets.FLOWCHAIN_ANDROID_KEYSTORE_BASE64 }} + + - name: Build signed release APK + if: env.HAS_ANDROID_RELEASE_KEYSTORE == 'true' + run: ./gradlew assembleRelease + working-directory: apps/dashboard/android + env: + FLOWCHAIN_ANDROID_KEYSTORE_FILE: ${{ github.workspace }}/apps/dashboard/flowchain-release.keystore + FLOWCHAIN_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.FLOWCHAIN_ANDROID_KEYSTORE_PASSWORD }} + FLOWCHAIN_ANDROID_KEY_ALIAS: ${{ secrets.FLOWCHAIN_ANDROID_KEY_ALIAS }} + FLOWCHAIN_ANDROID_KEY_PASSWORD: ${{ secrets.FLOWCHAIN_ANDROID_KEY_PASSWORD }} + + - name: Upload Android APKs + uses: actions/upload-artifact@v4 + with: + name: flowchain-wallet-android + path: | + apps/dashboard/android/app/build/outputs/apk/debug/*.apk + apps/dashboard/android/app/build/outputs/apk/release/*.apk + if-no-files-found: error + + github-release: + name: Publish tagged GitHub release + runs-on: ubuntu-latest + needs: + - desktop + - android + if: startsWith(github.ref, 'refs/tags/wallet-v') + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: release-artifacts + + - name: Publish release + shell: bash + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + gh release create "$GITHUB_REF_NAME" \ + --title "Flowchain Wallet $GITHUB_REF_NAME" \ + --notes "Desktop and Android wallet builds generated from $GITHUB_SHA." + find release-artifacts -type f -print0 \ + | xargs -0 -I {} gh release upload "$GITHUB_REF_NAME" "{}" --clobber diff --git a/.gitignore b/.gitignore index 1ed7aa60..6a9e295e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,6 @@ crates/flowmemory-devnet/target/ apps/dashboard/node_modules/ apps/dashboard/dist/ +apps/dashboard/test-results/ +apps/dashboard/playwright-report/ crypto/node_modules/ diff --git a/README.md b/README.md index 06cc0fe4..d6923f29 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ powershell -ExecutionPolicy Bypass -File .\INSTALL_FLOWCHAIN_WINDOWS.ps1 ``` Detailed guide: `docs/EASY_SECOND_COMPUTER_SETUP.md`. +FlowChain developer, wallet, bridge, node-operator, explorer/indexer, faucet, +and troubleshooting guides start at `docs/developer/README.md`. Every contributor and agent should read: @@ -59,7 +61,9 @@ Every contributor and agent should read: 7. `docs/V0_LAUNCH_ACCEPTANCE.md` 8. `docs/PRODUCTION_READINESS_CHECKLIST.md` 9. `docs/MARKETING_CLAIMS_GUARDRAILS.md` -10. `docs/DAILY_HQ_RUNBOOK.md` if operating HQ or coordinating agents +10. `docs/developer/README.md` if working on FlowChain L1, wallets, RPC, + bridge, SDK, or external tester flows +11. `docs/DAILY_HQ_RUNBOOK.md` if operating HQ or coordinating agents Then work only inside the assigned scope. diff --git a/apps/dashboard/.gitignore b/apps/dashboard/.gitignore index 361bf2de..4f5e528c 100644 --- a/apps/dashboard/.gitignore +++ b/apps/dashboard/.gitignore @@ -1,5 +1,6 @@ node_modules/ dist/ +release/ .vite/ coverage/ *.tsbuildinfo diff --git a/apps/dashboard/WALLET_DISTRIBUTION.md b/apps/dashboard/WALLET_DISTRIBUTION.md new file mode 100644 index 00000000..1433f63c --- /dev/null +++ b/apps/dashboard/WALLET_DISTRIBUTION.md @@ -0,0 +1,43 @@ +# Flowchain Wallet Distribution + +The wallet is distributed as native desktop builds through Electron and as a native Android shell through Capacitor. + +## Local Windows Desktop Build + +```powershell +npm run desktop:installer:win --prefix apps/dashboard +``` + +Artifacts are written to `apps/dashboard/release/`. + +For a local unsigned unpacked build plus zip: + +```powershell +npm run desktop:pack --prefix apps/dashboard +``` + +## Android Build + +The Android app source lives in `apps/dashboard/android`. + +```powershell +npm run mobile:android:sync --prefix apps/dashboard +``` + +Building an APK requires Java and the Android SDK. On CI, `.github/workflows/wallet-release.yml` builds a debug APK automatically. For a signed release APK, add these repository secrets: + +- `FLOWCHAIN_ANDROID_KEYSTORE_BASE64` +- `FLOWCHAIN_ANDROID_KEYSTORE_PASSWORD` +- `FLOWCHAIN_ANDROID_KEY_ALIAS` +- `FLOWCHAIN_ANDROID_KEY_PASSWORD` + +## Public Downloads + +Run the `Wallet release` GitHub workflow manually for downloadable CI artifacts, or push a tag like: + +```powershell +git tag wallet-v0.0.0 +git push origin wallet-v0.0.0 +``` + +Tagged runs publish a GitHub Release containing the desktop and Android artifacts. diff --git a/apps/dashboard/android/.gitignore b/apps/dashboard/android/.gitignore new file mode 100644 index 00000000..48354a3d --- /dev/null +++ b/apps/dashboard/android/.gitignore @@ -0,0 +1,101 @@ +# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore + +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# Android Profiling +*.hprof + +# Cordova plugins for Capacitor +capacitor-cordova-android-plugins + +# Copied web assets +app/src/main/assets/public + +# Generated Config files +app/src/main/assets/capacitor.config.json +app/src/main/assets/capacitor.plugins.json +app/src/main/res/xml/config.xml diff --git a/apps/dashboard/android/app/.gitignore b/apps/dashboard/android/app/.gitignore new file mode 100644 index 00000000..043df802 --- /dev/null +++ b/apps/dashboard/android/app/.gitignore @@ -0,0 +1,2 @@ +/build/* +!/build/.npmkeep diff --git a/apps/dashboard/android/app/build.gradle b/apps/dashboard/android/app/build.gradle new file mode 100644 index 00000000..7c046d0d --- /dev/null +++ b/apps/dashboard/android/app/build.gradle @@ -0,0 +1,76 @@ +apply plugin: 'com.android.application' + +def flowchainReleaseKeystore = System.getenv("FLOWCHAIN_ANDROID_KEYSTORE_FILE") +def flowchainReleaseStorePassword = System.getenv("FLOWCHAIN_ANDROID_KEYSTORE_PASSWORD") +def flowchainReleaseKeyAlias = System.getenv("FLOWCHAIN_ANDROID_KEY_ALIAS") +def flowchainReleaseKeyPassword = System.getenv("FLOWCHAIN_ANDROID_KEY_PASSWORD") +def hasFlowchainReleaseSigning = flowchainReleaseKeystore + && flowchainReleaseStorePassword + && flowchainReleaseKeyAlias + && flowchainReleaseKeyPassword + +android { + namespace = "ai.flowmemory.flowchain.wallet" + compileSdk = rootProject.ext.compileSdkVersion + defaultConfig { + applicationId "ai.flowmemory.flowchain.wallet" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + aaptOptions { + // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. + // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61 + ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~' + } + } + signingConfigs { + release { + if (hasFlowchainReleaseSigning) { + storeFile file(flowchainReleaseKeystore) + storePassword flowchainReleaseStorePassword + keyAlias flowchainReleaseKeyAlias + keyPassword flowchainReleaseKeyPassword + } + } + } + buildTypes { + release { + minifyEnabled false + if (hasFlowchainReleaseSigning) { + signingConfig signingConfigs.release + } + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +repositories { + flatDir{ + dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" + implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" + implementation project(':capacitor-android') + testImplementation "junit:junit:$junitVersion" + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" + implementation project(':capacitor-cordova-android-plugins') +} + +apply from: 'capacitor.build.gradle' + +try { + def servicesJSON = file('google-services.json') + if (servicesJSON.text) { + apply plugin: 'com.google.gms.google-services' + } +} catch(Exception e) { + logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work") +} diff --git a/apps/dashboard/android/app/capacitor.build.gradle b/apps/dashboard/android/app/capacitor.build.gradle new file mode 100644 index 00000000..bbfb44fa --- /dev/null +++ b/apps/dashboard/android/app/capacitor.build.gradle @@ -0,0 +1,19 @@ +// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN + +android { + compileOptions { + sourceCompatibility JavaVersion.VERSION_21 + targetCompatibility JavaVersion.VERSION_21 + } +} + +apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" +dependencies { + + +} + + +if (hasProperty('postBuildExtras')) { + postBuildExtras() +} diff --git a/apps/dashboard/android/app/proguard-rules.pro b/apps/dashboard/android/app/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/apps/dashboard/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/apps/dashboard/android/app/src/androidTest/java/ai/flowmemory/flowchain/wallet/ExampleInstrumentedTest.java b/apps/dashboard/android/app/src/androidTest/java/ai/flowmemory/flowchain/wallet/ExampleInstrumentedTest.java new file mode 100644 index 00000000..83098e70 --- /dev/null +++ b/apps/dashboard/android/app/src/androidTest/java/ai/flowmemory/flowchain/wallet/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package ai.flowmemory.flowchain.wallet; + +import static org.junit.Assert.*; + +import android.content.Context; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("ai.flowmemory.flowchain.wallet", appContext.getPackageName()); + } +} diff --git a/apps/dashboard/android/app/src/main/AndroidManifest.xml b/apps/dashboard/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b06ddbfd --- /dev/null +++ b/apps/dashboard/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/dashboard/android/app/src/main/java/ai/flowmemory/flowchain/wallet/MainActivity.java b/apps/dashboard/android/app/src/main/java/ai/flowmemory/flowchain/wallet/MainActivity.java new file mode 100644 index 00000000..dabd1d65 --- /dev/null +++ b/apps/dashboard/android/app/src/main/java/ai/flowmemory/flowchain/wallet/MainActivity.java @@ -0,0 +1,5 @@ +package ai.flowmemory.flowchain.wallet; + +import com.getcapacitor.BridgeActivity; + +public class MainActivity extends BridgeActivity {} diff --git a/apps/dashboard/android/app/src/main/res/drawable-land-hdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-land-hdpi/splash.png new file mode 100644 index 00000000..e31573b4 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-land-hdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-land-mdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-land-mdpi/splash.png new file mode 100644 index 00000000..f7a64923 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-land-mdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-land-xhdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-land-xhdpi/splash.png new file mode 100644 index 00000000..80772550 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-land-xhdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-land-xxhdpi/splash.png new file mode 100644 index 00000000..14c6c8fe Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-land-xxhdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-land-xxxhdpi/splash.png new file mode 100644 index 00000000..244ca250 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-land-xxxhdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-port-hdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-port-hdpi/splash.png new file mode 100644 index 00000000..74faaa58 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-port-hdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-port-mdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-port-mdpi/splash.png new file mode 100644 index 00000000..e944f4ad Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-port-mdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-port-xhdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-port-xhdpi/splash.png new file mode 100644 index 00000000..564a82ff Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-port-xhdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-port-xxhdpi/splash.png new file mode 100644 index 00000000..bfabe687 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-port-xxhdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/apps/dashboard/android/app/src/main/res/drawable-port-xxxhdpi/splash.png new file mode 100644 index 00000000..69290712 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable-port-xxxhdpi/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/apps/dashboard/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..c7bd21db --- /dev/null +++ b/apps/dashboard/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/apps/dashboard/android/app/src/main/res/drawable/ic_launcher_background.xml b/apps/dashboard/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..d5fccc53 --- /dev/null +++ b/apps/dashboard/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/dashboard/android/app/src/main/res/drawable/splash.png b/apps/dashboard/android/app/src/main/res/drawable/splash.png new file mode 100644 index 00000000..f7a64923 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/drawable/splash.png differ diff --git a/apps/dashboard/android/app/src/main/res/layout/activity_main.xml b/apps/dashboard/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..b5ad1387 --- /dev/null +++ b/apps/dashboard/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/apps/dashboard/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/apps/dashboard/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..036d09bc --- /dev/null +++ b/apps/dashboard/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/apps/dashboard/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..036d09bc --- /dev/null +++ b/apps/dashboard/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..c023e505 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/apps/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..2127973b Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/apps/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..b441f37d Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..72905b85 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/apps/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..8ed0605c Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/apps/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..9502e47a Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..4d1e0771 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/apps/dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..df0f1588 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/apps/dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..853db043 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..6cdf97c1 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/apps/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..2960cbb6 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/apps/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..8e3093a8 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apps/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..46de6e25 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/apps/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..d2ea9abe Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/apps/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/apps/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..a40d73e9 Binary files /dev/null and b/apps/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/apps/dashboard/android/app/src/main/res/values/ic_launcher_background.xml b/apps/dashboard/android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 00000000..c5d5899f --- /dev/null +++ b/apps/dashboard/android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + \ No newline at end of file diff --git a/apps/dashboard/android/app/src/main/res/values/strings.xml b/apps/dashboard/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..5310d8c6 --- /dev/null +++ b/apps/dashboard/android/app/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + Flowchain Wallet + Flowchain Wallet + ai.flowmemory.flowchain.wallet + ai.flowmemory.flowchain.wallet + diff --git a/apps/dashboard/android/app/src/main/res/values/styles.xml b/apps/dashboard/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..be874e54 --- /dev/null +++ b/apps/dashboard/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/android/app/src/main/res/xml/file_paths.xml b/apps/dashboard/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 00000000..bd0c4d80 --- /dev/null +++ b/apps/dashboard/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/android/app/src/test/java/ai/flowmemory/flowchain/wallet/ExampleUnitTest.java b/apps/dashboard/android/app/src/test/java/ai/flowmemory/flowchain/wallet/ExampleUnitTest.java new file mode 100644 index 00000000..837d73f2 --- /dev/null +++ b/apps/dashboard/android/app/src/test/java/ai/flowmemory/flowchain/wallet/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package ai.flowmemory.flowchain.wallet; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/apps/dashboard/android/build.gradle b/apps/dashboard/android/build.gradle new file mode 100644 index 00000000..b648f20e --- /dev/null +++ b/apps/dashboard/android/build.gradle @@ -0,0 +1,29 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.13.0' + classpath 'com.google.gms:google-services:4.4.4' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +apply from: "variables.gradle" + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/apps/dashboard/android/capacitor.settings.gradle b/apps/dashboard/android/capacitor.settings.gradle new file mode 100644 index 00000000..9a5fa872 --- /dev/null +++ b/apps/dashboard/android/capacitor.settings.gradle @@ -0,0 +1,3 @@ +// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN +include ':capacitor-android' +project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') diff --git a/apps/dashboard/android/gradle.properties b/apps/dashboard/android/gradle.properties new file mode 100644 index 00000000..2e87c52f --- /dev/null +++ b/apps/dashboard/android/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true diff --git a/apps/dashboard/android/gradle/wrapper/gradle-wrapper.jar b/apps/dashboard/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..1b33c55b Binary files /dev/null and b/apps/dashboard/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/apps/dashboard/android/gradle/wrapper/gradle-wrapper.properties b/apps/dashboard/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..7705927e --- /dev/null +++ b/apps/dashboard/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/apps/dashboard/android/gradlew b/apps/dashboard/android/gradlew new file mode 100755 index 00000000..23d15a93 --- /dev/null +++ b/apps/dashboard/android/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/apps/dashboard/android/gradlew.bat b/apps/dashboard/android/gradlew.bat new file mode 100644 index 00000000..db3a6ac2 --- /dev/null +++ b/apps/dashboard/android/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/apps/dashboard/android/settings.gradle b/apps/dashboard/android/settings.gradle new file mode 100644 index 00000000..3b4431d7 --- /dev/null +++ b/apps/dashboard/android/settings.gradle @@ -0,0 +1,5 @@ +include ':app' +include ':capacitor-cordova-android-plugins' +project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') + +apply from: 'capacitor.settings.gradle' \ No newline at end of file diff --git a/apps/dashboard/android/variables.gradle b/apps/dashboard/android/variables.gradle new file mode 100644 index 00000000..ee4ba41c --- /dev/null +++ b/apps/dashboard/android/variables.gradle @@ -0,0 +1,16 @@ +ext { + minSdkVersion = 24 + compileSdkVersion = 36 + targetSdkVersion = 36 + androidxActivityVersion = '1.11.0' + androidxAppCompatVersion = '1.7.1' + androidxCoordinatorLayoutVersion = '1.3.0' + androidxCoreVersion = '1.17.0' + androidxFragmentVersion = '1.8.9' + coreSplashScreenVersion = '1.2.0' + androidxWebkitVersion = '1.14.0' + junitVersion = '4.13.2' + androidxJunitVersion = '1.3.0' + androidxEspressoCoreVersion = '3.7.0' + cordovaAndroidVersion = '14.0.1' +} \ No newline at end of file diff --git a/apps/dashboard/capacitor.config.ts b/apps/dashboard/capacitor.config.ts new file mode 100644 index 00000000..f25cbc60 --- /dev/null +++ b/apps/dashboard/capacitor.config.ts @@ -0,0 +1,13 @@ +import type { CapacitorConfig } from "@capacitor/cli"; + +const config: CapacitorConfig = { + appId: "ai.flowmemory.flowchain.wallet", + appName: "Flowchain Wallet", + webDir: "dist", + bundledWebRuntime: false, + server: { + androidScheme: "https", + }, +}; + +export default config; diff --git a/apps/dashboard/e2e/flowchain-ui-readiness.spec.ts b/apps/dashboard/e2e/flowchain-ui-readiness.spec.ts new file mode 100644 index 00000000..d6504529 --- /dev/null +++ b/apps/dashboard/e2e/flowchain-ui-readiness.spec.ts @@ -0,0 +1,318 @@ +import { expect, test, type Page, type Route } from "@playwright/test"; + +const TESTER_TOKEN = "local-tester-write-token"; +const TESTER_ACCOUNT_A = "local-account:tester-browser-a"; +const TESTER_ACCOUNT_B = "local-account:tester-browser-b"; + +type BrowserState = { + created: boolean; + funded: boolean; + sent: boolean; +}; + +async function fulfillJson(route: Route, payload: unknown, status = 200) { + await route.fulfill({ + status, + contentType: "application/json", + body: JSON.stringify(payload), + }); +} + +async function installControlPlaneMocks(page: Page, state: BrowserState) { + await page.route("https://api.coinbase.com/**", async (route) => { + await fulfillJson(route, { data: { rates: { USD: "3241.17" } } }); + }); + + await page.route("http://127.0.0.1:8787/**", async (route) => { + const request = route.request(); + const url = new URL(request.url()); + const pathname = url.pathname; + const method = request.method(); + + if (pathname === "/health") { + await fulfillJson(route, { + status: "ok", + endpoints: [ + "GET /health", + "GET /state", + "GET /wallets/operator", + "GET /wallets/balances", + "GET /wallets/transfers", + "GET /tester/status", + "POST /tester/wallets/create", + "POST /tester/faucet", + "POST /tester/wallets/send", + ], + }); + return; + } + + if (pathname === "/state") { + await fulfillJson(route, { + state: { + chainId: "flowmemory-local-devnet-v0", + nextBlockNumber: 63002, + blocks: state.sent + ? [{ + blockNumber: 63001, + blockHash: `0x${"6".repeat(64)}`, + parentHash: `0x${"5".repeat(64)}`, + stateRoot: `0x${"7".repeat(64)}`, + txIds: ["tx:tester-browser-a-b"], + receipts: [{ txId: "tx:tester-browser-a-b", status: "applied" }], + }] + : [], + faucetRecords: state.funded + ? [{ + eventId: "faucet:tester-browser-a", + accountId: TESTER_ACCOUNT_A, + amountUnits: "2", + status: "observed", + blockNumber: 63000, + }] + : [], + walletMetadata: state.created + ? [{ + walletId: TESTER_ACCOUNT_A, + operatorId: TESTER_ACCOUNT_A, + status: "verified", + secretMaterialBoundary: "Public metadata only.", + }] + : [], + }, + }); + return; + } + + if (pathname === "/pilot/status") { + await fulfillJson(route, { + schema: "flowmemory.control_plane.real_value_pilot_status.v0", + state: "degraded", + label: "FlowChain capped owner real-value pilot", + cappedOwnerTesting: true, + broadPublicReadiness: false, + productionReady: false, + browserStoresSecrets: false, + nextOperatorStep: { + command: "npm run flowchain:public-deployment:contract -- -AllowBlocked", + }, + lifecycle: [], + }); + return; + } + + if (pathname === "/bridge/live-readiness") { + await fulfillJson(route, { + schema: "flowmemory.control_plane.bridge_live_readiness.v0", + baseChainId: 8453, + failClosedStatus: "BLOCKED", + readyForOperatorLivePilot: false, + missingEnvNames: ["FLOWCHAIN_BASE8453_RPC_URL"], + envValuesPrinted: false, + noSecrets: true, + productionReady: false, + }); + return; + } + + if (pathname === "/pilot/lifecycle") { + await fulfillJson(route, { schema: "flowmemory.control_plane.bridge_lifecycle_record_list.v0", count: 0, lifecycleRecords: [] }); + return; + } + + if (pathname === "/wallets/operator") { + await fulfillJson(route, { + schema: "flowmemory.control_plane.local_wallet_status.v0", + exists: state.created, + account: state.created ? { accountId: TESTER_ACCOUNT_A, address: TESTER_ACCOUNT_A, keyScheme: "secp256k1", status: "ready" } : null, + secretMaterialReturned: false, + noSecrets: true, + }); + return; + } + + if (pathname === "/wallets/balances") { + await fulfillJson(route, { + schema: "flowmemory.control_plane.wallet_balance_list.v0", + count: state.funded ? 1 : 0, + balances: state.funded + ? [{ balanceId: "balance:tester-browser-a", walletAddress: TESTER_ACCOUNT_A, asset: "local-test-unit", amount: "2", status: "credited", source: "tester-faucet" }] + : [], + }); + return; + } + + if (pathname === "/wallets/transfers") { + await fulfillJson(route, { + schema: "flowmemory.control_plane.wallet_transfer_history.v0", + count: state.sent ? 1 : 0, + transfers: state.sent + ? [{ transferId: "transfer:tester-browser-a-b", txId: "tx:tester-browser-a-b", fromAccountId: TESTER_ACCOUNT_A, toAccountId: TESTER_ACCOUNT_B, assetId: "local-test-unit", amount: "1", status: "applied" }] + : [], + }); + return; + } + + if (pathname === "/pilot/credits") { + await fulfillJson(route, { schema: "flowmemory.control_plane.pilot_credit_list.v0", credits: [] }); + return; + } + + if (pathname === "/bridge/status") { + await fulfillJson(route, { + deposits: 0, + credits: state.funded ? 1 : 0, + applied: state.sent ? 1 : 0, + publicProductionL1Ready: false, + liveRuntimeHandoffLoaded: true, + }); + return; + } + + if (pathname === "/tester/status") { + await fulfillJson(route, { + schema: "flowmemory.control_plane.tester_write_status.v0", + configured: true, + enabled: true, + tokenHashConfigured: true, + maxSendUnits: "2", + missingEnvNames: [], + invalidEnvNames: [], + envValuesPrinted: false, + noSecrets: true, + localOnly: true, + }); + return; + } + + if (pathname === "/tester/wallets/create" && method === "POST") { + expect(request.headers().authorization).toBe(`Bearer ${TESTER_TOKEN}`); + state.created = true; + await fulfillJson(route, { + schema: "flowmemory.control_plane.tester_wallet_create_result.v0", + created: true, + secretMaterialReturned: false, + credentialStored: false, + noSecrets: true, + account: { accountId: TESTER_ACCOUNT_A, address: TESTER_ACCOUNT_A, keyScheme: "secp256k1", status: "ready" }, + }); + return; + } + + if (pathname === "/tester/faucet" && method === "POST") { + expect(request.headers().authorization).toBe(`Bearer ${TESTER_TOKEN}`); + state.funded = true; + await fulfillJson(route, { + schema: "flowmemory.control_plane.tester_faucet_result.v0", + accepted: true, + applied: true, + status: "applied", + txIds: ["tx:tester-faucet-browser-a"], + accountId: TESTER_ACCOUNT_A, + assetId: "local-test-unit", + amountUnits: "2", + balancesAfter: { account: "2" }, + }); + return; + } + + if (pathname === "/tester/wallets/send" && method === "POST") { + expect(request.headers().authorization).toBe(`Bearer ${TESTER_TOKEN}`); + state.sent = true; + await fulfillJson(route, { + schema: "flowmemory.control_plane.tester_wallet_send_result.v0", + accepted: true, + applied: true, + transferId: "transfer:tester-browser-a-b", + txIds: ["tx:tester-browser-a-b"], + assetId: "local-test-unit", + amountUnits: "1", + status: "applied", + from: { requestedAccountId: TESTER_ACCOUNT_A, runtimeAccountId: TESTER_ACCOUNT_A, resolution: "matched" }, + to: { requestedAccountId: TESTER_ACCOUNT_B, runtimeAccountId: TESTER_ACCOUNT_B, resolution: "matched" }, + }); + return; + } + + await fulfillJson(route, { schema: "flowmemory.control_plane.not_found.v0", noSecrets: true }, 404); + }); +} + +async function expectNoUiLeakage(page: Page) { + const bodyText = await page.locator("body").innerText(); + expect(bodyText).not.toContain(TESTER_TOKEN); + expect(bodyText).not.toContain("privateKey"); + expect(bodyText).not.toContain("seed phrase"); + expect(bodyText).not.toContain("mnemonic"); + + const storage = await page.evaluate(() => ({ + localStorage: { ...window.localStorage }, + sessionStorage: { ...window.sessionStorage }, + })); + expect(JSON.stringify(storage)).not.toContain(TESTER_TOKEN); + expect(JSON.stringify(storage)).not.toContain("privateKey"); +} + +async function expectNoHorizontalOverflow(page: Page) { + const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth); + expect(overflow).toBeLessThanOrEqual(2); +} + +test.describe("FlowChain wallet, faucet, and explorer browser readiness", () => { + test("completes the tester wallet funding loop and keeps the explorer inspectable", async ({ page }) => { + const consoleErrors: string[] = []; + const state: BrowserState = { created: false, funded: false, sent: false }; + + page.on("console", (message) => { + if (message.type() === "error") { + consoleErrors.push(message.text()); + } + }); + page.on("pageerror", (error) => { + consoleErrors.push(error.message); + }); + + await installControlPlaneMocks(page, state); + await page.goto("/wallet?panel=tester"); + + await expect(page.getByText("Tester gateway configured")).toBeVisible(); + await page.getByLabel("Tester bearer token").fill(TESTER_TOKEN); + await page.getByLabel("Tester wallet label").fill("friend-browser-a"); + await page.getByLabel("Tester wallet passphrase").fill("browser-test-passphrase"); + await page.getByRole("button", { name: /Create tester wallet/ }).click(); + await expect(page.getByRole("status")).toContainText("Tester wallet created"); + + await page.getByRole("button", { name: "Tester", exact: true }).click(); + await page.getByLabel("Fund account").fill(TESTER_ACCOUNT_A); + await page.getByLabel("Faucet units").fill("2"); + await page.getByRole("button", { name: /Request tester faucet/ }).click(); + await expect(page.getByRole("status")).toContainText("Tester faucet accepted"); + + await page.getByRole("button", { name: "Tester", exact: true }).click(); + await page.getByLabel("Sender account").fill(TESTER_ACCOUNT_A); + await page.getByLabel("Recipient account").fill(TESTER_ACCOUNT_B); + await page.getByLabel("Amount units").fill("1"); + await page.getByRole("button", { name: /Send tester units/ }).click(); + await expect(page.getByRole("status")).toContainText("Tester send accepted"); + await expect(page.getByText("Tester send applied")).toBeVisible(); + + await page.getByRole("button", { name: "Tester", exact: true }).click(); + await page.getByRole("link", { name: /Inspect tester activity/ }).click(); + await expect(page).toHaveURL(/\/explorer$/); + await page.reload(); + await expect(page.getByRole("heading", { name: "Flowchain explorer" })).toBeVisible(); + await expect(page.getByLabel("Tester settlement trace").getByText("Create, fund, send, inspect")).toBeVisible(); + await expect(page.getByText("Funding proofs", { exact: true })).toBeVisible(); + await expect(page.getByText("Wallet records", { exact: true })).toBeVisible(); + + await page.getByRole("button", { name: /Faucet/ }).first().click(); + await expect(page.getByLabel("Explorer records")).toContainText(/faucet/i); + + await page.getByRole("button", { name: /Transactions/ }).first().click(); + await expect(page.getByLabel("Explorer records")).toContainText(/transaction|transfer/i); + + await expectNoUiLeakage(page); + await expectNoHorizontalOverflow(page); + expect(consoleErrors).toEqual([]); + }); +}); diff --git a/apps/dashboard/electron-builder.json b/apps/dashboard/electron-builder.json new file mode 100644 index 00000000..315a5c12 --- /dev/null +++ b/apps/dashboard/electron-builder.json @@ -0,0 +1,43 @@ +{ + "appId": "ai.flowmemory.flowchain.wallet", + "productName": "Flowchain Wallet", + "directories": { + "output": "release" + }, + "files": [ + "dist/**/*", + "electron/**/*", + "package.json" + ], + "asar": true, + "mac": { + "category": "public.app-category.finance", + "target": [ + "dmg", + "zip" + ], + "identity": null + }, + "win": { + "signAndEditExecutable": false, + "target": [ + "nsis", + "zip" + ] + }, + "nsis": { + "oneClick": false, + "perMachine": false, + "allowToChangeInstallationDirectory": true, + "createDesktopShortcut": true, + "createStartMenuShortcut": true, + "shortcutName": "Flowchain Wallet" + }, + "linux": { + "category": "Finance", + "target": [ + "AppImage", + "zip" + ] + } +} diff --git a/apps/dashboard/electron/main.cjs b/apps/dashboard/electron/main.cjs new file mode 100644 index 00000000..a1658334 --- /dev/null +++ b/apps/dashboard/electron/main.cjs @@ -0,0 +1,159 @@ +const { app, BrowserWindow, Menu, ipcMain, shell } = require("electron"); +const fs = require("node:fs/promises"); +const http = require("node:http"); +const path = require("node:path"); +const { createLocalDesktopWallet, publicDesktopWalletStatus } = require("./wallet-store.cjs"); + +const isDev = Boolean(process.env.FLOWCHAIN_WALLET_DESKTOP_DEV_URL); +let staticServer; + +const contentTypes = new Map([ + [".css", "text/css; charset=utf-8"], + [".html", "text/html; charset=utf-8"], + [".js", "text/javascript; charset=utf-8"], + [".json", "application/json; charset=utf-8"], + [".png", "image/png"], + [".svg", "image/svg+xml; charset=utf-8"], + [".woff", "font/woff"], + [".woff2", "font/woff2"], +]); + +function contentTypeFor(filePath) { + return contentTypes.get(path.extname(filePath).toLowerCase()) ?? "application/octet-stream"; +} + +function startBundledStaticServer() { + if (staticServer !== undefined) { + return staticServer; + } + + const distDir = path.resolve(__dirname, "..", "dist"); + staticServer = new Promise((resolve, reject) => { + const server = http.createServer(async (req, res) => { + try { + const requestUrl = new URL(req.url ?? "/", "http://127.0.0.1"); + const rawPath = decodeURIComponent(requestUrl.pathname); + const assetPath = rawPath === "/" || path.extname(rawPath) === "" ? "/index.html" : rawPath; + const filePath = path.resolve(distDir, `.${assetPath}`); + + if (!filePath.startsWith(distDir)) { + res.writeHead(403, { "content-type": "text/plain; charset=utf-8" }); + res.end("Forbidden"); + return; + } + + const body = await fs.readFile(filePath); + res.writeHead(200, { + "cache-control": "no-store", + "content-type": contentTypeFor(filePath), + }); + res.end(body); + } catch { + res.writeHead(404, { "content-type": "text/plain; charset=utf-8" }); + res.end("Not found"); + } + }); + + server.once("error", reject); + server.listen(0, "127.0.0.1", () => { + const address = server.address(); + if (typeof address !== "object" || address === null) { + reject(new Error("Flowchain Wallet static server did not bind to a local port.")); + return; + } + resolve({ server, origin: `http://127.0.0.1:${address.port}` }); + }); + }); + + return staticServer; +} + +async function createWalletWindow() { + const window = new BrowserWindow({ + width: 1480, + height: 940, + minWidth: 1120, + minHeight: 720, + title: "Flowchain Wallet", + backgroundColor: "#fff9ee", + show: false, + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + sandbox: true, + preload: path.join(__dirname, "preload.cjs"), + }, + }); + + window.once("ready-to-show", () => { + window.show(); + }); + + window.webContents.setWindowOpenHandler(({ url }) => { + if (url.startsWith("http://127.0.0.1") || url.startsWith("http://localhost")) { + return { action: "allow" }; + } + shell.openExternal(url).catch(() => undefined); + return { action: "deny" }; + }); + + window.webContents.on("will-navigate", (event, url) => { + const allowed = url.startsWith("file://") + || url.startsWith("http://127.0.0.1") + || url.startsWith("http://localhost") + || url.startsWith("http://192.168."); + if (!allowed) { + event.preventDefault(); + shell.openExternal(url).catch(() => undefined); + } + }); + + if (isDev) { + window.loadURL(`${process.env.FLOWCHAIN_WALLET_DESKTOP_DEV_URL.replace(/\/+$/, "")}/wallet`); + } else { + const { origin } = await startBundledStaticServer(); + window.loadURL(`${origin}/wallet`); + } + + return window; +} + +app.setName("Flowchain Wallet"); + +app.whenReady().then(() => { + ipcMain.handle("flowchain-wallet:get-local-wallet", () => publicDesktopWalletStatus(app.getPath("userData"))); + ipcMain.handle("flowchain-wallet:create-local-wallet", (_event, payload) => createLocalDesktopWallet(app.getPath("userData"), payload)); + + Menu.setApplicationMenu(Menu.buildFromTemplate([ + { + label: "Flowchain Wallet", + submenu: [ + { role: "reload" }, + { role: "toggleDevTools" }, + { type: "separator" }, + { role: "quit" }, + ], + }, + ])); + + createWalletWindow().catch((error) => { + console.error(error); + app.quit(); + }); + + app.on("activate", () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWalletWindow().catch((error) => { + console.error(error); + app.quit(); + }); + } + }); +}); + +app.on("window-all-closed", () => { + staticServer?.then(({ server }) => server.close()).catch(() => undefined); + if (process.platform !== "darwin") { + app.quit(); + } +}); diff --git a/apps/dashboard/electron/preload.cjs b/apps/dashboard/electron/preload.cjs new file mode 100644 index 00000000..7a3009b8 --- /dev/null +++ b/apps/dashboard/electron/preload.cjs @@ -0,0 +1,9 @@ +const { contextBridge, ipcRenderer } = require("electron"); + +contextBridge.exposeInMainWorld("flowchainDesktop", { + app: "Flowchain Wallet", + platform: process.platform, + packaged: process.env.NODE_ENV !== "development", + getLocalWallet: () => ipcRenderer.invoke("flowchain-wallet:get-local-wallet"), + createLocalWallet: (payload) => ipcRenderer.invoke("flowchain-wallet:create-local-wallet", payload), +}); diff --git a/apps/dashboard/electron/wallet-store.cjs b/apps/dashboard/electron/wallet-store.cjs new file mode 100644 index 00000000..30991915 --- /dev/null +++ b/apps/dashboard/electron/wallet-store.cjs @@ -0,0 +1,340 @@ +const { createCipheriv, randomBytes, scryptSync } = require("node:crypto"); +const { existsSync, mkdirSync, readFileSync, writeFileSync } = require("node:fs"); +const path = require("node:path"); + +const VAULT_SCHEMA = "flowmemory.crypto.local-test-vault.v0"; +const VAULT_SECRETS_SCHEMA = "flowmemory.crypto.local-test-vault-secrets.v0"; +const LOCAL_WALLET_PUBLIC_METADATA_SCHEMA = "flowchain.local_wallet_public_metadata.v0"; +const LOCAL_WALLET_KEY_SCHEME = "secp256k1"; +const DEFAULT_LOCAL_WALLET_CHAIN_ID = "31337"; +const LOCAL_ALPHA_SIGNER_ROLES = Object.freeze({ + operator: 1, + agent: 2, + verifier: 3, + hardware: 4, + user: 10, + validator: 11, + bridgeRelayer: 12, + bridgeReleaseAuthority: 13, + emergencyOperator: 14, +}); + +let nobleModules; + +async function loadNobleModules() { + if (nobleModules === undefined) { + nobleModules = Promise.all([ + import("@noble/secp256k1"), + import("@noble/hashes/sha3.js"), + ]).then(([secp, sha3]) => ({ secp, sha3 })); + } + return nobleModules; +} + +function walletPaths(userDataPath) { + const walletDir = path.join(userDataPath, "wallet"); + return { + walletDir, + vaultPath: path.join(walletDir, "flowchain-wallet-vault.local.json"), + metadataPath: path.join(walletDir, "flowchain-wallet-public-metadata.json"), + }; +} + +function publicDesktopWalletStatus(userDataPath) { + const paths = walletPaths(userDataPath); + const metadata = existsSync(paths.metadataPath) ? readJson(paths.metadataPath) : null; + const accounts = Array.isArray(metadata?.accounts) ? metadata.accounts : []; + const primaryAccount = accounts.find((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry)) ?? null; + return { + schema: "flowmemory.control_plane.local_wallet_public_status.v0", + exists: metadata !== null, + metadataPath: paths.metadataPath, + account: primaryAccount, + accounts, + secretMaterialReturned: false, + localOnly: true, + desktopLocal: true, + }; +} + +async function createLocalDesktopWallet(userDataPath, payload = {}) { + const request = parseWalletCreatePayload(payload); + const paths = walletPaths(userDataPath); + mkdirSync(paths.walletDir, { recursive: true }); + + if (!request.replace && existsSync(paths.vaultPath) && existsSync(paths.metadataPath)) { + return { + ...publicDesktopWalletStatus(userDataPath), + schema: "flowmemory.control_plane.local_wallet_create_result.v0", + created: false, + alreadyExists: true, + vaultPath: paths.vaultPath, + note: "Existing encrypted local wallet vault was left unchanged. Enable replace to rotate to a new wallet.", + }; + } + + const vault = await createEncryptedDesktopVault({ + password: request.password, + label: request.label, + signerRole: "user", + chainId: request.chainId, + }); + const metadata = exportLocalWalletPublicMetadata(vault); + writeJson(paths.vaultPath, vault); + writeJson(paths.metadataPath, metadata); + + const account = Array.isArray(metadata.accounts) ? metadata.accounts[0] : null; + return { + schema: "flowmemory.control_plane.local_wallet_create_result.v0", + created: true, + alreadyExists: false, + account, + accounts: metadata.accounts, + vaultPath: paths.vaultPath, + metadataPath: paths.metadataPath, + chainId: request.chainId, + keyScheme: account?.keyScheme ?? LOCAL_WALLET_KEY_SCHEME, + secretMaterialReturned: false, + credentialStored: false, + localOnly: true, + desktopLocal: true, + }; +} + +async function createEncryptedDesktopVault({ + password, + label, + signerRole, + createdAtUnixMs = Date.now().toString(), + privateKey, + chainId = DEFAULT_LOCAL_WALLET_CHAIN_ID, + lastKnownNonce = "0", +}) { + requirePassword(password); + const account = await createVaultAccount({ label, signerRole, createdAtUnixMs, privateKey, chainId, lastKnownNonce }); + return encryptVaultSecrets({ + password, + publicAccounts: [publicAccount(account)], + secrets: { + schema: VAULT_SECRETS_SCHEMA, + accounts: [account], + }, + createdAtUnixMs, + }); +} + +function encryptVaultSecrets({ + password, + publicAccounts, + secrets, + createdAtUnixMs, + vaultId = randomBytes32(), +}) { + requirePassword(password); + const salt = randomBytes(16); + const iv = randomBytes(12); + const kdf = { + name: "scrypt", + salt: bytesToHex(salt), + N: 16384, + r: 8, + p: 1, + keyLength: 32, + }; + const key = scryptSync(password, salt, kdf.keyLength, { N: kdf.N, r: kdf.r, p: kdf.p }); + const cipher = createCipheriv("aes-256-gcm", key, iv); + const ciphertext = Buffer.concat([ + cipher.update(JSON.stringify(secrets), "utf8"), + cipher.final(), + ]); + + return { + schema: VAULT_SCHEMA, + vaultId, + createdAtUnixMs, + kdf, + cipher: { + name: "aes-256-gcm", + iv: bytesToHex(iv), + authTag: bytesToHex(cipher.getAuthTag()), + }, + ciphertext: bytesToHex(ciphertext), + publicAccounts, + }; +} + +async function createVaultAccount({ + label, + signerRole, + createdAtUnixMs, + privateKey, + chainId = DEFAULT_LOCAL_WALLET_CHAIN_ID, + lastKnownNonce = "0", +}) { + if (LOCAL_ALPHA_SIGNER_ROLES[signerRole] === undefined) { + throw new Error(`unsupported signer role: ${signerRole}`); + } + if (!isUintString(String(chainId))) { + throw new Error(`unsupported wallet chain id: ${chainId}`); + } + if (!isUintString(String(lastKnownNonce))) { + throw new Error(`invalid wallet last known nonce: ${lastKnownNonce}`); + } + const effectivePrivateKey = privateKey ?? await randomPrivateKey(); + const publicKey = await publicKeyFromPrivateKey(effectivePrivateKey); + const publicKeyHash = await keccakUtf8(publicKey); + const signerId = await keccakUtf8(`flowchain.local-alpha.signer:${publicKey}`); + return { + label, + accountId: signerId, + address: signerId, + signerRole, + signerRoleCode: LOCAL_ALPHA_SIGNER_ROLES[signerRole], + signerId, + signerKeyId: await keccakUtf8(`flowchain.local-alpha.signer-key:${publicKey}`), + publicKey, + publicKeyHash, + keyScheme: LOCAL_WALLET_KEY_SCHEME, + chainId: String(chainId), + lastKnownNonce: String(lastKnownNonce), + nextNonce: nextNonce(lastKnownNonce), + createdAtUnixMs, + status: "active", + active: true, + privateKey: effectivePrivateKey, + }; +} + +function publicAccount(account) { + const { privateKey, ...metadata } = account; + return metadata; +} + +function exportLocalWalletPublicMetadata(vault, { updatedAtUnixMs = Date.now().toString() } = {}) { + return { + schema: LOCAL_WALLET_PUBLIC_METADATA_SCHEMA, + vaultId: vault.vaultId, + createdAtUnixMs: vault.createdAtUnixMs, + updatedAtUnixMs: String(updatedAtUnixMs), + accounts: (vault.publicAccounts ?? []).map(localWalletPublicAccount), + boundary: "Public local wallet metadata only. Signing material, vault encryption payloads, credentials, and webhooks are excluded.", + }; +} + +function localWalletPublicAccount(account) { + return { + accountId: account.accountId ?? account.signerId, + address: account.address ?? account.signerId, + signerId: account.signerId, + signerKeyId: account.signerKeyId, + signerRole: account.signerRole, + signerRoleCode: account.signerRoleCode, + publicKey: account.publicKey, + publicKeyHash: account.publicKeyHash, + keyScheme: account.keyScheme ?? LOCAL_WALLET_KEY_SCHEME, + label: account.label, + status: account.active === false ? "rotated" : account.status ?? "active", + active: account.active !== false, + createdAtUnixMs: account.createdAtUnixMs, + chainId: String(account.chainId ?? DEFAULT_LOCAL_WALLET_CHAIN_ID), + lastKnownNonce: String(account.lastKnownNonce ?? "0"), + nextNonce: String(account.nextNonce ?? nextNonce(account.lastKnownNonce ?? "0")), + rotatedFromSignerKeyId: account.rotatedFromSignerKeyId, + }; +} + +async function randomPrivateKey() { + for (;;) { + const candidate = bytesToHex(randomBytes(32)); + try { + await publicKeyFromPrivateKey(candidate); + return candidate; + } catch { + // Retry if the random bytes do not form a valid secp256k1 secret. + } + } +} + +async function publicKeyFromPrivateKey(privateKeyHex) { + const { secp } = await loadNobleModules(); + return bytesToHex(secp.getPublicKey(hexToBytes(privateKeyHex, 32))); +} + +async function keccakUtf8(value) { + const { sha3 } = await loadNobleModules(); + return bytesToHex(sha3.keccak_256(Buffer.from(String(value), "utf8"))); +} + +function parseWalletCreatePayload(payload) { + if (payload === null || typeof payload !== "object" || Array.isArray(payload)) { + throw new Error("wallet creation payload must be an object"); + } + const password = typeof payload.password === "string" ? payload.password : ""; + if (password.length < 8) { + throw new Error("wallet vault passphrase must be at least 8 characters"); + } + const chainId = typeof payload.chainId === "string" && /^\d+$/.test(payload.chainId) ? payload.chainId : DEFAULT_LOCAL_WALLET_CHAIN_ID; + return { + label: labelSlug(payload.label), + password, + chainId, + replace: payload.replace === true, + }; +} + +function labelSlug(value) { + const label = typeof value === "string" && value.trim().length > 0 ? value.trim() : "flowchain-wallet"; + const slug = label.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, ""); + return slug.length > 0 ? slug.slice(0, 64) : "flowchain-wallet"; +} + +function requirePassword(password) { + if (typeof password !== "string" || password.length < 8) { + throw new Error("wallet vault passphrase must be at least 8 characters"); + } +} + +function randomBytes32() { + return bytesToHex(randomBytes(32)); +} + +function bytesToHex(bytes) { + return `0x${Buffer.from(bytes).toString("hex")}`; +} + +function hexToBytes(value, expectedLength) { + if (typeof value !== "string") { + throw new TypeError("hex value must be a string"); + } + const raw = value.startsWith("0x") ? value.slice(2) : value; + if (raw.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(raw)) { + throw new Error(`invalid hex value: ${value}`); + } + const bytes = Uint8Array.from(Buffer.from(raw, "hex")); + if (expectedLength !== undefined && bytes.length !== expectedLength) { + throw new Error(`expected ${expectedLength} bytes, got ${bytes.length}`); + } + return bytes; +} + +function isUintString(value) { + return typeof value === "string" && /^[0-9]+$/.test(value); +} + +function nextNonce(value) { + return (BigInt(value) + 1n).toString(); +} + +function readJson(filePath) { + return JSON.parse(readFileSync(filePath, "utf8")); +} + +function writeJson(filePath, value) { + writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`); +} + +module.exports = { + createLocalDesktopWallet, + publicDesktopWalletStatus, + walletPaths, +}; diff --git a/apps/dashboard/index.html b/apps/dashboard/index.html index f705964a..33732dfe 100644 --- a/apps/dashboard/index.html +++ b/apps/dashboard/index.html @@ -5,9 +5,10 @@ - FlowMemory Dashboard V0 + + Flowchain Wallet
diff --git a/apps/dashboard/package-lock.json b/apps/dashboard/package-lock.json index 684afda4..8822c97c 100644 --- a/apps/dashboard/package-lock.json +++ b/apps/dashboard/package-lock.json @@ -8,840 +8,913 @@ "name": "@flowmemory/dashboard", "version": "0.0.0", "dependencies": { + "@capacitor/android": "^8.3.4", + "@capacitor/core": "^8.3.4", + "@noble/hashes": "^2.2.0", + "@noble/secp256k1": "^3.1.0", "lucide-react": "^0.468.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.28.0" }, "devDependencies": { + "@capacitor/cli": "^8.3.4", + "@playwright/test": "^1.60.0", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "@vitejs/plugin-react": "^6.0.1", + "electron": "^42.0.1", + "electron-builder": "^26.8.1", "typescript": "^5.6.3", "vite": "^8.0.12", "vitest": "^4.1.6" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, + "node_modules/@capacitor/android": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@capacitor/android/-/android-8.3.4.tgz", + "integrity": "sha512-7gJjrG3X32Am1QMLqgMztWTYMLMVNE+VZwhekNxhvYizH4mOV05vH+rC9B+f17bCkYZfyu/qXQX6hoY7kLeVZw==", "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" + "peerDependencies": { + "@capacitor/core": "^8.3.0" } }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "node_modules/@capacitor/cli": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-8.3.4.tgz", + "integrity": "sha512-QEmyNdiDDVNYl0Mahm7YTVA/3t2tKcy7FWYDapeKGavS6HDNHZSjyTVwQpUXQbDZrrs/PS2Wau3Aii+LIFwm/A==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@ionic/cli-framework-output": "^2.2.8", + "@ionic/utils-subprocess": "^3.0.1", + "@ionic/utils-terminal": "^2.3.5", + "commander": "^12.1.0", + "debug": "^4.4.0", + "env-paths": "^2.2.0", + "fs-extra": "^11.2.0", + "kleur": "^4.1.5", + "native-run": "^2.0.3", + "open": "^8.4.0", + "plist": "^3.1.0", + "prompts": "^2.4.2", + "rimraf": "^6.0.1", + "semver": "^7.6.3", + "tar": "^7.5.3", + "tslib": "^2.8.1", + "xml2js": "^0.6.2" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=22.0.0" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "node_modules/@capacitor/cli/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "engines": { + "node": ">=18" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "node_modules/@capacitor/cli/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "node_modules/@capacitor/cli/node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@capacitor/cli/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@oxc-project/types": { - "version": "0.129.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.129.0.tgz", - "integrity": "sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==", + "node_modules/@capacitor/cli/node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@remix-run/router": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", - "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "node_modules/@capacitor/core": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.3.4.tgz", + "integrity": "sha512-CqRQCkb6HXxcx/N7s+hHTN6ef2CmamFiRMITwm4qB840ph56mS42bzUgn6tKCP+RZjdDweiRHj9ytDDeN6jFag==", "license": "MIT", - "engines": { - "node": ">=14.0.0" + "dependencies": { + "tslib": "^2.1.0" } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0.tgz", - "integrity": "sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==", - "cpu": [ - "arm64" - ], + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0.tgz", - "integrity": "sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==", - "cpu": [ - "arm64" - ], + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=10.12.0" } }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0.tgz", - "integrity": "sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==", - "cpu": [ - "x64" - ], + "node_modules/@electron/asar/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "*" } }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0.tgz", - "integrity": "sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==", - "cpu": [ - "x64" - ], + "node_modules/@electron/fuses": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", + "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "dependencies": { + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" + }, + "bin": { + "electron-fuses": "dist/bin.js" } }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0.tgz", - "integrity": "sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==", - "cpu": [ - "arm" - ], + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=10" } }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0.tgz", - "integrity": "sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==", - "cpu": [ - "arm64" - ], + "node_modules/@electron/get": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.0.0.tgz", + "integrity": "sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==", "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^3.0.0", + "graceful-fs": "^4.2.11", + "progress": "^2.0.3", + "semver": "^7.6.3", + "sumchecker": "^3.0.1" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=22.12.0" + }, + "optionalDependencies": { + "undici": "^7.24.4" } }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0.tgz", - "integrity": "sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==", - "cpu": [ - "arm64" - ], + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", "dev": true, - "libc": [ - "musl" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 10.0.0" } }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0.tgz", - "integrity": "sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==", - "cpu": [ - "ppc64" - ], + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=10" } }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0.tgz", - "integrity": "sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==", - "cpu": [ - "s390x" - ], + "node_modules/@electron/osx-sign": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", + "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=12.0.0" } }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0.tgz", - "integrity": "sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==", - "cpu": [ - "x64" - ], + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" } }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0.tgz", - "integrity": "sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==", - "cpu": [ - "x64" - ], + "node_modules/@electron/rebuild": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.0.4.tgz", + "integrity": "sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg==", "dev": true, - "libc": [ - "musl" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.1.1", + "node-abi": "^4.2.0", + "node-api-version": "^0.2.1", + "node-gyp": "^12.2.0", + "read-binary-file-arch": "^1.0.6" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=22.12.0" } }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0.tgz", - "integrity": "sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==", - "cpu": [ - "arm64" - ], + "node_modules/@electron/universal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", + "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "dependencies": { + "@electron/asar": "^3.3.1", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=16.4" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0.tgz", - "integrity": "sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==", - "cpu": [ - "wasm32" - ], + "node_modules/@electron/universal/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" + "balanced-match": "^1.0.0" } }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0.tgz", - "integrity": "sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==", - "cpu": [ - "arm64" - ], + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=14.14" } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0.tgz", - "integrity": "sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==", - "cpu": [ - "x64" - ], + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.7", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", - "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", "dev": true, - "license": "MIT" + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" + "tslib": "^2.4.0" } }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz", + "integrity": "sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@types/react": { - "version": "18.3.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", - "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", "dev": true, "license": "MIT", "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.2.2" + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "node_modules/@ionic/utils-fs/node_modules/@types/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", "dev": true, "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" + "dependencies": { + "@types/node": "*" } }, - "node_modules/@vitejs/plugin-react": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", - "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "node_modules/@ionic/utils-fs/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-rc.7" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", - "babel-plugin-react-compiler": "^1.0.0", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@rolldown/plugin-babel": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - } + "node": ">=10" } }, - "node_modules/@vitest/expect": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", - "integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.6", - "@vitest/utils": "4.1.6", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" + "debug": "^4.0.0", + "tslib": "^2.0.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@vitest/mocker": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz", - "integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==", + "node_modules/@ionic/utils-process": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.12.tgz", + "integrity": "sha512-Jqkgyq7zBs/v/J3YvKtQQiIcxfJyplPgECMWgdO0E1fKrrH8EF0QGHNJ9mJCn6PYe2UtHNS8JJf5G21e09DfYg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.6", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz", - "integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==", + "node_modules/@ionic/utils-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.7.tgz", + "integrity": "sha512-eSELBE7NWNFIHTbTC2jiMvh1ABKGIpGdUIvARsNPMNQhxJB3wpwdiVnoBoTYp+5a6UUIww4Kpg7v6S7iTctH1w==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.1.0" + "debug": "^4.0.0", + "tslib": "^2.0.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@vitest/runner": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz", - "integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==", + "node_modules/@ionic/utils-subprocess": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-3.0.1.tgz", + "integrity": "sha512-cT4te3AQQPeIM9WCwIg8ohroJ8TjsYaMb2G4ZEgv9YzeDqHZ4JpeIKqG2SoaA3GmVQ3sOfhPM6Ox9sxphV/d1A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.6", - "pathe": "^2.0.3" + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.12", + "@ionic/utils-stream": "3.1.7", + "@ionic/utils-terminal": "2.3.5", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@vitest/snapshot": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz", - "integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==", + "node_modules/@ionic/utils-terminal": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.5.tgz", + "integrity": "sha512-3cKScz9Jx2/Pr9ijj1OzGlBDfcmx7OMVBt4+P1uRR0SSW4cm1/y3Mo4OY3lfkuaYifMNBW8Wz6lQHbs1bihr7A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.6", - "@vitest/utils": "4.1.6", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@vitest/spy": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz", - "integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz", - "integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==", + "node_modules/@ionic/utils-terminal/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.6", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 12.13.0" } }, - "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0" + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": ">=12.0.0" + "node": ">=10" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12.0.0" + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, + "node_modules/@noble/hashes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" + "node_modules/@noble/secp256k1": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-3.1.0.tgz", + "integrity": "sha512-+F7iS7tUMaNGXcc9X3PjmjvuQnXEuSjCRNzVVA2xAcKXgCaP0dHYz4SFyt4FKNHef7sOP//xihowcySSS7PK9g==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "node_modules/@oxc-project/types": { + "version": "0.129.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.129.0.tgz", + "integrity": "sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==", "dev": true, - "license": "MPL-2.0", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@playwright/test": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz", + "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" + "playwright": "1.60.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "bin": { + "playwright": "cli.js" }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "engines": { + "node": ">=18" } }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "node_modules/@remix-run/router": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0.tgz", + "integrity": "sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==", "cpu": [ "arm64" ], "dev": true, - "license": "MPL-2.0", + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0.tgz", + "integrity": "sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==", "cpu": [ "arm64" ], "dev": true, - "license": "MPL-2.0", + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0.tgz", + "integrity": "sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0.tgz", + "integrity": "sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0.tgz", + "integrity": "sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==", + "cpu": [ "arm" ], "dev": true, - "license": "MPL-2.0", + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0.tgz", + "integrity": "sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==", "cpu": [ "arm64" ], @@ -849,23 +922,19 @@ "libc": [ "glibc" ], - "license": "MPL-2.0", + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0.tgz", + "integrity": "sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==", "cpu": [ "arm64" ], @@ -873,363 +942,4327 @@ "libc": [ "musl" ], - "license": "MPL-2.0", + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0.tgz", + "integrity": "sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==", "cpu": [ - "x64" + "ppc64" ], "dev": true, "libc": [ "glibc" ], - "license": "MPL-2.0", + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0.tgz", + "integrity": "sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==", "cpu": [ - "x64" + "s390x" ], "dev": true, "libc": [ - "musl" + "glibc" ], - "license": "MPL-2.0", + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0.tgz", + "integrity": "sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==", "cpu": [ - "arm64" + "x64" ], "dev": true, - "license": "MPL-2.0", + "libc": [ + "glibc" + ], + "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0.tgz", + "integrity": "sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", + "libc": [ + "musl" + ], + "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0.tgz", + "integrity": "sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lucide-react": { - "version": "0.468.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", - "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0.tgz", + "integrity": "sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==", + "cpu": [ + "wasm32" + ], "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0.tgz", + "integrity": "sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==", + "cpu": [ + "arm64" ], + "dev": true, "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0.tgz", + "integrity": "sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==", + "cpu": [ + "x64" + ], "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" + "license": "MIT", + "optional": true, + "os": [ + "win32" ], - "license": "MIT" + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", "dev": true, "license": "MIT" }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/postcss": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", - "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, - "funding": [ + "license": "MIT" + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/verror": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", + "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", + "integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.6", + "@vitest/utils": "4.1.6", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz", + "integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.6", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz", + "integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz", + "integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.6", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz", + "integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "@vitest/utils": "4.1.6", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz", + "integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz", + "integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/app-builder-bin": { + "version": "5.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz", + "integrity": "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib": { + "version": "26.8.1", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.8.1.tgz", + "integrity": "sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/asar": "3.4.1", + "@electron/fuses": "^1.8.0", + "@electron/get": "^3.0.0", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.3", + "@electron/rebuild": "^4.0.3", + "@electron/universal": "2.0.3", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "builder-util": "26.8.1", + "builder-util-runtime": "9.5.1", + "chromium-pickle-js": "^0.2.0", + "ci-info": "4.3.1", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "ejs": "^3.1.8", + "electron-publish": "26.8.1", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "isbinaryfile": "^5.0.0", + "jiti": "^2.4.2", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "lazy-val": "^1.0.5", + "minimatch": "^10.0.3", + "plist": "3.1.0", + "proper-lockfile": "^4.1.2", + "resedit": "^1.7.0", + "semver": "~7.7.3", + "tar": "^7.5.7", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0", + "which": "^5.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "26.8.1", + "electron-builder-squirrel-windows": "26.8.1" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", + "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/app-builder-lib/node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/app-builder-lib/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builder-util": { + "version": "26.8.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.8.1.tgz", + "integrity": "sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "5.0.0-alpha.12", + "builder-util-runtime": "9.5.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.6", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz", + "integrity": "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.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==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dir-compare": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "node_modules/dir-compare/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dmg-builder": { + "version": "26.8.1", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.8.1.tgz", + "integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.8.1", + "builder-util": "26.8.1", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "node_modules/dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "bin": { + "dmg-license": "bin/dmg-license.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "42.0.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-42.0.1.tgz", + "integrity": "sha512-d8HnycE970DGESe91Nj30eonFBUcAI9EZ1TwUGJVzSAnJZdh0BkFEinAXjdklvDYst+bVDc8HsksCuqVLrnqdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^5.0.0", + "@types/node": "^24.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js", + "install-electron": "install.js" + }, + "engines": { + "node": ">= 22.12.0" + } + }, + "node_modules/electron-builder": { + "version": "26.8.1", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.8.1.tgz", + "integrity": "sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.8.1", + "builder-util": "26.8.1", + "builder-util-runtime": "9.5.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "dmg-builder": "26.8.1", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "26.8.1", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.8.1.tgz", + "integrity": "sha512-o288fIdgPLHA76eDrFADHPoo7VyGkDCYbLV1GzndaMSAVBoZrGvM9m2IehdcVMzdAZJ2eV9bgyissQXHv5tGzA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "26.8.1", + "builder-util": "26.8.1", + "electron-winstaller": "5.4.0" + } + }, + "node_modules/electron-publish": { + "version": "26.8.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.8.1.tgz", + "integrity": "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "26.8.1", + "builder-util-runtime": "9.5.1", + "chalk": "^4.1.2", + "form-data": "^4.0.5", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-winstaller": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", + "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@electron/asar": "^3.2.1", + "debug": "^4.1.1", + "fs-extra": "^7.0.1", + "lodash": "^4.17.21", + "temp": "^0.9.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "@electron/windows-sign": "^1.1.2" + } + }, + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/electron-winstaller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-winstaller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/elementtree/node_modules/sax": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + }, + "engines": { + "node": "^8.11.2 || >=10" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isbinaryfile": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz", + "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lucide-react": { + "version": "0.468.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", + "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/native-run": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/native-run/-/native-run-2.0.3.tgz", + "integrity": "sha512-U1PllBuzW5d1gfan+88L+Hky2eZx+9gv3Pf6rNBxKbORxi7boHzqiA6QFGSnqMem4j0A9tZ08NMIs5+0m/VS1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-fs": "^3.1.7", + "@ionic/utils-terminal": "^2.3.4", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^4.1.1", + "plist": "^3.1.0", + "split2": "^4.2.0", + "through2": "^4.0.2", + "tslib": "^2.6.2", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/node-abi": { + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.31.0.tgz", + "integrity": "sha512-Erq5w/t3syw3s4sDsUaX4QttIdBPsGKTT1DTRsCkTonGggczhlDKm/wDX3o+HPJpQ41EjXCbcmXf0tgr5YZJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.6.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-gyp": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.3.0.tgz", + "integrity": "sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/undici": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", + "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-router": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "bin": { + "read-binary-file-arch": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/rolldown": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0.tgz", + "integrity": "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.129.0", + "@rolldown/pluginutils": "1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0", + "@rolldown/binding-darwin-arm64": "1.0.0", + "@rolldown/binding-darwin-x64": "1.0.0", + "@rolldown/binding-freebsd-x64": "1.0.0", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", + "@rolldown/binding-linux-arm64-gnu": "1.0.0", + "@rolldown/binding-linux-arm64-musl": "1.0.0", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0", + "@rolldown/binding-linux-s390x-gnu": "1.0.0", + "@rolldown/binding-linux-x64-gnu": "1.0.0", + "@rolldown/binding-linux-x64-musl": "1.0.0", + "@rolldown/binding-openharmony-arm64": "1.0.0", + "@rolldown/binding-wasm32-wasi": "1.0.0", + "@rolldown/binding-win32-arm64-msvc": "1.0.0", + "@rolldown/binding-win32-x64-msvc": "1.0.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0.tgz", + "integrity": "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "type": "github", + "url": "https://github.com/sponsors/feross" }, { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" + "type": "patreon", + "url": "https://www.patreon.com/feross" }, { - "type": "github", - "url": "https://github.com/sponsors/ai" + "type": "consulting", + "url": "https://feross.org/support" } ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=10" } }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "debug": "^4.1.0" }, - "peerDependencies": { - "react": "^18.3.1" + "engines": { + "node": ">= 8.0" } }, - "node_modules/react-router": { - "version": "6.30.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", - "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { - "@remix-run/router": "1.23.2" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" + "node": ">=8" } }, - "node_modules/react-router-dom": { - "version": "6.30.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", - "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", - "license": "MIT", + "node_modules/tar": { + "version": "7.5.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz", + "integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "@remix-run/router": "1.23.2", - "react-router": "6.30.3" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "node": ">=18" } }, - "node_modules/rolldown": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0.tgz", - "integrity": "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==", + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@oxc-project/types": "=0.129.0", - "@rolldown/pluginutils": "1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" }, "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0", - "@rolldown/binding-darwin-arm64": "1.0.0", - "@rolldown/binding-darwin-x64": "1.0.0", - "@rolldown/binding-freebsd-x64": "1.0.0", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", - "@rolldown/binding-linux-arm64-gnu": "1.0.0", - "@rolldown/binding-linux-arm64-musl": "1.0.0", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0", - "@rolldown/binding-linux-s390x-gnu": "1.0.0", - "@rolldown/binding-linux-x64-gnu": "1.0.0", - "@rolldown/binding-linux-x64-musl": "1.0.0", - "@rolldown/binding-openharmony-arm64": "1.0.0", - "@rolldown/binding-wasm32-wasi": "1.0.0", - "@rolldown/binding-win32-arm64-msvc": "1.0.0", - "@rolldown/binding-win32-x64-msvc": "1.0.0" + "node": ">=6.0.0" } }, - "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0.tgz", - "integrity": "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==", + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", "dev": true, - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0" + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/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==", + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "readable-stream": "3" } }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", + "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "semver": "^5.5.0" + } }, - "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "MIT" + "license": "ISC", + "bin": { + "semver": "bin/semver" + } }, "node_modules/tinybench": { "version": "2.9.0", @@ -1275,13 +5308,65 @@ "node": ">=14.0.0" } }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true, - "license": "0BSD", - "optional": true + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/typescript": { "version": "5.9.3", @@ -1297,6 +5382,84 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/vite": { "version": "8.0.12", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.12.tgz", @@ -1465,6 +5628,22 @@ } } }, + "node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -1481,6 +5660,135 @@ "engines": { "node": ">=8" } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index cda2a83a..4c3c8b56 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -2,24 +2,44 @@ "name": "@flowmemory/dashboard", "private": true, "version": "0.0.0", + "description": "Flowchain desktop wallet and local bridge console.", + "author": "FlowmemoryAI", "type": "module", + "main": "electron/main.cjs", "scripts": { "dev": "npm run sync:fixtures && vite --host 127.0.0.1", "build": "npm run sync:fixtures && tsc -b && vite build", + "desktop": "npm run build && electron electron/main.cjs", + "desktop:pack": "npm run build && electron-builder --config electron-builder.json --win --x64 --dir && powershell -NoProfile -ExecutionPolicy Bypass -File scripts/package-desktop-wallet.ps1", + "desktop:installer:win": "npm run build && electron-builder --config electron-builder.json --win nsis zip --x64", + "desktop:installer:mac": "npm run build && electron-builder --config electron-builder.json --mac dmg zip", + "desktop:installer:linux": "npm run build && electron-builder --config electron-builder.json --linux AppImage zip", + "mobile:sync": "npm run build && cap sync", + "mobile:android:sync": "npm run build && cap sync android", + "mobile:android:debug": "npm run mobile:android:sync && cd android && gradlew.bat assembleDebug", "test": "vitest run", + "browser:e2e": "playwright test", "typecheck": "tsc -b", "sync:fixtures": "node scripts/sync-fixtures.mjs" }, "dependencies": { + "@capacitor/android": "^8.3.4", + "@capacitor/core": "^8.3.4", + "@noble/hashes": "^2.2.0", + "@noble/secp256k1": "^3.1.0", "lucide-react": "^0.468.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.28.0" }, "devDependencies": { + "@capacitor/cli": "^8.3.4", + "@playwright/test": "^1.60.0", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "@vitejs/plugin-react": "^6.0.1", + "electron": "^42.0.1", + "electron-builder": "^26.8.1", "typescript": "^5.6.3", "vite": "^8.0.12", "vitest": "^4.1.6" diff --git a/apps/dashboard/playwright.config.ts b/apps/dashboard/playwright.config.ts new file mode 100644 index 00000000..045351cc --- /dev/null +++ b/apps/dashboard/playwright.config.ts @@ -0,0 +1,37 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./e2e", + timeout: 45_000, + expect: { + timeout: 7_500, + }, + forbidOnly: Boolean(process.env.CI), + retries: process.env.CI ? 1 : 0, + reporter: [["list"]], + use: { + baseURL: "http://127.0.0.1:5273", + trace: "retain-on-failure", + }, + webServer: { + command: "npm run dev -- --port 5273 --strictPort", + url: "http://127.0.0.1:5273/", + reuseExistingServer: false, + timeout: 120_000, + }, + projects: [ + { + name: "chromium-desktop", + use: { + ...devices["Desktop Chrome"], + viewport: { width: 1440, height: 960 }, + }, + }, + { + name: "chromium-mobile", + use: { + ...devices["Pixel 7"], + }, + }, + ], +}); diff --git a/apps/dashboard/public/data/flowchain-bridge-test-deposit.json b/apps/dashboard/public/data/flowchain-bridge-test-deposit.json index f43813a2..19772f29 100644 --- a/apps/dashboard/public/data/flowchain-bridge-test-deposit.json +++ b/apps/dashboard/public/data/flowchain-bridge-test-deposit.json @@ -8,8 +8,8 @@ "token": "0x3333333333333333333333333333333333333333", "amount": "20000000", "sender": "0x4444444444444444444444444444444444444444", - "flowchainRecipient": "0x5555555555555555555555555555555555555555555555555555555555555555", + "flowchainRecipient": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "nonce": "1", - "metadataHash": "0x6666666666666666666666666666666666666666666666666666666666666666", + "metadataHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "status": "observed" } diff --git a/apps/dashboard/public/data/flowchain-live-readiness-report.json b/apps/dashboard/public/data/flowchain-live-readiness-report.json new file mode 100644 index 00000000..d5a93c9b --- /dev/null +++ b/apps/dashboard/public/data/flowchain-live-readiness-report.json @@ -0,0 +1,866 @@ +{ + "schema": "flowchain.live_readiness_dashboard_report.v0", + "generatedAt": "2026-05-18T15:18:49.2356414Z", + "status": "blocked", + "deploymentReady": false, + "packetShareable": false, + "blockedOnlyOnKnownExternalOwnerInputs": true, + "summary": "Public launch is still blocked by owner-provided RPC edge, backup, Base 8453 bridge, or tester packet inputs.", + "privateRpcUrl": "http://127.0.0.1:8787", + "metrics": { + "latestHeight": "73360", + "finalizedHeight": "73360", + "monitorHeightAdvanced": true, + "bridgeRelayerStatus": "blocked", + "bridgeQueuedTransactions": "0", + "bridgeRelayerChildTimeoutSeconds": "300", + "bridgeRelayerStepCount": 1, + "bridgeRelayerTimedOutStepCount": 0, + "bridgeRelayerNoChildTimeouts": true, + "bridgeRelayerGuardrailStatus": "passed", + "bridgeRelayerLoopValidationStatus": "passed", + "backupOwnerPathDryRunStatus": "passed", + "publicRpcDeploymentBundleStatus": "passed", + "publicRpcOwnerRenderValidationStatus": "passed", + "publicRpcDeploymentAutomationStatus": "passed", + "publicRpcDeploymentAutomationAction": "Validate", + "externalTesterPacketStatus": "blocked", + "externalTesterConnectPackStatus": "blocked", + "ownerInputReady": false, + "noSecretStatus": "passed", + "opsSnapshotStatus": "blocked", + "opsAlertState": "blocked", + "opsCriticalCount": "0", + "opsBlockedCount": "6", + "opsActiveRuleCount": 6, + "opsRuleCount": "18", + "opsCriticalRuleCount": "11", + "opsBlockedRuleCount": "7", + "opsUnmappedCurrentFindingCount": 0, + "incidentDrillStatus": "passed", + "alertInstallValidationStatus": "passed", + "opsEscalationDryRunStatus": "passed", + "opsEscalationDryRunEvents": "6", + "statusCounts": { + "passed": 7, + "blocked": 5 + } + }, + "ops": { + "snapshotStatus": "blocked", + "alertRulesStatus": "passed", + "alertState": "blocked", + "incidentDrillStatus": "passed", + "alertInstallValidationStatus": "passed", + "escalationDryRunStatus": "passed", + "escalationDryRunEvents": "6", + "bridgeRelayerGuardrailStatus": "passed", + "bridgeRelayerGuardrailReady": true, + "criticalCount": "0", + "blockedCount": "6", + "latestHeight": "73289", + "finalizedHeight": "73289", + "monitorStatus": "passed", + "monitorHeightAdvanced": true, + "findings": [ + { + "severity": "blocked", + "code": "public-rpc-not-ready", + "message": "Public RPC is not ready to share.", + "commands": [ + "npm run flowchain:public-rpc:check", + "npm run flowchain:public-rpc:validate", + "npm run flowchain:public-rpc:abuse-test" + ] + }, + { + "severity": "blocked", + "code": "backup-not-ready", + "message": "State backup is not ready for public operation.", + "commands": [ + "npm run flowchain:backup:restore:validate", + "npm run flowchain:backup:check" + ] + }, + { + "severity": "blocked", + "code": "bridge-not-ready", + "message": "Base 8453 bridge readiness is not ready for external funded testing.", + "commands": [ + "npm run flowchain:bridge:live:check", + "npm run flowchain:bridge:infra:check", + "npm run flowchain:bridge:emergency-stop" + ] + }, + { + "severity": "blocked", + "code": "bridge-relayer-not-ready", + "message": "Bridge relayer one-shot proof is not ready.", + "commands": [ + "npm run flowchain:bridge:relayer:once -- -AllowBlocked", + "npm run flowchain:bridge:live:check", + "npm run flowchain:bridge:infra:check" + ] + }, + { + "severity": "blocked", + "code": "external-tester-not-shareable", + "message": "External tester packet must remain not-shareable.", + "commands": [ + "npm run flowchain:tester:readiness", + "npm run flowchain:external-tester:packet" + ] + }, + { + "severity": "blocked", + "code": "deployment-contract-not-ready", + "message": "Public deployment contract is not ready.", + "commands": [ + "npm run flowchain:public-deployment:contract -- -AllowBlocked" + ] + } + ], + "activeRuleIds": [ + "public-rpc-not-shareable", + "backup-not-ready", + "bridge-not-ready", + "bridge-relayer-not-ready", + "external-tester-not-shareable", + "deployment-contract-not-ready" + ], + "coveredFindingCodes": [ + "node-not-running", + "control-plane-not-running", + "service-status-not-passed", + "chain-height-unreadable", + "height-not-advancing", + "state-stale", + "no-secret-scan-not-passed", + "bridge-relayer-latency-failed", + "bridge-relayer-cursor-unsafe", + "bridge-relayer-guardrail-failed", + "bridge-relayer-loop-unhealthy", + "deployment-refresh-aborted", + "external-tester-evidence-unsafe", + "public-rpc-not-ready", + "backup-not-ready", + "bridge-not-ready", + "bridge-relayer-not-ready", + "external-tester-not-shareable", + "external-tester-evidence-invalid", + "deployment-contract-not-ready" + ], + "activeRules": [ + { + "id": "public-rpc-not-shareable", + "severity": "blocked", + "signal": "Public RPC readiness gate is not passed.", + "threshold": "public RPC check status is not passed", + "commands": [ + "npm run flowchain:public-rpc:check", + "npm run flowchain:public-rpc:validate", + "npm run flowchain:public-rpc:abuse-test" + ] + }, + { + "id": "backup-not-ready", + "severity": "blocked", + "signal": "State backup readiness is not passed.", + "threshold": "backup check status is not passed", + "commands": [ + "npm run flowchain:backup:restore:validate", + "npm run flowchain:backup:check" + ] + }, + { + "id": "bridge-not-ready", + "severity": "blocked", + "signal": "Base 8453 bridge readiness is not passed.", + "threshold": "bridge live or infra readiness status is not passed", + "commands": [ + "npm run flowchain:bridge:live:check", + "npm run flowchain:bridge:infra:check", + "npm run flowchain:bridge:emergency-stop" + ] + }, + { + "id": "bridge-relayer-not-ready", + "severity": "blocked", + "signal": "Bridge relayer one-shot proof is not ready.", + "threshold": "bridge relayer one-shot status is not passed", + "commands": [ + "npm run flowchain:bridge:relayer:once -- -AllowBlocked", + "npm run flowchain:bridge:live:check", + "npm run flowchain:bridge:infra:check" + ] + }, + { + "id": "external-tester-not-shareable", + "severity": "blocked", + "signal": "External tester packet is not shareable.", + "threshold": "tester readiness status is not passed", + "commands": [ + "npm run flowchain:tester:readiness", + "npm run flowchain:external-tester:packet" + ] + }, + { + "id": "deployment-contract-not-ready", + "severity": "blocked", + "signal": "Public deployment contract is not passed.", + "threshold": "deployment contract status is not passed", + "commands": [ + "npm run flowchain:public-deployment:contract -- -AllowBlocked" + ] + } + ], + "ruleCount": "18", + "criticalRuleCount": "11", + "blockedRuleCount": "7", + "unmappedCurrentFindingCodes": [], + "incidentCommands": { + "status": [ + "npm run flowchain:ops:snapshot", + "npm run flowchain:service:status", + "npm run flowchain:service:monitor -- -DurationSeconds 300 -PollSeconds 30" + ], + "restart": [ + "npm run flowchain:service:restart -- -LiveProfile", + "npm run flowchain:service:status" + ], + "backupRecovery": [ + "npm run flowchain:backup:restore:validate", + "npm run flowchain:backup:create", + "npm run flowchain:backup:restore:verify" + ], + "publicExposure": [ + "npm run flowchain:public-rpc:check", + "npm run flowchain:public-rpc:abuse-test", + "npm run flowchain:external-tester:packet" + ], + "drills": [ + "npm run flowchain:ops:incident-drill", + "npm run flowchain:ops:snapshot -- -AllowBlocked -NoRefresh" + ], + "emergency": [ + "npm run flowchain:emergency:stop-local", + "npm run flowchain:bridge:emergency-stop", + "npm run flowchain:emergency:export-evidence" + ], + "bridgeRelayerLoop": [ + "npm run flowchain:service:status", + "npm run flowchain:bridge:relayer:loop:validate", + "npm run flowchain:service:restart -- -LiveProfile -StartBridgeRelayerLoop" + ] + }, + "dryRunEvents": [ + { + "findingCode": "public-rpc-not-ready", + "severity": "blocked", + "ruleId": "public-rpc-not-shareable", + "signal": "Public RPC readiness gate is not passed.", + "commands": [ + "npm run flowchain:public-rpc:check", + "npm run flowchain:public-rpc:validate", + "npm run flowchain:public-rpc:abuse-test" + ] + }, + { + "findingCode": "backup-not-ready", + "severity": "blocked", + "ruleId": "backup-not-ready", + "signal": "State backup readiness is not passed.", + "commands": [ + "npm run flowchain:backup:restore:validate", + "npm run flowchain:backup:check" + ] + }, + { + "findingCode": "bridge-not-ready", + "severity": "blocked", + "ruleId": "bridge-not-ready", + "signal": "Base 8453 bridge readiness is not passed.", + "commands": [ + "npm run flowchain:bridge:live:check", + "npm run flowchain:bridge:infra:check", + "npm run flowchain:bridge:emergency-stop" + ] + }, + { + "findingCode": "bridge-relayer-not-ready", + "severity": "blocked", + "ruleId": "bridge-relayer-not-ready", + "signal": "Bridge relayer one-shot proof is not ready.", + "commands": [ + "npm run flowchain:bridge:relayer:once -- -AllowBlocked", + "npm run flowchain:bridge:live:check", + "npm run flowchain:bridge:infra:check" + ] + }, + { + "findingCode": "external-tester-not-shareable", + "severity": "blocked", + "ruleId": "external-tester-not-shareable", + "signal": "External tester packet is not shareable.", + "commands": [ + "npm run flowchain:tester:readiness", + "npm run flowchain:external-tester:packet" + ] + }, + { + "findingCode": "deployment-contract-not-ready", + "severity": "blocked", + "ruleId": "deployment-contract-not-ready", + "signal": "Public deployment contract is not passed.", + "commands": [ + "npm run flowchain:public-deployment:contract -- -AllowBlocked" + ] + } + ], + "sendsNetworkNotifications": false, + "storesSecrets": false + }, + "testerLaunch": { + "status": "blocked", + "readinessStatus": "blocked", + "packetStatus": "blocked", + "connectPackStatus": "blocked", + "gatewayStatus": "passed", + "shareable": false, + "connectPackShareable": false, + "connectPackReady": true, + "connectPackNetwork": { + "name": "FlowChain friends-and-family pilot", + "chainId": "flowmemory-local-devnet-v0", + "rpcEndpointPlaceholder": "/rpc", + "explorerSummaryUrlPlaceholder": "/explorer/summary" + }, + "externalSharingReady": false, + "localTesterRehearsalReady": true, + "publicTesterGatewayReady": true, + "gatewayConfigured": true, + "testerNetworkFresh": true, + "faucetRouteValidated": true, + "packetExecutableSmokeValidated": true, + "packetSmokeRoutes": [ + "/health", + "/rpc/discover", + "/rpc/readiness", + "/chain/status", + "/wallets/create", + "/wallets/balances", + "/wallets/send", + "/wallets/transfers", + "/tester/status", + "/tester/wallets/create", + "/tester/faucet", + "/tester/wallets/send", + "/rpc balance_get" + ], + "connectPackReadOnlyRoutes": [ + "/health", + "/rpc/discover", + "/rpc/readiness", + "/chain/status", + "/explorer/summary", + "/wallets/balances", + "/wallets/transfers", + "/tester/status" + ], + "connectPackTesterWriteRoutes": [ + "/tester/wallets/create", + "/tester/faucet", + "/tester/wallets/send" + ], + "gatewayRoutes": [ + "/tester/status", + "/tester/wallets/create", + "/tester/faucet", + "/tester/wallets/send", + "/rpc balance_get" + ], + "ownerInputGroups": { + "public RPC edge": [ + "FLOWCHAIN_RPC_PUBLIC_URL", + "FLOWCHAIN_RPC_ALLOWED_ORIGINS", + "FLOWCHAIN_RPC_RATE_LIMIT_PER_MINUTE", + "FLOWCHAIN_RPC_TLS_TERMINATED" + ], + "backup storage": [ + "FLOWCHAIN_RPC_STATE_BACKUP_PATH" + ], + "tester write gateway": [ + "FLOWCHAIN_TESTER_WRITE_ENABLED", + "FLOWCHAIN_TESTER_WRITE_TOKEN_SHA256", + "FLOWCHAIN_TESTER_MAX_SEND_UNITS" + ], + "Base 8453 bridge": [ + "FLOWCHAIN_PILOT_OPERATOR_ACK", + "FLOWCHAIN_BASE8453_RPC_URL", + "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS", + "FLOWCHAIN_BASE8453_SUPPORTED_TOKEN", + "FLOWCHAIN_BASE8453_ASSET_DECIMALS", + "FLOWCHAIN_BASE8453_FROM_BLOCK", + "FLOWCHAIN_BASE8453_CURSOR_STATE", + "FLOWCHAIN_BASE8453_TO_BLOCK", + "FLOWCHAIN_PILOT_MAX_DEPOSIT_WEI", + "FLOWCHAIN_PILOT_TOTAL_CAP_WEI", + "FLOWCHAIN_PILOT_CONFIRMATIONS" + ] + }, + "commands": { + "readiness": [ + "npm run flowchain:tester:readiness -- -AllowBlocked", + "npm run flowchain:external-tester:packet -- -AllowBlocked" + ], + "gateway": [ + "npm run flowchain:tester:gateway:e2e" + ], + "wallet": [ + "npm run flowchain:wallet:live-tester:e2e" + ], + "explorer": [ + "npm run flowchain:public-deployment:contract -- -AllowBlocked" + ] + }, + "envValuesPrinted": false, + "noSecrets": true + }, + "ownerInputs": [ + { + "name": "FLOWCHAIN_RPC_PUBLIC_URL", + "group": "public RPC edge" + }, + { + "name": "FLOWCHAIN_RPC_ALLOWED_ORIGINS", + "group": "public RPC edge" + }, + { + "name": "FLOWCHAIN_RPC_RATE_LIMIT_PER_MINUTE", + "group": "public RPC edge" + }, + { + "name": "FLOWCHAIN_RPC_TLS_TERMINATED", + "group": "public RPC edge" + }, + { + "name": "FLOWCHAIN_RPC_STATE_BACKUP_PATH", + "group": "backup storage" + }, + { + "name": "FLOWCHAIN_TESTER_WRITE_ENABLED", + "group": "tester write gateway" + }, + { + "name": "FLOWCHAIN_TESTER_WRITE_TOKEN_SHA256", + "group": "tester write gateway" + }, + { + "name": "FLOWCHAIN_TESTER_MAX_SEND_UNITS", + "group": "tester write gateway" + }, + { + "name": "FLOWCHAIN_PILOT_OPERATOR_ACK", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_BASE8453_RPC_URL", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_BASE8453_SUPPORTED_TOKEN", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_BASE8453_ASSET_DECIMALS", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_BASE8453_FROM_BLOCK", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_BASE8453_CURSOR_STATE", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_BASE8453_TO_BLOCK", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_PILOT_MAX_DEPOSIT_WEI", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_PILOT_TOTAL_CAP_WEI", + "group": "Base 8453 bridge" + }, + { + "name": "FLOWCHAIN_PILOT_CONFIRMATIONS", + "group": "Base 8453 bridge" + } + ], + "gates": [ + { + "id": "private-service-origin", + "label": "Private L1 origin", + "status": "passed", + "summary": "The public deployment origin service is running privately in live profile before any owner TLS edge is considered shareable.", + "evidence": "serviceStatus=passed, privateBind=True, latestHeight=73312, finalizedHeight=73312", + "commands": [ + "npm run flowchain:service:status" + ], + "blockers": [] + }, + { + "id": "pre-share-monitoring", + "label": "Block production monitor", + "status": "passed", + "summary": "The deployment has recent service-monitor evidence that block height advances over multiple samples.", + "evidence": "monitorStatus=passed, samples=2, heightAdvanced=True", + "commands": [ + "npm run flowchain:service:monitor" + ], + "blockers": [] + }, + { + "id": "service-autorecovery", + "label": "Service autorecovery", + "status": "passed", + "summary": "The owner service has an autorecovery supervisor and an isolated recovery drill proving control-plane restart without touching live state.", + "evidence": "supervisorValidation=passed, restartAttempts=1", + "commands": [ + "npm run flowchain:service:supervisor:validate", + "npm run flowchain:service:supervisor -- -IntervalSeconds 30 -MaxRestartAttempts 3" + ], + "blockers": [] + }, + { + "id": "service-install-automation", + "label": "Windows service install", + "status": "passed", + "summary": "The owner host has a no-secret Windows install, read-only status, and safe absent-task uninstall no-op path for registering the live supervisor as a reboot-persistent scheduled task.", + "evidence": "serviceInstallValidation=passed, planDidNotMutate=True, statusCommand=True, statusDidNotMutate=True, uninstallNoop=True, liveProfileDefault=True, relayerDefaultOff=True, relayerOptIn=True, commandsPresent=True", + "commands": [ + "npm run flowchain:service:install:validate", + "npm run flowchain:service:install:windows -- -Action Plan", + "npm run flowchain:service:install:windows -- -Action Install", + "npm run flowchain:service:install:windows -- -Action Status" + ], + "blockers": [] + }, + { + "id": "public-rpc-edge", + "label": "Public RPC edge", + "status": "blocked", + "summary": "The owner TLS edge must pass endpoint, CORS, rate-limit, readiness, and response-hygiene checks before sharing.", + "evidence": "publicRpcStatus=blocked, publicRpcReady=False, validationStatus=passed, validationPassed=True, abuseStatus=passed, abusePassed=True", + "commands": [ + "npm run flowchain:public-rpc:validate", + "npm run flowchain:public-rpc:abuse-test", + "npm run flowchain:public-rpc:check" + ], + "blockers": [ + "FLOWCHAIN_RPC_PUBLIC_URL", + "FLOWCHAIN_RPC_ALLOWED_ORIGINS", + "FLOWCHAIN_RPC_RATE_LIMIT_PER_MINUTE", + "FLOWCHAIN_RPC_TLS_TERMINATED" + ] + }, + { + "id": "state-backup", + "label": "State backup proof", + "status": "blocked", + "summary": "The public deployment must prove the configured state backup directory can create a manifest-backed snapshot and restore it in rehearsal.", + "evidence": "backupStatus=blocked, snapshotProof=not-run, restoreProof=not-run, ownerPathDryRun=passed", + "commands": [ + "npm run flowchain:backup:create", + "npm run flowchain:backup:restore:verify", + "npm run flowchain:backup:owner-path:dry-run", + "npm run flowchain:backup:check" + ], + "blockers": [ + "FLOWCHAIN_RPC_STATE_BACKUP_PATH" + ] + }, + { + "id": "state-backup-owner-path-dry-run", + "label": "Backup dry run", + "status": "passed", + "summary": "Backup readiness has an owner-path dry run that injects an ignored local backup path into the production backup gate and proves snapshot plus restore evidence without using the owner's real directory.", + "evidence": "dryRun=passed, failedChecks=0, readiness=passed, snapshotProof=True, restoreProof=True", + "commands": [ + "npm run flowchain:backup:owner-path:dry-run" + ], + "blockers": [] + }, + { + "id": "base8453-bridge-edge", + "label": "Base 8453 bridge edge", + "status": "blocked", + "summary": "The public deployment must not invite bridge-funded testing until Base 8453 live and infra checks pass with owner guardrails.", + "evidence": "bridgeLive=blocked, bridgeInfra=blocked", + "commands": [ + "npm run flowchain:bridge:live:check", + "npm run flowchain:bridge:infra:check" + ], + "blockers": [ + "FLOWCHAIN_PILOT_OPERATOR_ACK", + "FLOWCHAIN_BASE8453_RPC_URL", + "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS", + "FLOWCHAIN_BASE8453_SUPPORTED_TOKEN", + "FLOWCHAIN_BASE8453_ASSET_DECIMALS", + "FLOWCHAIN_BASE8453_FROM_BLOCK", + "FLOWCHAIN_PILOT_MAX_DEPOSIT_WEI", + "FLOWCHAIN_PILOT_TOTAL_CAP_WEI", + "FLOWCHAIN_PILOT_CONFIRMATIONS" + ] + }, + { + "id": "base8453-bridge-relayer-queue", + "label": "Bridge relayer queue", + "status": "blocked", + "summary": "The bridge relayer has a no-broadcast one-shot path plus an isolated loop validation that checks owner guardrails, proves fresh no-secret/no-broadcast loop health, observes Base 8453 deposits with a staged cursor, filters replays, queues new credits into the running L1, waits for main-state credit evidence, records handoff-to-spendable latency, only commits the Base cursor after safe proof, and proves missing-owner-input runs leave cursor state untouched.", + "evidence": "relayer=blocked, guardrail=passed, loopValidation=passed, loopFailedChecks=0, loopReportHealthy=True, observed=0, new=0, queued=0, applied=0, latencyGate=not-run, cursorCommitRequired=True, cursorCommitted=False, cursorReason=not-run, handoffToSpendableSeconds=", + "commands": [ + "npm run flowchain:bridge:relayer:once", + "npm run flowchain:bridge:relayer:guardrail:validate", + "npm run flowchain:bridge:relayer:loop:validate" + ], + "blockers": [ + "FLOWCHAIN_PILOT_OPERATOR_ACK", + "FLOWCHAIN_BASE8453_RPC_URL", + "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS", + "FLOWCHAIN_BASE8453_SUPPORTED_TOKEN", + "FLOWCHAIN_BASE8453_ASSET_DECIMALS", + "FLOWCHAIN_BASE8453_FROM_BLOCK", + "FLOWCHAIN_PILOT_MAX_DEPOSIT_WEI", + "FLOWCHAIN_PILOT_TOTAL_CAP_WEI", + "FLOWCHAIN_PILOT_CONFIRMATIONS" + ] + }, + { + "id": "external-tester-sharing", + "label": "External tester packet", + "status": "blocked", + "summary": "External tester packet and machine-readable connection pack must remain not-shareable until owner public RPC, backup, and bridge gates pass, and they must rely on fresh tester-wallet evidence plus authenticated tester faucet/send gateway smoke.", + "evidence": "externalTester=blocked, localTesterRehearsalReady=True, testerNetworkFresh=True, publicTesterGatewayReady=True, faucetRoute=True, packetSmoke=True, testerFaucet=True, capRejected=True, connectPackReady=True, externalSharingReady=False, packet=blocked, packetShareable=False", + "commands": [ + "npm run flowchain:tester:readiness", + "npm run flowchain:external-tester:packet" + ], + "blockers": [ + "FLOWCHAIN_RPC_PUBLIC_URL", + "FLOWCHAIN_RPC_ALLOWED_ORIGINS", + "FLOWCHAIN_RPC_RATE_LIMIT_PER_MINUTE", + "FLOWCHAIN_RPC_TLS_TERMINATED", + "FLOWCHAIN_RPC_STATE_BACKUP_PATH", + "FLOWCHAIN_PILOT_OPERATOR_ACK", + "FLOWCHAIN_BASE8453_RPC_URL", + "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS", + "FLOWCHAIN_BASE8453_SUPPORTED_TOKEN", + "FLOWCHAIN_BASE8453_ASSET_DECIMALS", + "FLOWCHAIN_BASE8453_FROM_BLOCK", + "FLOWCHAIN_PILOT_MAX_DEPOSIT_WEI", + "FLOWCHAIN_PILOT_TOTAL_CAP_WEI", + "FLOWCHAIN_PILOT_CONFIRMATIONS" + ] + }, + { + "id": "public-tester-write-gateway", + "label": "Tester write gateway", + "status": "passed", + "summary": "The public deployment has a local production-shaped proof for authenticated tester wallet creation, capped tester faucet funding, capped tester sends, balance settlement, and over-cap rejection.", + "evidence": "gatewayStatus=passed, testerFaucetSchema=flowmemory.control_plane.tester_faucet_result.v0, transferAccepted=True, capRejected=True", + "commands": [ + "npm run flowchain:tester:gateway:e2e" + ], + "blockers": [] + }, + { + "id": "no-secret-no-broadcast", + "label": "No secrets or broadcasts", + "status": "passed", + "summary": "Deployment contract and current readiness reports preserve no-secret, no-env-value, and no-live-broadcast boundaries.", + "evidence": "noSecretStatus=passed, scansGeneratedReports=True, reportPathMatchesProductionGate=True", + "commands": [ + "npm run flowchain:no-secret:scan" + ], + "blockers": [] + } + ], + "commands": { + "preExposure": [ + "npm run flowchain:service:status", + "npm run flowchain:service:monitor -- -DurationSeconds 300 -PollSeconds 30", + "npm run flowchain:service:install:validate", + "npm run flowchain:service:install:windows -- -Action Plan", + "npm run flowchain:ops:snapshot -- -AllowBlocked", + "npm run flowchain:ops:alerts -- -AllowBlocked", + "npm run flowchain:ops:alerts:install:validate", + "npm run flowchain:ops:escalation:dry-run -- -AllowBlocked", + "npm run flowchain:ops:alerts:install:windows -- -Action Plan", + "npm run flowchain:owner:onboarding", + "npm run flowchain:owner-env:template", + "npm run flowchain:owner-inputs" + ], + "rollback": [ + "npm run flowchain:service:status", + "npm run flowchain:service:install:windows -- -Action Status", + "npm run flowchain:service:install:windows -- -Action Uninstall", + "npm run flowchain:backup:install:windows -- -Action Status", + "npm run flowchain:backup:install:windows -- -Action Uninstall", + "npm run flowchain:ops:alerts:install:windows -- -Action Status", + "npm run flowchain:ops:alerts:install:windows -- -Action Uninstall", + "npm run flowchain:service:stop", + "npm run flowchain:service:restart -- -LiveProfile", + "npm run flowchain:emergency:stop-local" + ] + }, + "sourceReports": [ + { + "fileName": "public-deployment-contract-report.json", + "schema": "flowchain.public_deployment_contract_report.v0", + "status": "blocked", + "generatedAt": "2026-05-18T15:18:49.2356414Z" + }, + { + "fileName": "flowchain-live-infra-check-report.json", + "schema": "flowchain.live_infra_check_report.v0", + "status": "blocked", + "generatedAt": "2026-05-18T15:21:26.2767425Z" + }, + { + "fileName": "service-status-report.json", + "schema": "flowchain.service_status_report.v0", + "status": "passed", + "generatedAt": "2026-05-18T15:22:05.2861073Z" + }, + { + "fileName": "service-monitor-report.json", + "schema": "flowchain.service_monitor_report.v0", + "status": "passed", + "generatedAt": "2026-05-18T15:16:04.8496922Z" + }, + { + "fileName": "public-rpc-deployment-bundle-report.json", + "schema": "flowchain.public_rpc_deployment_bundle_report.v3", + "status": "passed", + "generatedAt": "2026-05-18T06:51:17.2713954Z" + }, + { + "fileName": "public-rpc-deployment-automation-report.json", + "schema": "flowchain.public_rpc_deployment_automation_report.v1", + "status": "passed", + "generatedAt": "2026-05-18T06:51:20.1880423Z" + }, + { + "fileName": "public-rpc-readiness-report.json", + "schema": "flowchain.public_rpc_readiness_report.v0", + "status": "blocked", + "generatedAt": "2026-05-18T15:20:06.8347091Z" + }, + { + "fileName": "backup-readiness-report.json", + "schema": "flowchain.backup_readiness_report.v1", + "status": "blocked", + "generatedAt": "2026-05-18T15:21:18.9644465Z" + }, + { + "fileName": "backup-owner-path-dry-run-report.json", + "schema": "flowchain.backup_owner_path_dry_run_report.v1", + "status": "passed", + "generatedAt": "2026-05-18T04:44:23.9884243Z" + }, + { + "fileName": "bridge-relayer-once-report.json", + "schema": "flowchain.bridge_relayer_once_report.v0", + "status": "blocked", + "generatedAt": "2026-05-18T15:21:24.2522874Z" + }, + { + "fileName": "bridge-relayer-guardrail-validation-report.json", + "schema": "flowchain.bridge_relayer_guardrail_validation_report.v1", + "status": "passed", + "generatedAt": "2026-05-18T04:31:12.2070538Z" + }, + { + "fileName": "bridge-relayer-loop-validation-report.json", + "schema": "flowchain.bridge_relayer_loop_validation_report.v1", + "status": "passed", + "generatedAt": "2026-05-18T04:36:28.6180686Z" + }, + { + "fileName": "external-tester-packet-report.json", + "schema": "flowchain.external_tester_packet_report.v0", + "status": "blocked", + "generatedAt": "2026-05-18T15:23:34.1357530Z" + }, + { + "fileName": "external-tester-connect-pack.json", + "schema": "flowchain.external_tester_connect_pack.v0", + "status": "blocked", + "generatedAt": "2026-05-18T15:23:34.1357530Z" + }, + { + "fileName": "external-tester-readiness-report.json", + "schema": "flowchain.external_tester_readiness_report.v0", + "status": "blocked", + "generatedAt": "2026-05-18T15:23:31.9122762Z" + }, + { + "fileName": "public-tester-gateway-e2e-report.json", + "schema": "flowchain.public_tester_gateway_e2e_report.v0", + "status": "passed", + "generatedAt": "2026-05-18T14:55:36.9733820Z" + }, + { + "fileName": "ops-snapshot-report.json", + "schema": "flowchain.ops_snapshot_report.v1", + "status": "blocked", + "generatedAt": "2026-05-18T15:16:05.4387092Z" + }, + { + "fileName": "ops-alert-rules-report.json", + "schema": "flowchain.ops_alert_rules_report.v0", + "status": "passed", + "generatedAt": "2026-05-18T15:16:06.7011437Z" + }, + { + "fileName": "incident-drill-report.json", + "schema": "flowchain.incident_drill_report.v0", + "status": "passed", + "generatedAt": "2026-05-18T15:14:02.2188427Z" + }, + { + "fileName": "alert-install-validation-report.json", + "schema": "flowchain.alert_install_validation_report.v0", + "status": "passed", + "generatedAt": "2026-05-18T06:51:03.8641137Z" + }, + { + "fileName": "ops-escalation-dry-run-report.json", + "schema": "flowchain.ops_escalation_dry_run_report.v0", + "status": "passed", + "generatedAt": "2026-05-18T06:51:05.9119884Z" + }, + { + "fileName": "owner-inputs-report.json", + "schema": "flowchain.owner_inputs_report.v0", + "status": "blocked", + "generatedAt": "2026-05-18T15:23:33.2844595Z" + }, + { + "fileName": "no-secret-scan-report.json", + "schema": "flowchain.no_secret_scan_report.v0", + "status": "passed", + "generatedAt": "2026-05-18T17:48:56.2016268Z" + } + ], + "envValuesPrinted": false, + "noSecrets": true +} diff --git a/apps/dashboard/public/favicon.svg b/apps/dashboard/public/favicon.svg new file mode 100644 index 00000000..10d04dab --- /dev/null +++ b/apps/dashboard/public/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/dashboard/scripts/package-desktop-wallet.ps1 b/apps/dashboard/scripts/package-desktop-wallet.ps1 new file mode 100644 index 00000000..f041d287 --- /dev/null +++ b/apps/dashboard/scripts/package-desktop-wallet.ps1 @@ -0,0 +1,20 @@ +$ErrorActionPreference = "Stop" + +$releaseDir = Resolve-Path (Join-Path $PSScriptRoot "..\release") +$unpackedDir = Join-Path $releaseDir "win-unpacked" +$exePath = Join-Path $unpackedDir "Flowchain Wallet.exe" +$packageJsonPath = Resolve-Path (Join-Path $PSScriptRoot "..\package.json") +$packageJson = Get-Content -LiteralPath $packageJsonPath -Raw | ConvertFrom-Json +$zipPath = Join-Path $releaseDir "Flowchain-Wallet-$($packageJson.version)-win-x64.zip" + +if (-not (Test-Path -LiteralPath $exePath)) { + throw "Desktop wallet executable was not found at $exePath" +} + +if (Test-Path -LiteralPath $zipPath) { + Remove-Item -LiteralPath $zipPath -Force +} + +Compress-Archive -Path (Join-Path $unpackedDir "*") -DestinationPath $zipPath -Force + +Write-Host "Created desktop wallet package: $zipPath" diff --git a/apps/dashboard/scripts/sync-fixtures.mjs b/apps/dashboard/scripts/sync-fixtures.mjs index c3f47b94..43e3e30e 100644 --- a/apps/dashboard/scripts/sync-fixtures.mjs +++ b/apps/dashboard/scripts/sync-fixtures.mjs @@ -1,10 +1,11 @@ -import { copyFileSync, existsSync, mkdirSync } from "node:fs"; +import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; const scriptDir = dirname(fileURLToPath(import.meta.url)); const repoRoot = resolve(scriptDir, "../../.."); const destinationDir = resolve(repoRoot, "apps/dashboard/public/data"); +const liveInfraReportDir = resolve(repoRoot, "docs/agent-runs/live-product-infra-rpc"); const fixtureCopies = [ { label: "dashboard fixture", @@ -38,6 +39,359 @@ const fixtureCopies = [ }, ]; +const liveReadinessReportCopies = [ + "public-deployment-contract-report.json", + "flowchain-live-infra-check-report.json", + "service-status-report.json", + "service-monitor-report.json", + "public-rpc-deployment-bundle-report.json", + "public-rpc-deployment-automation-report.json", + "public-rpc-readiness-report.json", + "backup-readiness-report.json", + "backup-owner-path-dry-run-report.json", + "bridge-relayer-once-report.json", + "bridge-relayer-guardrail-validation-report.json", + "bridge-relayer-loop-validation-report.json", + "external-tester-packet-report.json", + "external-tester-connect-pack.json", + "external-tester-readiness-report.json", + "public-tester-gateway-e2e-report.json", + "ops-snapshot-report.json", + "ops-alert-rules-report.json", + "incident-drill-report.json", + "alert-install-validation-report.json", + "ops-escalation-dry-run-report.json", + "owner-inputs-report.json", + "no-secret-scan-report.json", +]; + +const liveReadinessGateLabels = new Map([ + ["private-service-origin", "Private L1 origin"], + ["pre-share-monitoring", "Block production monitor"], + ["service-autorecovery", "Service autorecovery"], + ["service-install-automation", "Windows service install"], + ["public-rpc-edge", "Public RPC edge"], + ["state-backup", "State backup proof"], + ["state-backup-owner-path-dry-run", "Backup dry run"], + ["base8453-bridge-edge", "Base 8453 bridge edge"], + ["base8453-bridge-relayer-queue", "Bridge relayer queue"], + ["external-tester-sharing", "External tester packet"], + ["public-tester-write-gateway", "Tester write gateway"], + ["no-secret-no-broadcast", "No secrets or broadcasts"], +]); + +function readJsonIfExists(fileName) { + const fullPath = resolve(liveInfraReportDir, fileName); + if (!existsSync(fullPath)) { + return null; + } + + return JSON.parse(readFileSync(fullPath, "utf8")); +} + +function asArray(value) { + return Array.isArray(value) ? value : []; +} + +function asText(value, fallback = "not recorded") { + if (value === null || value === undefined || value === "") { + return fallback; + } + + return String(value); +} + +function sanitizeText(value) { + return asText(value) + .replaceAll(repoRoot, "") + .replace(/[A-Za-z]:\\[^\s"',)]+/g, ""); +} + +function sourceReportSummary(fileName, payload) { + return { + fileName, + schema: asText(payload?.schema), + status: asText(payload?.status, payload ? "observed" : "missing"), + generatedAt: asText(payload?.generatedAt), + }; +} + +function contractItemById(contract, id) { + return asArray(contract?.items).find((item) => item && item.id === id) ?? null; +} + +function commandList(value, limit = 4) { + return asArray(value).map((command) => sanitizeText(command)).slice(0, limit); +} + +function blockerList(value) { + return asArray(value).map((blocker) => sanitizeText(blocker)); +} + +function recordEntries(value) { + return value && typeof value === "object" && !Array.isArray(value) ? Object.entries(value) : []; +} + +function gateFromContractItem(contract, id) { + const item = contractItemById(contract, id); + const label = liveReadinessGateLabels.get(id) ?? id; + + if (!item) { + return { + id, + label, + status: "unresolved", + summary: "The deployment contract did not include this gate in the current report.", + evidence: "missing from public deployment contract report", + commands: [], + blockers: [], + }; + } + + return { + id, + label, + status: asText(item.status, "unresolved"), + summary: sanitizeText(item.requirement ?? item.summary ?? label), + evidence: sanitizeText(item.evidence ?? "not recorded"), + commands: commandList(item.commands), + blockers: blockerList(item.blockers), + }; +} + +function ownerInputGroup(name) { + if (name.startsWith("FLOWCHAIN_TESTER_")) { + return "tester write gateway"; + } + if (name === "FLOWCHAIN_RPC_STATE_BACKUP_PATH") { + return "backup storage"; + } + if (name.startsWith("FLOWCHAIN_RPC_")) { + return "public RPC edge"; + } + if (name.startsWith("FLOWCHAIN_BASE8453_") || name.startsWith("FLOWCHAIN_PILOT_")) { + return "Base 8453 bridge"; + } + + return "operator input"; +} + +function statusCounts(gates) { + return gates.reduce((counts, gate) => { + const status = asText(gate.status, "unresolved"); + counts[status] = (counts[status] ?? 0) + 1; + return counts; + }, {}); +} + +function timedOutSteps(steps) { + return asArray(steps).filter((step) => step?.timedOut === true); +} + +function writeLiveReadinessSummary() { + const reports = Object.fromEntries(liveReadinessReportCopies.map((fileName) => [fileName, readJsonIfExists(fileName)])); + const contract = reports["public-deployment-contract-report.json"]; + const serviceStatus = reports["service-status-report.json"]; + const monitor = reports["service-monitor-report.json"]; + const bridgeRelayer = reports["bridge-relayer-once-report.json"]; + const bridgeRelayerGuardrail = reports["bridge-relayer-guardrail-validation-report.json"]; + const bridgeRelayerLoopValidation = reports["bridge-relayer-loop-validation-report.json"]; + const backupOwnerPathDryRun = reports["backup-owner-path-dry-run-report.json"]; + const publicRpcDeploymentBundle = reports["public-rpc-deployment-bundle-report.json"]; + const publicRpcDeploymentAutomation = reports["public-rpc-deployment-automation-report.json"]; + const externalTesterPacket = reports["external-tester-packet-report.json"]; + const externalTesterConnectPack = reports["external-tester-connect-pack.json"]; + const externalTesterReadiness = reports["external-tester-readiness-report.json"]; + const publicTesterGateway = reports["public-tester-gateway-e2e-report.json"]; + const opsSnapshot = reports["ops-snapshot-report.json"]; + const opsAlertRules = reports["ops-alert-rules-report.json"]; + const incidentDrill = reports["incident-drill-report.json"]; + const alertInstallValidation = reports["alert-install-validation-report.json"]; + const opsEscalationDryRun = reports["ops-escalation-dry-run-report.json"]; + const ownerInputs = reports["owner-inputs-report.json"]; + const noSecretScan = reports["no-secret-scan-report.json"]; + const gates = [...liveReadinessGateLabels.keys()].map((id) => gateFromContractItem(contract, id)); + const activeRuleIds = asArray(opsAlertRules?.activeRuleIds).map((id) => sanitizeText(id)); + const alertRules = asArray(opsAlertRules?.rules).map((rule) => ({ + id: sanitizeText(rule?.id), + severity: sanitizeText(rule?.severity), + signal: sanitizeText(rule?.signal), + threshold: sanitizeText(rule?.threshold), + commands: commandList(rule?.commands, 6), + })); + const knownOwnerInputs = asArray(contract?.knownOwnerInputs).map((name) => sanitizeText(name)); + const requiredOwnerInputs = knownOwnerInputs.length > 0 + ? knownOwnerInputs + : gates.flatMap((gate) => gate.blockers); + const ownerInputSummaries = [...new Set(requiredOwnerInputs)].map((name) => ({ + name, + group: ownerInputGroup(name), + })); + const connectPackCheckValues = Object.values(externalTesterPacket?.connectPackChecks ?? {}); + const bridgeRelayerSteps = asArray(bridgeRelayer?.steps); + const bridgeRelayerTimedOutSteps = timedOutSteps(bridgeRelayerSteps); + const latestHeight = asText(serviceStatus?.chain?.latestHeight, "not recorded"); + const finalizedHeight = asText(serviceStatus?.chain?.finalizedHeight, "not recorded"); + const privateRpcUrl = serviceStatus?.bind + ? `http://${asText(serviceStatus.bind.host, "127.0.0.1")}:${asText(serviceStatus.bind.port, "8787")}` + : "http://127.0.0.1:8787"; + const summary = contract?.deploymentReady === true + ? "Public launch gates are passing; run the pre-exposure commands before sharing the tester packet." + : "Public launch is still blocked by owner-provided RPC edge, backup, Base 8453 bridge, or tester packet inputs."; + const generatedAt = asText( + contract?.generatedAt ?? + reports["flowchain-live-infra-check-report.json"]?.generatedAt ?? + serviceStatus?.generatedAt, + "not recorded", + ); + const liveReadiness = { + schema: "flowchain.live_readiness_dashboard_report.v0", + generatedAt, + status: asText(contract?.status ?? reports["flowchain-live-infra-check-report.json"]?.status, "unresolved"), + deploymentReady: contract?.deploymentReady === true, + packetShareable: contract?.packetShareable === true || externalTesterPacket?.packetShareable === true, + blockedOnlyOnKnownExternalOwnerInputs: contract?.blockedOnlyOnKnownExternalOwnerInputs === true, + summary, + privateRpcUrl, + metrics: { + latestHeight, + finalizedHeight, + monitorHeightAdvanced: monitor?.heightAdvanced === true, + bridgeRelayerStatus: asText(bridgeRelayer?.status, "not recorded"), + bridgeQueuedTransactions: asText(bridgeRelayer?.counts?.queuedTransactions, "0"), + bridgeRelayerChildTimeoutSeconds: asText(bridgeRelayer?.childTimeoutSeconds, "not recorded"), + bridgeRelayerStepCount: bridgeRelayerSteps.length, + bridgeRelayerTimedOutStepCount: bridgeRelayerTimedOutSteps.length, + bridgeRelayerNoChildTimeouts: bridgeRelayerSteps.length > 0 && bridgeRelayerTimedOutSteps.length === 0, + bridgeRelayerGuardrailStatus: asText(bridgeRelayerGuardrail?.status, "not recorded"), + bridgeRelayerLoopValidationStatus: asText(bridgeRelayerLoopValidation?.status, "not recorded"), + backupOwnerPathDryRunStatus: asText(backupOwnerPathDryRun?.status, "not recorded"), + publicRpcDeploymentBundleStatus: asText(publicRpcDeploymentBundle?.status, "not recorded"), + publicRpcOwnerRenderValidationStatus: asText(publicRpcDeploymentBundle?.renderValidation?.status, "not recorded"), + publicRpcDeploymentAutomationStatus: asText(publicRpcDeploymentAutomation?.status, "not recorded"), + publicRpcDeploymentAutomationAction: asText(publicRpcDeploymentAutomation?.action, "not recorded"), + externalTesterPacketStatus: asText(externalTesterPacket?.status, "not recorded"), + externalTesterConnectPackStatus: asText(externalTesterConnectPack?.status, "not recorded"), + ownerInputReady: ownerInputs?.ownerInputReady === true, + noSecretStatus: asText(noSecretScan?.status, "not recorded"), + opsSnapshotStatus: asText(opsSnapshot?.status, "not recorded"), + opsAlertState: asText(opsAlertRules?.currentAlertState, "not recorded"), + opsCriticalCount: asText(opsSnapshot?.criticalCount, "0"), + opsBlockedCount: asText(opsSnapshot?.blockedCount, "0"), + opsActiveRuleCount: activeRuleIds.length, + opsRuleCount: asText(opsAlertRules?.ruleCount, String(alertRules.length)), + opsCriticalRuleCount: asText(opsAlertRules?.criticalRuleCount, "0"), + opsBlockedRuleCount: asText(opsAlertRules?.blockedRuleCount, "0"), + opsUnmappedCurrentFindingCount: asArray(opsAlertRules?.unmappedCurrentFindingCodes).length, + incidentDrillStatus: asText(incidentDrill?.status, "not recorded"), + alertInstallValidationStatus: asText(alertInstallValidation?.status, "not recorded"), + opsEscalationDryRunStatus: asText(opsEscalationDryRun?.status, "not recorded"), + opsEscalationDryRunEvents: asText(opsEscalationDryRun?.dryRunEventCount, "0"), + statusCounts: statusCounts(gates), + }, + ops: { + snapshotStatus: asText(opsSnapshot?.status, "not recorded"), + alertRulesStatus: asText(opsAlertRules?.status, "not recorded"), + alertState: asText(opsAlertRules?.currentAlertState, "not recorded"), + incidentDrillStatus: asText(incidentDrill?.status, "not recorded"), + alertInstallValidationStatus: asText(alertInstallValidation?.status, "not recorded"), + escalationDryRunStatus: asText(opsEscalationDryRun?.status, "not recorded"), + escalationDryRunEvents: asText(opsEscalationDryRun?.dryRunEventCount, "0"), + bridgeRelayerGuardrailStatus: asText(opsSnapshot?.reportStatuses?.bridgeRelayerGuardrail ?? bridgeRelayerGuardrail?.status, "not recorded"), + bridgeRelayerGuardrailReady: opsSnapshot?.reportStatuses?.bridgeRelayerGuardrailReady === true, + criticalCount: asText(opsSnapshot?.criticalCount, "0"), + blockedCount: asText(opsSnapshot?.blockedCount, "0"), + latestHeight: asText(opsSnapshot?.chain?.latestHeight, latestHeight), + finalizedHeight: asText(opsSnapshot?.chain?.finalizedHeight, finalizedHeight), + monitorStatus: asText(opsSnapshot?.chain?.monitorStatus, "not recorded"), + monitorHeightAdvanced: opsSnapshot?.chain?.monitorHeightAdvanced === true, + findings: asArray(opsSnapshot?.findings).map((finding) => ({ + severity: sanitizeText(finding?.severity), + code: sanitizeText(finding?.code), + message: sanitizeText(finding?.message), + commands: commandList(finding?.commands, 6), + })), + activeRuleIds, + coveredFindingCodes: asArray(opsAlertRules?.coveredFindingCodes).map((code) => sanitizeText(code)), + activeRules: alertRules.filter((rule) => activeRuleIds.includes(rule.id)), + ruleCount: asText(opsAlertRules?.ruleCount, String(alertRules.length)), + criticalRuleCount: asText(opsAlertRules?.criticalRuleCount, "0"), + blockedRuleCount: asText(opsAlertRules?.blockedRuleCount, "0"), + unmappedCurrentFindingCodes: asArray(opsAlertRules?.unmappedCurrentFindingCodes).map((code) => sanitizeText(code)), + incidentCommands: Object.fromEntries( + recordEntries(opsSnapshot?.incidentCommands).map(([group, commands]) => [sanitizeText(group), commandList(commands, 10)]), + ), + dryRunEvents: asArray(opsEscalationDryRun?.dryRunEvents).map((event) => ({ + findingCode: sanitizeText(event?.findingCode), + severity: sanitizeText(event?.severity), + ruleId: sanitizeText(event?.ruleId), + signal: sanitizeText(event?.signal), + commands: commandList(event?.commands, 6), + })), + sendsNetworkNotifications: opsAlertRules?.notificationPlan?.sendsNetworkNotifications === true, + storesSecrets: opsAlertRules?.notificationPlan?.storesSecrets === true, + }, + testerLaunch: { + status: asText(externalTesterPacket?.status ?? externalTesterReadiness?.status, "not recorded"), + readinessStatus: asText(externalTesterReadiness?.status, "not recorded"), + packetStatus: asText(externalTesterPacket?.status, "not recorded"), + connectPackStatus: asText(externalTesterConnectPack?.status, "not recorded"), + gatewayStatus: asText(publicTesterGateway?.status, "not recorded"), + shareable: externalTesterPacket?.packetShareable === true, + connectPackShareable: externalTesterPacket?.connectPackShareable === true || externalTesterConnectPack?.shareable === true, + connectPackReady: connectPackCheckValues.length > 0 && connectPackCheckValues.every((value) => value === true), + connectPackNetwork: { + name: sanitizeText(externalTesterConnectPack?.network?.name), + chainId: sanitizeText(externalTesterConnectPack?.network?.chainId), + rpcEndpointPlaceholder: sanitizeText(externalTesterConnectPack?.network?.rpcEndpointPlaceholder), + explorerSummaryUrlPlaceholder: sanitizeText(externalTesterConnectPack?.network?.explorerSummaryUrlPlaceholder), + }, + externalSharingReady: externalTesterReadiness?.externalSharingReady === true || externalTesterPacket?.externalSharingReady === true, + localTesterRehearsalReady: externalTesterReadiness?.localTesterRehearsalReady === true, + publicTesterGatewayReady: externalTesterReadiness?.checks?.publicTesterGatewayReady === true, + gatewayConfigured: publicTesterGateway?.testerGatewayConfigured === true, + testerNetworkFresh: externalTesterReadiness?.checks?.testerWalletNetworkFresh === true, + faucetRouteValidated: externalTesterReadiness?.checks?.publicTesterGatewayFaucetRouteValidated === true, + packetExecutableSmokeValidated: externalTesterPacket?.packetExecutableSmokeValidated === true || externalTesterReadiness?.checks?.packetExecutableSmokeValidated === true, + packetSmokeRoutes: asArray(externalTesterPacket?.packetSmokeRoutes).map((route) => sanitizeText(route)), + connectPackReadOnlyRoutes: asArray(externalTesterConnectPack?.endpoints?.readOnlyRoutes).map((route) => sanitizeText(route)), + connectPackTesterWriteRoutes: asArray(externalTesterConnectPack?.endpoints?.testerWriteRoutes).map((route) => sanitizeText(route)), + gatewayRoutes: asArray(publicTesterGateway?.routes).map((route) => sanitizeText(route)), + ownerInputGroups: Object.fromEntries( + recordEntries( + ownerInputSummaries.reduce((groups, input) => { + groups[input.group] = [...(groups[input.group] ?? []), input.name]; + return groups; + }, {}), + ), + ), + commands: { + readiness: [ + "npm run flowchain:tester:readiness -- -AllowBlocked", + "npm run flowchain:external-tester:packet -- -AllowBlocked", + ], + gateway: ["npm run flowchain:tester:gateway:e2e"], + wallet: ["npm run flowchain:wallet:live-tester:e2e"], + explorer: ["npm run flowchain:public-deployment:contract -- -AllowBlocked"], + }, + envValuesPrinted: false, + noSecrets: publicTesterGateway?.noSecrets === true, + }, + ownerInputs: ownerInputSummaries, + gates, + commands: { + preExposure: commandList(contract?.operatorCommands?.preExposure, 12), + rollback: commandList(contract?.operatorCommands?.rollback, 10), + }, + sourceReports: liveReadinessReportCopies.map((fileName) => sourceReportSummary(fileName, reports[fileName])), + envValuesPrinted: false, + noSecrets: true, + }; + + writeFileSync(resolve(destinationDir, "flowchain-live-readiness-report.json"), `${JSON.stringify(liveReadiness, null, 2)}\n`); + console.log(`Synced FlowChain live readiness dashboard report: ${resolve(destinationDir, "flowchain-live-readiness-report.json")}`); +} + mkdirSync(destinationDir, { recursive: true }); for (const fixture of fixtureCopies) { @@ -47,3 +401,5 @@ for (const fixture of fixtureCopies) { copyFileSync(fixture.source, fixture.destination); console.log(`Synced ${fixture.label}: ${fixture.destination}`); } + +writeLiveReadinessSummary(); diff --git a/apps/dashboard/src/App.tsx b/apps/dashboard/src/App.tsx index 750ab52e..4116c42c 100644 --- a/apps/dashboard/src/App.tsx +++ b/apps/dashboard/src/App.tsx @@ -6,15 +6,20 @@ import { DEFAULT_CANARY_DASHBOARD_DATA_PATH, fetchDashboardData } from "./data/l import type { DashboardData } from "./data/types"; import { DEFAULT_CONTROL_PLANE_URL, buildWorkbenchSnapshot, fetchWorkbenchSnapshot, type WorkbenchSnapshot } from "./data/workbench"; import { AlertsView } from "./views/AlertsView"; +import { BridgePilotView } from "./views/BridgePilotView"; import { CanaryDeploymentView } from "./views/CanaryDeploymentView"; import { DevnetBlocksView } from "./views/DevnetBlocksView"; +import { ExternalTesterLaunchView } from "./views/ExternalTesterLaunchView"; +import { ExplorerView } from "./views/ExplorerView"; import { FlowMemoryView } from "./views/FlowMemoryView"; import { FlowPulseStreamView } from "./views/FlowPulseStreamView"; import { HardwareNodesView } from "./views/HardwareNodesView"; +import { OpsView } from "./views/OpsView"; import { OverviewView } from "./views/OverviewView"; import { RawJsonInspectorView } from "./views/RawJsonInspectorView"; import { RootfieldsView } from "./views/RootfieldsView"; import { VerifierReportsView } from "./views/VerifierReportsView"; +import { WalletView } from "./views/WalletView"; import { WorkbenchView } from "./views/WorkbenchView"; import { WorkReceiptsView } from "./views/WorkReceiptsView"; @@ -116,6 +121,11 @@ export default function App() { setVersion((current) => current + 1)} />} /> + } /> + } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/apps/dashboard/src/components/AppShell.tsx b/apps/dashboard/src/components/AppShell.tsx index 765d1308..7e18af83 100644 --- a/apps/dashboard/src/components/AppShell.tsx +++ b/apps/dashboard/src/components/AppShell.tsx @@ -8,12 +8,17 @@ import { BrainCircuit, Boxes, ClipboardCheck, + ArrowRightLeft, + Compass, RadioReceiver, LayoutDashboard, Monitor, Network, RadioTower, + ShieldAlert, ShieldCheck, + UserPlus, + Wallet, } from "lucide-react"; import type { DashboardData } from "../data/types"; import type { WorkbenchSnapshot } from "../data/workbench"; @@ -28,6 +33,11 @@ interface AppShellProps { const NAV_ITEMS = [ { to: "/", label: "Workbench", icon: Monitor }, + { to: "/wallet", label: "Wallet", icon: Wallet }, + { to: "/tester", label: "Tester launch", icon: UserPlus }, + { to: "/bridge", label: "Bridge pilot", icon: ArrowRightLeft }, + { to: "/explorer", label: "Explorer", icon: Compass }, + { to: "/ops", label: "Ops", icon: ShieldAlert }, { to: "/overview", label: "Overview", icon: LayoutDashboard }, { to: "/canary", label: "Base canary", icon: RadioReceiver }, { to: "/flowmemory", label: "Flow Memory", icon: BrainCircuit }, @@ -43,6 +53,12 @@ const NAV_ITEMS = [ export function AppShell({ data, canaryData, workbench, children }: AppShellProps) { const location = useLocation(); + const isBridgeRoute = location.pathname.startsWith("/bridge"); + const isWalletRoute = location.pathname.startsWith("/wallet"); + if (isBridgeRoute || isWalletRoute) { + return <>{children}; + } + const isCanaryRoute = location.pathname.startsWith("/canary"); const activeData = isCanaryRoute && canaryData ? canaryData : data; const bannerMode = isCanaryRoute diff --git a/apps/dashboard/src/data/loadDashboardData.ts b/apps/dashboard/src/data/loadDashboardData.ts index 59c6296d..d76d782a 100644 --- a/apps/dashboard/src/data/loadDashboardData.ts +++ b/apps/dashboard/src/data/loadDashboardData.ts @@ -3,6 +3,17 @@ import type { DashboardData } from "./types"; export const DEFAULT_DASHBOARD_DATA_PATH = "/data/flowmemory-dashboard-v0.json"; export const DEFAULT_CANARY_DASHBOARD_DATA_PATH = "/data/flowmemory-dashboard-base-canary-v0.json"; +export function publicAssetPath(path: string): string { + const baseUrl = (import.meta as ImportMeta & { env?: { BASE_URL?: string } }).env?.BASE_URL ?? "/"; + if (/^[a-z][a-z0-9+.-]*:/i.test(path)) { + return path; + } + if (baseUrl === "./") { + return `./${path.replace(/^\/+/, "")}`; + } + return path; +} + function assertArray(value: unknown, label: string): void { if (!Array.isArray(value)) { throw new Error(`Dashboard fixture field "${label}" must be an array.`); @@ -45,7 +56,7 @@ export function validateDashboardData(payload: unknown): DashboardData { export async function fetchDashboardData( path = DEFAULT_DASHBOARD_DATA_PATH, ): Promise { - const response = await fetch(path, { cache: "no-store" }); + const response = await fetch(publicAssetPath(path), { cache: "no-store" }); if (!response.ok) { throw new Error(`Unable to load dashboard fixture at ${path}: ${response.status}`); } diff --git a/apps/dashboard/src/data/types.ts b/apps/dashboard/src/data/types.ts index 61e190d8..07b4a7ea 100644 --- a/apps/dashboard/src/data/types.ts +++ b/apps/dashboard/src/data/types.ts @@ -19,7 +19,8 @@ export type SourceSubsystem = | "devnet" | "worker" | "hardware" - | "alerts"; + | "alerts" + | "ops"; export interface Provenance { subsystem: SourceSubsystem; diff --git a/apps/dashboard/src/data/workbench.ts b/apps/dashboard/src/data/workbench.ts index d4bb4688..698be46b 100644 --- a/apps/dashboard/src/data/workbench.ts +++ b/apps/dashboard/src/data/workbench.ts @@ -1,9 +1,11 @@ import type { DashboardData, DashboardStatus, Provenance, SourceSubsystem } from "./types"; +import { publicAssetPath } from "./loadDashboardData"; export const DEFAULT_CONTROL_PLANE_URL = "http://127.0.0.1:8787"; export const WORKBENCH_DEVNET_STATE_PATH = "/data/flowchain-local-devnet-state.json"; export const WORKBENCH_DEVNET_DASHBOARD_STATE_PATH = "/data/flowchain-local-devnet-dashboard-state.json"; export const WORKBENCH_BRIDGE_TEST_DEPOSIT_PATH = "/data/flowchain-bridge-test-deposit.json"; +export const WORKBENCH_LIVE_READINESS_REPORT_PATH = "/data/flowchain-live-readiness-report.json"; export const WORKBENCH_EXPLORER_FALLBACK_PATH = "/data/flowchain-l1-explorer-fallback.json"; const FIXTURE_CHAIN_CONTEXT = "flowchain-private-local-testnet"; @@ -63,6 +65,7 @@ export type WorkbenchSectionKey = | "receiptEvents" | "explorerRecords" | "realValuePilot" + | "liveReadiness" | "rootfields" | "agents" | "models" @@ -116,6 +119,10 @@ export interface ControlPlaneProbe { health?: unknown; state?: unknown; pilotStatus?: unknown; + bridgeLiveReadiness?: unknown; + pilotLifecycle?: unknown; + walletBalances?: unknown; + walletTransfers?: unknown; rpc?: Record; } @@ -154,8 +161,13 @@ export interface WorkbenchSnapshot { devnetState: unknown | null; devnetDashboardState: unknown | null; bridgeTestDeposit: unknown | null; + liveReadinessReport: unknown | null; explorerFallback: unknown | null; controlPlanePilotStatus: unknown | null; + controlPlaneBridgeReadiness: unknown | null; + controlPlanePilotLifecycle: unknown | null; + controlPlaneWalletBalances: unknown | null; + controlPlaneWalletTransfers: unknown | null; controlPlaneHealth: unknown | null; controlPlaneState: unknown | null; controlPlaneRpc: Record | null; @@ -296,11 +308,19 @@ export const WORKBENCH_SECTIONS: WorkbenchSectionDefinition[] = [ { key: "realValuePilot", label: "Real-Value Pilot", - detail: "Capped owner-testing lifecycle for Base deposit observation, local credit, replay/retry status, withdrawal intent, release evidence, caps, pause, and emergency state.", - expectedEndpoint: "GET /pilot/status + POST /rpc pilot_status", + detail: "Capped owner-testing lifecycle for Base deposit observation, exact local credit, wallet transferability, withdrawal/release evidence, readiness blockers, caps, pause, and emergency state.", + expectedEndpoint: "GET /bridge/live-readiness + GET /pilot/lifecycle + GET /pilot/status", missingCommand: "npm run control-plane:serve", missingService: "FlowChain real-value pilot control-plane /pilot/status", }, + { + key: "liveReadiness", + label: "Live Readiness", + detail: "Public launch contract, private L1 origin, public RPC, backup, bridge relayer, tester packet, and no-secret gates from the latest infra reports.", + expectedEndpoint: WORKBENCH_LIVE_READINESS_REPORT_PATH, + missingCommand: "npm run flowchain:public-deployment:contract", + missingService: "FlowChain live deployment readiness summary", + }, { key: "rootfields", label: "Rootfields", @@ -568,16 +588,39 @@ function stringArray(value: unknown): string[] { function statusFrom(value: unknown, fallback: DashboardStatus = "observed"): DashboardStatus { const normalized = text(value, fallback).toLowerCase(); - if (normalized === "applied" || normalized === "success" || normalized === "active" || normalized === "live" || normalized === "recorded") { + if ( + normalized === "applied" || + normalized === "success" || + normalized === "active" || + normalized === "live" || + normalized === "ok" || + normalized === "passed" || + normalized === "ready_for_operator_live_pilot" || + normalized === "ready" || + normalized === "recorded" + ) { return "verified"; } if (normalized === "finalized") { return "finalized"; } - if (normalized === "failed" || normalized === "invalid" || normalized === "reverted" || normalized === "rejected" || normalized.includes("rejected")) { + if ( + normalized === "failed" || + normalized === "invalid" || + normalized === "reverted" || + normalized === "failure" || + normalized === "rejected" || + normalized.includes("rejected") + ) { return "failed"; } - if (normalized === "pending" || normalized === "requested" || normalized === "local-placeholder" || normalized === "degraded") { + if ( + normalized === "pending" || + normalized === "requested" || + normalized === "local-placeholder" || + normalized === "degraded" || + normalized === "blocked" + ) { return "pending"; } if (normalized === "error") { @@ -785,7 +828,7 @@ async function fetchControlPlaneRpc(url: string): Promise { try { - return { value: await fetchJsonWithTimeout(path, CONTROL_PLANE_TIMEOUT_MS) }; + return { value: await fetchJsonWithTimeout(publicAssetPath(path), CONTROL_PLANE_TIMEOUT_MS) }; } catch (error) { return { value: null, @@ -797,12 +840,24 @@ async function fetchOptionalJson(path: string): Promise<{ value: unknown | null; async function probeControlPlane(): Promise { const url = getControlPlaneUrl(); const checkedAt = new Date().toISOString(); - const defaultEndpoints = ["GET /health", "GET /state", "GET /pilot/status"]; + const defaultEndpoints = [ + "GET /health", + "GET /state", + "GET /pilot/status", + "GET /bridge/live-readiness", + "GET /pilot/lifecycle", + "GET /wallets/balances", + "GET /wallets/transfers", + ]; try { const health = await fetchJsonWithTimeout(`${url}/health`, CONTROL_PLANE_TIMEOUT_MS); let state: unknown | undefined; let pilotStatus: unknown | undefined; + let bridgeLiveReadiness: unknown | undefined; + let pilotLifecycle: unknown | undefined; + let walletBalances: unknown | undefined; + let walletTransfers: unknown | undefined; let rpc: Record | undefined; try { @@ -811,6 +866,30 @@ async function probeControlPlane(): Promise { pilotStatus = undefined; } + try { + bridgeLiveReadiness = await fetchJsonWithTimeout(`${url}/bridge/live-readiness`, CONTROL_PLANE_TIMEOUT_MS); + } catch { + bridgeLiveReadiness = undefined; + } + + try { + pilotLifecycle = await fetchJsonWithTimeout(`${url}/pilot/lifecycle`, CONTROL_PLANE_TIMEOUT_MS); + } catch { + pilotLifecycle = undefined; + } + + try { + walletBalances = await fetchJsonWithTimeout(`${url}/wallets/balances`, CONTROL_PLANE_TIMEOUT_MS); + } catch { + walletBalances = undefined; + } + + try { + walletTransfers = await fetchJsonWithTimeout(`${url}/wallets/transfers`, CONTROL_PLANE_TIMEOUT_MS); + } catch { + walletTransfers = undefined; + } + try { state = await fetchJsonWithTimeout(`${url}/state`, CONTROL_PLANE_TIMEOUT_MS); try { @@ -826,6 +905,10 @@ async function probeControlPlane(): Promise { endpoints: uniqueEndpoints(defaultEndpoints, ["POST /rpc"], collectEndpointHints(health)), health, pilotStatus, + bridgeLiveReadiness, + pilotLifecycle, + walletBalances, + walletTransfers, error: `Health endpoint responded, but state endpoint was not loaded: ${ error instanceof Error ? error.message : "unknown state error" }`, @@ -840,6 +923,10 @@ async function probeControlPlane(): Promise { health, state, pilotStatus, + bridgeLiveReadiness, + pilotLifecycle, + walletBalances, + walletTransfers, rpc, }; } catch (error) { @@ -1234,8 +1321,232 @@ function commandFromStep(step: unknown): string { return isRecord(step) ? text(step.command, "npm run flowchain:real-value-pilot:e2e") : "npm run flowchain:real-value-pilot:e2e"; } +function readinessStatus(value: unknown): DashboardStatus { + const normalized = text(value, "BLOCKED").toUpperCase(); + if (normalized === "READY_FOR_OPERATOR_LIVE_PILOT") { + return "verified"; + } + if (normalized === "FAILED") { + return "failed"; + } + return "pending"; +} + +function readinessPayload(controlPlane: ControlPlaneProbe, pilot: UnknownRecord | null): UnknownRecord | null { + if (isRecord(controlPlane.bridgeLiveReadiness)) { + return controlPlane.bridgeLiveReadiness; + } + if (isRecord(pilot?.bridgeLiveReadiness)) { + return pilot.bridgeLiveReadiness as UnknownRecord; + } + return null; +} + +function bridgeReadinessRecord(controlPlane: ControlPlaneProbe, readiness: UnknownRecord | null): WorkbenchRecord { + if (!readiness) { + return makeLocalRecord( + "devnet", + controlPlane.url, + { + id: "bridge-live-readiness", + kind: "Bridge live readiness", + title: "Bridge live readiness BLOCKED", + summary: "The live readiness endpoint is unavailable; operator live pilot remains fail-closed until the control-plane returns readiness details.", + status: controlPlane.status === "available" ? "pending" : "offline", + facts: [ + { label: "fail-closed status", value: "BLOCKED" }, + { label: "base chain", value: "8453" }, + { label: "missing env names", value: "endpoint unavailable" }, + { label: "env values printed", value: "false" }, + { label: "mock presented as live", value: "false" }, + { label: "owner verified lockbox", value: "false" }, + ], + raw: { endpoint: "/bridge/live-readiness", status: "unavailable" }, + }, + controlPlane.checkedAt, + ); + } + + const node = isRecord(readiness.node) ? readiness.node : {}; + const lockbox = isRecord(readiness.lockbox) ? readiness.lockbox : {}; + const confirmationDepth = isRecord(readiness.confirmationDepth) ? readiness.confirmationDepth : {}; + const artifacts = isRecord(readiness.currentArtifacts) ? readiness.currentArtifacts : {}; + const missingEnvNames = stringArray(readiness.missingEnvNames); + const failClosedStatus = text(readiness.failClosedStatus, "BLOCKED"); + + return makeLocalRecord( + "devnet", + controlPlane.url, + { + id: "bridge-live-readiness", + kind: "Bridge live readiness", + title: `Bridge live readiness ${failClosedStatus}`, + summary: + missingEnvNames.length > 0 + ? `Fail-closed with missing env names: ${missingEnvNames.join(", ")}.` + : text(readiness.machineStatus, "Live readiness is available from the control-plane."), + status: readinessStatus(failClosedStatus), + facts: [ + { label: "fail-closed status", value: failClosedStatus }, + { label: "base chain", value: `${text(readiness.baseChainName, "Base")} ${text(readiness.baseChainId, "8453")}` }, + { label: "node running", value: text(node.running, "false") }, + { label: "lockbox configured", value: text(lockbox.configured, "false") }, + { label: "confirmation configured", value: text(confirmationDepth.configured, "false") }, + { label: "missing env names", value: missingEnvNames.join(", ") || "none" }, + { label: "env values printed", value: text(readiness.envValuesPrinted, "false") }, + { label: "mock presented as live", value: text(artifacts.mockPresentedAsLive, "false") }, + ], + raw: readiness, + }, + controlPlane.checkedAt, + ); +} + +function bridgeReadinessIssueRecords(controlPlane: ControlPlaneProbe, readiness: UnknownRecord | null): WorkbenchRecord[] { + if (!readiness) { + return []; + } + + return collectionFrom(readiness, ["issues"]).map((issue, index) => + makeLocalRecord( + "devnet", + controlPlane.url, + { + id: `bridge-readiness-issue:${text(issue.reasonCode, String(index + 1))}`, + kind: "Operational empty/error state", + title: text(issue.title, "Bridge readiness issue"), + summary: text(issue.summary, "A live-pilot readiness issue is visible in the control-plane response."), + status: statusFrom(issue.status, "pending"), + facts: [ + { label: "reason code", value: text(issue.reasonCode) }, + { label: "status", value: text(issue.status, "blocked") }, + { label: "env names", value: stringArray(issue.envNames).join(", ") || "none" }, + { label: "machine readable", value: "true" }, + ], + raw: issue, + }, + controlPlane.checkedAt, + ), + ); +} + +function lifecycleRows(controlPlane: ControlPlaneProbe, pilot: UnknownRecord | null): UnknownRecord[] { + if (isRecord(controlPlane.pilotLifecycle)) { + return collectionFrom(controlPlane.pilotLifecycle, ["lifecycleRecords"]); + } + if (pilot) { + return collectionFrom(pilot, ["lifecycleRecords"]); + } + return []; +} + +function bridgeLifecycleRecord(controlPlane: ControlPlaneProbe, row: UnknownRecord, index: number): WorkbenchRecord { + const equality = isRecord(row.equality) ? row.equality : {}; + const equalities = isRecord(equality.equalities) ? equality.equalities : {}; + const depositObservation = isRecord(row.depositObservation) ? row.depositObservation : {}; + const withdrawalIntent = isRecord(row.withdrawalIntent) ? row.withdrawalIntent : {}; + const releaseEvidence = isRecord(row.releaseEvidence) ? row.releaseEvidence : {}; + const liveArtifact = row.liveArtifact === true; + const artifactClass = text(row.artifactClass, liveArtifact ? "live-base8453" : "local-or-mock"); + const baseTxHash = text(row.baseTxHash ?? row.txHash, `lifecycle:${index + 1}`); + const creditId = text(row.creditId, "credit pending"); + const amount = text(row.amountSmallestUnits ?? equality.depositAmount, "0"); + const replayKey = text(row.replayKey ?? depositObservation.replayKey); + const withdrawalIntentId = text(row.withdrawalIntentId ?? withdrawalIntent.withdrawalIntentId); + const releaseEvidenceId = text(row.releaseEvidenceId ?? releaseEvidence.releaseEvidenceId); + + return makeLocalRecord( + "devnet", + controlPlane.url, + { + id: text(row.lifecycleRecordId, `${baseTxHash}:${text(row.logIndex, String(index))}`), + kind: "Bridge exact lifecycle", + title: `${baseTxHash} / ${creditId}`, + summary: `${artifactClass} record with deposit, observed, credited, wallet delta, transferable, withdrawal, and release amount equality ${text(equality.allEqual, "false")}.`, + status: statusFrom(row.status, "pending"), + facts: [ + { label: "base tx hash", value: baseTxHash }, + { label: "log index", value: text(row.logIndex) }, + { label: "deposit id", value: text(row.depositId ?? depositObservation.depositId) }, + { label: "replay key", value: replayKey }, + { label: "replay status", value: text(row.replayStatus ?? depositObservation.replayStatus) }, + { label: "credit id", value: creditId }, + { label: "recipient wallet", value: text(row.recipientWallet) }, + { label: "withdrawal intent", value: withdrawalIntentId }, + { label: "withdrawal status", value: text(row.withdrawalStatus ?? withdrawalIntent.status) }, + { label: "release evidence", value: releaseEvidenceId }, + { label: "release status", value: text(row.releaseStatus ?? releaseEvidence.status) }, + { label: "asset", value: text(row.asset) }, + { label: "amount smallest units", value: amount }, + { label: "deposit amount", value: text(equality.depositAmount) }, + { label: "credited amount", value: text(equality.creditedAmount) }, + { label: "withdrawal amount", value: text(equality.withdrawalAmount) }, + { label: "release amount", value: text(equality.releaseAmount) }, + { label: "all values equal", value: text(equality.allEqual, "false") }, + { label: "wallet delta equal", value: text(equalities.walletDelta, "false") }, + { label: "evidence path", value: text(row.evidenceFilePath) }, + ], + raw: row, + }, + controlPlane.checkedAt, + ); +} + +function buildControlPlaneWalletBalanceRecords(controlPlane: ControlPlaneProbe): WorkbenchRecord[] { + return collectionFrom(controlPlane.walletBalances, ["balances"]).map((balance, index) => + makeLocalRecord( + "devnet", + controlPlane.url, + { + id: text(balance.balanceId, `wallet-balance:${index + 1}`), + kind: "Wallet balance", + title: text(balance.walletAddress, `wallet:${index + 1}`), + summary: `Wallet balance ${text(balance.status, "observed")} for ${text(balance.asset, "asset")} is ${text(balance.amount, "0")} smallest units.`, + status: statusFrom(balance.status, "observed"), + facts: [ + { label: "wallet", value: text(balance.walletAddress) }, + { label: "asset", value: text(balance.asset) }, + { label: "amount", value: text(balance.amount, "0") }, + { label: "previous amount", value: text(balance.previousAmount) }, + { label: "credit id", value: text(balance.creditId) }, + { label: "transfer id", value: text(balance.transferId) }, + ], + raw: balance, + }, + controlPlane.checkedAt, + ), + ); +} + +function buildControlPlaneWalletTransferRecords(controlPlane: ControlPlaneProbe): WorkbenchRecord[] { + return collectionFrom(controlPlane.walletTransfers, ["transfers"]).map((transfer, index) => + makeLocalRecord( + "devnet", + controlPlane.url, + { + id: text(transfer.transferId ?? transfer.txId, `wallet-transfer:${index + 1}`), + kind: "Wallet transfer history", + title: text(transfer.txId ?? transfer.transferId, `transfer:${index + 1}`), + summary: `${text(transfer.amount, "0")} ${text(transfer.assetId, "asset")} transferred from ${text(transfer.fromAccountId)} to ${text(transfer.toAccountId)}.`, + status: statusFrom(transfer.status, "observed"), + facts: [ + { label: "from wallet", value: text(transfer.fromAccountId) }, + { label: "to wallet", value: text(transfer.toAccountId) }, + { label: "asset", value: text(transfer.assetId) }, + { label: "amount", value: text(transfer.amount, "0") }, + { label: "status", value: text(transfer.status) }, + { label: "evidence path", value: text(transfer.evidenceFilePath) }, + ], + raw: transfer, + }, + controlPlane.checkedAt, + ), + ); +} + function buildPilotRecords(controlPlane: ControlPlaneProbe): WorkbenchRecord[] { const pilot = isRecord(controlPlane.pilotStatus) ? controlPlane.pilotStatus : null; + const readiness = readinessPayload(controlPlane, pilot); const records: WorkbenchRecord[] = []; if (!pilot) { @@ -1260,6 +1571,10 @@ function buildPilotRecords(controlPlane: ControlPlaneProbe): WorkbenchRecord[] { controlPlane.checkedAt, ), ); + records.push(bridgeReadinessRecord(controlPlane, readiness)); + records.push(...bridgeReadinessIssueRecords(controlPlane, readiness)); + records.push(...buildControlPlaneWalletBalanceRecords(controlPlane)); + records.push(...buildControlPlaneWalletTransferRecords(controlPlane)); return records; } @@ -1295,6 +1610,14 @@ function buildPilotRecords(controlPlane: ControlPlaneProbe): WorkbenchRecord[] { ), ); + records.push(bridgeReadinessRecord(controlPlane, readiness)); + records.push(...bridgeReadinessIssueRecords(controlPlane, readiness)); + lifecycleRows(controlPlane, pilot).forEach((row, index) => { + records.push(bridgeLifecycleRecord(controlPlane, row, index)); + }); + records.push(...buildControlPlaneWalletBalanceRecords(controlPlane)); + records.push(...buildControlPlaneWalletTransferRecords(controlPlane)); + lifecycle.forEach((step, index) => { records.push( makeLocalRecord( @@ -2459,6 +2782,133 @@ function buildFinalityRecords(data: DashboardData, devnetState: unknown): Workbe return records; } +function buildLiveReadinessRecords(liveReadinessReport: unknown | null): WorkbenchRecord[] { + const report = isRecord(liveReadinessReport) ? liveReadinessReport : null; + + if (!report) { + return [ + makeRecord("ops", WORKBENCH_LIVE_READINESS_REPORT_PATH, { + id: "live-readiness-report", + kind: "Public launch readiness", + title: "Live readiness report missing", + summary: "The dashboard did not load the generated live-readiness summary. Run the public deployment contract and sync dashboard fixtures.", + status: "unresolved", + facts: [ + { label: "deployment ready", value: "false" }, + { label: "packet shareable", value: "false" }, + { label: "next command", value: "npm run flowchain:public-deployment:contract" }, + ], + raw: null, + }), + ]; + } + + const metrics = isRecord(report.metrics) ? report.metrics : {}; + const statusCounts = isRecord(metrics.statusCounts) + ? Object.entries(metrics.statusCounts) + .map(([status, count]) => `${status}:${text(count)}`) + .join(", ") + : "not recorded"; + const gates = collectionFrom(report, ["gates"]); + const ownerInputs = collectionFrom(report, ["ownerInputs"]); + const sourceReports = collectionFrom(report, ["sourceReports"]); + const ownerInputGroups = ownerInputs.reduce>((groups, input) => { + const group = text(input.group, "operator input"); + const current = groups.get(group) ?? []; + current.push(text(input.name)); + groups.set(group, current); + return groups; + }, new Map()); + const records: WorkbenchRecord[] = [ + makeRecord("ops", WORKBENCH_LIVE_READINESS_REPORT_PATH, { + id: "live-readiness-summary", + kind: "Public launch readiness", + title: report.deploymentReady === true ? "Public launch gates passed" : "Public launch blocked", + summary: text(report.summary, "Public launch readiness is derived from the latest live infrastructure reports."), + status: statusFrom(report.status, "pending"), + facts: [ + { label: "deployment ready", value: text(report.deploymentReady, "false") }, + { label: "packet shareable", value: text(report.packetShareable, "false") }, + { label: "private RPC", value: text(report.privateRpcUrl, DEFAULT_CONTROL_PLANE_URL) }, + { label: "latest height", value: text(metrics.latestHeight) }, + { label: "finalized height", value: text(metrics.finalizedHeight) }, + { label: "height advanced", value: text(metrics.monitorHeightAdvanced, "false") }, + { label: "owner input ready", value: text(metrics.ownerInputReady, "false") }, + { label: "bridge relayer", value: text(metrics.bridgeRelayerStatus) }, + { label: "relayer child timeout", value: text(metrics.bridgeRelayerChildTimeoutSeconds) }, + { label: "relayer timed out steps", value: text(metrics.bridgeRelayerTimedOutStepCount, "0") }, + { label: "tester packet", value: text(metrics.externalTesterPacketStatus) }, + { label: "alert rules", value: text(metrics.opsRuleCount, text(metrics.opsActiveRuleCount, "0")) }, + { label: "unmapped findings", value: text(metrics.opsUnmappedCurrentFindingCount, "0") }, + { label: "no-secret scan", value: text(metrics.noSecretStatus) }, + { label: "status counts", value: statusCounts }, + { label: "env values printed", value: text(report.envValuesPrinted, "false") }, + ], + raw: report, + }), + ]; + + gates.forEach((gate, index) => { + const blockers = stringArray(gate.blockers); + const commands = stringArray(gate.commands); + records.push( + makeRecord("ops", WORKBENCH_LIVE_READINESS_REPORT_PATH, { + id: text(gate.id, `live-gate:${index + 1}`), + kind: "Launch gate", + title: text(gate.label ?? gate.id, `Launch gate ${index + 1}`), + summary: text(gate.summary, "Launch gate loaded from the deployment contract report."), + status: statusFrom(gate.status, "pending"), + facts: [ + { label: "gate status", value: text(gate.status, "unresolved") }, + { label: "blockers", value: blockers.join(", ") || "none" }, + { label: "next command", value: commands[0] ?? "not recorded" }, + { label: "command count", value: commands.length.toString() }, + { label: "evidence", value: text(gate.evidence) }, + ], + raw: gate, + }), + ); + }); + + [...ownerInputGroups.entries()].forEach(([group, names]) => { + records.push( + makeRecord("ops", WORKBENCH_LIVE_READINESS_REPORT_PATH, { + id: `owner-inputs:${group}`, + kind: "Owner input group", + title: group, + summary: `${names.length} owner-provided env name${names.length === 1 ? "" : "s"} must be configured outside the repo before public sharing.`, + status: "pending", + facts: [ + { label: "required names", value: names.join(", ") }, + { label: "values printed", value: "false" }, + { label: "setup command", value: "npm run flowchain:owner-env:template" }, + ], + raw: { group, names }, + }), + ); + }); + + sourceReports.forEach((sourceReport, index) => { + records.push( + makeRecord("ops", WORKBENCH_LIVE_READINESS_REPORT_PATH, { + id: `source-report:${text(sourceReport.fileName, `${index + 1}`)}`, + kind: "Readiness source report", + title: text(sourceReport.fileName, `source report ${index + 1}`), + summary: `${text(sourceReport.schema)} reported ${text(sourceReport.status)}.`, + status: statusFrom(sourceReport.status, "observed"), + facts: [ + { label: "schema", value: text(sourceReport.schema) }, + { label: "status", value: text(sourceReport.status) }, + { label: "generated", value: text(sourceReport.generatedAt) }, + ], + raw: sourceReport, + }), + ); + }); + + return records; +} + function buildHardwareSignalRecords(data: DashboardData, devnetState: unknown): WorkbenchRecord[] { const nativeSignals = collectionFrom(devnetState, [ "hardwareSignals", @@ -2518,6 +2968,7 @@ function buildRawJsonRecords( devnetState: unknown | null, devnetDashboardState: unknown | null, bridgeTestDeposit: unknown | null, + liveReadinessReport: unknown | null, explorerFallback: unknown | null, ): WorkbenchRecord[] { return [ @@ -2574,6 +3025,20 @@ function buildRawJsonRecords( ], raw: bridgeTestDeposit, }), + makeRecord("ops", WORKBENCH_LIVE_READINESS_REPORT_PATH, { + id: "raw-live-readiness", + kind: "Raw JSON", + title: WORKBENCH_LIVE_READINESS_REPORT_PATH, + summary: liveReadinessReport + ? "Live infrastructure readiness summary loaded for public launch gates." + : "Live infrastructure readiness summary was not loaded.", + status: liveReadinessReport ? "verified" : "unresolved", + facts: [ + { label: "schema", value: isRecord(liveReadinessReport) ? text(liveReadinessReport.schema) : "missing" }, + { label: "keys", value: topLevelKeys(liveReadinessReport) }, + ], + raw: liveReadinessReport, + }), makeRecord("devnet", WORKBENCH_EXPLORER_FALLBACK_PATH, { id: "raw-explorer-fallback", kind: "Raw JSON", @@ -2610,6 +3075,10 @@ function buildRawJsonRecords( raw: { health: controlPlane.health ?? null, state: controlPlane.state ?? null, + bridgeLiveReadiness: controlPlane.bridgeLiveReadiness ?? null, + pilotLifecycle: controlPlane.pilotLifecycle ?? null, + walletBalances: controlPlane.walletBalances ?? null, + walletTransfers: controlPlane.walletTransfers ?? null, rpc: controlPlane.rpc ?? null, error: controlPlane.error ?? null, }, @@ -2625,6 +3094,7 @@ function buildProvenanceRecords( devnetState: unknown | null, devnetDashboardState: unknown | null, bridgeTestDeposit: unknown | null, + liveReadinessReport: unknown | null, explorerFallback: unknown | null, ): WorkbenchRecord[] { return [ @@ -2703,6 +3173,20 @@ function buildProvenanceRecords( ], raw: bridgeTestDeposit, }), + makeRecord("ops", WORKBENCH_LIVE_READINESS_REPORT_PATH, { + id: "live-readiness-report", + kind: "Live readiness report", + title: WORKBENCH_LIVE_READINESS_REPORT_PATH, + summary: liveReadinessReport + ? "Generated summary of the public deployment contract and dependent infra readiness reports." + : "Generated live readiness report was not loaded.", + status: liveReadinessReport ? "verified" : "unresolved", + facts: [ + { label: "schema", value: isRecord(liveReadinessReport) ? text(liveReadinessReport.schema) : "missing" }, + { label: "source", value: "docs/agent-runs/live-product-infra-rpc" }, + ], + raw: liveReadinessReport, + }), makeRecord("devnet", WORKBENCH_EXPLORER_FALLBACK_PATH, { id: "flowchain-l1-explorer-fallback", kind: "Explorer fixture", @@ -2793,6 +3277,7 @@ export function buildWorkbenchSnapshot( devnetState?: unknown | null; devnetDashboardState?: unknown | null; bridgeTestDeposit?: unknown | null; + liveReadinessReport?: unknown | null; explorerFallback?: unknown | null; loadIssues?: string[]; } = {}, @@ -2809,6 +3294,7 @@ export function buildWorkbenchSnapshot( const controlPlaneState = extractControlPlaneState(controlPlane.state); const activeDevnetState = controlPlaneState ?? options.devnetState ?? null; const bridgeTestDeposit = options.bridgeTestDeposit ?? null; + const liveReadinessReport = options.liveReadinessReport ?? null; const rawExplorerFallback = rpcPayload(controlPlane, "raw_json_explorer_fallback"); const explorerFallback = options.explorerFallback ?? (isRecord(rawExplorerFallback?.raw) ? rawExplorerFallback.raw : null); const source: WorkbenchSource = controlPlane.status === "available" && controlPlaneState ? "control-plane" : "fixture-fallback"; @@ -2820,7 +3306,7 @@ export function buildWorkbenchSnapshot( transactions: buildTransactionRecords(data, activeDevnetState), mempool: buildMempoolRecords(activeDevnetState), accounts: buildAccountRecords(activeDevnetState), - balances: buildBalanceRecords(activeDevnetState), + balances: [...buildBalanceRecords(activeDevnetState), ...buildControlPlaneWalletBalanceRecords(controlPlane)], faucetEvents: buildFaucetEventRecords(activeDevnetState), walletMetadata: buildWalletMetadataRecords(activeDevnetState), tokenLaunches: buildTokenLaunchRecords(activeDevnetState), @@ -2846,6 +3332,7 @@ export function buildWorkbenchSnapshot( bridgeWithdrawals: buildBridgeRecords(activeDevnetState, "withdrawals", bridgeTestDeposit), bridgeReleases: [], realValuePilot: buildPilotRecords(controlPlane), + liveReadiness: buildLiveReadinessRecords(liveReadinessReport), errorsRecovery: [], provenance: [], hardwareSignals: buildHardwareSignalRecords(data, activeDevnetState), @@ -2913,6 +3400,7 @@ export function buildWorkbenchSnapshot( options.devnetState ?? null, options.devnetDashboardState ?? null, bridgeTestDeposit, + liveReadinessReport, explorerFallback, ); sections.rawJson = buildRawJsonRecords( @@ -2921,6 +3409,7 @@ export function buildWorkbenchSnapshot( options.devnetState ?? null, options.devnetDashboardState ?? null, bridgeTestDeposit, + liveReadinessReport, explorerFallback, ); const displayedSections = source === "control-plane" ? relabelDevnetRecordsAsControlPlane(sections, controlPlane) : sections; @@ -2939,8 +3428,13 @@ export function buildWorkbenchSnapshot( devnetState: options.devnetState ?? null, devnetDashboardState: options.devnetDashboardState ?? null, bridgeTestDeposit, + liveReadinessReport, explorerFallback, controlPlanePilotStatus: controlPlane.pilotStatus ?? null, + controlPlaneBridgeReadiness: controlPlane.bridgeLiveReadiness ?? null, + controlPlanePilotLifecycle: controlPlane.pilotLifecycle ?? null, + controlPlaneWalletBalances: controlPlane.walletBalances ?? null, + controlPlaneWalletTransfers: controlPlane.walletTransfers ?? null, controlPlaneHealth: controlPlane.health ?? null, controlPlaneState: controlPlane.state ?? null, controlPlaneRpc: controlPlane.rpc ?? null, @@ -2949,17 +3443,26 @@ export function buildWorkbenchSnapshot( } export async function fetchWorkbenchSnapshot(data: DashboardData): Promise { - const [controlPlane, devnetStateResult, devnetDashboardStateResult, bridgeTestDepositResult, explorerFallbackResult] = await Promise.all([ + const [ + controlPlane, + devnetStateResult, + devnetDashboardStateResult, + bridgeTestDepositResult, + liveReadinessResult, + explorerFallbackResult, + ] = await Promise.all([ probeControlPlane(), fetchOptionalJson(WORKBENCH_DEVNET_STATE_PATH), fetchOptionalJson(WORKBENCH_DEVNET_DASHBOARD_STATE_PATH), fetchOptionalJson(WORKBENCH_BRIDGE_TEST_DEPOSIT_PATH), + fetchOptionalJson(WORKBENCH_LIVE_READINESS_REPORT_PATH), fetchOptionalJson(WORKBENCH_EXPLORER_FALLBACK_PATH), ]); const loadIssues = [ devnetStateResult.error, devnetDashboardStateResult.error, bridgeTestDepositResult.error, + liveReadinessResult.error, explorerFallbackResult.error, ].filter((issue): issue is string => typeof issue === "string" && issue.length > 0); @@ -2968,6 +3471,7 @@ export async function fetchWorkbenchSnapshot(data: DashboardData): Promise - + - + , ); diff --git a/apps/dashboard/src/styles.css b/apps/dashboard/src/styles.css index 10025494..96f2781a 100644 --- a/apps/dashboard/src/styles.css +++ b/apps/dashboard/src/styles.css @@ -67,1459 +67,5553 @@ input { color: var(--ink); } -select { - min-height: 38px; - padding: 0 12px; +.wallet-app-shell { + position: relative; + display: grid; + grid-template-columns: 270px minmax(560px, 1fr) 385px; + min-height: 100dvh; + overflow: hidden; + color: #0a2147; + background: + radial-gradient(circle at 72% 18%, rgba(255, 255, 255, 0.82), transparent 27rem), + radial-gradient(circle at 5% 96%, rgba(20, 91, 255, 0.12), transparent 24rem), + linear-gradient(135deg, #fff9ee 0%, #f8efe1 46%, #fffaf0 100%); +} + +.wallet-app-shell::before { + position: fixed; + inset: 0; + pointer-events: none; + content: ""; + background-image: + linear-gradient(rgba(21, 42, 84, 0.035) 1px, transparent 1px), + linear-gradient(90deg, rgba(21, 42, 84, 0.024) 1px, transparent 1px); + background-size: 10px 10px, 10px 10px; + opacity: 0.45; +} + +.wallet-app-shell::after { + position: fixed; + right: -8vw; + bottom: -7vh; + left: -9vw; + height: 38vh; + pointer-events: none; + content: ""; + opacity: 0.38; + background: + repeating-linear-gradient(164deg, transparent 0 20px, rgba(19, 95, 255, 0.18) 21px 23px, transparent 25px 48px), + radial-gradient(ellipse at 20% 70%, rgba(10, 91, 255, 0.2), transparent 34rem); + filter: blur(0.2px); + transform: rotate(-4deg); } -table { - width: 100%; - border-collapse: collapse; - table-layout: fixed; +.wallet-app-sidebar, +.wallet-app-main, +.wallet-app-rightbar { + position: relative; + z-index: 1; } -th, -td { - border-bottom: 1px solid var(--line); - padding: 12px 14px; - text-align: left; - vertical-align: top; +.wallet-app-sidebar { + display: flex; + flex-direction: column; + gap: 28px; + padding: 28px 24px; } -th { - color: #555d55; - font-size: 0.72rem; - font-weight: 700; - text-transform: uppercase; +.wallet-brand { + display: inline-flex; + gap: 14px; + align-items: center; + color: #061d43; + text-decoration: none; } -td { - font-size: 0.86rem; +.wallet-brand strong { + font-family: Georgia, "Times New Roman", serif; + font-size: 2.1rem; + font-weight: 500; + letter-spacing: -0.03em; } -th, -td { - overflow-wrap: anywhere; +.wallet-flow-mark { + position: relative; + display: inline-block; + width: 42px; + height: 34px; + color: #0f5fff; } -small { - color: var(--muted); +.wallet-flow-mark::before, +.wallet-flow-mark::after, +.wallet-flow-mark span { + position: absolute; + left: 0; + width: 38px; + height: 12px; + content: ""; + border-top: 3px solid currentColor; + border-radius: 50%; + transform-origin: 50% 50%; } -.app-shell { - display: grid; - grid-template-columns: 252px minmax(0, 1fr); - min-height: 100dvh; +.wallet-flow-mark::before { + top: 6px; + transform: rotate(-18deg); } -.sidebar { - position: sticky; - top: 0; - display: flex; - flex-direction: column; - gap: 22px; - height: 100dvh; - padding: 18px 14px; - background: var(--sidebar); - color: #f6f8f2; - border-right: 1px solid #32382f; +.wallet-flow-mark span { + top: 14px; + transform: rotate(8deg); } -.brand-block { - display: grid; - grid-template-columns: 42px 1fr; - gap: 10px; - align-items: center; - padding: 4px 4px 14px; - border-bottom: 1px solid #343a33; +.wallet-flow-mark::after { + top: 21px; + transform: rotate(-10deg); } -.brand-mark { +.wallet-side-nav { display: grid; - place-items: center; - width: 38px; - height: 38px; - border: 1px solid #556054; - border-radius: 8px; - background: #252b26; - color: #d8efe5; + gap: 8px; + margin-top: 8px; } -.brand-kicker, -.eyebrow { +.wallet-side-nav::before, +.wallet-side-nav::after { display: block; - margin-bottom: 4px; - color: var(--subtle); - font-size: 0.71rem; - font-weight: 700; - text-transform: uppercase; -} - -.brand-block strong { - font-size: 1rem; + height: 1px; + margin: 12px 2px; + content: ""; + background: rgba(10, 33, 71, 0.14); } -.nav-list { - display: grid; - gap: 4px; -} - -.nav-link { +.wallet-side-nav button, +.wallet-side-nav a { display: grid; - grid-template-columns: 22px 1fr; - gap: 9px; + grid-template-columns: 26px 1fr; + gap: 12px; align-items: center; - min-height: 38px; - padding: 8px 10px; - color: var(--sidebar-muted); - text-decoration: none; + min-height: 50px; + padding: 0 18px; + color: #132b55; border: 1px solid transparent; - border-radius: 7px; - transition: background 140ms ease, border-color 140ms ease, color 140ms ease, transform 140ms ease; + border-radius: 8px; + background: transparent; + font-weight: 650; + text-align: left; + text-decoration: none; } -.nav-link:hover, -.nav-link.active { - background: #29302b; - border-color: #3c463e; - color: #f6f8f2; +.wallet-side-nav button.active, +.wallet-side-nav button:hover, +.wallet-side-nav a:hover { + color: #0f5fff; + border-color: rgba(10, 33, 71, 0.04); + background: rgba(255, 255, 255, 0.48); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.65); } -.sidebar-footer { - display: grid; - gap: 7px; +.wallet-collapse-button { + display: inline-flex; + gap: 10px; + align-items: center; + width: fit-content; margin-top: auto; - padding: 12px; - color: var(--sidebar-muted); - border: 1px solid #3a433b; - border-radius: 8px; - background: #232923; + padding: 10px 14px; + color: #33486b; + border-color: rgba(10, 33, 71, 0.12); + border-radius: 999px; + background: rgba(255, 255, 255, 0.32); } -.workspace { +.wallet-app-main { min-width: 0; + padding: 22px 16px 28px; } -.topbar { - position: sticky; - top: 0; - display: flex; +.wallet-app-topbar { + display: grid; + grid-template-columns: 180px minmax(260px, 1fr) 48px 240px; + gap: 22px; align-items: center; - justify-content: space-between; - gap: 18px; - padding: 14px 24px; - background: rgba(244, 245, 241, 0.92); - border-bottom: 1px solid var(--line); - backdrop-filter: blur(12px); -} - -.topbar h2 { - margin: 0; - font-size: 1.05rem; + margin-bottom: 20px; } -.topbar-meta { - display: flex; - flex-wrap: wrap; - gap: 8px; - justify-content: flex-end; +.wallet-network-switch, +.wallet-profile-pill, +.wallet-search, +.wallet-icon-button { + border: 1px solid rgba(10, 33, 71, 0.12); + background: rgba(255, 255, 255, 0.46); + box-shadow: + 0 14px 34px rgba(37, 31, 18, 0.06), + inset 0 1px 0 rgba(255, 255, 255, 0.72); + backdrop-filter: blur(16px); } -.topbar-meta span, -.provenance-chip, -.severity-label { +.wallet-network-switch { display: inline-flex; - align-items: center; - gap: 5px; - min-height: 24px; - padding: 4px 7px; - color: #4f584f; - border: 1px solid var(--line); - border-radius: 999px; - background: #f9faf6; - font-size: 0.72rem; - white-space: nowrap; -} - -.fixture-banner { - display: flex; gap: 10px; align-items: center; - margin: 18px 24px 0; - padding: 10px 12px; - color: #394139; - border: 1px solid #cfd7cd; - border-radius: 8px; - background: #f9faf6; - font-size: 0.86rem; -} - -.content { - width: min(100%, 1480px); - margin: 0 auto; - padding: 24px; + justify-content: center; + height: 50px; + padding: 0 16px; + border-radius: 14px; + font-weight: 720; } -.view-stack { - display: grid; - gap: 22px; +.wallet-network-switch .wallet-flow-mark { + width: 26px; + height: 24px; + color: #ffffff; + border-radius: 50%; + background: #0f5fff; } -.section-header { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 18px; - align-items: end; +.wallet-network-switch .wallet-flow-mark::before, +.wallet-network-switch .wallet-flow-mark::after, +.wallet-network-switch .wallet-flow-mark span { + left: 5px; + width: 16px; + height: 6px; + border-top-width: 2px; } -.section-header h1 { - margin: 0; - font-size: 1.8rem; - line-height: 1.15; +.wallet-network-switch .wallet-flow-mark::before { + top: 7px; } -.section-header p { - max-width: 76ch; - margin: 8px 0 0; - color: var(--muted); - line-height: 1.5; +.wallet-network-switch .wallet-flow-mark span { + top: 11px; } -.section-action { - min-width: min(520px, 100%); +.wallet-network-switch .wallet-flow-mark::after { + top: 15px; } -.workbench-header-actions { +.wallet-search { display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 10px; + grid-template-columns: 22px minmax(0, 1fr) auto; + gap: 12px; align-items: center; + height: 50px; + padding: 0 14px; + border-radius: 14px; } -.filter-row { - display: grid; - grid-template-columns: minmax(240px, 1fr) 150px; - gap: 10px; +.wallet-search input { + color: #0a2147; } -.search-box { - display: grid; - grid-template-columns: 18px 1fr; - gap: 8px; - align-items: center; - min-height: 38px; - padding: 0 10px; - border: 1px solid var(--line-strong); +.wallet-search kbd { + padding: 4px 7px; + color: #4d5d78; + border: 1px solid rgba(10, 33, 71, 0.14); border-radius: 7px; - background: var(--surface); + background: rgba(255, 255, 255, 0.62); + font-size: 0.73rem; } -.metric-grid { +.wallet-icon-button { + position: relative; display: grid; - grid-template-columns: repeat(5, minmax(0, 1fr)); - gap: 10px; + place-items: center; + width: 48px; + height: 48px; + border-radius: 50%; } -.flowmemory-hero { +.wallet-icon-button span { + position: absolute; + top: 9px; + right: 10px; + width: 8px; + height: 8px; + background: #0f5fff; + border-radius: 50%; +} + +.wallet-profile-pill { display: grid; - grid-template-columns: minmax(0, 1fr) minmax(280px, 0.34fr); - gap: 14px; - align-items: stretch; - border: 1px solid var(--line); - border-radius: 8px; - background: linear-gradient(135deg, #fbfcf8 0%, #edf4ef 100%); - box-shadow: var(--shadow); + grid-template-columns: 42px 1fr 18px; + grid-template-rows: auto auto; + column-gap: 10px; + align-items: center; + min-height: 54px; + padding: 8px 12px; + border-radius: 15px; + text-align: left; } -.flowmemory-hero-main { +.wallet-profile-pill > span { display: grid; - gap: 12px; - padding: 18px; + grid-row: 1 / span 2; + place-items: center; + width: 38px; + height: 38px; + color: #ffffff; + background: #0b2c75; + border-radius: 50%; + font-weight: 800; } -.flowmemory-hero-main h2 { - max-width: 780px; - margin: 0; - font-size: 1.48rem; - line-height: 1.14; +.wallet-profile-pill strong { + font-size: 0.95rem; } -.flowmemory-hero-main p { - max-width: 82ch; - margin: 0; - color: #465047; - line-height: 1.5; +.wallet-profile-pill small { + color: #61718e; } -.flowmemory-spine { - display: grid; - grid-template-columns: repeat(5, minmax(0, 1fr)); - gap: 8px; +.wallet-profile-pill svg { + grid-row: 1 / span 2; } -.flowmemory-spine span { - min-height: 34px; - padding: 8px 9px; - color: #284238; - border: 1px solid #bfd1c7; - border-radius: 7px; - background: rgba(251, 252, 248, 0.78); - font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; - font-size: 0.74rem; - text-align: center; +.wallet-portfolio-panel, +.wallet-side-card, +.wallet-action-grid button, +.wallet-action-grid a, +.wallet-action-panel { + border: 1px solid rgba(10, 33, 71, 0.1); + background: rgba(255, 255, 255, 0.34); + box-shadow: + 0 18px 44px rgba(50, 37, 18, 0.07), + inset 0 1px 0 rgba(255, 255, 255, 0.72); + backdrop-filter: blur(18px); } -.flowmemory-hero-side { +.wallet-portfolio-panel { display: grid; - gap: 1px; - min-width: 0; - padding: 1px; - border-left: 1px solid var(--line); + grid-template-columns: minmax(220px, 0.78fr) minmax(360px, 1.4fr); + gap: 18px; + min-height: 286px; + padding: 30px 34px 24px; + border-radius: 18px; } -.flowmemory-hero-side div { - display: grid; - gap: 5px; - align-content: center; - min-width: 0; - padding: 12px; - background: rgba(251, 252, 248, 0.72); +.wallet-portfolio-copy h1 { + margin: 0 0 22px; + color: #071d44; + font-family: Georgia, "Times New Roman", serif; + font-size: clamp(2.4rem, 4vw, 3.35rem); + font-weight: 500; + letter-spacing: -0.04em; } -.canary-hero { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(300px, 0.36fr); - gap: 14px; - align-items: stretch; - padding: 16px; - border: 1px solid #c9d5ce; - border-radius: 8px; - background: - linear-gradient(135deg, rgba(251, 252, 248, 0.94), rgba(229, 239, 235, 0.92)), - linear-gradient(90deg, rgba(47, 125, 107, 0.18), transparent); - box-shadow: var(--shadow); +.wallet-portfolio-copy > span { + display: inline-flex; + gap: 10px; + align-items: center; + color: #65728a; + font-size: 1.05rem; + font-weight: 650; } -.canary-hero h2 { - max-width: 760px; - margin: 0; - font-size: 1.52rem; - line-height: 1.14; +.wallet-portfolio-copy > strong { + display: block; + margin-top: 8px; + color: #071d44; + font-family: Georgia, "Times New Roman", serif; + font-size: clamp(2.6rem, 5vw, 4.1rem); + font-weight: 500; + line-height: 1; + letter-spacing: -0.05em; } -.canary-hero p { - max-width: 84ch; - margin: 10px 0 0; - color: #465047; - line-height: 1.5; +.wallet-portfolio-copy p { + display: flex; + gap: 18px; + align-items: center; + margin: 18px 0 0; + color: #607089; } -.canary-read-window { - display: grid; - grid-template-columns: 28px 1fr; - gap: 10px; - align-items: start; - padding: 12px; - border: 1px solid #bfd1c7; - border-radius: 8px; - background: rgba(251, 252, 248, 0.78); +.wallet-portfolio-copy b { + color: #0aa873; + font-weight: 700; } -.canary-read-window dl { - display: grid; - gap: 9px; - margin: 0; +.wallet-chart-card { + min-width: 0; } -.canary-operator-strip { - display: grid; - grid-template-columns: 1.15fr 1fr 1.1fr; - gap: 10px; +.wallet-chart-tabs { + display: flex; + gap: 18px; + justify-content: flex-end; + margin-bottom: 8px; } -.canary-operator-strip article { - display: grid; - gap: 7px; - min-width: 0; - padding: 12px; - border: 1px solid #c9d5ce; - border-radius: 8px; - background: #fbfcf8; - box-shadow: var(--shadow); +.wallet-chart-tabs button { + min-width: 42px; + min-height: 34px; + color: #53637d; + border: 0; + border-radius: 10px; + background: transparent; + font-weight: 700; } -.canary-operator-strip span, -.canary-operator-strip strong { - overflow-wrap: anywhere; +.wallet-chart-tabs button.active { + color: #071d44; + border: 1px solid rgba(10, 33, 71, 0.1); + background: rgba(255, 255, 255, 0.56); } -.canary-operator-strip span { - color: var(--muted); - font-size: 0.72rem; - font-weight: 800; - text-transform: uppercase; +.wallet-chart-card svg { + width: 100%; + height: 190px; + overflow: visible; } -.canary-boundaries { - display: grid; - gap: 8px; - padding-top: 12px; +.wallet-chart-fill { + fill: url(#walletChartFill); } -.canary-boundaries span { - padding: 9px 10px; - color: #4d564d; - border: 1px solid var(--line); - border-radius: 7px; - background: #f8f9f4; - line-height: 1.35; +.wallet-chart-line { + fill: none; + stroke: #0f5fff; + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 4; } -.metric-tile, -.panel, -.table-panel, -.rootfield-tile, -.lane-tile, -.node-tile, -.alert-row, -.json-panel { - border: 1px solid var(--line); - border-radius: 8px; - background: var(--surface); - box-shadow: var(--shadow); +.wallet-chart-grid path { + stroke: rgba(10, 33, 71, 0.08); + stroke-width: 1; } -.metric-tile { +.wallet-chart-axis { display: grid; - gap: 8px; - padding: 14px; + grid-template-columns: repeat(7, 1fr); + color: #61718e; + font-size: 0.78rem; + font-weight: 650; } -.metric-tile > span { - color: var(--muted); - font-size: 0.75rem; - font-weight: 700; - text-transform: uppercase; +.wallet-action-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); + gap: 16px; + margin-top: 18px; } -.metric-tile strong { - font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; - font-size: 1.65rem; +.wallet-action-grid button, +.wallet-action-grid a { + display: grid; + grid-template-columns: 52px 1fr; + grid-template-rows: auto auto; + column-gap: 14px; + align-items: center; + min-height: 82px; + padding: 14px 18px; + color: #071d44; + border-radius: 13px; + text-align: left; + text-decoration: none; } -.metric-tile div { - display: flex; - flex-wrap: wrap; - gap: 7px; - align-items: center; +.wallet-action-grid button:hover, +.wallet-action-grid a:hover, +.wallet-side-card button:hover, +.wallet-side-card a:hover { + transform: translateY(-1px); } -.overview-grid { +.wallet-action-grid span { display: grid; - grid-template-columns: minmax(0, 1.4fr) minmax(290px, 0.6fr); - gap: 14px; + grid-row: 1 / span 2; + place-items: center; + width: 44px; + height: 44px; + color: #ffffff; + background: #0f5fff; + border-radius: 50%; } -.panel { - min-width: 0; - padding: 14px; +.wallet-action-grid strong { + font-size: 1rem; } -.panel-wide { - grid-column: span 1; +.wallet-action-grid small { + color: #697893; } -.panel-side-bottom { - grid-column: 2; +.wallet-assets-section, +.wallet-activity-section { + margin-top: 26px; } -.panel-heading { +.wallet-section-title { display: flex; align-items: center; justify-content: space-between; - gap: 10px; - padding-bottom: 10px; - border-bottom: 1px solid var(--line); + margin-bottom: 12px; } -.panel-heading div, -.tile-heading, -.record-title, -.json-panel-header { - display: flex; - gap: 8px; - align-items: center; - min-width: 0; +.wallet-section-title h2, +.wallet-side-card h2, +.wallet-action-panel h2 { + margin: 0; + color: #071d44; + font-family: Georgia, "Times New Roman", serif; + font-size: 1.35rem; + font-weight: 540; + letter-spacing: -0.02em; } -.panel-heading h2 { - margin: 0; - font-size: 0.98rem; +.wallet-section-title button, +.wallet-side-title button, +.wallet-text-action { + display: inline-flex; + gap: 8px; + align-items: center; + color: #0f5fff; + border: 0; + background: transparent; + font-weight: 700; } -.panel-heading > span { - color: var(--muted); - font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; - font-size: 0.78rem; +.wallet-asset-table, +.wallet-activity-table { + overflow: hidden; + border: 1px solid rgba(10, 33, 71, 0.1); + border-radius: 14px; + background: rgba(255, 255, 255, 0.2); } -.record-list, -.compact-list, -.alert-list { +.wallet-asset-table > div, +.wallet-activity-table > div { display: grid; + align-items: center; + min-height: 48px; + padding: 0 16px; + border-bottom: 1px solid rgba(10, 33, 71, 0.09); } -.status-strip { - display: flex; - flex-wrap: wrap; - gap: 8px; - padding: 10px 0 2px; +.wallet-asset-table > div { + grid-template-columns: minmax(190px, 1.25fr) 1fr 1fr 0.7fr; } -.status-strip span { - display: inline-flex; - gap: 6px; - align-items: center; - padding: 3px 6px 3px 3px; - border: 1px solid var(--line); - border-radius: 999px; - background: #f8f9f4; +.wallet-activity-table > div { + grid-template-columns: 1.2fr 0.9fr 0.8fr 1.2fr 0.9fr 0.8fr; } -.status-strip strong { - font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; - font-size: 0.74rem; +.wallet-asset-table > div:last-child, +.wallet-activity-table > div:last-child { + border-bottom: 0; } -.record-row { - display: grid; - grid-template-columns: minmax(0, 1fr) 270px; - gap: 16px; - padding: 14px 0; - border-bottom: 1px solid var(--line); +.wallet-asset-table > div:first-child, +.wallet-activity-table > div:first-child { + min-height: 38px; + color: #687895; + font-size: 0.72rem; + font-weight: 760; } -.record-row:last-child { - border-bottom: 0; +.wallet-asset-table span, +.wallet-activity-table span { + min-width: 0; + overflow-wrap: anywhere; } -.record-row p, -.alert-row p { - margin: 8px 0; - color: #3f483f; - line-height: 1.45; +.wallet-asset-table div:not(:first-child) > span:first-child { + display: grid; + grid-template-columns: 36px minmax(0, 1fr); + grid-template-rows: auto auto; + column-gap: 12px; + align-items: center; } -.record-facts, -.definition-grid, -.lane-stats, -.alert-facts { +.wallet-asset-table i { display: grid; - gap: 10px; - margin: 0; + grid-row: 1 / span 2; + place-items: center; + width: 30px; + height: 30px; + color: #ffffff; + background: #0f5fff; + border-radius: 50%; + font-style: normal; + font-weight: 800; } -.record-facts { - grid-template-columns: repeat(2, minmax(0, 1fr)); +.wallet-asset-table strong, +.wallet-asset-table b, +.wallet-activity-table b { + color: #071d44; + font-weight: 720; } -.record-facts div, -.definition-grid div, -.lane-stats div, -.alert-facts div { - min-width: 0; +.wallet-asset-table small { + color: #72809b; } -dt { - color: var(--subtle); - font-size: 0.68rem; - font-weight: 700; - text-transform: uppercase; +.wallet-change-up { + color: #0aa873 !important; } -dd { - margin: 3px 0 0; - overflow-wrap: anywhere; +.wallet-change-flat { + color: #687895 !important; } -.compact-list article { - display: grid; - grid-template-columns: auto minmax(0, 1fr); - gap: 9px; - align-items: start; - padding: 12px 0; - border-bottom: 1px solid var(--line); +.wallet-activity-table b { + display: inline-flex; + align-items: center; + justify-content: center; + width: fit-content; + min-height: 24px; + padding: 0 10px; + color: #0a8f65; + border: 1px solid rgba(10, 168, 115, 0.22); + border-radius: 999px; + background: rgba(10, 168, 115, 0.08); + font-size: 0.78rem; } -.compact-list article:last-child { - border-bottom: 0; +.wallet-app-rightbar { + display: flex; + flex-direction: column; + gap: 18px; + padding: 88px 28px 28px 8px; } -.compact-list strong, -.compact-list small { +.wallet-side-card { + overflow: hidden; + padding: 24px; + border-radius: 18px; +} + +.wallet-account-card > span { display: block; + margin-top: 20px; + color: #61718e; + font-size: 0.82rem; + font-weight: 650; +} + +.wallet-copy-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 38px 38px; + gap: 8px; + align-items: center; + padding: 8px 0 18px; + border-bottom: 1px solid rgba(10, 33, 71, 0.1); +} + +.wallet-copy-row strong { overflow-wrap: anywhere; } -.contract-event-list { +.wallet-copy-row button, +.wallet-account-card > button { + border: 0; + background: transparent; +} + +.wallet-copy-row button { display: grid; + place-items: center; + width: 38px; + height: 38px; + color: #314568; } -.contract-event-list article { +.wallet-account-card > button { display: grid; - gap: 9px; - padding: 12px 0; - border-bottom: 1px solid var(--line); + grid-template-columns: 28px 1fr auto 18px; + gap: 12px; + align-items: center; + width: 100%; + min-height: 58px; + padding: 0; + color: #071d44; + text-align: left; } -.contract-event-list article:last-child { - border-bottom: 0; +.wallet-account-card > button small, +.wallet-side-title b { + color: #0aa873; } -.contract-event-list article > div { +.wallet-side-title { display: flex; - gap: 8px; align-items: center; - min-width: 0; + justify-content: space-between; + gap: 16px; } -.contract-event-list strong { - overflow-wrap: anywhere; - font-size: 0.84rem; +.wallet-network-facts { + display: grid; + gap: 16px; + margin: 22px 0; } -.contract-event-list dl { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 8px; +.wallet-network-facts div { + display: flex; + justify-content: space-between; + gap: 18px; +} + +.wallet-network-facts dt { + color: #61718e; +} + +.wallet-network-facts dd { margin: 0; + color: #071d44; + font-weight: 750; } -.bundle-grid { +.wallet-bridge-card { display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 10px; - padding-top: 14px; + gap: 18px; } -.bundle-grid article { +.wallet-tester-card { display: grid; - gap: 8px; - min-width: 0; - padding: 12px; - border: 1px solid var(--line); - border-radius: 7px; - background: #f7f8f4; + gap: 12px; } -.bundle-grid strong, -.bundle-grid small { - display: block; +.wallet-tester-card small { overflow-wrap: anywhere; + color: #61718e; + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.72rem; + line-height: 1.38; } -.block-strip { +.wallet-bridge-card div { display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 10px; - padding-top: 14px; + grid-template-columns: 28px 1fr; + column-gap: 12px; + align-items: center; } -.block-strip article { - display: grid; - gap: 7px; - padding: 12px; - border: 1px solid var(--line); - border-radius: 7px; - background: #f7f8f4; +.wallet-bridge-card div svg { + color: #0f5fff; } -.workbench-command-center { +.wallet-bridge-card p { + grid-column: 2; + margin: 4px 0 0; + color: #697893; +} + +.wallet-bridge-card a { + display: inline-flex; + gap: 10px; + align-items: center; + justify-content: center; + min-height: 46px; + color: #ffffff; + border-radius: 8px; + background: #0f5fff; + font-weight: 750; + text-decoration: none; +} + +.wallet-watchlist-card { display: grid; - grid-template-columns: minmax(0, 1.08fr) minmax(360px, 0.92fr); - gap: 14px; + gap: 16px; } -.workbench-boundary-strip { +.wallet-watch-row { display: grid; - grid-template-columns: 1.05fr 1.15fr 1.2fr; + grid-template-columns: 34px 1fr auto auto 52px; gap: 10px; + align-items: center; } -.workbench-boundary-strip article { +.wallet-watch-row span { display: grid; - gap: 6px; - min-width: 0; - padding: 12px; - border: 1px solid #cfd7cd; - border-radius: 8px; - background: #f9faf6; + place-items: center; + width: 30px; + height: 30px; + color: #ffffff; + background: #0f5fff; + border-radius: 50%; + font-weight: 800; } -.workbench-boundary-strip strong { - font-size: 0.82rem; +.wallet-watch-row small { + color: #071d44; + font-weight: 650; } -.workbench-boundary-strip span { - color: #4c554c; - font-size: 0.84rem; - line-height: 1.42; +.wallet-watch-row b { + color: #0aa873; } -.pilot-status-panel article { - display: grid; - gap: 14px; - min-width: 0; - padding: 14px; - border: 1px solid #b8c7c0; - border-radius: 8px; - background: #f4f8f6; +.wallet-watch-row i { + height: 24px; + background: + linear-gradient(135deg, transparent 10%, #0f5fff 11% 14%, transparent 15% 28%, #0f5fff 29% 32%, transparent 33% 48%, #0f5fff 49% 53%, transparent 54% 100%); } -.pilot-status-body { - display: grid; - grid-template-columns: minmax(0, 0.92fr) minmax(320px, 1.08fr); - gap: 14px; - align-items: start; +.wallet-action-panel { + position: fixed; + z-index: 6; + right: 32px; + bottom: 30px; + width: min(520px, calc(100vw - 32px)); + max-height: min(78dvh, 720px); + overflow: auto; + padding: 22px; + border-radius: 20px; } -.pilot-status-body h3 { - margin: 4px 0 8px; - font-size: 1.35rem; - text-transform: capitalize; +.wallet-action-panel header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + margin-bottom: 18px; } -.pilot-status-body p { - margin: 0; - color: #3f483f; - line-height: 1.48; +.wallet-action-panel header span { + color: #0f5fff; + font-size: 0.75rem; + font-weight: 800; + text-transform: uppercase; } -.product-surface-grid { - display: grid; - grid-template-columns: 1.2fr 1fr 1.2fr; - gap: 10px; +.wallet-action-panel header button { + min-height: 36px; + padding: 0 12px; + border-color: rgba(10, 33, 71, 0.14); + background: rgba(255, 255, 255, 0.5); } -.product-surface { +.wallet-panel-form { display: grid; - grid-template-columns: 34px minmax(0, 1fr) auto auto; - gap: 10px; - align-items: start; - min-width: 0; - min-height: 118px; - padding: 12px; - text-align: left; - transition: background 140ms ease, border-color 140ms ease, transform 140ms ease; + gap: 14px; } -.product-surface:hover { - border-color: #9fb4a8; - background: #eaf1eb; +.wallet-tester-panel { + display: grid; + gap: 16px; } -.product-surface-icon { +.wallet-tester-readiness { display: grid; - place-items: center; - width: 32px; - height: 32px; - color: #235f52; - border: 1px solid #bfd1c7; - border-radius: 7px; - background: #edf5ef; + gap: 12px; + padding: 14px; + border: 1px solid rgba(10, 33, 71, 0.1); + border-radius: 13px; + background: rgba(255, 255, 255, 0.34); } -.product-surface strong, -.product-surface small, -.product-surface code { +.wallet-tester-readiness strong, +.wallet-tester-readiness span, +.wallet-tester-readiness p { display: block; overflow-wrap: anywhere; } -.product-surface small { - margin-top: 4px; - line-height: 1.36; +.wallet-tester-readiness strong { + color: #071d44; } -.product-surface b { - color: var(--ink); - font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; - font-size: 1.1rem; - line-height: 1.1; +.wallet-tester-readiness span { + margin-top: 5px; + color: #61718e; + line-height: 1.42; } -.workbench-node-body { +.wallet-tester-readiness dl { display: grid; - gap: 16px; - padding-top: 14px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; + margin: 0; } -.workbench-node-body h3 { - margin: 0 0 7px; - font-size: 1.22rem; +.wallet-tester-readiness dl div { + min-width: 0; + padding: 9px; + border: 1px solid rgba(10, 33, 71, 0.1); + border-radius: 10px; + background: rgba(255, 255, 255, 0.38); } -.workbench-node-body p, -.workbench-section-detail, -.boundary-copy p { +.wallet-tester-readiness dt { + color: #65728a; + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; +} + +.wallet-tester-readiness dd { + margin: 4px 0 0; + overflow-wrap: anywhere; + color: #071d44; + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.82rem; +} + +.wallet-tester-readiness p { margin: 0; - color: #465047; - line-height: 1.48; + color: #7a571b; + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.72rem; + line-height: 1.38; } -.workbench-fact-grid { +.wallet-tester-links { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; - margin: 0; } -.workbench-fact-grid div { - min-width: 0; - padding: 10px; - border: 1px solid var(--line); - border-radius: 7px; - background: #f7f8f4; +.wallet-tester-links a { + display: inline-flex; + gap: 8px; + align-items: center; + justify-content: center; + min-height: 42px; + padding: 0 12px; + color: #071d44; + border: 1px solid rgba(10, 33, 71, 0.12); + border-radius: 10px; + background: rgba(255, 255, 255, 0.42); + font-weight: 780; + text-decoration: none; } -.setup-step-list { - display: grid; - padding-top: 6px; +.wallet-tester-links a:hover { + border-color: rgba(15, 95, 255, 0.34); + background: rgba(255, 255, 255, 0.68); } -.setup-step-list article { +.wallet-panel-form label { display: grid; - grid-template-columns: auto minmax(0, 1fr); - gap: 9px; - align-items: start; - padding: 11px 0; - border-bottom: 1px solid var(--line); + gap: 7px; + color: #53637d; + font-weight: 700; } -.setup-step-list .status-badge { - align-self: start; +.wallet-panel-form input:not([type="checkbox"]) { + min-height: 46px; + padding: 0 13px; + border: 1px solid rgba(10, 33, 71, 0.14); + border-radius: 10px; + background: rgba(255, 255, 255, 0.46); } -.setup-step-list article:last-child { - border-bottom: 0; +.wallet-panel-form button, +.wallet-receive-panel button { + display: inline-flex; + gap: 9px; + align-items: center; + justify-content: center; + min-height: 44px; + padding: 0 16px; + color: #ffffff; + border: 0; + border-radius: 9px; + background: #0f5fff; + font-weight: 760; } -.setup-step-list strong, -.setup-step-list code, -.setup-step-list small { - display: block; - overflow-wrap: anywhere; +.wallet-panel-form button:disabled { + cursor: not-allowed; + opacity: 0.55; } -code { - width: fit-content; +.wallet-inline-check { + grid-template-columns: 18px 1fr; + align-items: center; + color: #071d44 !important; +} + +.wallet-swap-route { + display: grid; + grid-template-columns: 1fr 42px 1fr; + gap: 10px; + align-items: center; +} + +.wallet-swap-route span { + display: grid; + place-items: center; + min-height: 48px; + border: 1px solid rgba(10, 33, 71, 0.12); + border-radius: 12px; + background: rgba(255, 255, 255, 0.38); + font-weight: 800; +} + +.wallet-receive-panel { + display: grid; + gap: 16px; +} + +.wallet-receive-panel p { + margin: 0; + padding: 13px; + border: 1px solid rgba(10, 33, 71, 0.12); + border-radius: 10px; + background: rgba(255, 255, 255, 0.42); + overflow-wrap: anywhere; + color: #071d44; + font-weight: 700; +} + +.wallet-qr-block { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + width: 156px; + height: 156px; + padding: 14px; + border: 1px solid rgba(10, 33, 71, 0.16); + border-radius: 18px; + background: + linear-gradient(90deg, rgba(15, 95, 255, 0.13) 50%, transparent 50%) 0 0 / 28px 28px, + linear-gradient(rgba(15, 95, 255, 0.2) 50%, transparent 50%) 0 0 / 22px 22px, + rgba(255, 255, 255, 0.5); +} + +.wallet-qr-block span { + border: 8px solid #071d44; + border-radius: 7px; +} + +.wallet-panel-list { + display: grid; + gap: 10px; +} + +.wallet-panel-list article { + display: grid; + grid-template-columns: 1fr auto; + gap: 5px 12px; + padding: 13px; + border: 1px solid rgba(10, 33, 71, 0.1); + border-radius: 12px; + background: rgba(255, 255, 255, 0.35); +} + +.wallet-panel-list small { + overflow-wrap: anywhere; +} + +.wallet-panel-list b { + color: #0aa873; +} + +.wallet-security-grid { + display: grid; + gap: 12px; +} + +.wallet-security-grid article, +.wallet-empty-panel { + display: grid; + gap: 8px; + padding: 14px; + border: 1px solid rgba(10, 33, 71, 0.1); + border-radius: 13px; + background: rgba(255, 255, 255, 0.34); +} + +.wallet-security-grid svg, +.wallet-empty-panel svg { + color: #0f5fff; +} + +.wallet-empty-panel strong { + color: #071d44; +} + +.wallet-toast { + position: fixed; + z-index: 7; + left: 50%; + bottom: 24px; + display: inline-flex; + gap: 10px; + align-items: center; + max-width: min(640px, calc(100vw - 28px)); + margin: 0; + padding: 12px 16px; + color: #071d44; + border: 1px solid rgba(10, 33, 71, 0.13); + border-radius: 999px; + background: rgba(255, 255, 255, 0.78); + box-shadow: 0 18px 42px rgba(50, 37, 18, 0.12); + transform: translateX(-50%); + backdrop-filter: blur(18px); +} + +@media (max-width: 1220px) { + .wallet-app-shell { + grid-template-columns: 220px minmax(0, 1fr); + overflow: auto; + } + + .wallet-app-rightbar { + grid-column: 2; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + padding: 0 24px 28px 16px; + } + + .wallet-app-topbar { + grid-template-columns: 170px minmax(0, 1fr) 48px; + } + + .wallet-profile-pill { + grid-column: 1 / -1; + width: min(280px, 100%); + justify-self: end; + } +} + +@media (max-width: 860px) { + .wallet-app-shell { + display: block; + min-height: 100dvh; + overflow: auto; + } + + .wallet-app-sidebar { + position: relative; + display: block; + padding: 20px 18px 0; + } + + .wallet-side-nav { + display: flex; + gap: 8px; + overflow-x: auto; + margin-top: 16px; + padding-bottom: 8px; + } + + .wallet-side-nav::before, + .wallet-side-nav::after, + .wallet-collapse-button { + display: none; + } + + .wallet-side-nav button, + .wallet-side-nav a { + display: inline-flex; + grid-template-columns: none; + min-width: max-content; + min-height: 42px; + padding: 0 13px; + } + + .wallet-app-main { + padding: 18px; + } + + .wallet-app-topbar { + grid-template-columns: 1fr 48px; + gap: 12px; + } + + .wallet-network-switch, + .wallet-search { + grid-column: 1 / -1; + } + + .wallet-profile-pill { + grid-column: 1; + justify-self: stretch; + } + + .wallet-portfolio-panel, + .wallet-app-rightbar { + grid-template-columns: 1fr; + } + + .wallet-portfolio-panel { + padding: 24px 20px; + } + + .wallet-action-grid { + gap: 10px; + } + + .wallet-asset-table, + .wallet-activity-table { + overflow-x: auto; + } + + .wallet-asset-table > div { + min-width: 680px; + } + + .wallet-activity-table > div { + min-width: 860px; + } + + .wallet-app-rightbar { + padding: 0 18px 24px; + } + + .wallet-action-panel { + right: 12px; + bottom: 12px; + left: 12px; + width: auto; + } + + .wallet-tester-readiness dl { + grid-template-columns: 1fr; + } +} + +input[type="checkbox"] { + width: 16px; + height: 16px; + accent-color: var(--accent); +} + +select { + min-height: 38px; + padding: 0 12px; +} + +table { + width: 100%; + border-collapse: collapse; + table-layout: fixed; +} + +th, +td { + border-bottom: 1px solid var(--line); + padding: 12px 14px; + text-align: left; + vertical-align: top; +} + +th { + color: #555d55; + font-size: 0.72rem; + font-weight: 700; + text-transform: uppercase; +} + +td { + font-size: 0.86rem; +} + +th, +td { + overflow-wrap: anywhere; +} + +small { + color: var(--muted); +} + +.app-shell { + display: grid; + grid-template-columns: 252px minmax(0, 1fr); + min-height: 100dvh; +} + +.sidebar { + position: sticky; + top: 0; + display: flex; + flex-direction: column; + gap: 22px; + height: 100dvh; + padding: 18px 14px; + background: var(--sidebar); + color: #f6f8f2; + border-right: 1px solid #32382f; +} + +.brand-block { + display: grid; + grid-template-columns: 42px 1fr; + gap: 10px; + align-items: center; + padding: 4px 4px 14px; + border-bottom: 1px solid #343a33; +} + +.brand-mark { + display: grid; + place-items: center; + width: 38px; + height: 38px; + border: 1px solid #556054; + border-radius: 8px; + background: #252b26; + color: #d8efe5; +} + +.brand-kicker, +.eyebrow { + display: block; + margin-bottom: 4px; + color: var(--subtle); + font-size: 0.71rem; + font-weight: 700; + text-transform: uppercase; +} + +.brand-block strong { + font-size: 1rem; +} + +.nav-list { + display: grid; + gap: 4px; +} + +.nav-link { + display: grid; + grid-template-columns: 22px 1fr; + gap: 9px; + align-items: center; + min-height: 38px; + padding: 8px 10px; + color: var(--sidebar-muted); + text-decoration: none; + border: 1px solid transparent; + border-radius: 7px; + transition: background 140ms ease, border-color 140ms ease, color 140ms ease, transform 140ms ease; +} + +.nav-link:hover, +.nav-link.active { + background: #29302b; + border-color: #3c463e; + color: #f6f8f2; +} + +.sidebar-footer { + display: grid; + gap: 7px; + margin-top: auto; + padding: 12px; + color: var(--sidebar-muted); + border: 1px solid #3a433b; + border-radius: 8px; + background: #232923; +} + +.workspace { + min-width: 0; +} + +.topbar { + position: sticky; + top: 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + padding: 14px 24px; + background: rgba(244, 245, 241, 0.92); + border-bottom: 1px solid var(--line); + backdrop-filter: blur(12px); +} + +.topbar h2 { + margin: 0; + font-size: 1.05rem; +} + +.topbar-meta { + display: flex; + flex-wrap: wrap; + gap: 8px; + justify-content: flex-end; +} + +.topbar-meta span, +.provenance-chip, +.severity-label { + display: inline-flex; + align-items: center; + gap: 5px; + min-height: 24px; + padding: 4px 7px; + color: #4f584f; + border: 1px solid var(--line); + border-radius: 999px; + background: #f9faf6; + font-size: 0.72rem; + white-space: nowrap; +} + +.fixture-banner { + display: flex; + gap: 10px; + align-items: center; + margin: 18px 24px 0; + padding: 10px 12px; + color: #394139; + border: 1px solid #cfd7cd; + border-radius: 8px; + background: #f9faf6; + font-size: 0.86rem; +} + +.content { + width: min(100%, 1480px); + margin: 0 auto; + padding: 24px; +} + +.flowchain-bridge-page { + position: relative; + min-height: 100dvh; + overflow: hidden; + background: + radial-gradient(circle at 50% 18%, rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0) 38rem), + radial-gradient(circle at 18% 62%, rgba(41, 123, 255, 0.18), rgba(41, 123, 255, 0) 22rem), + linear-gradient(180deg, #f4ebdf 0%, #efe2d1 48%, #ead9c5 100%); + color: #1c1a17; +} + +.flowchain-bridge-page::before { + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + content: ""; + opacity: 0.42; + background-image: + linear-gradient(rgba(58, 39, 18, 0.035) 1px, transparent 1px), + linear-gradient(90deg, rgba(58, 39, 18, 0.025) 1px, transparent 1px); + background-size: 9px 9px, 11px 11px; + mix-blend-mode: multiply; +} + +.flowchain-bridge-page::after { + position: fixed; + inset: auto -12vw 16dvh -12vw; + z-index: 0; + height: 27dvh; + pointer-events: none; + content: ""; + opacity: 0.9; + background: + radial-gradient(ellipse at 12% 54%, rgba(0, 77, 208, 0.52), rgba(0, 113, 255, 0.18) 34%, transparent 67%), + radial-gradient(ellipse at 42% 44%, rgba(0, 94, 255, 0.72), rgba(0, 111, 255, 0.34) 28%, transparent 66%), + radial-gradient(ellipse at 76% 55%, rgba(0, 91, 229, 0.58), rgba(0, 127, 255, 0.22) 32%, transparent 68%); + filter: blur(10px) saturate(1.12); + transform: rotate(-3deg) skewX(-11deg); +} + +.bridge-flow-ribbon { + position: fixed; + inset: auto -10vw 26dvh -10vw; + z-index: 0; + height: 19dvh; + pointer-events: none; + opacity: 0.86; + background: + linear-gradient(92deg, transparent 0%, rgba(0, 95, 255, 0.2) 14%, rgba(12, 103, 255, 0.72) 42%, rgba(21, 127, 255, 0.7) 64%, transparent 100%), + radial-gradient(ellipse at 35% 45%, rgba(11, 82, 213, 0.85), rgba(29, 130, 255, 0.18) 42%, transparent 70%); + border-radius: 999px 18% 999px 28%; + filter: blur(3px); + transform: rotate(4deg) skewX(-18deg); +} + +.flowchain-bridge-nav, +.flowchain-bridge-main { + position: relative; + z-index: 1; +} + +.flowchain-bridge-nav { + display: flex; + align-items: center; + justify-content: space-between; + gap: 24px; + width: min(100%, 1720px); + margin: 0 auto; + padding: 24px 34px 0; +} + +.flowchain-brand, +.flowchain-wallet-pill { + display: inline-flex; + align-items: center; + text-decoration: none; +} + +.flowchain-brand { + gap: 10px; + color: #17213a; +} + +.flowchain-brand strong { + font-family: ui-serif, Georgia, "Times New Roman", serif; + font-size: clamp(1.45rem, 2.4vw, 2rem); + font-weight: 600; +} + +.flowchain-brand-mark { + position: relative; + width: 52px; + height: 30px; +} + +.flowchain-brand-mark, +.flowchain-brand-mark span { + display: block; +} + +.flowchain-brand-mark::before, +.flowchain-brand-mark::after, +.flowchain-brand-mark span { + position: absolute; + left: 0; + width: 48px; + height: 12px; + content: ""; + border: solid #256bda; + border-width: 3px 0 0; + border-radius: 50%; + transform-origin: left center; +} + +.flowchain-brand-mark::before { + top: 4px; + transform: rotate(15deg); +} + +.flowchain-brand-mark span { + top: 11px; + border-color: #1d54b6; + transform: rotate(-8deg); +} + +.flowchain-brand-mark::after { + top: 18px; + width: 36px; + border-color: #5ba2ff; + transform: rotate(20deg); +} + +.flowchain-wallet-pill { + gap: 9px; + min-height: 44px; + padding: 0 16px; + border: 1px solid rgba(98, 76, 52, 0.14); + border-radius: 8px; + background: rgba(248, 241, 231, 0.84); + box-shadow: 0 14px 34px rgba(44, 31, 17, 0.13); + color: #30281f; + font-size: 0.9rem; + backdrop-filter: blur(18px); + transition: transform 180ms ease, border-color 180ms ease, background 180ms ease; +} + +.flowchain-wallet-pill:hover { + border-color: rgba(37, 107, 218, 0.32); + background: rgba(255, 250, 243, 0.92); +} + +.wallet-orb { + width: 19px; + height: 19px; + border-radius: 50%; + background: + radial-gradient(circle at 36% 32%, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0) 38%), + linear-gradient(135deg, #79c2ff, #0b63ff 70%); + box-shadow: inset 0 0 10px rgba(255, 255, 255, 0.52), 0 7px 16px rgba(0, 91, 255, 0.28); +} + +.flowchain-bridge-main { + display: grid; + gap: 20px; + width: min(100%, 1540px); + margin: 0 auto; + padding: 14px 34px 46px; +} + +.bridge-title-block { + display: grid; + justify-items: center; + text-align: center; +} + +.bridge-title-block span { + color: rgba(49, 40, 31, 0.68); + font-size: 0.76rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.bridge-title-block h1 { + margin: 8px 0 8px; + color: #211b16; + font-family: ui-serif, Georgia, "Times New Roman", serif; + font-size: clamp(3.05rem, 5.7vw, 5.25rem); + font-weight: 500; + line-height: 0.95; + text-wrap: balance; +} + +.bridge-title-block p { + width: min(560px, 100%); + max-width: 560px; + margin: 0; + color: rgba(47, 39, 31, 0.7); + font-family: ui-serif, Georgia, "Times New Roman", serif; + font-size: 1.18rem; + line-height: 1.34; + text-wrap: balance; +} + +.flowchain-bridge-layout { + display: grid; + grid-template-columns: minmax(210px, 270px) minmax(520px, 650px) minmax(220px, 270px); + gap: clamp(20px, 5vw, 92px); + align-items: center; + justify-content: center; +} + +.bridge-side-card, +.bridge-console { + border: 1px solid rgba(89, 68, 45, 0.14); + background: rgba(249, 241, 229, 0.74); + box-shadow: 0 28px 68px rgba(46, 31, 15, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.48); + backdrop-filter: blur(18px); +} + +.bridge-side-card { + display: grid; + gap: 16px; + padding: 24px; + border-radius: 8px; +} + +.bridge-card-heading { + display: flex; + gap: 10px; + align-items: center; +} + +.bridge-card-heading h2 { + margin: 0; + color: #281f18; + font-family: ui-serif, Georgia, "Times New Roman", serif; + font-size: 1.18rem; + font-weight: 500; +} + +.bridge-transfer-list, +.bridge-side-stack, +.bridge-route-list, +.bridge-help-card { + display: grid; + gap: 12px; +} + +.bridge-transfer-row { + display: grid; + grid-template-columns: 28px minmax(0, 1fr) auto; + gap: 10px; + align-items: center; +} + +.bridge-transfer-row strong, +.bridge-transfer-row span, +.bridge-readiness-line span, +.bridge-route-list strong, +.bridge-route-list span { + overflow-wrap: anywhere; +} + +.bridge-transfer-row strong { + display: block; + color: #221a14; + font-size: 0.9rem; + font-weight: 650; +} + +.bridge-transfer-row span { + color: rgba(42, 33, 25, 0.66); + font-size: 0.8rem; +} + +.bridge-transfer-icon { + display: grid; + place-items: center; + width: 26px; + height: 26px; + color: #0f62db; + border-radius: 50%; + background: #eef5ff; +} + +.bridge-empty-transfer { + display: grid; + gap: 5px; + padding: 14px; + color: rgba(43, 34, 26, 0.68); + border: 1px dashed rgba(89, 68, 45, 0.18); + border-radius: 8px; + background: rgba(255, 248, 238, 0.62); + line-height: 1.36; +} + +.bridge-empty-transfer strong { + color: #271f18; +} + +.bridge-text-link, +.bridge-help-card a, +.bridge-tx-link { + display: inline-flex; + gap: 8px; + align-items: center; + color: #0d57c4; + text-decoration: none; +} + +.bridge-text-link { + justify-content: center; + min-height: 40px; + border-radius: 8px; + background: rgba(255, 250, 243, 0.48); + font-weight: 650; +} + +.bridge-console { + display: grid; + gap: 14px; + min-width: 0; + padding: 24px; + border-radius: 10px; +} + +.bridge-route-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 140px minmax(0, 1fr); + gap: 14px; + align-items: end; +} + +.bridge-route-row label, +.bridge-field { + min-width: 0; +} + +.bridge-route-row label > span, +.bridge-field > label, +.bridge-estimate-strip span, +.bridge-mini-facts dt, +.bridge-calldata-facts dt { + display: block; + margin-bottom: 6px; + color: rgba(54, 43, 33, 0.7); + font-size: 0.68rem; + font-weight: 750; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.bridge-select-shell, +.bridge-token-field > div, +.bridge-field { + border: 1px solid rgba(93, 72, 49, 0.13); + border-radius: 8px; + background: rgba(255, 250, 243, 0.68); + box-shadow: 0 9px 18px rgba(47, 32, 14, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.bridge-select-shell { + display: grid; + grid-template-columns: 32px minmax(0, 1fr) 18px; + gap: 10px; + align-items: center; + min-height: 58px; + padding: 0 14px; +} + +.bridge-select-shell strong, +.bridge-token-field strong { + font-family: ui-serif, Georgia, "Times New Roman", serif; + font-size: 1.12rem; + font-weight: 500; +} + +.bridge-chain-icon, +.bridge-token-orb { + display: grid; + place-items: center; + color: #fff; + font-size: 0.72rem; + font-weight: 800; + border-radius: 50%; +} + +.bridge-chain-icon { + width: 30px; + height: 30px; +} + +.bridge-chain-icon.base, +.bridge-token-orb { + background: linear-gradient(135deg, #89c6ff, #0a61dd 68%); +} + +.bridge-chain-icon.flow { + background: linear-gradient(135deg, #8bc7ff, #163d96 72%); +} + +.bridge-route-connector { + display: grid; + grid-template-columns: 1fr 44px 1fr; + gap: 8px; + align-items: center; + padding-bottom: 7px; +} + +.bridge-route-connector span { + height: 3px; + border-radius: 999px; + background-image: linear-gradient(90deg, rgba(12, 99, 221, 0.22) 0 45%, transparent 45% 100%); + background-size: 12px 3px; +} + +.bridge-route-connector button, +.bridge-icon-action { + display: grid; + place-items: center; + border: 1px solid rgba(38, 107, 218, 0.26); + background: rgba(244, 249, 255, 0.78); + color: #145ed3; + text-decoration: none; + transition: transform 180ms ease, border-color 180ms ease, background 180ms ease; +} + +.bridge-route-connector button { + width: 44px; + height: 44px; + border-radius: 50%; +} + +.bridge-field { + display: grid; + gap: 7px; + padding: 11px 14px; +} + +.bridge-token-field { + grid-template-columns: minmax(0, 1fr); +} + +.bridge-token-field > div { + display: grid; + grid-template-columns: 34px minmax(0, auto) 1fr; + gap: 10px; + align-items: center; + min-height: 52px; + padding: 0 12px; +} + +.bridge-token-field small { + color: rgba(50, 39, 29, 0.62); +} + +.bridge-token-orb { + width: 30px; + height: 30px; +} + +.bridge-amount-field { + grid-template-columns: minmax(0, 1fr) auto; + align-items: end; +} + +.bridge-amount-field label, +.bridge-recipient-field label { + grid-column: 1 / -1; +} + +.bridge-amount-field input { + font-size: 1.18rem; +} + +.bridge-amount-field > span { + color: #211b16; + font-family: ui-serif, Georgia, "Times New Roman", serif; + font-size: 1.12rem; +} + +.bridge-recipient-field { + grid-template-columns: minmax(0, 1fr) 32px; + align-items: center; +} + +.bridge-recipient-field button { + display: grid; + place-items: center; + width: 30px; + height: 30px; + padding: 0; + border-color: transparent; + background: transparent; + color: rgba(47, 38, 29, 0.54); +} + +.bridge-field input:not([type="checkbox"]) { + min-height: 28px; + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.82rem; +} + +.bridge-advanced { + display: grid; + gap: 12px; + color: rgba(46, 36, 28, 0.78); +} + +.bridge-advanced summary { + display: flex; + gap: 8px; + align-items: center; + width: fit-content; + cursor: pointer; + color: rgba(41, 32, 24, 0.76); + font-size: 0.84rem; + font-weight: 650; +} + +.bridge-advanced[open] summary { + margin-bottom: 10px; +} + +.bridge-calldata-facts, +.bridge-mini-facts { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; + margin: 10px 0 0; +} + +.bridge-calldata-facts div, +.bridge-mini-facts div { + min-width: 0; + padding: 10px; + border: 1px solid rgba(89, 68, 45, 0.12); + border-radius: 8px; + background: rgba(255, 250, 243, 0.52); +} + +.bridge-calldata-facts dd, +.bridge-mini-facts dd { + margin: 0; + overflow-wrap: anywhere; + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.74rem; + font-weight: 700; +} + +.bridge-estimate-strip { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + overflow: hidden; + border-radius: 8px; + background: rgba(71, 50, 29, 0.06); +} + +.bridge-estimate-strip div { + display: grid; + gap: 6px; + min-width: 0; + padding: 14px; + text-align: center; + border-right: 1px solid rgba(92, 70, 47, 0.08); +} + +.bridge-estimate-strip div:last-child { + border-right: 0; +} + +.bridge-estimate-strip span { + margin-bottom: 0; +} + +.bridge-estimate-strip strong { + overflow-wrap: anywhere; + font-size: 0.9rem; + font-weight: 600; +} + +.bridge-ack { + display: grid; + grid-template-columns: 18px minmax(0, 1fr); + gap: 10px; + align-items: start; + padding: 12px; + border: 1px solid rgba(93, 72, 49, 0.16); + border-radius: 8px; + background: rgba(255, 250, 243, 0.54); + color: rgba(38, 30, 23, 0.72); + font-size: 0.84rem; + line-height: 1.35; +} + +.bridge-action-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 44px 44px; + gap: 10px; +} + +.bridge-primary-action { + min-height: 56px; + justify-content: center; + border: 0; + border-radius: 8px; + background: linear-gradient(135deg, #043aa9, #1b78ff); + color: #fff; + box-shadow: 0 16px 32px rgba(0, 78, 216, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.32); + font-family: ui-serif, Georgia, "Times New Roman", serif; + font-size: 1.08rem; + transition: transform 180ms ease, box-shadow 180ms ease, opacity 180ms ease; +} + +.bridge-primary-action:hover:not(:disabled) { + transform: translateY(-1px); + box-shadow: 0 20px 38px rgba(0, 78, 216, 0.34), inset 0 1px 0 rgba(255, 255, 255, 0.34); +} + +.bridge-icon-action { + width: 44px; + height: 56px; + border-radius: 8px; +} + +.bridge-icon-action:hover, +.bridge-route-connector button:hover { + border-color: rgba(20, 94, 211, 0.42); + background: rgba(255, 255, 255, 0.86); +} + +.bridge-validation, +.bridge-status-message, +.bridge-tx-link { + margin: 0; + padding: 11px 12px; + border-radius: 8px; + font-size: 0.88rem; +} + +.bridge-validation { + display: flex; + gap: 8px; + align-items: center; + color: #6f2525; + border: 1px solid #e3b5b5; + background: rgba(255, 240, 240, 0.86); +} + +.bridge-status-message { + color: #234e44; + border: 1px solid #b9d8cf; + background: rgba(238, 248, 244, 0.86); +} + +.bridge-tx-link { + justify-self: start; + border: 1px solid #b9d8cf; + background: rgba(238, 248, 244, 0.86); +} + +.bridge-safety-rail { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + overflow: hidden; + margin: 4px -24px -24px; + border-top: 1px solid rgba(88, 67, 44, 0.11); + border-radius: 0 0 10px 10px; + background: rgba(241, 230, 216, 0.72); +} + +.bridge-safety-rail span { + display: grid; + grid-template-columns: 24px minmax(0, 1fr); + gap: 10px; + align-items: center; + min-width: 0; + padding: 15px 18px; + color: rgba(38, 30, 23, 0.74); + border-right: 1px solid rgba(88, 67, 44, 0.09); + font-size: 0.84rem; + line-height: 1.2; +} + +.bridge-safety-rail span:last-child { + border-right: 0; +} + +.bridge-readiness-line { + display: flex; + gap: 10px; + align-items: center; + justify-content: space-between; +} + +.bridge-readiness-line > span { + color: rgba(38, 30, 23, 0.76); + font-size: 0.88rem; + font-weight: 650; +} + +.bridge-mini-facts { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.bridge-route-list div { + display: grid; + grid-template-columns: 22px minmax(0, 1fr) auto; + gap: 10px; + align-items: center; + min-height: 28px; +} + +.bridge-chain-dot { + width: 19px; + height: 19px; + border-radius: 50%; + background: #b5aa9a; +} + +.bridge-chain-dot.active { + background: linear-gradient(135deg, #7fc2ff, #0b63ff); +} + +.bridge-chain-dot.staged { + background: linear-gradient(135deg, #d1c3ae, #a58f72); +} + +.bridge-route-list strong { + font-size: 0.88rem; + font-weight: 650; +} + +.bridge-route-list span:not(.bridge-chain-dot) { + color: rgba(43, 34, 26, 0.58); + font-size: 0.76rem; +} + +.bridge-help-card a { + justify-content: space-between; + min-height: 28px; + color: #2b251f; + font-size: 0.86rem; +} + +@media (max-width: 1280px) { + .flowchain-bridge-layout { + grid-template-columns: minmax(0, 650px); + } + + .bridge-recent-card, + .bridge-side-stack { + width: min(650px, 100%); + justify-self: center; + } +} + +@media (max-width: 720px) { + .flowchain-bridge-nav { + display: grid; + grid-template-columns: 1fr; + align-items: flex-start; + gap: 10px; + padding: 18px 16px 0; + } + + .flowchain-wallet-pill { + justify-self: start; + min-height: 38px; + padding: 0 10px; + } + + .flowchain-bridge-main { + padding: 18px 16px 38px; + } + + .flowchain-bridge-layout { + grid-template-columns: minmax(0, 1fr); + gap: 16px; + } + + .bridge-title-block h1 { + font-size: clamp(2.7rem, 16vw, 4rem); + } + + .bridge-title-block p { + max-width: 340px; + font-size: 1.08rem; + } + + .bridge-route-row, + .bridge-action-row, + .bridge-estimate-strip, + .bridge-safety-rail, + .bridge-calldata-facts, + .bridge-mini-facts { + grid-template-columns: 1fr; + } + + .bridge-route-connector { + grid-template-columns: 1fr 44px 1fr; + padding: 0; + } + + .bridge-console { + padding: 18px; + } + + .bridge-transfer-row { + grid-template-columns: 28px minmax(0, 1fr); + } + + .bridge-transfer-row .status-badge { + grid-column: 2; + justify-self: start; + } + + .bridge-route-list div { + grid-template-columns: 22px minmax(0, 1fr); + } + + .bridge-route-list span:not(.bridge-chain-dot) { + grid-column: 2; + } + + .bridge-safety-rail { + margin: 4px -18px -18px; + } +} + +button:disabled { + cursor: not-allowed; + opacity: 0.55; + transform: none; +} + +.view-stack { + display: grid; + gap: 22px; +} + +.section-header { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 18px; + align-items: end; +} + +.section-header h1 { + margin: 0; + font-size: 1.8rem; + line-height: 1.15; +} + +.section-header p { + max-width: 76ch; + margin: 8px 0 0; + color: var(--muted); + line-height: 1.5; +} + +.section-action { + min-width: min(520px, 100%); +} + +.workbench-header-actions { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; + align-items: center; +} + +.filter-row { + display: grid; + grid-template-columns: minmax(240px, 1fr) 150px; + gap: 10px; +} + +.search-box { + display: grid; + grid-template-columns: 18px 1fr; + gap: 8px; + align-items: center; + min-height: 38px; + padding: 0 10px; + border: 1px solid var(--line-strong); + border-radius: 7px; + background: var(--surface); +} + +.metric-grid { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 10px; +} + +.flowmemory-hero { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(280px, 0.34fr); + gap: 14px; + align-items: stretch; + border: 1px solid var(--line); + border-radius: 8px; + background: linear-gradient(135deg, #fbfcf8 0%, #edf4ef 100%); + box-shadow: var(--shadow); +} + +.flowmemory-hero-main { + display: grid; + gap: 12px; + padding: 18px; +} + +.flowmemory-hero-main h2 { + max-width: 780px; + margin: 0; + font-size: 1.48rem; + line-height: 1.14; +} + +.flowmemory-hero-main p { + max-width: 82ch; + margin: 0; + color: #465047; + line-height: 1.5; +} + +.flowmemory-spine { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 8px; +} + +.flowmemory-spine span { + min-height: 34px; + padding: 8px 9px; + color: #284238; + border: 1px solid #bfd1c7; + border-radius: 7px; + background: rgba(251, 252, 248, 0.78); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.74rem; + text-align: center; +} + +.flowmemory-hero-side { + display: grid; + gap: 1px; + min-width: 0; + padding: 1px; + border-left: 1px solid var(--line); +} + +.flowmemory-hero-side div { + display: grid; + gap: 5px; + align-content: center; + min-width: 0; + padding: 12px; + background: rgba(251, 252, 248, 0.72); +} + +.canary-hero { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(300px, 0.36fr); + gap: 14px; + align-items: stretch; + padding: 16px; + border: 1px solid #c9d5ce; + border-radius: 8px; + background: + linear-gradient(135deg, rgba(251, 252, 248, 0.94), rgba(229, 239, 235, 0.92)), + linear-gradient(90deg, rgba(47, 125, 107, 0.18), transparent); + box-shadow: var(--shadow); +} + +.canary-hero h2 { + max-width: 760px; + margin: 0; + font-size: 1.52rem; + line-height: 1.14; +} + +.canary-hero p { + max-width: 84ch; + margin: 10px 0 0; + color: #465047; + line-height: 1.5; +} + +.canary-read-window { + display: grid; + grid-template-columns: 28px 1fr; + gap: 10px; + align-items: start; + padding: 12px; + border: 1px solid #bfd1c7; + border-radius: 8px; + background: rgba(251, 252, 248, 0.78); +} + +.canary-read-window dl { + display: grid; + gap: 9px; + margin: 0; +} + +.canary-operator-strip { + display: grid; + grid-template-columns: 1.15fr 1fr 1.1fr; + gap: 10px; +} + +.canary-operator-strip article { + display: grid; + gap: 7px; + min-width: 0; + padding: 12px; + border: 1px solid #c9d5ce; + border-radius: 8px; + background: #fbfcf8; + box-shadow: var(--shadow); +} + +.canary-operator-strip span, +.canary-operator-strip strong { + overflow-wrap: anywhere; +} + +.canary-operator-strip span { + color: var(--muted); + font-size: 0.72rem; + font-weight: 800; + text-transform: uppercase; +} + +.canary-boundaries { + display: grid; + gap: 8px; + padding-top: 12px; +} + +.canary-boundaries span { + padding: 9px 10px; + color: #4d564d; + border: 1px solid var(--line); + border-radius: 7px; + background: #f8f9f4; + line-height: 1.35; +} + +.metric-tile, +.panel, +.table-panel, +.rootfield-tile, +.lane-tile, +.node-tile, +.alert-row, +.json-panel { + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: var(--shadow); +} + +.metric-tile { + display: grid; + gap: 8px; + padding: 14px; +} + +.metric-tile > span { + color: var(--muted); + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; +} + +.metric-tile strong { + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 1.65rem; +} + +.metric-tile div { + display: flex; + flex-wrap: wrap; + gap: 7px; + align-items: center; +} + +.overview-grid { + display: grid; + grid-template-columns: minmax(0, 1.4fr) minmax(290px, 0.6fr); + gap: 14px; +} + +.panel { + min-width: 0; + padding: 14px; +} + +.panel-wide { + grid-column: span 1; +} + +.panel-side-bottom { + grid-column: 2; +} + +.panel-heading { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding-bottom: 10px; + border-bottom: 1px solid var(--line); +} + +.panel-heading div, +.tile-heading, +.record-title, +.json-panel-header { + display: flex; + gap: 8px; + align-items: center; + min-width: 0; +} + +.panel-heading h2 { + margin: 0; + font-size: 0.98rem; +} + +.panel-heading > span { + color: var(--muted); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.78rem; +} + +.record-list, +.compact-list, +.alert-list { + display: grid; +} + +.status-strip { + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 10px 0 2px; +} + +.status-strip span { + display: inline-flex; + gap: 6px; + align-items: center; + padding: 3px 6px 3px 3px; + border: 1px solid var(--line); + border-radius: 999px; + background: #f8f9f4; +} + +.status-strip strong { + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.74rem; +} + +.record-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 270px; + gap: 16px; + padding: 14px 0; + border-bottom: 1px solid var(--line); +} + +.record-row:last-child { + border-bottom: 0; +} + +.record-row p, +.alert-row p { + margin: 8px 0; + color: #3f483f; + line-height: 1.45; +} + +.record-facts, +.definition-grid, +.lane-stats, +.alert-facts { + display: grid; + gap: 10px; + margin: 0; +} + +.record-facts { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.record-facts div, +.definition-grid div, +.lane-stats div, +.alert-facts div { + min-width: 0; +} + +dt { + color: var(--subtle); + font-size: 0.68rem; + font-weight: 700; + text-transform: uppercase; +} + +dd { + margin: 3px 0 0; + overflow-wrap: anywhere; +} + +.compact-list article { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 9px; + align-items: start; + padding: 12px 0; + border-bottom: 1px solid var(--line); +} + +.compact-list article:last-child { + border-bottom: 0; +} + +.compact-list strong, +.compact-list small { + display: block; + overflow-wrap: anywhere; +} + +.contract-event-list { + display: grid; +} + +.contract-event-list article { + display: grid; + gap: 9px; + padding: 12px 0; + border-bottom: 1px solid var(--line); +} + +.contract-event-list article:last-child { + border-bottom: 0; +} + +.contract-event-list article > div { + display: flex; + gap: 8px; + align-items: center; + min-width: 0; +} + +.contract-event-list strong { + overflow-wrap: anywhere; + font-size: 0.84rem; +} + +.contract-event-list dl { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; + margin: 0; +} + +.bundle-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; + padding-top: 14px; +} + +.bundle-grid article { + display: grid; + gap: 8px; + min-width: 0; + padding: 12px; + border: 1px solid var(--line); + border-radius: 7px; + background: #f7f8f4; +} + +.bundle-grid strong, +.bundle-grid small { + display: block; + overflow-wrap: anywhere; +} + +.block-strip { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; + padding-top: 14px; +} + +.block-strip article { + display: grid; + gap: 7px; + padding: 12px; + border: 1px solid var(--line); + border-radius: 7px; + background: #f7f8f4; +} + +.workbench-command-center { + display: grid; + grid-template-columns: minmax(0, 1.08fr) minmax(360px, 0.92fr); + gap: 14px; +} + +.workbench-boundary-strip { + display: grid; + grid-template-columns: 1.05fr 1.15fr 1.2fr; + gap: 10px; +} + +.workbench-boundary-strip article { + display: grid; + gap: 6px; + min-width: 0; + padding: 12px; + border: 1px solid #cfd7cd; + border-radius: 8px; + background: #f9faf6; +} + +.workbench-boundary-strip strong { + font-size: 0.82rem; +} + +.workbench-boundary-strip span { + color: #4c554c; + font-size: 0.84rem; + line-height: 1.42; +} + +.live-readiness-overview { + display: grid; + grid-template-columns: minmax(0, 0.88fr) minmax(420px, 1.12fr); + gap: 12px; + align-items: stretch; +} + +.live-readiness-summary { + display: grid; + gap: 14px; + min-width: 0; + border-color: #b6c8bd; + background: #f5f9f4; +} + +.live-readiness-copy h3 { + margin: 4px 0 8px; + font-size: 1.36rem; +} + +.live-readiness-copy p { + margin: 0; + color: #3f493f; + line-height: 1.5; +} + +.live-readiness-metrics { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 9px; + margin: 0; +} + +.live-readiness-metrics div { + min-width: 0; + padding: 10px; + border: 1px solid var(--line); + border-radius: 7px; + background: #f9faf6; +} + +.live-readiness-metrics dt { + color: #647061; + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.live-readiness-metrics dd { + margin: 5px 0 0; + overflow-wrap: anywhere; + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.94rem; +} + +.live-readiness-gates { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.live-readiness-gate { + display: grid; + grid-template-rows: auto auto minmax(46px, auto) auto auto; + gap: 7px; + min-width: 0; + padding: 12px; + text-align: left; + border-color: #ccd8cf; + background: #f9faf6; + transition: background 140ms ease, border-color 140ms ease, transform 140ms ease; +} + +.live-readiness-gate:hover { + border-color: #9fb4a8; + background: #eef5ee; +} + +.live-readiness-gate:focus-visible { + outline: 2px solid #2f7d6b; + outline-offset: 2px; +} + +.live-readiness-gate-top { + display: flex; + gap: 8px; + align-items: center; + justify-content: space-between; + min-width: 0; +} + +.live-readiness-gate-icon { + display: grid; + flex: 0 0 auto; + place-items: center; + width: 29px; + height: 29px; + color: #235f52; + border: 1px solid #bfd1c7; + border-radius: 7px; + background: #edf5ef; +} + +.live-readiness-gate strong, +.live-readiness-gate span, +.live-readiness-gate small, +.live-readiness-gate code { + min-width: 0; + overflow-wrap: anywhere; +} + +.live-readiness-gate strong { + font-size: 0.86rem; +} + +.live-readiness-gate > span:not(.live-readiness-gate-top) { + color: #4a554b; + font-size: 0.8rem; + line-height: 1.35; +} + +.live-readiness-gate small { + color: #667064; + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.72rem; + line-height: 1.34; +} + +.tester-launch-rail { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 10px; +} + +.tester-launch-step { + display: grid; + grid-template-rows: auto auto minmax(42px, auto) auto auto auto; + gap: 7px; + align-content: start; + min-width: 0; + min-height: 188px; + padding: 12px; + color: inherit; + text-align: left; + text-decoration: none; + border: 1px solid #cbd6ce; + border-radius: 8px; + background: #f7f9f4; + transition: background 140ms ease, border-color 140ms ease, transform 140ms ease; +} + +.tester-launch-step:hover { + border-color: #9fb4a8; + background: #ecf3ed; +} + +.tester-launch-step:focus-visible { + outline: 2px solid #2f7d6b; + outline-offset: 2px; +} + +.tester-launch-step-head { + display: flex; + gap: 8px; + align-items: center; + justify-content: space-between; + min-width: 0; +} + +.tester-launch-step-icon { + display: grid; + flex: 0 0 auto; + place-items: center; + width: 30px; + height: 30px; + color: #235f52; + border: 1px solid #bfd1c7; + border-radius: 7px; + background: #edf5ef; +} + +.tester-launch-step strong, +.tester-launch-step span, +.tester-launch-step code, +.tester-launch-step b { + min-width: 0; + overflow-wrap: anywhere; +} + +.tester-launch-step strong { + font-size: 0.86rem; +} + +.tester-launch-step > span:not(.tester-launch-step-head) { + color: #4a554b; + font-size: 0.8rem; + line-height: 1.35; +} + +.tester-launch-step code { + width: 100%; + margin: 0; + align-self: end; +} + +.tester-launch-step b { + color: var(--ink); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 1.04rem; + line-height: 1.1; +} + +.tester-launch-command-panel { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); + gap: 1px; + overflow: hidden; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--line); + box-shadow: var(--shadow); +} + +.tester-launch-command-panel div { + display: grid; + gap: 6px; + min-width: 0; + padding: 14px; + background: var(--surface); +} + +.tester-launch-command-panel span, +.tester-launch-card > span:not(.tester-launch-card-head), +.tester-launch-owner-inputs summary span, +.tester-launch-gateway-routes strong { + color: var(--muted); + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; +} + +.tester-launch-command-panel strong { + overflow-wrap: anywhere; + color: var(--ink); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 1.35rem; +} + +.tester-launch-command-panel small { + color: var(--muted); + line-height: 1.35; +} + +.tester-launch-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 380px; + gap: 14px; + align-items: start; +} + +.tester-launch-main, +.tester-launch-side { + min-width: 0; +} + +.tester-launch-main, +.tester-launch-side, +.tester-launch-workflow, +.tester-launch-connect-pack, +.tester-launch-checklist, +.tester-launch-routes, +.tester-launch-owner-inputs, +.tester-launch-commands { + display: grid; + gap: 12px; +} + +.tester-launch-side { + position: sticky; + top: 18px; +} + +.tester-launch-card-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; +} + +.tester-launch-profile-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; +} + +.tester-launch-profile-grid div { + display: grid; + align-content: start; + gap: 7px; + min-width: 0; + min-height: 92px; + padding: 12px; + border: 1px solid #d4ddd6; + border-radius: 8px; + background: #f8faf6; +} + +.tester-launch-profile-grid span { + color: var(--muted); + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; +} + +.tester-launch-profile-grid strong, +.tester-launch-profile-grid code { + min-width: 0; + overflow-wrap: anywhere; +} + +.tester-launch-profile-grid strong { + color: var(--ink); + font-size: 0.92rem; +} + +.tester-launch-profile-grid code { + align-self: end; + width: 100%; + margin: 0; +} + +.tester-launch-card { + display: grid; + grid-template-rows: auto auto minmax(38px, auto) auto auto; + gap: 8px; + min-width: 0; + min-height: 166px; + padding: 12px; + color: inherit; + text-decoration: none; + border: 1px solid #cbd6ce; + border-radius: 8px; + background: #f8faf6; + transition: background 140ms ease, border-color 140ms ease, transform 140ms ease; +} + +.tester-launch-card:hover { + border-color: #9fb4a8; + background: #eef5ef; + transform: translateY(-1px); +} + +.tester-launch-card:focus-visible { + outline: 2px solid #2f7d6b; + outline-offset: 2px; +} + +.tester-launch-card-head { + display: flex; + gap: 8px; + align-items: center; + justify-content: space-between; + min-width: 0; +} + +.tester-launch-card-icon { + display: grid; + flex: 0 0 auto; + place-items: center; + width: 30px; + height: 30px; + color: #235f52; + border: 1px solid #bfd1c7; + border-radius: 7px; + background: #edf5ef; +} + +.tester-launch-card strong, +.tester-launch-card span, +.tester-launch-card code, +.tester-launch-card b { + min-width: 0; + overflow-wrap: anywhere; +} + +.tester-launch-card strong { + font-size: 0.92rem; +} + +.tester-launch-card > span:not(.tester-launch-card-head) { + line-height: 1.35; +} + +.tester-launch-card code { + width: 100%; + margin: 0; + align-self: end; +} + +.tester-launch-card b { + color: var(--ink); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 1.08rem; + line-height: 1.1; +} + +.tester-launch-route-grid, +.tester-launch-gateway-routes div, +.tester-launch-route-pair { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 8px; +} + +.tester-launch-route-pair > div { + display: grid; + align-content: start; + gap: 8px; + min-width: 0; + padding: 10px; + border: 1px solid var(--line); + border-radius: 8px; + background: #f9faf6; +} + +.tester-launch-route-pair > div > div { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 8px; +} + +.tester-launch-route-grid code, +.tester-launch-gateway-routes code, +.tester-launch-route-pair code, +.tester-launch-owner-inputs code, +.tester-launch-commands code { + min-width: 0; + overflow-wrap: anywhere; +} + +.tester-launch-gateway-routes, +.tester-launch-route-pair { + display: grid; + gap: 8px; + padding-top: 2px; +} + +.tester-launch-route-pair strong { + color: var(--muted); + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; +} + +.tester-launch-owner-inputs details, +.tester-launch-commands details { + display: grid; + gap: 8px; + padding: 10px; + border: 1px solid var(--line); + border-radius: 8px; + background: #f9faf6; +} + +.tester-launch-owner-inputs summary, +.tester-launch-commands summary { + display: flex; + gap: 8px; + align-items: center; + justify-content: space-between; + min-width: 0; + cursor: pointer; +} + +.tester-launch-owner-inputs details > div, +.tester-launch-commands details > div { + display: grid; + gap: 7px; + padding-top: 8px; +} + +.pilot-status-panel article { + display: grid; + gap: 14px; + min-width: 0; + padding: 14px; + border: 1px solid #b8c7c0; + border-radius: 8px; + background: #f4f8f6; +} + +.pilot-status-body { + display: grid; + grid-template-columns: minmax(0, 0.92fr) minmax(320px, 1.08fr); + gap: 14px; + align-items: start; +} + +.pilot-status-body h3 { + margin: 4px 0 8px; + font-size: 1.35rem; + text-transform: capitalize; +} + +.pilot-status-body p { + margin: 0; + color: #3f483f; + line-height: 1.48; +} + +.product-surface-grid { + display: grid; + grid-template-columns: 1.2fr 1fr 1.2fr; + gap: 10px; +} + +.product-surface { + display: grid; + grid-template-columns: 34px minmax(0, 1fr) auto auto; + gap: 10px; + align-items: start; + min-width: 0; + min-height: 118px; + padding: 12px; + text-align: left; + transition: background 140ms ease, border-color 140ms ease, transform 140ms ease; +} + +.product-surface:hover { + border-color: #9fb4a8; + background: #eaf1eb; +} + +.product-surface-icon { + display: grid; + place-items: center; + width: 32px; + height: 32px; + color: #235f52; + border: 1px solid #bfd1c7; + border-radius: 7px; + background: #edf5ef; +} + +.product-surface strong, +.product-surface small, +.product-surface code { + display: block; + overflow-wrap: anywhere; +} + +.product-surface small { + margin-top: 4px; + line-height: 1.36; +} + +.product-surface b { + color: var(--ink); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 1.1rem; + line-height: 1.1; +} + +.workbench-node-body { + display: grid; + gap: 16px; + padding-top: 14px; +} + +.workbench-node-body h3 { + margin: 0 0 7px; + font-size: 1.22rem; +} + +.workbench-node-body p, +.workbench-section-detail, +.boundary-copy p { + margin: 0; + color: #465047; + line-height: 1.48; +} + +.workbench-fact-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin: 0; +} + +.workbench-fact-grid div { + min-width: 0; + padding: 10px; + border: 1px solid var(--line); + border-radius: 7px; + background: #f7f8f4; +} + +.setup-step-list { + display: grid; + padding-top: 6px; +} + +.setup-step-list article { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 9px; + align-items: start; + padding: 11px 0; + border-bottom: 1px solid var(--line); +} + +.setup-step-list .status-badge { + align-self: start; +} + +.setup-step-list article:last-child { + border-bottom: 0; +} + +.setup-step-list strong, +.setup-step-list code, +.setup-step-list small { + display: block; + overflow-wrap: anywhere; +} + +code { + width: fit-content; + max-width: 100%; + margin: 5px 0; + padding: 3px 6px; + color: #25332d; + border: 1px solid var(--line); + border-radius: 6px; + background: #eef1eb; + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.78rem; +} + +.workbench-warning { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 9px; + align-items: start; + padding: 11px 12px; + color: #6b4a16; + border: 1px solid #d9c393; + border-radius: 8px; + background: #fbf6e8; +} + +.workbench-warning strong, +.workbench-warning span { + display: block; + overflow-wrap: anywhere; +} + +.workbench-api-panel { + display: grid; + gap: 12px; +} + +.endpoint-strip { + display: flex; + flex-wrap: wrap; + gap: 7px; +} + +.endpoint-strip span { + padding: 4px 7px; + color: #34443c; + border: 1px solid var(--line); + border-radius: 999px; + background: #f7f8f4; + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.72rem; +} + +.local-action-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.local-action-grid article { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; + align-items: center; + min-width: 0; + padding: 11px; + border: 1px solid var(--line); + border-radius: 7px; + background: #f7f8f4; +} + +.local-action-grid strong, +.local-action-grid small { + display: block; + overflow-wrap: anywhere; +} + +.action-result, +.boot-hint { + margin: 0; + color: var(--muted); + line-height: 1.45; +} + +.workbench-layout { + display: grid; + grid-template-columns: 222px minmax(0, 1fr); + gap: 14px; + align-items: start; +} + +.workbench-switcher { + position: sticky; + top: 88px; + display: grid; + gap: 7px; + max-height: calc(100dvh - 112px); + overflow: auto; +} + +.workbench-switch { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; + align-items: center; + min-height: 38px; + padding: 8px 10px; + text-align: left; + transition: background 140ms ease, border-color 140ms ease, transform 140ms ease; +} + +.workbench-switch.active, +.workbench-switch:hover { + border-color: #9fb4a8; + background: #eaf1eb; +} + +.workbench-switch span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.workbench-switch strong { + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.78rem; +} + +.workbench-record-panel { + display: grid; + gap: 14px; +} + +.workbench-record-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.workbench-record { + display: grid; + gap: 11px; + min-width: 0; + padding: 13px; + border: 1px solid var(--line); + border-radius: 7px; + background: #f7f8f4; +} + +.workbench-record h3 { + min-width: 0; + margin: 0; + overflow-wrap: anywhere; + font-size: 0.96rem; +} + +.workbench-record p { + margin: 0; + color: #465047; + line-height: 1.44; +} + +.workbench-boundary-panel { + display: grid; + gap: 12px; +} + +.boundary-copy { + display: grid; + gap: 9px; +} + +.table-panel { + min-width: 0; + overflow: hidden; +} + +.table-scroll { + overflow-x: auto; +} + +.table-scroll table { + min-width: 1120px; +} + +.explorer-filter { + display: grid; + grid-template-columns: 18px minmax(0, 1fr); + gap: 8px; + align-items: center; + min-height: 38px; + padding: 0 10px; + border: 1px solid var(--line-strong); + border-radius: 7px; + background: var(--surface); +} + +.explorer-filter select { + width: 100%; + min-width: 0; + border: 0; + background: transparent; +} + +.explorer-command-panel { + display: grid; + grid-template-columns: 1.2fr 1fr 1fr 1fr; + gap: 1px; + overflow: hidden; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--line); + box-shadow: var(--shadow); +} + +.explorer-command-panel div { + display: grid; + gap: 6px; + min-width: 0; + padding: 14px; + background: var(--surface); +} + +.explorer-command-panel span, +.explorer-category-strip span, +.explorer-side-panel span, +.explorer-row-title span { + color: var(--muted); + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; +} + +.explorer-command-panel strong { + overflow-wrap: anywhere; + color: var(--ink); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 1.55rem; +} + +.explorer-command-panel small, +.explorer-side-panel small { + color: var(--muted); + line-height: 1.35; +} + +.explorer-settlement-trace { + display: grid; + grid-template-columns: minmax(220px, 0.62fr) minmax(0, 1.38fr); + gap: 12px; + align-items: stretch; +} + +.explorer-trace-head, +.explorer-trace-steps button { + min-width: 0; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: var(--shadow); +} + +.explorer-trace-head { + display: grid; + gap: 7px; + padding: 14px; +} + +.explorer-trace-head span, +.explorer-trace-copy strong { + color: var(--muted); + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; +} + +.explorer-trace-head strong { + overflow-wrap: anywhere; + color: var(--ink); +} + +.explorer-trace-head small, +.explorer-trace-copy small { + color: var(--muted); + line-height: 1.35; +} + +.explorer-trace-steps { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 8px; +} + +.explorer-trace-steps button { + display: grid; + grid-template-rows: auto minmax(52px, 1fr) auto auto; + gap: 8px; + align-items: start; + min-height: 148px; + padding: 12px; + text-align: left; +} + +.explorer-trace-steps button.active, +.explorer-trace-steps button:hover { + border-color: #9bb5aa; + background: #eaf3ee; +} + +.explorer-trace-icon { + display: grid; + place-items: center; + width: 30px; + height: 30px; + color: var(--accent-strong); + border: 1px solid #b7cac1; + border-radius: 8px; + background: #e7f1ec; +} + +.explorer-trace-copy { + display: grid; + gap: 4px; + min-width: 0; +} + +.explorer-trace-copy small { + display: -webkit-box; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.explorer-trace-steps code { max-width: 100%; - margin: 5px 0; - padding: 3px 6px; - color: #25332d; + overflow: hidden; + color: var(--ink); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.76rem; + text-overflow: ellipsis; + white-space: nowrap; +} + +.explorer-launch-boundary { + display: grid; + grid-template-columns: minmax(220px, 0.56fr) minmax(0, 1.44fr); + gap: 12px; + align-items: stretch; +} + +.explorer-launch-summary, +.explorer-launch-grid button { + min-width: 0; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: var(--shadow); +} + +.explorer-launch-summary { + display: grid; + gap: 8px; + padding: 14px; +} + +.explorer-launch-summary span, +.explorer-launch-grid strong { + color: var(--muted); + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; +} + +.explorer-launch-summary strong { + overflow-wrap: anywhere; + color: var(--ink); + font-size: 1.06rem; +} + +.explorer-launch-summary small, +.explorer-launch-grid button > span:not(.explorer-launch-top) { + color: var(--muted); + line-height: 1.35; +} + +.explorer-launch-grid { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 8px; +} + +.explorer-launch-grid button { + display: grid; + grid-template-rows: auto auto minmax(48px, 1fr) auto; + gap: 8px; + min-height: 150px; + padding: 12px; + text-align: left; +} + +.explorer-launch-grid button.active, +.explorer-launch-grid button:hover { + border-color: #9bb5aa; + background: #eaf3ee; +} + +.explorer-launch-top { + display: flex; + gap: 8px; + align-items: center; + justify-content: space-between; + min-width: 0; +} + +.explorer-launch-icon { + display: grid; + flex: 0 0 auto; + place-items: center; + width: 30px; + height: 30px; + color: var(--accent-strong); + border: 1px solid #b7cac1; + border-radius: 8px; + background: #e7f1ec; +} + +.explorer-launch-grid button > span:not(.explorer-launch-top) { + display: -webkit-box; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.explorer-launch-grid code { + max-width: 100%; + overflow: hidden; + color: var(--ink); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.75rem; + text-overflow: ellipsis; + white-space: nowrap; +} + +.explorer-category-strip { + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + gap: 8px; +} + +.explorer-category-strip button { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + min-width: 0; + min-height: 42px; + padding: 0 11px; + text-align: left; +} + +.explorer-category-strip button.active, +.explorer-category-strip button:hover { + border-color: #9bb5aa; + background: #eaf3ee; +} + +.explorer-category-strip strong { + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.82rem; +} + +.explorer-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 310px; + gap: 14px; + align-items: start; +} + +.explorer-stream, +.explorer-side-panel { + min-width: 0; +} + +.explorer-stream { + display: grid; + gap: 10px; +} + +.explorer-row { + display: grid; + grid-template-columns: 46px minmax(0, 1fr); + gap: 12px; + padding: 13px; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: var(--shadow); +} + +.explorer-row-icon { + display: grid; + place-items: center; + width: 42px; + height: 42px; + color: var(--accent-strong); + border: 1px solid #b7cac1; + border-radius: 8px; + background: #e7f1ec; +} + +.explorer-row-main { + display: grid; + gap: 9px; + min-width: 0; +} + +.explorer-row-title { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} + +.explorer-row-title h2 { + margin: 0; + overflow-wrap: anywhere; + font-size: 1rem; +} + +.explorer-row p { + margin: 0; + color: #3f483f; + line-height: 1.45; +} + +.explorer-facts { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 8px; + margin: 0; +} + +.explorer-facts div { + min-width: 0; + padding: 9px; border: 1px solid var(--line); - border-radius: 6px; - background: #eef1eb; + border-radius: 7px; + background: #f8f9f4; +} + +.explorer-facts dd { + color: var(--ink); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.78rem; +} + +.explorer-side-panel { + position: sticky; + top: 18px; + display: grid; + gap: 10px; +} + +.explorer-side-panel > div { + display: grid; + gap: 8px; + padding: 13px; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: var(--shadow); +} + +.explorer-side-panel strong { + color: var(--ink); +} + +.explorer-side-panel a { + display: inline-flex; + justify-content: center; + min-height: 36px; + padding: 8px 10px; + color: #ffffff; + border-radius: 7px; + background: var(--accent-strong); + font-weight: 750; + text-decoration: none; +} + +.ops-command-panel { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + gap: 1px; + overflow: hidden; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--line); + box-shadow: var(--shadow); +} + +.ops-command-panel div { + display: grid; + gap: 6px; + min-width: 0; + padding: 14px; + background: var(--surface); +} + +.ops-command-panel span, +.ops-finding-head span, +.ops-rule-list span { + color: var(--muted); + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; +} + +.ops-command-panel strong { + overflow-wrap: anywhere; + color: var(--ink); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 1.28rem; +} + +.ops-command-panel small, +.ops-side small { + color: var(--muted); + line-height: 1.35; +} + +.ops-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 340px; + gap: 14px; + align-items: start; +} + +.ops-main, +.ops-side { + min-width: 0; +} + +.ops-findings-panel { + display: grid; + gap: 12px; +} + +.ops-heading-status { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + justify-content: flex-end; + min-width: 0; +} + +.ops-heading-status span { + display: inline-flex; + gap: 5px; + align-items: center; + min-height: 25px; + padding: 4px 8px; + border: 1px solid var(--line); + border-radius: 7px; + color: var(--muted); + font-size: 0.72rem; + font-weight: 800; + text-transform: uppercase; +} + +.ops-finding-list { + display: grid; + gap: 10px; +} + +.ops-finding { + display: grid; + gap: 9px; + padding: 13px; + border: 1px solid var(--line); + border-left: 4px solid #9baaa0; + border-radius: 8px; + background: #fbfcf9; +} + +.ops-finding-critical, +.ops-finding-failed { + border-left-color: #a65353; +} + +.ops-finding-blocked, +.ops-finding-pending { + border-left-color: #9a7b2f; +} + +.ops-finding-head { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} + +.ops-finding h3 { + margin: 0; + overflow-wrap: anywhere; + font-size: 1rem; +} + +.ops-finding p { + margin: 0; + color: #3f483f; + line-height: 1.45; +} + +.ops-command-list, +.ops-incident-groups details div { + display: grid; + gap: 6px; +} + +.ops-command-list code, +.ops-rule-list code, +.ops-incident-groups code { + display: block; + min-width: 0; + padding: 7px 8px; + overflow-wrap: anywhere; + border: 1px solid var(--line); + border-radius: 7px; + background: #f5f6f1; +} + +.ops-side { + position: sticky; + top: 18px; + display: grid; + gap: 10px; +} + +.ops-side > article { + display: grid; + gap: 12px; + padding: 13px; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: var(--shadow); +} + +.ops-side-heading { + display: flex; + gap: 8px; + align-items: center; +} + +.ops-side-heading svg { + color: var(--accent-strong); +} + +.ops-rule-list { + display: grid; + gap: 8px; +} + +.ops-rule-list > div { + display: grid; + gap: 6px; + min-width: 0; + padding: 10px; + border: 1px solid var(--line); + border-radius: 7px; + background: #f9faf5; +} + +.ops-rule-list strong { + overflow-wrap: anywhere; +} + +.ops-incident-groups { + display: grid; + gap: 8px; +} + +.ops-incident-groups details { + min-width: 0; + padding: 9px; + border: 1px solid var(--line); + border-radius: 7px; + background: #f9faf5; +} + +.ops-incident-groups summary { + cursor: pointer; + color: var(--ink); + font-weight: 800; +} + +.ops-incident-groups details div { + margin-top: 8px; +} + +.cell-stack { + display: grid; + gap: 4px; + min-width: 0; +} + +.hash-value { + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + font-size: 0.82rem; + overflow-wrap: anywhere; +} + +.provenance-line { + display: flex; + flex-wrap: wrap; + gap: 5px; +} + +.rootfield-grid, +.hardware-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; +} + +.rootfield-tile, +.node-tile, +.lane-tile { + display: grid; + gap: 14px; + padding: 14px; +} + +.definition-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.lane-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; +} + +.lane-stats { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.alert-list { + gap: 12px; +} + +.alert-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 300px; + gap: 16px; + padding: 14px; + border-left-width: 4px; +} + +.severity-critical { + border-left-color: var(--danger); +} + +.severity-warning { + border-left-color: var(--warning); +} + +.severity-info { + border-left-color: var(--info); +} + +.alert-action { + padding: 12px; + border-left: 1px solid var(--line); +} + +.alert-action span { + color: var(--subtle); + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; +} + +.alert-action p { + margin-bottom: 0; +} + +.json-panel { + overflow: hidden; +} + +.json-panel-header { + justify-content: space-between; + padding: 12px 14px; + border-bottom: 1px solid var(--line); +} + +.json-panel pre { + max-height: 68dvh; + margin: 0; + padding: 16px; + overflow: auto; + background: #1f241f; + color: #eef4ec; font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; font-size: 0.78rem; + line-height: 1.5; +} + +.status-badge { + display: inline-flex; + gap: 6px; + align-items: center; + width: fit-content; + max-width: 100%; + min-height: 24px; + padding: 4px 8px; + border: 1px solid var(--status-border); + border-radius: 999px; + background: var(--status-bg); + color: var(--status-fg); + font-size: 0.72rem; + font-weight: 800; + text-transform: uppercase; + white-space: nowrap; +} + +.status-dot { + width: 7px; + height: 7px; + border-radius: 999px; + background: currentColor; +} + +.status-observed { + --status-bg: #e7f0f3; + --status-border: #b9d0d8; + --status-fg: #326b89; +} + +.status-pending { + --status-bg: #f5eddb; + --status-border: #d9c393; + --status-fg: #8a5e14; +} + +.status-finalized { + --status-bg: #e9eee8; + --status-border: #bdcabc; + --status-fg: #3e6541; +} + +.status-verified { + --status-bg: #e2f0e8; + --status-border: #a8ceb7; + --status-fg: #1f6a45; +} + +.status-unresolved { + --status-bg: #f3e7dc; + --status-border: #d4b495; + --status-fg: #8c5527; +} + +.status-failed { + --status-bg: #f5e2e2; + --status-border: #d4a0a0; + --status-fg: #963b3b; +} + +.status-unsupported { + --status-bg: #ece7f1; + --status-border: #c1b6d0; + --status-fg: #665283; +} + +.status-reorged { + --status-bg: #eee6dd; + --status-border: #c6b19e; + --status-fg: #714f35; +} + +.status-offline { + --status-bg: #e7e8e3; + --status-border: #b9beb5; + --status-fg: #555d55; +} + +.status-stale { + --status-bg: #f0ecdc; + --status-border: #cfc38c; + --status-fg: #766a23; +} + +.empty-state { + display: flex; + gap: 12px; + align-items: flex-start; + padding: 22px; + color: var(--muted); + border: 1px dashed var(--line-strong); + border-radius: 8px; + background: #f8f9f4; +} + +.empty-state h3 { + margin: 0 0 5px; + color: var(--ink); + font-size: 1rem; +} + +.empty-state p { + margin: 0; + line-height: 1.45; +} + +.boot-screen { + display: grid; + place-items: center; + min-height: 100dvh; + padding: 24px; + background: #f4f5f1; +} + +.boot-panel, +.error-panel { + width: min(620px, 100%); + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: var(--shadow); +} + +.boot-panel { + display: grid; + gap: 14px; + padding: 24px; +} + +.skeleton-line { + height: 14px; + border-radius: 999px; + background: linear-gradient(90deg, #e1e6de 0%, #f5f6f0 45%, #e1e6de 100%); + background-size: 220% 100%; + animation: shimmer 1.4s ease-in-out infinite; +} + +.skeleton-title { + width: 70%; + height: 24px; +} + +.skeleton-short { + width: 46%; +} + +.boot-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + margin-top: 8px; +} + +.boot-grid div { + height: 92px; + border-radius: 8px; + background: #edf0e9; +} + +.error-panel { + display: grid; + grid-template-columns: 42px 1fr; + gap: 14px; + padding: 22px; + color: #873a3a; +} + +.error-panel h1 { + margin: 0 0 8px; + color: var(--ink); + font-size: 1.25rem; +} + +.error-panel p { + margin: 0 0 14px; + color: #6f4c4c; +} + +.button { + display: inline-flex; + gap: 8px; + align-items: center; + min-height: 36px; + padding: 0 12px; +} + +.button-primary { + border-color: #1f6a45; + background: #2f7d6b; + color: #f9fcf7; +} + +.wallet-view { + display: grid; + gap: 18px; +} + +.wallet-heading { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 16px; } -.workbench-warning { +.wallet-heading h1, +.wallet-heading p, +.wallet-panel-title h2 { + margin: 0; +} + +.wallet-heading p { + color: var(--muted); +} + +.wallet-grid { display: grid; - grid-template-columns: auto minmax(0, 1fr); - gap: 9px; + grid-template-columns: minmax(0, 1.45fr) minmax(320px, 0.75fr); + gap: 18px; align-items: start; - padding: 11px 12px; - color: #6b4a16; - border: 1px solid #d9c393; +} + +.wallet-primary-panel, +.wallet-create-panel { + display: grid; + gap: 18px; + padding: 18px; + border: 1px solid var(--line); border-radius: 8px; - background: #fbf6e8; + background: var(--surface); + box-shadow: var(--shadow); } -.workbench-warning strong, -.workbench-warning span { +.wallet-account-topline { + display: grid; + grid-template-columns: 52px minmax(0, 1fr); + gap: 14px; + align-items: center; +} + +.wallet-account-icon { + display: grid; + place-items: center; + width: 52px; + height: 52px; + border: 1px solid #a7b8b1; + border-radius: 8px; + background: #e3eee8; + color: var(--accent-strong); +} + +.wallet-account-topline span, +.wallet-form-field span, +.wallet-toggle-row span, +.wallet-fact-grid dt { + color: var(--muted); + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; +} + +.wallet-account-topline strong { display: block; + margin-top: 4px; overflow-wrap: anywhere; + font-size: clamp(1.25rem, 2vw, 1.9rem); + letter-spacing: 0; } -.workbench-api-panel { +.wallet-fact-grid { display: grid; - gap: 12px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin: 0; } -.endpoint-strip { +.wallet-fact-grid div { + min-width: 0; + padding: 12px; + border: 1px solid var(--line); + border-radius: 7px; + background: var(--surface-2); +} + +.wallet-fact-grid dd { + margin: 6px 0 0; + overflow-wrap: anywhere; + color: var(--ink); + font-size: 0.86rem; +} + +.wallet-action-row { display: flex; flex-wrap: wrap; - gap: 7px; + gap: 10px; } -.endpoint-strip span { - padding: 4px 7px; - color: #34443c; - border: 1px solid var(--line); - border-radius: 999px; - background: #f7f8f4; - font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; - font-size: 0.72rem; +.wallet-action-row a { + text-decoration: none; +} + +.wallet-panel-title { + display: flex; + gap: 10px; + align-items: center; +} + +.wallet-form-field { + display: grid; + gap: 8px; +} + +.wallet-form-field input { + min-height: 42px; + padding: 0 12px; + border: 1px solid var(--line-strong); + border-radius: 7px; + background: #ffffff; +} + +.wallet-toggle-row { + display: flex; + gap: 10px; + align-items: center; +} + +.wallet-create-button { + justify-content: center; +} + +.wallet-message { + display: flex; + gap: 9px; + align-items: center; + margin: 0; + padding: 12px 14px; + border: 1px solid #dfc184; + border-radius: 8px; + background: #fff7df; + color: #6e4a13; +} + +@keyframes shimmer { + 0% { + background-position: 100% 0; + } + 100% { + background-position: -100% 0; + } +} + +@media (max-width: 1180px) { + .metric-grid, + .block-strip { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .overview-grid, + .rootfield-grid, + .hardware-grid, + .lane-grid, + .canary-operator-strip, + .workbench-boundary-strip, + .live-readiness-overview, + .tester-launch-rail, + .tester-launch-command-panel, + .tester-launch-layout, + .tester-launch-card-grid, + .tester-launch-profile-grid, + .product-surface-grid, + .pilot-status-body, + .local-action-grid, + .workbench-command-center, + .workbench-record-grid, + .explorer-command-panel, + .explorer-settlement-trace, + .explorer-launch-boundary, + .explorer-layout, + .ops-command-panel, + .ops-layout { + grid-template-columns: 1fr; + } + + .wallet-grid { + grid-template-columns: 1fr; + } + + .flowmemory-hero, + .canary-hero, + .flowmemory-spine, + .contract-event-list dl, + .bundle-grid { + grid-template-columns: 1fr; + } + + .flowmemory-hero-side, + .panel-side-bottom { + grid-column: auto; + } + + .flowmemory-hero-side { + border-left: 0; + border-top: 1px solid var(--line); + } + + .workbench-layout { + grid-template-columns: 1fr; + } + + .workbench-switcher { + position: static; + grid-template-columns: repeat(2, minmax(0, 1fr)); + max-height: none; + overflow: visible; + } + + .explorer-side-panel { + position: static; + } + + .ops-side { + position: static; + } + + .tester-launch-side { + position: static; + } +} + +@media (max-width: 860px) { + .app-shell { + grid-template-columns: 1fr; + } + + .sidebar { + position: static; + height: auto; + } + + .nav-list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .topbar, + .section-header, + .fixture-banner, + .record-row, + .alert-row { + display: grid; + grid-template-columns: 1fr; + } + + .content { + padding: 18px 14px 28px; + } + + .section-action, + .filter-row { + min-width: 0; + grid-template-columns: 1fr; + } + + .fixture-banner { + margin: 14px 14px 0; + } + + .alert-action { + border-left: 0; + border-top: 1px solid var(--line); + } + + .workbench-fact-grid { + grid-template-columns: 1fr; + } + + .live-readiness-gates { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .explorer-category-strip, + .explorer-facts, + .explorer-launch-grid, + .explorer-trace-steps, + .tester-launch-card-grid, + .tester-launch-profile-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 560px) { + .nav-list, + .metric-grid, + .block-strip, + .definition-grid, + .lane-stats, + .record-facts, + .live-readiness-metrics, + .live-readiness-gates, + .tester-launch-command-panel, + .tester-launch-card-grid, + .tester-launch-profile-grid, + .tester-launch-route-grid, + .tester-launch-gateway-routes div, + .tester-launch-route-pair, + .tester-launch-route-pair > div > div, + .workbench-switcher, + .explorer-category-strip, + .explorer-facts, + .explorer-launch-grid, + .explorer-trace-steps, + .wallet-tester-links { + grid-template-columns: 1fr; + } + + .topbar { + padding: 12px 14px; + } + + th, + td { + padding: 10px; + } +} + +/* Flowchain bridge reference layout */ +.flowchain-bridge-page { + min-height: 100dvh; + overflow-x: hidden; + overflow-y: auto; + background: + radial-gradient(circle at 38% 25%, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0) 34rem), + radial-gradient(circle at 8% 86%, rgba(255, 255, 255, 0.58), rgba(255, 255, 255, 0) 20rem), + linear-gradient(180deg, #f5ebdc 0%, #f0dfc7 100%); + color: #0d1b35; +} + +.flowchain-bridge-page::before { + opacity: 0.36; + background-image: + linear-gradient(rgba(77, 56, 34, 0.035) 1px, transparent 1px), + linear-gradient(90deg, rgba(77, 56, 34, 0.025) 1px, transparent 1px), + radial-gradient(circle at 28% 18%, rgba(107, 78, 42, 0.06), transparent 22rem); + background-size: 8px 8px, 10px 10px, auto; +} + +.flowchain-bridge-page::after { + inset: auto -14vw -7dvh -14vw; + height: 32dvh; + opacity: 0.52; + background: + radial-gradient(ellipse at 18% 50%, rgba(1, 88, 224, 0.66), rgba(16, 112, 255, 0.26) 34%, transparent 70%), + radial-gradient(ellipse at 44% 40%, rgba(0, 91, 255, 0.9), rgba(0, 105, 255, 0.32) 36%, transparent 74%), + radial-gradient(ellipse at 79% 54%, rgba(1, 95, 236, 0.68), rgba(38, 132, 255, 0.22) 40%, transparent 76%); + filter: blur(14px) saturate(1.22); + transform: rotate(-4deg) skewX(-14deg); +} + +.bridge-flow-ribbon { + inset: auto -12vw 5dvh -12vw; + height: 18dvh; + opacity: 0.58; + background: + linear-gradient(92deg, transparent 0%, rgba(0, 93, 255, 0.16) 10%, rgba(1, 83, 221, 0.74) 38%, rgba(44, 145, 255, 0.48) 72%, transparent 100%), + radial-gradient(ellipse at 36% 40%, rgba(1, 79, 220, 0.88), rgba(36, 130, 255, 0.26) 48%, transparent 72%); + filter: blur(9px); + transform: rotate(-8deg) skewX(-18deg); +} + +.flowchain-bridge-nav { + align-items: flex-start; + width: min(100%, 1700px); + padding: 24px 66px 0; } -.local-action-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 10px; +.flowchain-brand { + gap: 14px; + color: #071831; } -.local-action-grid article { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 10px; - align-items: center; - min-width: 0; - padding: 11px; - border: 1px solid var(--line); - border-radius: 7px; - background: #f7f8f4; +.flowchain-brand strong { + color: #071831; + font-size: clamp(1.75rem, 2.3vw, 2.35rem); + letter-spacing: 0; } -.local-action-grid strong, -.local-action-grid small { - display: block; - overflow-wrap: anywhere; +.flowchain-brand-mark { + width: 48px; + height: 38px; } -.action-result, -.boot-hint { - margin: 0; - color: var(--muted); - line-height: 1.45; +.flowchain-brand-mark::before, +.flowchain-brand-mark::after, +.flowchain-brand-mark span { + width: 44px; + height: 15px; + border-color: #1b6fff; + border-width: 5px 0 0; } -.workbench-layout { - display: grid; - grid-template-columns: 222px minmax(0, 1fr); - gap: 14px; - align-items: start; +.flowchain-brand-mark::before { + top: 2px; + transform: rotate(-10deg); } -.workbench-switcher { - position: sticky; - top: 88px; +.flowchain-brand-mark span { + top: 13px; + width: 36px; + border-color: #246de0; + transform: rotate(-12deg); +} + +.flowchain-brand-mark::after { + top: 24px; + width: 28px; + border-color: #73adff; + transform: rotate(-12deg); +} + +.bridge-system-rail { display: grid; - gap: 7px; - max-height: calc(100dvh - 112px); - overflow: auto; + grid-template-columns: minmax(190px, 1fr) minmax(185px, 1fr) minmax(210px, 1.08fr) 48px; + align-items: stretch; + width: min(730px, 100%); + min-height: 78px; + border: 1px solid rgba(70, 48, 25, 0.14); + border-radius: 10px; + background: rgba(249, 240, 227, 0.8); + box-shadow: 0 18px 45px rgba(48, 32, 14, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.58); + backdrop-filter: blur(18px); } -.workbench-switch { +.bridge-system-item, +.bridge-system-info { + min-width: 0; + border: 0; + background: transparent; + color: #162138; +} + +.bridge-system-item { display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 10px; + grid-template-columns: 42px minmax(0, 1fr) 10px; + gap: 12px; align-items: center; - min-height: 38px; - padding: 8px 10px; + padding: 15px 20px; text-align: left; - transition: background 140ms ease, border-color 140ms ease, transform 140ms ease; + border-right: 1px solid rgba(85, 64, 39, 0.12); } -.workbench-switch.active, -.workbench-switch:hover { - border-color: #9fb4a8; - background: #eaf1eb; +.bridge-system-wallet { + cursor: pointer; } -.workbench-switch span { +.bridge-system-item strong, +.bridge-system-item small { + display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.workbench-switch strong { - font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; - font-size: 0.78rem; +.bridge-system-item strong { + font-size: 0.88rem; + font-weight: 750; } -.workbench-record-panel { +.bridge-system-item small { + margin-top: 4px; + color: rgba(22, 33, 56, 0.62); + font-size: 0.82rem; +} + +.bridge-system-icon { display: grid; - gap: 14px; + place-items: center; + width: 40px; + height: 40px; + color: #171a21; + border-radius: 50%; + background: rgba(73, 54, 34, 0.08); } -.workbench-record-grid { +.bridge-system-item i { + width: 7px; + height: 7px; + border-radius: 50%; +} + +.bridge-system-item i.is-green { + background: #1a9c68; +} + +.bridge-system-item i.is-amber { + background: #d79916; +} + +.bridge-system-item i.is-red { + background: #b74638; +} + +.bridge-system-info { display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 10px; + place-items: center; + text-decoration: none; } -.workbench-record { +.flowchain-bridge-main { display: grid; - gap: 11px; - min-width: 0; - padding: 13px; - border: 1px solid var(--line); - border-radius: 7px; - background: #f7f8f4; + grid-template-columns: minmax(420px, 660px) minmax(650px, 820px); + gap: clamp(48px, 6vw, 96px); + align-items: start; + width: min(100%, 1700px); + min-height: calc(100dvh - 104px); + padding: 16px 66px 38px; } -.workbench-record h3 { - min-width: 0; - margin: 0; - overflow-wrap: anywhere; - font-size: 0.96rem; +.bridge-title-block { + justify-items: start; + align-self: start; + max-width: 620px; + padding-bottom: 80px; + text-align: left; } -.workbench-record p { - margin: 0; - color: #465047; - line-height: 1.44; +.bridge-title-block > span { + display: inline-flex; + min-height: 28px; + align-items: center; + padding: 0 15px; + border-radius: 999px; + background: rgba(101, 76, 45, 0.08); + color: rgba(31, 25, 20, 0.72); + font-size: 0.75rem; + font-weight: 800; + letter-spacing: 0.22em; + text-transform: uppercase; } -.workbench-boundary-panel { +.bridge-title-block h1 { + max-width: 610px; + margin: 28px 0 18px; + color: #081a36; + font-size: clamp(4.2rem, 6.25vw, 6.25rem); + font-weight: 500; + line-height: 0.88; + letter-spacing: 0; +} + +.bridge-title-block p { + width: min(520px, 100%); + max-width: 520px; + color: rgba(31, 27, 24, 0.82); + font-family: Geist, Satoshi, "Segoe UI", system-ui, sans-serif; + font-size: clamp(1.24rem, 1.55vw, 1.55rem); + line-height: 1.36; +} + +.bridge-benefit-list { display: grid; - gap: 12px; + gap: 26px; + margin-top: 38px; } -.boundary-copy { +.bridge-benefit-list article { display: grid; - gap: 9px; + grid-template-columns: 50px minmax(0, 1fr); + gap: 16px; + align-items: center; } -.table-panel { - min-width: 0; - overflow: hidden; +.bridge-benefit-list article > span { + display: grid; + place-items: center; + width: 44px; + height: 44px; + color: #1d1c18; + border-radius: 50%; + background: rgba(57, 41, 23, 0.12); } -.table-scroll { - overflow-x: auto; +.bridge-benefit-list strong, +.bridge-benefit-list small { + display: block; } -.table-scroll table { - min-width: 1120px; +.bridge-benefit-list strong { + color: #111923; + font-size: 0.95rem; } -.cell-stack { - display: grid; - gap: 4px; +.bridge-benefit-list small { + margin-top: 4px; + color: rgba(33, 28, 23, 0.72); + font-size: 0.85rem; + line-height: 1.35; +} + +.flowchain-bridge-layout { + display: block; + width: 100%; +} + +.bridge-console { + width: 100%; min-width: 0; + gap: 12px; + padding: 26px 32px 22px; + border: 1px solid rgba(72, 50, 27, 0.16); + border-radius: 14px; + background: rgba(250, 241, 229, 0.82); + box-shadow: 0 28px 78px rgba(46, 31, 15, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.62); } -.hash-value { - font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; - font-size: 0.82rem; - overflow-wrap: anywhere; +.bridge-route-row { + grid-template-columns: minmax(0, 1fr) 52px minmax(0, 1fr); + gap: 24px; + align-items: center; } -.provenance-line { - display: flex; - flex-wrap: wrap; - gap: 5px; +.bridge-route-row label > span, +.bridge-field > label, +.bridge-label-row label, +.bridge-estimate-grid span, +.bridge-calldata-facts dt { + color: rgba(42, 32, 22, 0.78); + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0.12em; } -.rootfield-grid, -.hardware-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 14px; +.bridge-route-row label > small { + display: block; + margin: 5px 0 8px; + color: rgba(42, 32, 22, 0.7); + font-size: 0.9rem; } -.rootfield-tile, -.node-tile, -.lane-tile { +.bridge-route-connector { display: grid; - gap: 14px; - padding: 14px; + place-items: center; + padding: 30px 0 0; + color: #101820; } -.definition-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); +.bridge-route-connector span, +.bridge-route-connector button { + display: none; } -.lane-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 12px; +.bridge-select-shell, +.bridge-token-field > div, +.bridge-field { + border: 1px solid rgba(83, 61, 37, 0.18); + border-radius: 8px; + background: rgba(255, 250, 243, 0.58); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5); } -.lane-stats { - grid-template-columns: repeat(4, minmax(0, 1fr)); +.bridge-select-shell { + grid-template-columns: 36px minmax(0, 1fr) 18px; + min-height: 56px; + padding: 0 16px; } -.alert-list { - gap: 12px; +.bridge-select-shell strong, +.bridge-token-field strong { + font-family: Geist, Satoshi, "Segoe UI", system-ui, sans-serif; + font-size: 1.02rem; + font-weight: 700; } -.alert-row { +.bridge-chain-icon, +.bridge-token-orb { + width: 32px; + height: 32px; +} + +.bridge-token-amount-row { display: grid; - grid-template-columns: minmax(0, 1fr) 300px; - gap: 16px; - padding: 14px; - border-left-width: 4px; + grid-template-columns: minmax(220px, 0.78fr) minmax(300px, 1.42fr); + gap: 30px; } -.severity-critical { - border-left-color: var(--danger); +.bridge-field { + padding: 0; + background: transparent; + border: 0; + box-shadow: none; } -.severity-warning { - border-left-color: var(--warning); +.bridge-token-field > div { + grid-template-columns: 36px auto minmax(0, 1fr) 18px; + min-height: 56px; + padding: 0 14px; } -.severity-info { - border-left-color: var(--info); +.bridge-token-field small { + color: rgba(43, 34, 26, 0.64); + font-size: 0.82rem; } -.alert-action { - padding: 12px; - border-left: 1px solid var(--line); +.bridge-amount-field { + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + min-height: 56px; + padding: 10px 14px; + border: 1px solid rgba(83, 61, 37, 0.18); + border-radius: 8px; + background: rgba(255, 250, 243, 0.58); } -.alert-action span { - color: var(--subtle); - font-size: 0.7rem; - font-weight: 700; - text-transform: uppercase; +.bridge-amount-field label { + grid-column: 1 / -1; + margin: 0; } -.alert-action p { - margin-bottom: 0; +.bridge-amount-field input { + min-height: 30px; + font-family: Geist, Satoshi, "Segoe UI", system-ui, sans-serif; + font-size: 1.55rem; + font-weight: 600; } -.json-panel { - overflow: hidden; +.bridge-amount-field > span { + font-family: Geist, Satoshi, "Segoe UI", system-ui, sans-serif; + font-size: 0.95rem; + font-weight: 800; } -.json-panel-header { - justify-content: space-between; - padding: 12px 14px; - border-bottom: 1px solid var(--line); +.bridge-amount-field small { + grid-column: 1; + color: rgba(43, 34, 26, 0.62); + font-size: 0.82rem; } -.json-panel pre { - max-height: 68dvh; - margin: 0; - padding: 16px; - overflow: auto; - background: #1f241f; - color: #eef4ec; - font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; - font-size: 0.78rem; - line-height: 1.5; +.bridge-amount-field .bridge-usd-note { + color: #0b62df; + font-weight: 750; } -.status-badge { - display: inline-flex; - gap: 6px; - align-items: center; - width: fit-content; - max-width: 100%; - min-height: 24px; - padding: 4px 8px; - border: 1px solid var(--status-border); - border-radius: 999px; - background: var(--status-bg); - color: var(--status-fg); - font-size: 0.72rem; +.bridge-amount-field button { + grid-column: 2; + justify-self: end; + border: 0; + background: transparent; + color: #125bd8; + font-size: 0.82rem; font-weight: 800; - text-transform: uppercase; +} + +.bridge-recipient-field { + display: grid; + grid-template-columns: minmax(0, 1fr) 34px; + gap: 8px; + align-items: center; + min-height: 56px; + padding: 10px 14px; + border: 1px solid rgba(83, 61, 37, 0.18); + border-radius: 8px; + background: rgba(255, 250, 243, 0.58); +} + +.bridge-label-row { + display: flex; + grid-column: 1 / -1; + align-items: center; + justify-content: space-between; + gap: 10px; +} + +.bridge-label-row button { + display: inline-flex; + gap: 6px; + align-items: center; + width: auto; + height: auto; + padding: 0; + border: 0; + background: transparent; + color: #125bd8; + font-size: 0.84rem; white-space: nowrap; } -.status-dot { - width: 7px; - height: 7px; - border-radius: 999px; - background: currentColor; +.bridge-recipient-field input { + font-family: Geist, Satoshi, "Segoe UI", system-ui, sans-serif; + font-size: 1.02rem; } -.status-observed { - --status-bg: #e7f0f3; - --status-border: #b9d0d8; - --status-fg: #326b89; +.bridge-recipient-field button[title="Copy recipient"] { + color: rgba(42, 32, 22, 0.52); } -.status-pending { - --status-bg: #f5eddb; - --status-border: #d9c393; - --status-fg: #8a5e14; +.bridge-recipient-field button[title="Copy recipient"]:disabled { + cursor: not-allowed; + opacity: 0.42; } -.status-finalized { - --status-bg: #e9eee8; - --status-border: #bdcabc; - --status-fg: #3e6541; +.bridge-recipient-options { + display: flex; + grid-column: 1 / -1; + flex-wrap: wrap; + gap: 7px; } -.status-verified { - --status-bg: #e2f0e8; - --status-border: #a8ceb7; - --status-fg: #1f6a45; +.bridge-recipient-options button { + display: inline-flex; + align-items: center; + width: auto; + min-height: 30px; + height: auto; + gap: 7px; + padding: 5px 8px; + border: 1px solid rgba(83, 61, 37, 0.16); + border-radius: 8px; + background: rgba(255, 255, 255, 0.62); + color: #2d231a; + font-size: 0.76rem; } -.status-unresolved { - --status-bg: #f3e7dc; - --status-border: #d4b495; - --status-fg: #8c5527; +.bridge-recipient-options span { + max-width: 150px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -.status-failed { - --status-bg: #f5e2e2; - --status-border: #d4a0a0; - --status-fg: #963b3b; +.bridge-recipient-options small { + color: rgba(45, 35, 26, 0.6); + font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; } -.status-unsupported { - --status-bg: #ece7f1; - --status-border: #c1b6d0; - --status-fg: #665283; +.bridge-estimate-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + overflow: hidden; + border: 1px solid rgba(83, 61, 37, 0.15); + border-radius: 8px; + background: rgba(235, 222, 205, 0.34); } -.status-reorged { - --status-bg: #eee6dd; - --status-border: #c6b19e; - --status-fg: #714f35; +.bridge-estimate-grid > div { + display: grid; + gap: 4px; + min-height: 54px; + padding: 9px 16px; + border-right: 1px solid rgba(83, 61, 37, 0.12); + border-bottom: 1px solid rgba(83, 61, 37, 0.12); } -.status-offline { - --status-bg: #e7e8e3; - --status-border: #b9beb5; - --status-fg: #555d55; +.bridge-estimate-grid > div:nth-child(2n) { + border-right: 0; } -.status-stale { - --status-bg: #f0ecdc; - --status-border: #cfc38c; - --status-fg: #766a23; +.bridge-estimate-grid > div:nth-last-child(-n + 2) { + border-bottom: 0; } -.empty-state { - display: flex; - gap: 12px; - align-items: flex-start; - padding: 22px; - color: var(--muted); - border: 1px dashed var(--line-strong); - border-radius: 8px; - background: #f8f9f4; +.bridge-estimate-grid span { + display: inline-flex; + gap: 6px; + align-items: center; } -.empty-state h3 { - margin: 0 0 5px; - color: var(--ink); +.bridge-estimate-grid strong { + color: #151a21; font-size: 1rem; + font-weight: 650; } -.empty-state p { - margin: 0; - line-height: 1.45; +.bridge-estimate-grid small { + color: rgba(33, 28, 23, 0.62); + font-size: 0.82rem; } -.boot-screen { - display: grid; - place-items: center; - min-height: 100dvh; - padding: 24px; - background: #f4f5f1; +.bridge-estimate-primary strong { + color: #0b62df; + font-size: 1.12rem; } -.boot-panel, -.error-panel { - width: min(620px, 100%); - border: 1px solid var(--line); +.bridge-advanced { + border: 1px solid rgba(83, 61, 37, 0.16); border-radius: 8px; - background: var(--surface); - box-shadow: var(--shadow); + background: rgba(255, 250, 243, 0.5); } -.boot-panel { - display: grid; - gap: 14px; - padding: 24px; +.bridge-advanced summary { + display: flex; + justify-content: space-between; + width: 100%; + min-height: 42px; + padding: 0 16px; + font-size: 0.95rem; } -.skeleton-line { - height: 14px; - border-radius: 999px; - background: linear-gradient(90deg, #e1e6de 0%, #f5f6f0 45%, #e1e6de 100%); - background-size: 220% 100%; - animation: shimmer 1.4s ease-in-out infinite; +.bridge-advanced-body { + padding: 0 14px 12px; } -.skeleton-title { - width: 70%; - height: 24px; +.bridge-advanced[open] summary { + margin: 0; } -.skeleton-short { - width: 46%; +.bridge-calldata-facts { + grid-template-columns: repeat(3, minmax(0, 1fr)); + padding: 0 14px 14px; } -.boot-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 10px; - margin-top: 8px; +.bridge-action-row { + grid-template-columns: 1fr; } -.boot-grid div { - height: 92px; - border-radius: 8px; - background: #edf0e9; +.bridge-primary-action { + display: flex; + gap: 10px; + align-items: center; + justify-content: center; + min-height: 54px; + font-family: ui-serif, Georgia, "Times New Roman", serif; + font-size: 1.26rem; } -.error-panel { +.bridge-terms-row { display: grid; - grid-template-columns: 42px 1fr; - gap: 14px; - padding: 22px; - color: #873a3a; -} - -.error-panel h1 { - margin: 0 0 8px; - color: var(--ink); - font-size: 1.25rem; + grid-template-columns: 18px minmax(0, 1fr); + gap: 8px; + align-items: center; + color: rgba(35, 30, 25, 0.64); + font-size: 0.82rem; + line-height: 1.35; } -.error-panel p { - margin: 0 0 14px; - color: #6f4c4c; +.bridge-safety-rail { + grid-template-columns: minmax(0, 1fr) auto auto; + gap: 24px; + align-items: center; + margin: 4px 0 0; + padding-top: 10px; + border-top: 1px solid rgba(83, 61, 37, 0.14); + border-radius: 0; + background: transparent; } -.button { +.bridge-safety-rail span, +.bridge-safety-rail a { display: inline-flex; gap: 8px; align-items: center; - min-height: 36px; - padding: 0 12px; + min-width: 0; + padding: 0; + color: rgba(35, 30, 25, 0.64); + border: 0; + font-size: 0.82rem; + text-decoration: none; } -.button-primary { - border-color: #1f6a45; - background: #2f7d6b; - color: #f9fcf7; +.bridge-safety-rail a { + color: rgba(29, 24, 20, 0.82); } -@keyframes shimmer { - 0% { - background-position: 100% 0; - } - 100% { - background-position: -100% 0; - } +.bridge-validation, +.bridge-status-message, +.bridge-tx-link { + padding: 10px 12px; } -@media (max-width: 1180px) { - .metric-grid, - .block-strip { - grid-template-columns: repeat(2, minmax(0, 1fr)); +@media (max-width: 1280px) { + .flowchain-bridge-nav { + display: grid; + grid-template-columns: 1fr; + gap: 18px; + padding: 32px 28px 0; } - .overview-grid, - .rootfield-grid, - .hardware-grid, - .lane-grid, - .canary-operator-strip, - .workbench-boundary-strip, - .product-surface-grid, - .pilot-status-body, - .local-action-grid, - .workbench-command-center, - .workbench-record-grid { - grid-template-columns: 1fr; + .bridge-system-rail { + width: 100%; } - .flowmemory-hero, - .canary-hero, - .flowmemory-spine, - .contract-event-list dl, - .bundle-grid { + .flowchain-bridge-main { grid-template-columns: 1fr; + gap: 28px; + padding: 28px; } - .flowmemory-hero-side, - .panel-side-bottom { - grid-column: auto; + .bridge-title-block { + max-width: 760px; + padding-bottom: 0; } +} - .flowmemory-hero-side { - border-left: 0; - border-top: 1px solid var(--line); +@media (max-width: 760px) { + .flowchain-bridge-nav, + .flowchain-bridge-main { + width: 100%; + max-width: 100vw; + padding-left: 16px; + padding-right: 16px; } - .workbench-layout { + .bridge-system-rail { grid-template-columns: 1fr; } - .workbench-switcher { - position: static; - grid-template-columns: repeat(2, minmax(0, 1fr)); - max-height: none; - overflow: visible; + .bridge-system-item { + border-right: 0; + border-bottom: 1px solid rgba(85, 64, 39, 0.12); } -} -@media (max-width: 860px) { - .app-shell { - grid-template-columns: 1fr; + .bridge-title-block h1 { + font-size: clamp(3.25rem, 16vw, 4.6rem); } - .sidebar { - position: static; - height: auto; + .bridge-title-block { + width: calc(100vw - 32px) !important; + max-width: calc(100vw - 32px) !important; + overflow: hidden; } - .nav-list { - grid-template-columns: repeat(3, minmax(0, 1fr)); + .bridge-title-block p { + width: calc(100vw - 32px) !important; + max-width: calc(100vw - 32px) !important; + font-size: 1.08rem; + overflow-wrap: break-word; + text-wrap: pretty; } - .topbar, - .section-header, - .fixture-banner, - .record-row, - .alert-row { - display: grid; - grid-template-columns: 1fr; + .bridge-benefit-list { + gap: 16px; } - .content { - padding: 18px 14px 28px; + .bridge-benefit-list article { + grid-template-columns: 50px minmax(0, 1fr); } - .section-action, - .filter-row { - min-width: 0; - grid-template-columns: 1fr; + .bridge-benefit-list small, + .bridge-benefit-list strong { + max-width: calc(100vw - 98px); + overflow-wrap: break-word; } - .fixture-banner { - margin: 14px 14px 0; + .bridge-console { + padding: 18px; } - .alert-action { - border-left: 0; - border-top: 1px solid var(--line); + .bridge-route-row, + .bridge-token-amount-row, + .bridge-estimate-grid, + .bridge-calldata-facts, + .bridge-safety-rail { + grid-template-columns: 1fr; } - .workbench-fact-grid { - grid-template-columns: 1fr; + .bridge-route-connector { + padding: 0; } -} -@media (max-width: 560px) { - .nav-list, - .metric-grid, - .block-strip, - .definition-grid, - .lane-stats, - .record-facts, - .workbench-switcher { - grid-template-columns: 1fr; + .bridge-estimate-grid > div, + .bridge-estimate-grid > div:nth-child(2n), + .bridge-estimate-grid > div:nth-last-child(-n + 2) { + border-right: 0; + border-bottom: 1px solid rgba(83, 61, 37, 0.12); } - .topbar { - padding: 12px 14px; + .bridge-estimate-grid > div:last-child { + border-bottom: 0; } - th, - td { - padding: 10px; + .bridge-safety-rail { + gap: 12px; } } diff --git a/apps/dashboard/src/test/dashboardData.test.ts b/apps/dashboard/src/test/dashboardData.test.ts index 1be798f7..f0be20d4 100644 --- a/apps/dashboard/src/test/dashboardData.test.ts +++ b/apps/dashboard/src/test/dashboardData.test.ts @@ -1,12 +1,14 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; +import { MemoryRouter } from "react-router-dom"; import canaryFixture from "../../../../fixtures/dashboard/flowmemory-dashboard-base-canary-v0.json"; import fixture from "../../../../fixtures/dashboard/flowmemory-dashboard-v0.json"; import explorerFallback from "../../../../fixtures/dashboard/flowchain-l1-explorer-fallback.json"; import devnetDashboardState from "../../../../fixtures/launch-core/generated/devnet/dashboard-state.json"; import devnetState from "../../../../fixtures/launch-core/generated/devnet/state.json"; import bridgeTestDeposit from "../../public/data/flowchain-bridge-test-deposit.json"; +import liveReadinessReport from "../../public/data/flowchain-live-readiness-report.json"; import { validateDashboardData } from "../data/loadDashboardData"; import { DASHBOARD_STATUSES } from "../data/status"; import { computeOverviewMetrics, searchRecords } from "../data/selectors"; @@ -16,11 +18,16 @@ import { WORKBENCH_BRIDGE_TEST_DEPOSIT_PATH, WORKBENCH_DEVNET_DASHBOARD_STATE_PATH, WORKBENCH_DEVNET_STATE_PATH, + WORKBENCH_LIVE_READINESS_REPORT_PATH, WORKBENCH_EXPLORER_FALLBACK_PATH, WORKBENCH_SECTIONS, buildWorkbenchSnapshot, fetchWorkbenchSnapshot, } from "../data/workbench"; +import { WalletView } from "../views/WalletView"; +import { ExternalTesterLaunchView } from "../views/ExternalTesterLaunchView"; +import { ExplorerView } from "../views/ExplorerView"; +import { OpsView } from "../views/OpsView"; import { WorkbenchView } from "../views/WorkbenchView"; describe("dashboard fixture", () => { @@ -114,6 +121,7 @@ describe("dashboard fixture", () => { devnetState, devnetDashboardState, bridgeTestDeposit, + liveReadinessReport, explorerFallback, }); @@ -151,6 +159,9 @@ describe("dashboard fixture", () => { expect(workbench.sections.bridgeReleases).toHaveLength(1); expect(workbench.sections.errorsRecovery.length).toBeGreaterThanOrEqual(6); expect(workbench.sections.realValuePilot.length).toBeGreaterThan(0); + expect(workbench.sections.liveReadiness.length).toBeGreaterThan(0); + expect(workbench.sections.liveReadiness[0].facts.find((fact) => fact.label === "deployment ready")?.value).toBe("false"); + expect(workbench.sections.liveReadiness.some((record) => record.id === "public-rpc-edge")).toBe(true); expect(workbench.sections.realValuePilot.some((record) => record.facts.some((fact) => fact.label === "scope" && fact.value === "capped owner testing"))).toBe(true); expect(workbench.sections.realValuePilot.some((record) => record.facts.some((fact) => fact.label === "source chain ID" && fact.value === "8453"))).toBe(true); expect(workbench.sections.explorerRecords.length).toBeGreaterThan(0); @@ -257,6 +268,96 @@ describe("dashboard fixture", () => { lifecycle: [], }); } + if (url.endsWith("/bridge/live-readiness")) { + return Response.json({ + schema: "flowmemory.control_plane.bridge_live_readiness.v0", + baseChainId: 8453, + baseChainName: "Base", + failClosedStatus: "BLOCKED", + readyForOperatorLivePilot: false, + lockbox: { configured: false, envName: "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS", ownerVerified: false }, + node: { running: true, chainId: "flowmemory-local-devnet-v0" }, + confirmationDepth: { configured: false, envName: "FLOWCHAIN_BASE8453_CONFIRMATION_DEPTH" }, + missingEnvNames: ["FLOWCHAIN_BASE8453_RPC_URL", "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS"], + currentArtifacts: { base8453DepositCount: 0, localOrMockDepositCount: 1, mockPresentedAsLive: false }, + issues: [{ + reasonCode: "missing_env", + status: "blocked", + title: "Missing live pilot env", + summary: "Live readiness is blocked until all required env names are present.", + envNames: ["FLOWCHAIN_BASE8453_RPC_URL", "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS"], + }], + envValuesPrinted: false, + localOnly: true, + productionReady: false, + }); + } + if (url.endsWith("/pilot/lifecycle")) { + return Response.json({ + schema: "flowmemory.control_plane.bridge_lifecycle_record_list.v0", + count: 1, + lifecycleRecords: [{ + lifecycleRecordId: "lifecycle:1", + baseTxHash: `0x${"1".repeat(64)}`, + logIndex: 0, + depositId: "deposit:1", + replayKey: "replay:1", + replayStatus: "accepted", + creditId: "credit:1", + recipientWallet: "wallet:credited", + withdrawalIntentId: "withdrawal:1", + withdrawalStatus: "requested", + releaseEvidenceId: "release:1", + releaseStatus: "recorded", + asset: "local-test-unit", + amountSmallestUnits: "100", + status: "credited", + artifactClass: "local-or-mock", + liveArtifact: false, + evidenceFilePath: "fixtures/bridge/local-runtime-bridge-handoff.json", + equality: { + depositAmount: "100", + observedAmount: "100", + creditedAmount: "100", + walletDelta: "100", + transferableAmount: "100", + withdrawalAmount: "100", + releaseAmount: "100", + allEqual: true, + equalities: { walletDelta: true }, + }, + }], + }); + } + if (url.endsWith("/wallets/balances")) { + return Response.json({ + schema: "flowmemory.control_plane.wallet_balance_list.v0", + count: 1, + balances: [{ + balanceId: "balance:credited", + walletAddress: "wallet:credited", + asset: "local-test-unit", + amount: "100", + status: "credited", + creditId: "credit:1", + }], + }); + } + if (url.endsWith("/wallets/transfers")) { + return Response.json({ + schema: "flowmemory.control_plane.wallet_transfer_history.v0", + count: 1, + transfers: [{ + transferId: "transfer:1", + txId: "tx:transfer:1", + fromAccountId: "wallet:credited", + toAccountId: "wallet:recipient", + assetId: "local-test-unit", + amount: "100", + status: "applied", + }], + }); + } if (url === WORKBENCH_DEVNET_STATE_PATH) { return Response.json(devnetState); } @@ -266,6 +367,9 @@ describe("dashboard fixture", () => { if (url === WORKBENCH_BRIDGE_TEST_DEPOSIT_PATH) { return Response.json(bridgeTestDeposit); } + if (url === WORKBENCH_LIVE_READINESS_REPORT_PATH) { + return Response.json(liveReadinessReport); + } if (url === WORKBENCH_EXPLORER_FALLBACK_PATH) { return Response.json(explorerFallback); } @@ -280,15 +384,208 @@ describe("dashboard fixture", () => { expect(workbench.raw.controlPlaneHealth).toEqual({ status: "ok" }); expect(workbench.raw.controlPlaneState).toEqual({ state: devnetState }); expect(workbench.raw.controlPlanePilotStatus).toMatchObject({ state: "degraded" }); + expect(workbench.raw.controlPlaneBridgeReadiness).toMatchObject({ failClosedStatus: "BLOCKED" }); + expect(workbench.raw.controlPlanePilotLifecycle).toMatchObject({ count: 1 }); + expect(workbench.sections.realValuePilot.some((record) => record.kind === "Bridge live readiness")).toBe(true); + expect(workbench.sections.realValuePilot.some((record) => record.kind === "Bridge exact lifecycle")).toBe(true); + expect(workbench.sections.realValuePilot.some((record) => record.kind === "Wallet transfer history")).toBe(true); + const lifecycleRecord = workbench.sections.realValuePilot.find((record) => record.kind === "Bridge exact lifecycle"); + expect(lifecycleRecord?.facts.find((fact) => fact.label === "replay key")?.value).toBe("replay:1"); + expect(lifecycleRecord?.facts.find((fact) => fact.label === "withdrawal intent")?.value).toBe("withdrawal:1"); + expect(lifecycleRecord?.facts.find((fact) => fact.label === "release evidence")?.value).toBe("release:1"); + expect(lifecycleRecord?.facts.find((fact) => fact.label === "withdrawal amount")?.value).toBe("100"); + expect(lifecycleRecord?.facts.find((fact) => fact.label === "release amount")?.value).toBe("100"); expect(workbench.raw.devnetState).toEqual(devnetState); expect(workbench.raw.bridgeTestDeposit).toEqual(bridgeTestDeposit); expect(workbench.raw.explorerFallback).toEqual(explorerFallback); expect(workbench.loadIssues).toEqual([]); expect(fetchMock).toHaveBeenCalledWith("http://127.0.0.1:8787/health", expect.any(Object)); expect(fetchMock).toHaveBeenCalledWith("http://127.0.0.1:8787/pilot/status", expect.any(Object)); + expect(fetchMock).toHaveBeenCalledWith("http://127.0.0.1:8787/bridge/live-readiness", expect.any(Object)); + expect(fetchMock).toHaveBeenCalledWith("http://127.0.0.1:8787/pilot/lifecycle", expect.any(Object)); expect(fetchMock).toHaveBeenCalledWith(WORKBENCH_DEVNET_STATE_PATH, expect.any(Object)); expect(fetchMock).toHaveBeenCalledWith(WORKBENCH_BRIDGE_TEST_DEPOSIT_PATH, expect.any(Object)); - expect(fetchMock).toHaveBeenCalledWith(WORKBENCH_EXPLORER_FALLBACK_PATH, expect.any(Object)); + expect(fetchMock).toHaveBeenCalledWith(WORKBENCH_LIVE_READINESS_REPORT_PATH, expect.any(Object)); + }); + + it("renders live public launch readiness from infra reports", () => { + const workbench = buildWorkbenchSnapshot(data, { + devnetState, + devnetDashboardState, + bridgeTestDeposit, + liveReadinessReport, + }); + const html = renderToStaticMarkup(createElement(WorkbenchView, { data, workbench })); + + expect(html).toContain("Public launch readiness"); + expect(html).toContain("Public launch blocked"); + expect(html).toContain("Public RPC edge"); + expect(html).toContain("State backup proof"); + expect(html).toContain("Backup dry run"); + expect(html).toContain("Bridge relayer queue"); + expect(html).toContain("External tester packet"); + expect(html).toContain("FLOWCHAIN_RPC_PUBLIC_URL"); + expect(html).toContain("flowchain:public-deployment:contract"); + }); + + it("renders wallet tester gateway controls without secrets", () => { + const workbench = buildWorkbenchSnapshot(data, { + devnetState, + devnetDashboardState, + bridgeTestDeposit, + liveReadinessReport, + }); + const html = renderToStaticMarkup(createElement(MemoryRouter, { initialEntries: ["/wallet?panel=tester"] }, createElement(WalletView, { workbench }))); + + expect(html).toContain("Tester gateway"); + expect(html).toContain("Open tester tools"); + expect(html).toContain("Inspect tester activity"); + expect(html).toContain("Open ops status"); + expect(html).toContain("Friend access"); + expect(html).toContain("FLOWCHAIN_TESTER_WRITE_ENABLED"); + expect(html).not.toContain("local-tester-write-token"); + }); + + it("renders the dedicated friends-and-family launch flow without secrets", () => { + const workbench = buildWorkbenchSnapshot(data, { + devnetState, + devnetDashboardState, + bridgeTestDeposit, + liveReadinessReport, + }); + const html = renderToStaticMarkup(createElement(MemoryRouter, { initialEntries: ["/tester"] }, createElement(ExternalTesterLaunchView, { workbench }))); + + expect(html).toContain("Friends-and-family launch"); + expect(html).toContain("Shareable"); + expect(html).toContain("Packet smoke"); + expect(html).toContain("Gateway proof"); + expect(html).toContain("Relayer timeout"); + expect(html).toContain("Alert rules"); + expect(html).toContain("Tester workflow"); + expect(html).toContain("Connection profile"); + expect(html).toContain("Connect pack"); + expect(html).toContain("FlowChain friends-and-family pilot"); + expect(html).toContain("flowmemory-local-devnet-v0"); + expect(html).toContain("<OWNER_PUBLIC_ENDPOINT>/rpc"); + expect(html).toContain("<OWNER_PUBLIC_ENDPOINT>/explorer/summary"); + expect(html).toContain("Create tester wallet"); + expect(html).toContain("Faucet fund"); + expect(html).toContain("Send capped transfer"); + expect(html).toContain("Inspect explorer"); + expect(html).toContain("Owner inputs"); + expect(html).toContain("FLOWCHAIN_RPC_PUBLIC_URL"); + expect(html).toContain("FLOWCHAIN_TESTER_WRITE_ENABLED"); + expect(html).toContain("/tester/wallets/create"); + expect(html).toContain("/tester/faucet"); + expect(html).toContain("/tester/wallets/send"); + expect(html).toContain("/explorer/summary"); + expect(html).toContain("npm run flowchain:external-tester:packet"); + expect(html).not.toContain("local-tester-write-token"); + }); + + it("renders the explorer route for tester-visible chain activity without secrets", () => { + const workbench = buildWorkbenchSnapshot(data, { + devnetState, + devnetDashboardState, + bridgeTestDeposit, + liveReadinessReport, + }); + const html = renderToStaticMarkup(createElement(MemoryRouter, null, createElement(ExplorerView, { data, workbench }))); + + expect(html).toContain("Flowchain explorer"); + expect(html).toContain("Create, fund, send, inspect"); + expect(html).toContain("Launch boundary"); + expect(html).toContain("Private chain live"); + expect(html).toContain("Public RPC"); + expect(html).toContain("Tester sharing"); + expect(html).toContain("Alert coverage"); + expect(html).toContain("timeout 300s"); + expect(html).toContain("Open readiness"); + expect(html).toContain("Open tester tools"); + expect(html).toContain("Funding proofs"); + expect(html).toContain("Blocks"); + expect(html).toContain("Transactions"); + expect(html).toContain("Wallets"); + expect(html).toContain("Bridge"); + expect(html).not.toContain("local-tester-write-token"); + }); + + it("renders the ops center from alert and incident reports without secrets", () => { + const workbench = buildWorkbenchSnapshot(data, { + devnetState, + devnetDashboardState, + bridgeTestDeposit, + liveReadinessReport, + }); + const html = renderToStaticMarkup(createElement(MemoryRouter, null, createElement(OpsView, { workbench }))); + + expect(html).toContain("Ops center"); + expect(html).toContain("Current findings"); + expect(html).toContain("Incident commands"); + expect(html).toContain("network sends; stores secrets"); + expect(html).toContain("Relayer loop"); + expect(html).toContain("Escalation dry run"); + expect(html).toContain("public-rpc-not-ready"); + expect(html).not.toContain("local-tester-write-token"); + }); + + it("renders bridge readiness live-blocked without env values", () => { + const configuredButHidden = "https://example.invalid/rpc-redacted"; + const workbench = buildWorkbenchSnapshot(data, { + controlPlane: { + url: "http://127.0.0.1:8787", + status: "available", + checkedAt: "2026-05-14T15:00:00.000Z", + endpoints: ["GET /health", "GET /state", "GET /bridge/live-readiness", "GET /pilot/lifecycle"], + health: { status: "ok" }, + state: devnetState, + pilotStatus: { + schema: "flowmemory.control_plane.real_value_pilot_status.v0", + state: "degraded", + stateReason: "Waiting for Base 8453 deposit.", + baseChainId: 8453, + cappedOwnerTesting: true, + broadPublicReadiness: false, + productionReady: false, + browserStoresSecrets: false, + nextOperatorStep: { command: "npm run control-plane:serve" }, + lifecycle: [], + }, + bridgeLiveReadiness: { + schema: "flowmemory.control_plane.bridge_live_readiness.v0", + baseChainId: 8453, + baseChainName: "Base", + failClosedStatus: "BLOCKED", + readyForOperatorLivePilot: false, + lockbox: { configured: false, envName: "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS", ownerVerified: false }, + node: { running: true, chainId: "flowmemory-local-devnet-v0" }, + confirmationDepth: { configured: false, envName: "FLOWCHAIN_BASE8453_CONFIRMATION_DEPTH" }, + missingEnvNames: ["FLOWCHAIN_BASE8453_RPC_URL", "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS"], + currentArtifacts: { base8453DepositCount: 0, localOrMockDepositCount: 1, mockPresentedAsLive: false }, + issues: [{ + reasonCode: "missing_env", + status: "blocked", + title: "Missing live pilot env", + summary: "Live readiness is blocked until all required env names are present.", + envNames: ["FLOWCHAIN_BASE8453_RPC_URL", "FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS"], + }], + envValuesPrinted: false, + localOnly: true, + productionReady: false, + }, + }, + devnetState, + devnetDashboardState, + }); + const html = renderToStaticMarkup(createElement(WorkbenchView, { data, workbench })); + + expect(html).toContain("Bridge live readiness"); + expect(html).toContain("BLOCKED"); + expect(html).toContain("FLOWCHAIN_BASE8453_RPC_URL"); + expect(html).toContain("FLOWCHAIN_BASE8453_LOCKBOX_ADDRESS"); + expect(html).toContain("env values printed"); + expect(html).toContain("false"); + expect(html).not.toContain(configuredButHidden); }); it("renders the critical workbench view labels from fixture fallback", () => { @@ -296,6 +593,7 @@ describe("dashboard fixture", () => { devnetState, devnetDashboardState, bridgeTestDeposit, + liveReadinessReport, explorerFallback, }); const html = renderToStaticMarkup(createElement(WorkbenchView, { data, workbench })); @@ -304,6 +602,9 @@ describe("dashboard fixture", () => { expect(html).toContain("Node and API status"); expect(html).toContain("Control-plane offline"); expect(html).toContain("Real-value pilot"); + expect(html).toContain("Bridge live readiness"); + expect(html).toContain("Public launch readiness"); + expect(html).toContain("Live Readiness"); expect(html).toContain("capped owner testing"); expect(html).toContain("public readiness"); expect(html).toContain("Wallet Metadata"); diff --git a/apps/dashboard/src/views/BridgePilotView.tsx b/apps/dashboard/src/views/BridgePilotView.tsx new file mode 100644 index 00000000..1f403212 --- /dev/null +++ b/apps/dashboard/src/views/BridgePilotView.tsx @@ -0,0 +1,754 @@ +import { useEffect, useMemo, useState } from "react"; +import { + AlertTriangle, + ArrowRightLeft, + ChevronDown, + CircleDollarSign, + Copy, + ExternalLink, + Info, + Lock, + ShieldCheck, + Wallet, + Zap, +} from "lucide-react"; +import { StatusBadge } from "../components/StatusBadge"; +import type { DashboardStatus } from "../data/types"; +import type { WorkbenchSnapshot } from "../data/workbench"; + +type EthereumProvider = { + request(args: { method: string; params?: unknown[] | Record }): Promise; +}; + +declare global { + interface Window { + ethereum?: EthereumProvider; + } +} + +interface BridgePilotViewProps { + workbench: WorkbenchSnapshot; +} + +type BridgeReadiness = { + failClosedStatus?: string; + readyForOperatorLivePilot?: boolean; + missingEnvNames?: string[]; + issues?: Array<{ reasonCode?: string; status?: string; title?: string; summary?: string }>; + lockbox?: { configured?: boolean; ownerVerified?: boolean }; + currentArtifacts?: { base8453DepositCount?: number }; + envValuesPrinted?: boolean; + productionReady?: boolean; +}; + +const BASE_CHAIN_ID_DECIMAL = 8453; +const BASE_CHAIN_ID_HEX = "0x2105"; +const BASE_RPC_URL = "https://mainnet.base.org"; +const BASE_EXPLORER_URL = "https://basescan.org"; +const LOCKBOX_ADDRESS = "0xe731Bc6b117d92deDCA40a7ccAec11d16205026a"; +const LOCK_NATIVE_SELECTOR = "0x1326d1ec"; +const MAX_DEPOSIT_WEI = 100000000000000n; +const ETH_USD_RATE_URL = "https://api.coinbase.com/v2/exchange-rates?currency=ETH"; +const ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000"; +const BLOCKED_RECIPIENT_PLACEHOLDER = "0x5555555555555555555555555555555555555555555555555555555555555555"; +const BLOCKED_METADATA_PLACEHOLDER = "0x6666666666666666666666666666666666666666666666666666666666666666"; +const RECIPIENT_CANDIDATE_FIELDS = new Set(["accountId", "signerId", "address", "flowchainRecipient", "recipient"]); + +type BridgeRecipientOption = { + value: string; + label: string; + source: string; +}; + +function asReadiness(value: unknown): BridgeReadiness | null { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + return null; + } + + return value as BridgeReadiness; +} + +function statusFromReadiness(readiness: BridgeReadiness | null): DashboardStatus { + if (!readiness) { + return "pending"; + } + + if (readiness.readyForOperatorLivePilot || readiness.failClosedStatus === "READY_FOR_OPERATOR_LIVE_PILOT") { + return "verified"; + } + + if (readiness.failClosedStatus === "FAILED") { + return "failed"; + } + + return "pending"; +} + +function isBytes32(value: string): boolean { + return /^0x[0-9a-fA-F]{64}$/.test(value); +} + +function isZeroBytes32(value: string): boolean { + return /^0x0{64}$/i.test(value); +} + +function normalizeInput(value: string): string { + return value.trim(); +} + +function isRecord(value: unknown): value is Record { + return value !== null && typeof value === "object" && !Array.isArray(value); +} + +function blockedPlaceholderLabel(value: string): string | null { + const normalized = value.toLowerCase(); + if (normalized === BLOCKED_RECIPIENT_PLACEHOLDER) { + return "old 0x555... placeholder"; + } + if (normalized === BLOCKED_METADATA_PLACEHOLDER) { + return "old 0x666... placeholder"; + } + return null; +} + +function addRecipientOption( + options: BridgeRecipientOption[], + seen: Set, + value: unknown, + label: string, + source: string, +): void { + if (typeof value !== "string") { + return; + } + const normalized = normalizeInput(value); + if (!isBytes32(normalized) || isZeroBytes32(normalized) || blockedPlaceholderLabel(normalized)) { + return; + } + const key = normalized.toLowerCase(); + if (seen.has(key)) { + return; + } + seen.add(key); + options.push({ value: normalized, label, source }); +} + +function collectRecipientOptionsFromRaw( + value: unknown, + options: BridgeRecipientOption[], + seen: Set, + source: string, + label: string, + depth = 0, +): void { + if (depth > 3) { + return; + } + if (Array.isArray(value)) { + value.forEach((item, index) => { + collectRecipientOptionsFromRaw(item, options, seen, source, `${label} ${index + 1}`, depth + 1); + }); + return; + } + if (!isRecord(value)) { + return; + } + + for (const [key, nestedValue] of Object.entries(value)) { + if (RECIPIENT_CANDIDATE_FIELDS.has(key)) { + addRecipientOption(options, seen, nestedValue, label, source); + } + if (Array.isArray(nestedValue) || isRecord(nestedValue)) { + collectRecipientOptionsFromRaw(nestedValue, options, seen, source, label, depth + 1); + } + } +} + +function collectRecipientOptions(workbench: WorkbenchSnapshot): BridgeRecipientOption[] { + const options: BridgeRecipientOption[] = []; + const seen = new Set(); + const records = [ + ...workbench.sections.walletMetadata, + ...workbench.sections.accounts, + ...workbench.sections.bridgeCredits, + ...workbench.sections.bridgeDeposits, + ]; + + for (const record of records) { + collectRecipientOptionsFromRaw(record.raw, options, seen, record.kind, record.title); + } + + return options.slice(0, 6); +} + +function validateRecipient(value: string): string | null { + if (value.length === 0) { + return "Enter a real Flowchain account id before bridging."; + } + if (!isBytes32(value)) { + return "Recipient must be a 32-byte Flowchain account id."; + } + if (isZeroBytes32(value)) { + return "Recipient cannot be the zero account id."; + } + const blockedLabel = blockedPlaceholderLabel(value); + if (blockedLabel) { + return `Recipient cannot be the ${blockedLabel}.`; + } + return null; +} + +function validateMetadataHash(value: string): string | null { + if (value.length === 0) { + return null; + } + if (!isBytes32(value)) { + return "Metadata hash must be blank or a bytes32 value."; + } + const blockedLabel = blockedPlaceholderLabel(value); + if (blockedLabel) { + return `Metadata hash cannot be the ${blockedLabel}.`; + } + return null; +} + +function shorten(value: string): string { + return `${value.slice(0, 6)}...${value.slice(-4)}`; +} + +function parseEthToWei(value: string): { wei: bigint | null; error: string | null } { + const trimmed = value.trim(); + if (!/^\d+(\.\d{0,18})?$/.test(trimmed)) { + return { wei: null, error: "Use a decimal ETH amount with up to 18 decimals." }; + } + + const [wholePart, fractionPart = ""] = trimmed.split("."); + const whole = BigInt(wholePart.length > 0 ? wholePart : "0"); + const fraction = BigInt(fractionPart.padEnd(18, "0")); + const wei = whole * 10n ** 18n + fraction; + + if (wei <= 0n) { + return { wei: null, error: "Amount must be greater than zero." }; + } + + if (wei > MAX_DEPOSIT_WEI) { + return { wei: null, error: "Amount exceeds the 0.0001 ETH pilot cap." }; + } + + return { wei, error: null }; +} + +function parseEthDecimal(value: string): number | null { + const trimmed = value.trim(); + if (!/^\d+(\.\d*)?$/.test(trimmed)) { + return null; + } + + const parsed = Number(trimmed); + return Number.isFinite(parsed) ? parsed : null; +} + +function formatUsd(value: number): string { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + maximumFractionDigits: value < 1 ? 4 : 2, + }).format(value); +} + +function usdEstimateFromEth(value: string, rate: number | null): string | null { + const parsed = parseEthDecimal(value); + if (parsed === null || rate === null) { + return null; + } + + return `~ ${formatUsd(parsed * rate)} USD`; +} + +function toQuantityHex(value: bigint): string { + return `0x${value.toString(16)}`; +} + +function buildLockNativeData(flowchainRecipient: string, metadataHash: string): string { + return `${LOCK_NATIVE_SELECTOR}${flowchainRecipient.slice(2)}${metadataHash.slice(2)}`; +} + +async function switchToBase(provider: EthereumProvider): Promise { + try { + await provider.request({ + method: "wallet_switchEthereumChain", + params: [{ chainId: BASE_CHAIN_ID_HEX }], + }); + } catch (error) { + const code = typeof error === "object" && error !== null && "code" in error ? Number((error as { code?: unknown }).code) : 0; + if (code !== 4902) { + throw error; + } + + await provider.request({ + method: "wallet_addEthereumChain", + params: [ + { + chainId: BASE_CHAIN_ID_HEX, + chainName: "Base", + nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, + rpcUrls: [BASE_RPC_URL], + blockExplorerUrls: [BASE_EXPLORER_URL], + }, + ], + }); + } +} + +function errorMessage(error: unknown): string { + if (error instanceof Error && error.message.trim().length > 0) { + return error.message; + } + + return "Wallet request failed or was rejected."; +} + +export function BridgePilotView({ workbench }: BridgePilotViewProps) { + const readiness = asReadiness(workbench.controlPlane.bridgeLiveReadiness); + const [walletAddress, setWalletAddress] = useState(null); + const [chainId, setChainId] = useState(null); + const [amountEth, setAmountEth] = useState("0.00001"); + const [recipient, setRecipient] = useState(""); + const [metadataHash, setMetadataHash] = useState(""); + const [ethUsdRate, setEthUsdRate] = useState(null); + const [priceStatus, setPriceStatus] = useState<"loading" | "ready" | "unavailable">("loading"); + const [statusMessage, setStatusMessage] = useState(null); + const [txHash, setTxHash] = useState(null); + + const normalizedRecipient = normalizeInput(recipient); + const normalizedMetadataHash = normalizeInput(metadataHash); + const effectiveMetadataHash = normalizedMetadataHash.length === 0 ? ZERO_BYTES32 : normalizedMetadataHash; + const amount = useMemo(() => parseEthToWei(amountEth), [amountEth]); + const usdEstimate = useMemo(() => usdEstimateFromEth(amountEth, ethUsdRate), [amountEth, ethUsdRate]); + const pilotCapUsdEstimate = useMemo(() => usdEstimateFromEth("0.0001", ethUsdRate), [ethUsdRate]); + const recipientOptions = useMemo(() => collectRecipientOptions(workbench), [workbench]); + const hardIssues = useMemo( + () => + (readiness?.issues ?? []).filter( + (issue) => issue.status === "blocked" && issue.reasonCode !== "no_deposits_observed", + ), + [readiness], + ); + const missingEnvNames = readiness?.missingEnvNames ?? []; + const validationIssue = + amount.error ?? + validateRecipient(normalizedRecipient) ?? + validateMetadataHash(normalizedMetadataHash) ?? + (missingEnvNames.length > 0 ? `Control-plane is missing ${missingEnvNames.join(", ")}.` : null) ?? + (hardIssues.length > 0 ? hardIssues.map((issue) => issue.reasonCode ?? issue.title ?? "blocked").join(", ") : null); + const sendDisabled = !walletAddress || validationIssue !== null; + const readinessStatus = statusFromReadiness(readiness); + const chainLabel = chainId === BASE_CHAIN_ID_HEX ? `Base ${BASE_CHAIN_ID_DECIMAL}` : chainId ?? "Connect wallet"; + const routeLabel = readinessStatus === "verified" ? "Verified" : "Pilot route"; + const receivedAmount = amount.wei === null ? "0 ETH" : `${amountEth || "0"} ETH`; + const usdEstimateLabel = + usdEstimate ?? + (priceStatus === "loading" ? "Loading ETH/USD quote" : "USD quote unavailable"); + + useEffect(() => { + const controller = new AbortController(); + + async function loadEthUsdRate() { + try { + const response = await fetch(ETH_USD_RATE_URL, { signal: controller.signal }); + if (!response.ok) { + throw new Error(`ETH/USD quote failed with ${response.status}`); + } + + const payload = (await response.json()) as { data?: { rates?: { USD?: string } } }; + const parsed = Number(payload.data?.rates?.USD); + if (!Number.isFinite(parsed) || parsed <= 0) { + throw new Error("ETH/USD quote payload did not include a positive USD rate."); + } + + setEthUsdRate(parsed); + setPriceStatus("ready"); + } catch (error) { + if (error instanceof DOMException && error.name === "AbortError") { + return; + } + setPriceStatus("unavailable"); + } + } + + void loadEthUsdRate(); + + return () => controller.abort(); + }, []); + + const connectWallet = async () => { + const provider = window.ethereum; + if (!provider) { + setStatusMessage("No browser wallet detected."); + return; + } + + try { + const accounts = await provider.request({ method: "eth_requestAccounts" }); + const nextChainId = await provider.request({ method: "eth_chainId" }); + const accountList = Array.isArray(accounts) ? accounts : []; + setWalletAddress(typeof accountList[0] === "string" ? accountList[0] : null); + setChainId(typeof nextChainId === "string" ? nextChainId : null); + setStatusMessage(null); + } catch (error) { + setStatusMessage(errorMessage(error)); + } + }; + + const sendDeposit = async () => { + const provider = window.ethereum; + if (!provider || !walletAddress || amount.wei === null || validationIssue !== null) { + return; + } + + try { + setStatusMessage("Waiting for wallet confirmation."); + setTxHash(null); + await switchToBase(provider); + const transactionHash = await provider.request({ + method: "eth_sendTransaction", + params: [ + { + from: walletAddress, + to: LOCKBOX_ADDRESS, + value: toQuantityHex(amount.wei), + data: buildLockNativeData(normalizedRecipient, effectiveMetadataHash), + }, + ], + }); + if (typeof transactionHash === "string") { + setTxHash(transactionHash); + setStatusMessage("Transaction submitted on Base."); + } else { + setStatusMessage("Wallet returned without a transaction hash."); + } + const nextChainId = await provider.request({ method: "eth_chainId" }); + setChainId(typeof nextChainId === "string" ? nextChainId : null); + } catch (error) { + setStatusMessage(errorMessage(error)); + } + }; + + const handlePrimaryAction = walletAddress ? sendDeposit : connectWallet; + const primaryActionLabel = walletAddress ? "Bridge to Flowchain" : "Connect wallet"; + + return ( +
+