diff --git a/.circleci/config.yml b/.circleci/config.yml index 3c2169bbe..17e9ee42f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,28 +1,32 @@ version: 2.1 -executors: - default-executor: - docker: - - image: circleci/android:api-30-ndk - resource_class: large - - environment: - _JAVA_OPTIONS: "-Xmx1500m -XX:+UnlockExperimentalVMOptions -XX:+UseContainerSupport -XX:ParallelGCThreads=2 -XX:ConcGCThreads=2 -XX:ParallelGCThreads=2 -Djava.util.concurrent.ForkJoinPool.common.parallelism=2" - TERM: 'dumb' - +orbs: + android: circleci/android@2.3.0 jobs: snapshot: - executor: default-executor + environment: + TERM: 'dumb' + executor: + name: android/android-machine + tag: 2023.04.1 + resource-class: large steps: - checkout + - android/restore-gradle-cache: + cache-prefix: v1a - run: name: install retry command: scripts/install-retry.sh - run: - name: build and deploy + name: build + command: | + yes | sdkmanager "platforms;android-33" || true + /tmp/retry -m 3 ./gradlew :android:assembleRelease --info + - run: + name: deploy snapshot command: | - yes | sdkmanager "platforms;android-27" || true - /tmp/retry -m 3 ./gradlew :android:assembleRelease /tmp/retry -m 3 scripts/publish-android-snapshot.sh + - android/save-gradle-cache: + cache-prefix: v1a workflows: version: 2 build-and-deploy: diff --git a/.github/workflows/android-sample.yml b/.github/workflows/android-sample.yml index 3b12b5744..2bd77507a 100644 --- a/.github/workflows/android-sample.yml +++ b/.github/workflows/android-sample.yml @@ -1,5 +1,5 @@ name: Build Android Sample App - +# This action runs on 'git push' and PRs on: [push, pull_request] jobs: @@ -7,14 +7,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: set up JDK - uses: actions/setup-java@v1 + uses: actions/setup-java@v3.11.0 with: - java-version: 11 + distribution: 'temurin' + java-version: 17 - name: Compute build cache run: ./scripts/checksum-android.sh checksum-android.txt - - uses: actions/cache@v2 + - uses: actions/cache@v3.3.1 with: path: | ~/.gradle/caches/modules-* @@ -30,7 +31,7 @@ jobs: - name: Build remaining artifacts with Gradle run: ./gradlew assembleDebug - name: upload artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3.1.2 with: name: sample-app.apk path: android/sample/build/outputs/apk/debug/sample-debug.apk diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index fab1b7535..286f826d8 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,4 +1,5 @@ name: Docs +# This action runs on push to 'main' on: push: branches: @@ -9,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2.3.1 + uses: actions/checkout@v3.5.3 with: persist-credentials: false @@ -19,10 +20,12 @@ jobs: yarn build working-directory: website/ - - name: Deploy - uses: JamesIves/github-pages-deploy-action@4.1.3 + - name: Deploy to GitHub Pages + uses: JamesIves/github-pages-deploy-action@v4.4.2 with: branch: gh-pages folder: website/build clean: true + clean-exclude: | + .circleci commit-message: "[ci skip] Deploying documentation update" diff --git a/.github/workflows/iOS-Sample.yml b/.github/workflows/iOS-Sample.yml index 8ff94db75..b530a1fab 100644 --- a/.github/workflows/iOS-Sample.yml +++ b/.github/workflows/iOS-Sample.yml @@ -1,4 +1,5 @@ name: Build iOS apps +# This action runs on 'git push' and PRs to below specified paths on: push: paths: @@ -21,7 +22,7 @@ jobs: working-directory: iOS/Sample steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install dependencies run: pod install --repo-update - name: Build Sample app @@ -36,7 +37,7 @@ jobs: working-directory: iOS/SampleSwift steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install dependencies run: pod install --repo-update - name: Build SampleSwift app @@ -51,7 +52,7 @@ jobs: working-directory: iOS/Tutorial steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install dependencies run: pod install --repo-update - name: Build Tutorial app diff --git a/.github/workflows/iOS-dependent-pod-lint.yml b/.github/workflows/iOS-dependent-pod-lint.yml index ee8afac0f..9a6316a3d 100644 --- a/.github/workflows/iOS-dependent-pod-lint.yml +++ b/.github/workflows/iOS-dependent-pod-lint.yml @@ -1,4 +1,5 @@ name: Validate Dependent Podspecs +# This action runs on push and PRs to below specified paths on: push: paths: @@ -14,7 +15,7 @@ jobs: run: working-directory: iOS/Podspecs steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint Folly @@ -26,7 +27,7 @@ jobs: run: working-directory: iOS/Podspecs steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint Peertalk diff --git a/.github/workflows/iOS-pod-lint.yml b/.github/workflows/iOS-pod-lint.yml index 104895799..9ed21c858 100644 --- a/.github/workflows/iOS-pod-lint.yml +++ b/.github/workflows/iOS-pod-lint.yml @@ -1,4 +1,5 @@ name: Validate Podspecs +# This action runs on 'git push' and PRs to below specified paths. on: push: paths: @@ -19,11 +20,11 @@ jobs: lint-flipperkit_fbdefines_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FBDefines - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -32,11 +33,11 @@ jobs: lint-flipperkit_core_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/Core - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -45,11 +46,11 @@ jobs: lint-flipperkit_cppbridge_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/CppBridge - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -58,11 +59,11 @@ jobs: lint-flipperkit_dynamic_convert_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FBCxxFollyDynamicConvert - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -71,11 +72,11 @@ jobs: lint-flipperkit_port_forwarding_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FKPortForwarding - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -84,11 +85,11 @@ jobs: lint-flipperkit_layout_highlight_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitHighlightOverlay - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -97,11 +98,11 @@ jobs: lint-flipperkit_layout_text_searchable_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitHighlightOverlay - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -110,11 +111,11 @@ jobs: lint-flipperkit_layout_helpers_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitLayoutHelpers - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -123,11 +124,11 @@ jobs: lint-flipperkit_layout_ios_descriptors_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitLayoutIOSDescriptors - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -136,11 +137,11 @@ jobs: lint-flipperkit_layout_plugin_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitLayoutPlugin - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -149,11 +150,11 @@ jobs: lint-flipperkit_layout_ck_plugin_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitLayoutComponentKitSupport - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -162,11 +163,11 @@ jobs: lint-flipperkit_network_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitNetworkPlugin - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -175,11 +176,11 @@ jobs: lint-flipperkit_skiosnetwork_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/SKIOSNetworkPlugin - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -188,11 +189,11 @@ jobs: lint-flipperkit_user_default_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitUserDefaultsPlugin - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -201,11 +202,11 @@ jobs: lint-flipperkit_example_plugin_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitExamplePlugin - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -214,11 +215,11 @@ jobs: lint-flipperkit_react_plugin_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint FlipperKit/FlipperKitReactPlugin - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 @@ -227,11 +228,11 @@ jobs: lint-flipper_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update - name: Lint Flipper - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 60 max_attempts: 3 diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index fb47d142d..b2d14ae0a 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -1,17 +1,26 @@ name: Label issues - # This workflow is triggered on issue comments. on: issue_comment: types: created +permissions: + contents: read + jobs: applyNeedsAttentionLabel: + permissions: + contents: read # for actions/checkout to fetch code + issues: write # for hramos/needs-attention to label issues name: Apply Needs Attention Label runs-on: ubuntu-latest + if: github.repository == 'facebook/flipper' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Apply Needs Attention Label - uses: hramos/needs-attention@v1 + uses: hramos/needs-attention@v2.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} + id: needs-attention + - name: Result + run: echo '${{ steps.needs-attention.outputs.result }}' diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 7a8e5c82f..d573e1c69 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -1,5 +1,5 @@ name: js-flipper - +# This action runs on 'git push' and PRs on: [push, pull_request] jobs: @@ -9,12 +9,12 @@ jobs: working-directory: js/js-flipper runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v3.5.3 + - uses: actions/setup-node@v3.6.0 with: - node-version: '16.x' + node-version: '18.x' - name: yarn install (with retry) - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: command: cd js/js-flipper && yarn timeout_minutes: 30 @@ -30,12 +30,12 @@ jobs: working-directory: js/react-flipper-example runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v3.5.3 + - uses: actions/setup-node@v3.6.0 with: - node-version: '16.x' + node-version: '18.x' - name: js-flipper - yarn install (with retry) - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: command: cd js/js-flipper && yarn timeout_minutes: 30 @@ -44,7 +44,7 @@ jobs: run: yarn build working-directory: js/js-flipper - name: yarn install (with retry) - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: command: cd js/react-flipper-example && yarn timeout_minutes: 30 diff --git a/.github/workflows/nodejs-doctor.yml b/.github/workflows/nodejs-doctor.yml index 7dab0b5b7..0ca49a429 100644 --- a/.github/workflows/nodejs-doctor.yml +++ b/.github/workflows/nodejs-doctor.yml @@ -1,20 +1,17 @@ name: Doctor Node CI - +# This action runs on 'git push' and PRs on: [push, pull_request] jobs: build: - runs-on: 'ubuntu-latest' - env: doctor-directory: ./desktop/doctor - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3.5.3 + - uses: actions/setup-node@v3.6.0 with: - node-version: '16.x' + node-version: '18.x' - name: install working-directory: ${{env.doctor-directory}} run: yarn diff --git a/.github/workflows/nodejs-pkg.yml b/.github/workflows/nodejs-pkg.yml index c9e4166e9..22ed081d1 100644 --- a/.github/workflows/nodejs-pkg.yml +++ b/.github/workflows/nodejs-pkg.yml @@ -1,20 +1,17 @@ name: PKG Node CI - +# This actions runs on 'git push' and PRs on: [push, pull_request] jobs: build: - runs-on: ubuntu-latest - env: pkg-directory: ./desktop/pkg - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3.5.3 + - uses: actions/setup-node@v3.6.0 with: - node-version: '16.x' + node-version: '18.x' - name: install working-directory: ${{env.pkg-directory}} run: yarn diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 9e31450c6..e16f8d179 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -1,25 +1,21 @@ name: Desktop Node CI - +# This action run on 'git push' and PRs on: [push, pull_request] jobs: build: - runs-on: ${{ matrix.os }} - env: desktop-directory: ./desktop - strategy: fail-fast: false matrix: - node-version: [14.x] + node-version: [18.x] os: ['ubuntu-latest', 'windows-latest', 'macos-latest'] - steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3.6.0 with: node-version: ${{ matrix.node-version }} - name: Get yarn cache directory path @@ -64,25 +60,25 @@ jobs: run: yarn build --win working-directory: ${{env.desktop-directory}} - name: upload linux artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3.1.2 if: matrix.os == 'ubuntu-latest' with: name: Flipper-linux.zip path: dist/Flipper-linux.zip - name: upload windows artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3.1.2 if: matrix.os == 'windows-latest' with: name: Flipper-win.zip path: dist/Flipper-win.zip - name: upload mac zip artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3.1.2 if: matrix.os == 'macos-latest' with: name: Flipper-mac.zip path: dist/Flipper-mac.zip - name: upload mac dmg artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3.1.2 if: matrix.os == 'macos-latest' with: name: Flipper-mac.dmg diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index ab4e87bc7..6706b55ef 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -1,5 +1,5 @@ name: Packer - +# This action runs on 'git push' and PRs on: [push, pull_request] jobs: @@ -7,11 +7,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Setup toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.48.0 + uses: dtolnay/rust-toolchain@stable + # The selection of Rust toolchain is made based on the particular '@rev' + # of this Action being requested. For example "dtolnay/rust-toolchain@nightly" + # pulls in the nightly Rust toolchain, while "dtolnay/rust-toolchain@1.42.0" + # pulls in '1.42.0'. - name: Test run: cd packer && cargo test - name: Format diff --git a/.github/workflows/publish-android.yml b/.github/workflows/publish-android.yml index 69f59297a..3c8c288b4 100644 --- a/.github/workflows/publish-android.yml +++ b/.github/workflows/publish-android.yml @@ -1,5 +1,5 @@ name: Publish Android - +# This action runs on 'git push tag v*' and worflow dispatch as specified below on: push: tags: @@ -10,25 +10,23 @@ on: description: "Tag to upload artifacts to" required: false - jobs: build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: set up JDK - uses: actions/setup-java@v1 + uses: actions/setup-java@v3.11.0 with: - java-version: 11 + distribution: 'temurin' + java-version: 17 - name: Write GPG Sec Ring run: echo '${{ secrets.GPG_KEY_CONTENTS }}' | base64 -d > /tmp/secring.gpg - name: Update gradle.properties - run: echo -e "signing.secretKeyRingFile=/tmp/secring.gpg\nsigning.keyId=${{ secrets.SIGNING_KEY_ID }}\nsigning.password=${{ secrets.SIGNING_PASSWORD }}\nmavenCentralPassword=${{ secrets.SONATYPE_NEXUS_PASSWORD }}\nmavenCentralUsername=${{ secrets.SONATYPE_NEXUS_USERNAME }}" >> gradle.properties + run: echo -e "signing.secretKeyRingFile=/tmp/secring.gpg\nsigning.keyId=${{ secrets.SIGNING_KEY_ID }}\nsigning.password=${{ secrets.SIGNING_PASSWORD }}\nmavenCentralPassword=${{ secrets.SONATYPE_NEXUS_PASSWORD }}\nmavenCentralUsername=${{ secrets.SONATYPE_NEXUS_USERNAME }}\nSONATYPE_HOST=DEFAULT\nRELEASE_SIGNING_ENABLED=true\nSONATYPE_AUTOMATIC_RELEASE=true" >> gradle.properties - name: Compute build cache run: ./scripts/checksum-android.sh checksum-android.txt - - uses: actions/cache@v2 + - uses: actions/cache@v3.3.1 with: path: | ~/.gradle/caches/modules-* @@ -38,9 +36,7 @@ jobs: - name: Build artifacts run: ./gradlew :sample:assembleDebug :sample:assembleRelease && ./gradlew :android:assembleRelease - name: Upload Archives - run: ./gradlew publish -info --no-parallel --no-daemon - - name: Release and close - run: ./gradlew closeAndReleaseRepository + run: ./gradlew publish -info - name: Clean secrets if: always() run: rm /tmp/secring.gpg @@ -48,7 +44,7 @@ jobs: run: mv android/sample/build/outputs/apk/debug/sample-debug.apk SampleApp-android.apk - name: Attach sample APK to release if: ${{ github.event.inputs.tag != '' }} - uses: passy/github-upload-release-artifacts-action@v2.2.2 + uses: aigoncharov/github-upload-release-artifacts-action@2.2.3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 8847a8582..4a9a6c72c 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -1,4 +1,5 @@ name: Publish NPM +# This action runs on 'git push tag v*' on: push: tags: @@ -13,10 +14,10 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3.5.3 + - uses: actions/setup-node@v3.6.0 with: - node-version: '16.x' + node-version: '18.x' - name: Install run: yarn - name: Set versions diff --git a/.github/workflows/publish-pods.yml b/.github/workflows/publish-pods.yml index 2ef2e2b6e..b0279ac0f 100644 --- a/.github/workflows/publish-pods.yml +++ b/.github/workflows/publish-pods.yml @@ -1,4 +1,5 @@ name: Publish Pods +# This action runs on 'git push tag v*' on: push: tags: @@ -9,7 +10,7 @@ jobs: publish_flipper_pod: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update @@ -29,7 +30,7 @@ jobs: needs: publish_flipper_pod runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Install Dependences run: pod repo update @@ -49,7 +50,7 @@ jobs: needs: publish_flipperkit_pod runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 - name: Update Flipper's Podspec run: ./scripts/update-pod-versions.sh ./ ./Flipper.podspec @@ -123,7 +124,7 @@ jobs: git branch - name: Create PR to Update Podfile.lock - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v5.0.2 with: title: "Automated: Update Podfile.lock" body: | diff --git a/.github/workflows/react-native-example.yml b/.github/workflows/react-native-example.yml index 03ab0071f..b56ddabaa 100644 --- a/.github/workflows/react-native-example.yml +++ b/.github/workflows/react-native-example.yml @@ -1,5 +1,7 @@ name: Build React Native example +# This action runs on 'git push' and PRs on: [push, pull_request] + jobs: build-react-native-example-ios: runs-on: macos-latest @@ -7,11 +9,11 @@ jobs: run: working-directory: react-native/ReactNativeFlipperExample steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2.1.5 + - uses: actions/checkout@v3.5.3 + - uses: actions/setup-node@v3.6.0 with: - node-version: 14.x - - uses: maxim-lobanov/setup-cocoapods@v1 + node-version: '18.x' + - uses: maxim-lobanov/setup-cocoapods@v1.3.0 with: # Path to Podfile.lock file to determine Cocoapods version # n.b. doesn't seem to respect cwd: @@ -34,17 +36,18 @@ jobs: run: working-directory: react-native/ReactNativeFlipperExample steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2.1.5 + - uses: actions/checkout@v3.5.3 + - uses: actions/setup-node@v3.6.0 with: - node-version: 14.x + node-version: '18.x' - name: set up JDK - uses: actions/setup-java@v1 + uses: actions/setup-java@v3.11.0 with: - java-version: 11 + distribution: 'temurin' + java-version: '11' - name: Compute build cache run: ${GITHUB_WORKSPACE}/scripts/checksum-android.sh checksum-android.txt - - uses: actions/cache@v2 + - uses: actions/cache@v3.3.1 with: path: | ~/.gradle/caches/modules-* @@ -67,14 +70,16 @@ jobs: run: working-directory: react-native/ReactNativeFlipperExample steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2.1.5 + - uses: actions/checkout@v3.5.3 + - uses: actions/setup-node@v3.6.0 with: - node-version: 14.x - - uses: nuget/setup-nuget@v1 + node-version: '18.x' + - name: Set up NuGet.exe + - uses: NuGet/setup-nuget@v1.2.0 with: nuget-version: '5.x' - - uses: microsoft/setup-msbuild@v1.1 + - name: Add msbuild to PATH + - uses: microsoft/setup-msbuild@v1.3.1 - name: Gather environment info run: npx envinfo - name: Install vcpkg packages diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 183421857..3a1ff8029 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,5 @@ name: Release +# This action runs on push to 'main' and below specified paths on: push: branches: @@ -11,12 +12,13 @@ jobs: runs-on: ubuntu-latest outputs: tag: ${{ steps.tag-version-commit.outputs.tag }} + steps: - uses: passy/extract-version-commit@v1.0.0 id: extract-version-commit with: version_regex: '^Flipper Release: v([0-9]+\.[0-9]+\.[0-9]+)(?:\n|$)' - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 if: ${{ steps.extract-version-commit.outputs.commit != ''}} with: ref: ${{ steps.extract-version-commit.outputs.commit }} @@ -31,12 +33,12 @@ jobs: version_assertion_command: 'grep -q "\"version\": \"$version\"" desktop/package.json' - name: Create release if: ${{ steps.tag-version-commit.outputs.tag != '' }} - uses: actions/create-release@v1 + uses: softprops/action-gh-release@v0.1.15 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ steps.tag-version-commit.outputs.tag }} - release_name: ${{ steps.tag-version-commit.outputs.tag }} + name: ${{ steps.tag-version-commit.outputs.tag }} body: | See https://github.com/facebook/flipper/blob/main/desktop/static/CHANGELOG.md for full notes. @@ -51,30 +53,65 @@ jobs: desktop-directory: ./desktop steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 with: ref: ${{ needs.release.outputs.tag }} - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3.6.0 with: - node-version: '16.x' + node-version: '18.x' - name: Install - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 10 max_attempts: 3 command: cd ${{env.desktop-directory}} && yarn - name: Build - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 30 max_attempts: 3 command: cd ${{env.desktop-directory}} && yarn build --mac --mac-dmg - name: Upload - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3.1.2 with: name: 'Flipper-mac.dmg' path: 'dist/Flipper-mac.dmg' + build-server-mac: + needs: + - release + runs-on: macos-latest + env: + desktop-directory: ./desktop + + steps: + - uses: actions/checkout@v3.5.3 + with: + ref: ${{ needs.release.outputs.tag }} + - uses: actions/setup-node@v3.6.0 + with: + node-version: '18.x' + - name: Install + uses: nick-invision/retry@v2.0.0 + with: + timeout_minutes: 10 + max_attempts: 3 + command: cd ${{env.desktop-directory}} && yarn + - name: Build + run: cd ${{env.desktop-directory}} && yarn build:flipper-server --mac --dmg + - name: List dist artifacts + run: ls -l dist/ + - name: Upload x86-64 + uses: actions/upload-artifact@v3.1.2 + with: + name: 'Flipper-server-mac-x64.dmg' + path: 'dist/Flipper-server-mac-x64.dmg' + - name: Upload aarch64 + uses: actions/upload-artifact@v3.1.2 + with: + name: 'Flipper-server-mac-aarch64.dmg' + path: 'dist/Flipper-server-mac-aarch64.dmg' + build-linux: needs: - release @@ -83,26 +120,26 @@ jobs: desktop-directory: ./desktop steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 with: ref: ${{ needs.release.outputs.tag }} - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3.6.0 with: - node-version: '16.x' + node-version: '18.x' - name: Install - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 10 max_attempts: 3 command: cd ${{env.desktop-directory}} && yarn - name: Build - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 30 max_attempts: 3 command: cd ${{env.desktop-directory}} && yarn build --linux - name: Upload Linux - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3.1.2 with: name: 'Flipper-linux.zip' path: 'dist/Flipper-linux.zip' @@ -115,28 +152,28 @@ jobs: desktop-directory: ./desktop steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 with: ref: ${{ needs.release.outputs.tag }} - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3.6.0 with: - node-version: '16.x' + node-version: '18.x' - name: Install - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 10 max_attempts: 3 shell: pwsh command: cd ${{env.desktop-directory}}; yarn - name: Build - uses: nick-invision/retry@v2.6.0 + uses: nick-fields/retry@v2.8.3 with: timeout_minutes: 30 max_attempts: 3 shell: pwsh command: cd ${{env.desktop-directory}}; yarn build --win - name: Upload Windows - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3.1.2 with: name: 'Flipper-win.zip' path: 'dist/Flipper-win.zip' @@ -149,12 +186,12 @@ jobs: desktop-directory: ./desktop steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.5.3 with: ref: ${{ needs.release.outputs.tag }} - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3.6.0 with: - node-version: '16.x' + node-version: '18.x' - name: Install uses: nick-invision/retry@v2.0.0 with: @@ -166,7 +203,7 @@ jobs: - name: List dist artifacts run: ls -l dist/ - name: Upload flipper-server - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.2 with: name: 'flipper-server.tgz' path: 'dist/flipper-server.tgz' @@ -176,17 +213,33 @@ jobs: - build-win - build-linux - build-mac + - build-server-mac - build-flipper-server - release runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3.5.3 + with: + ref: ${{ needs.release.outputs.tag }} - name: Download Mac if: ${{ needs.release.outputs.tag != '' }} uses: actions/download-artifact@v1 with: name: 'Flipper-mac.dmg' path: 'Flipper-mac.dmg' + - name: Download Flipper Server x86-64 + if: ${{ needs.release.outputs.tag != '' }} + uses: actions/download-artifact@v1 + with: + name: 'Flipper-server-mac-x64.dmg' + path: 'Flipper-server-mac-x64.dmg' + - name: Download Flipper Server aarch64 + if: ${{ needs.release.outputs.tag != '' }} + uses: actions/download-artifact@v1 + with: + name: 'Flipper-server-mac-aarch64.dmg' + path: 'Flipper-server-mac-aarch64.dmg' - name: Download Linux if: ${{ needs.release.outputs.tag != '' }} uses: actions/download-artifact@v1 @@ -207,12 +260,12 @@ jobs: path: 'flipper-server.tgz' - name: GitHub Upload Release Artifacts if: ${{ needs.release.outputs.tag != '' }} - uses: passy/github-upload-release-artifacts-action@v2.2.2 + uses: aigoncharov/github-upload-release-artifacts-action@2.2.3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: created_tag: ${{ needs.release.outputs.tag }} - args: Flipper-mac.dmg/Flipper-mac.dmg Flipper-linux.zip/Flipper-linux.zip Flipper-win.zip/Flipper-win.zip flipper-server.tgz/flipper-server.tgz + args: Flipper-mac.dmg/Flipper-mac.dmg Flipper-linux.zip/Flipper-linux.zip Flipper-win.zip/Flipper-win.zip flipper-server.tgz/flipper-server.tgz Flipper-server-mac-x64.dmg/Flipper-server-mac-x64.dmg Flipper-server-mac-aarch64.dmg/Flipper-server-mac-aarch64.dmg - name: Set up npm token run: echo "//registry.yarnpkg.com/:_authToken=${{ secrets.FLIPPER_NPM_TOKEN }}" >> ~/.npmrc - name: Publish flipper-server on NPM @@ -223,7 +276,7 @@ jobs: yarn publish - name: Open issue on failure if: failure() - uses: JasonEtco/create-an-issue@v2.4.0 + uses: JasonEtco/create-an-issue@v2.9.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPOSITORY: ${{ github.repository }} @@ -231,6 +284,7 @@ jobs: WORKFLOW_NAME: "Publish" with: filename: .github/action-failure-template.md + dispatch: needs: - release @@ -239,23 +293,20 @@ jobs: steps: - name: Publish Workflow Dispatch if: ${{ needs.release.outputs.tag != '' }} - uses: benc-uk/workflow-dispatch@v1.1 + uses: benc-uk/workflow-dispatch@v1.2.2 with: workflow: Publish Pods - token: ${{ secrets.PERSONAL_TOKEN }} ref: ${{ needs.release.outputs.tag }} - name: Publish NPM if: ${{ needs.release.outputs.tag != '' }} - uses: benc-uk/workflow-dispatch@v1.1 + uses: benc-uk/workflow-dispatch@v1.2.2 with: workflow: Publish NPM - token: ${{ secrets.PERSONAL_TOKEN }} ref: ${{ needs.release.outputs.tag }} - name: Publish Android if: ${{ needs.release.outputs.tag != '' }} - uses: benc-uk/workflow-dispatch@v1.1 + uses: benc-uk/workflow-dispatch@v1.2.2 with: workflow: Publish Android - token: ${{ secrets.PERSONAL_TOKEN }} ref: ${{ needs.release.outputs.tag }} inputs: '{"tag": "${{ needs.release.outputs.tag }}"}' diff --git a/.gitignore b/.gitignore index 961268360..9a301999a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,10 +8,20 @@ bundle.map *.bundle.map .env desktop-branch-*/ +auth.token +manifest.json # conflicts with FB internal infra .watchmanconfig +# Don't checkout the yarn/npm lock files that we don't need +./yarn.lock +./package-lock.json +js/yarn.lock +js/package-lock.json +react-native/yarn.lock +react-native/package-lock.json + # iOS / Xcode *.xcworkspace **/Pods/ @@ -49,3 +59,5 @@ website/src/embedded-pages/docs/plugins/ # Logs **/*/flipper-server-log.out + +*.salive diff --git a/Flipper.podspec b/Flipper.podspec index 946e61fe2..2040bbef2 100644 --- a/Flipper.podspec +++ b/Flipper.podspec @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -flipperkit_version = '0.162.0' +flipperkit_version = '0.222.0' Pod::Spec.new do |spec| spec.name = 'Flipper' spec.cocoapods_version = '>= 1.10' diff --git a/FlipperKit.podspec b/FlipperKit.podspec index 6c3c31960..8ea9f97b5 100644 --- a/FlipperKit.podspec +++ b/FlipperKit.podspec @@ -4,8 +4,7 @@ # LICENSE file in the root directory of this source tree. folly_compiler_flags = '-DDEBUG=1 -DFLIPPER_OSS=1 -DFB_SONARKIT_ENABLED=1 -DFOLLY_HAVE_BACKTRACE=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_HAVE_LIBGFLAGS=0 -DFOLLY_HAVE_LIBJEMALLOC=0 -DFOLLY_HAVE_PREADV=0 -DFOLLY_HAVE_PWRITEV=0 -DFOLLY_HAVE_TFO=0 -DFOLLY_USE_SYMBOLIZER=0' -yogakit_version = '~> 1.18' -flipperkit_version = '0.162.0' +flipperkit_version = '0.222.0' Pod::Spec.new do |spec| spec.name = 'FlipperKit' spec.version = flipperkit_version @@ -17,7 +16,7 @@ Pod::Spec.new do |spec| spec.source = { :git => 'https://github.com/facebook/flipper.git', :tag=> "v"+flipperkit_version } spec.module_name = 'FlipperKit' - spec.platforms = { :ios => "10.0" } + spec.platforms = { :ios => "11.0" } spec.default_subspecs = "Core" # This subspec is necessary since FBDefines.h is imported as @@ -77,7 +76,7 @@ Pod::Spec.new do |spec| ss.dependency 'FlipperKit/CppBridge' ss.dependency 'FlipperKit/FKPortForwarding' ss.dependency 'Flipper', '~>'+flipperkit_version - ss.dependency 'SocketRocket', '~> 0.6.0' + ss.dependency 'SocketRocket', '~> 0.7.0' ss.compiler_flags = folly_compiler_flags ss.source_files = 'iOS/FlipperKit/*.{h,m,mm}', 'iOS/FlipperKit/CppBridge/*.{h,mm}' ss.public_header_files = 'iOS/FlipperKit/**/{FlipperDiagnosticsViewController,FlipperStateUpdateListener,FlipperClient,FlipperPlugin,FlipperConnection,FlipperResponder,SKMacros,FlipperKitCertificateProvider}.h' @@ -119,8 +118,7 @@ Pod::Spec.new do |spec| ss.private_header_files = 'iOS/Plugins/FlipperKitPluginUtils/FlipperKitLayoutHelpers/FlipperKitLayoutHelpers/SKObject.h', 'iOS/Plugins/FlipperKitPluginUtils/FlipperKitLayoutHelpers/FlipperKitLayoutHelpers/UIColor+SKSonarValueCoder.h', 'iOS/Plugins/FlipperKitPluginUtils/FlipperKitLayoutHelpers/FlipperKitLayoutHelpers/utils/SKObjectHash.h', - 'iOS/Plugins/FlipperKitPluginUtils/FlipperKitLayoutHelpers/FlipperKitLayoutHelpers/utils/SKSwizzle.h', - 'iOS/Plugins/FlipperKitPluginUtils/FlipperKitLayoutHelpers/FlipperKitLayoutHelpers/utils/SKYogaKitHelper.h' + 'iOS/Plugins/FlipperKitPluginUtils/FlipperKitLayoutHelpers/FlipperKitLayoutHelpers/utils/SKSwizzle.h' end spec.subspec 'FlipperKitLayoutIOSDescriptors' do |ss| @@ -128,7 +126,6 @@ Pod::Spec.new do |spec| ss.dependency 'FlipperKit/Core' ss.dependency 'FlipperKit/FlipperKitHighlightOverlay' ss.dependency 'FlipperKit/FlipperKitLayoutHelpers' - ss.dependency 'YogaKit', yogakit_version ss.compiler_flags = folly_compiler_flags ss.source_files = 'iOS/Plugins/FlipperKitPluginUtils/FlipperKitLayoutIOSDescriptors/**/*.{h,mm,m}' end @@ -140,7 +137,6 @@ Pod::Spec.new do |spec| ss.dependency 'FlipperKit/FlipperKitHighlightOverlay' ss.dependency 'FlipperKit/FlipperKitLayoutHelpers' ss.dependency 'FlipperKit/FlipperKitLayoutIOSDescriptors' - ss.dependency 'YogaKit', yogakit_version ss.compiler_flags = folly_compiler_flags ss.public_header_files = 'iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h', 'iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKDescriptorMapper.h' @@ -149,11 +145,20 @@ Pod::Spec.new do |spec| ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)\"/Headers/Private/FlipperKit/**", "ONLY_ACTIVE_ARCH": "YES" } end + spec.subspec "FlipperKitUIDebuggerPlugin" do |ss| + ss.header_dir = "FlipperKitUIDebuggerPlugin" + ss.dependency 'FlipperKit/Core' + ss.public_header_files = 'iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin.h' + ss.source_files = 'iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/**/*.{h,cpp,m,mm}' + ss.exclude_files = ['iOS/Plugins/FlipperKitUIDebuggerPlugin/fb/*','iOS/Plugins/FlipperKitUIDebuggerPlugin/facebook/*','iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/fb/*' ,'iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/facebook/*'] + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)\"/Headers/Private/FlipperKit/**", "ONLY_ACTIVE_ARCH": "YES" } + end + spec.subspec "FlipperKitLayoutComponentKitSupport" do |ss| ss.header_dir = "FlipperKitLayoutComponentKitSupport" ss.dependency 'FlipperKit/Core' ss.dependency 'ComponentKit', '0.31' - ss.dependency 'RenderCore', '0.31' # Pinning it to 0.30, as there won't be any new releases from CK team. + ss.dependency 'RenderCore', '0.31' ss.dependency 'FlipperKit/FlipperKitLayoutPlugin' ss.dependency 'FlipperKit/FlipperKitLayoutTextSearchable' ss.dependency 'FlipperKit/FlipperKitHighlightOverlay' diff --git a/README.md b/README.md index 77b551455..727eed423 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@

- Flipper (formerly Sonar) is a platform for debugging mobile apps on iOS and Android and, recently, even JS apps in your browser or in Node.js. Visualize, inspect, and control your apps from a simple desktop interface. Use Flipper as is or extend it using the plugin API. + Flipper (formerly Sonar) is a platform for debugging mobile apps on iOS and Android and JS apps in your browser or in Node.js. Visualize, inspect, and control your apps from a simple desktop interface. Use Flipper as is or extend it using the plugin API.

![Flipper](website/static/img/inspector.png) @@ -139,6 +139,8 @@ Start up an android emulator and run the following in the project root: ## React Native SDK + Sample app +> Requires RN 0.69+! + ```bash cd react-native/ReactNativeFlipperExample yarn diff --git a/android/build.gradle b/android/build.gradle index 6f789dcbe..763ebccef 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,11 +7,10 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' - - apply plugin: 'kotlinx-serialization' android { + namespace 'com.facebook.flipper' compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion ndkVersion rootProject.ndkVersion @@ -32,7 +31,7 @@ android { externalNativeBuild { cmake { arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_shared' - targets 'flipper', 'event_shared', 'event_extra_shared', 'event_core_shared' + targets 'flipper' } } } @@ -49,6 +48,11 @@ android { } } + compileOptions { + targetCompatibility rootProject.javaTargetVersion + sourceCompatibility rootProject.javaTargetVersion + } + buildFeatures { prefab true } @@ -68,6 +72,7 @@ android { compileOnly deps.proguardAnnotations implementation deps.kotlinStdLibrary + implementation deps.kotlinCoroutinesAndroid implementation deps.openssl implementation deps.fbjni implementation deps.soloader diff --git a/android/no-op/build.gradle b/android/no-op/build.gradle index 7301ca418..3caf12b4c 100644 --- a/android/no-op/build.gradle +++ b/android/no-op/build.gradle @@ -8,6 +8,7 @@ apply plugin: 'com.android.library' android { + namespace 'com.facebook.flipper.noop' compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion @@ -30,10 +31,3 @@ android { } apply plugin: 'com.vanniktech.maven.publish' - -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -artifacts.add('archives', sourcesJar) diff --git a/android/no-op/src/main/AndroidManifest.xml b/android/no-op/src/main/AndroidManifest.xml index 674594014..260fd956e 100644 --- a/android/no-op/src/main/AndroidManifest.xml +++ b/android/no-op/src/main/AndroidManifest.xml @@ -7,5 +7,5 @@ --> + package="com.facebook.flipper.noop"> diff --git a/android/plugins/fresco/src/main/AndroidManifest.xml b/android/plugins/fresco/src/main/AndroidManifest.xml deleted file mode 100644 index 0bafe358f..000000000 --- a/android/plugins/fresco/src/main/AndroidManifest.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/FrescoFlipperDebugPrefHelper.java b/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/FrescoFlipperDebugPrefHelper.java deleted file mode 100644 index bc17585ae..000000000 --- a/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/FrescoFlipperDebugPrefHelper.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.flipper.plugins.fresco; - -public interface FrescoFlipperDebugPrefHelper { - - interface Listener { - void onEnabledStatusChanged(boolean enabled); - } - - void setDebugOverlayEnabled(boolean enabled); - - boolean isDebugOverlayEnabled(); - - void setDebugOverlayEnabledListener(Listener l); -} diff --git a/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/FrescoFlipperPlugin.java b/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/FrescoFlipperPlugin.java deleted file mode 100644 index 0d9c0f31a..000000000 --- a/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/FrescoFlipperPlugin.java +++ /dev/null @@ -1,651 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.flipper.plugins.fresco; - -import android.graphics.Bitmap; -import android.util.Base64; -import android.util.Pair; -import bolts.Continuation; -import bolts.Task; -import com.facebook.cache.common.CacheKey; -import com.facebook.cache.common.SimpleCacheKey; -import com.facebook.cache.disk.DiskStorage; -import com.facebook.common.internal.ByteStreams; -import com.facebook.common.internal.Preconditions; -import com.facebook.common.internal.Predicate; -import com.facebook.common.memory.PooledByteBuffer; -import com.facebook.common.memory.PooledByteBufferInputStream; -import com.facebook.common.memory.manager.DebugMemoryManager; -import com.facebook.common.memory.manager.NoOpDebugMemoryManager; -import com.facebook.common.references.CloseableReference; -import com.facebook.common.references.SharedReference; -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.drawee.backends.pipeline.info.ImageLoadStatus; -import com.facebook.drawee.backends.pipeline.info.ImageOriginUtils; -import com.facebook.drawee.backends.pipeline.info.ImagePerfData; -import com.facebook.drawee.backends.pipeline.info.ImagePerfDataListener; -import com.facebook.flipper.core.FlipperArray; -import com.facebook.flipper.core.FlipperConnection; -import com.facebook.flipper.core.FlipperObject; -import com.facebook.flipper.core.FlipperReceiver; -import com.facebook.flipper.core.FlipperResponder; -import com.facebook.flipper.perflogger.FlipperPerfLogger; -import com.facebook.flipper.perflogger.NoOpFlipperPerfLogger; -import com.facebook.flipper.plugins.common.BufferingFlipperPlugin; -import com.facebook.flipper.plugins.fresco.objecthelper.FlipperObjectHelper; -import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory; -import com.facebook.imagepipeline.cache.CountingMemoryCacheInspector; -import com.facebook.imagepipeline.cache.CountingMemoryCacheInspector.DumpInfoEntry; -import com.facebook.imagepipeline.core.ImagePipelineFactory; -import com.facebook.imagepipeline.debug.CloseableReferenceLeakTracker; -import com.facebook.imagepipeline.debug.DebugImageTracker; -import com.facebook.imagepipeline.debug.FlipperImageTracker; -import com.facebook.imagepipeline.image.CloseableBitmap; -import com.facebook.imagepipeline.image.CloseableImage; -import com.facebook.imagepipeline.image.EncodedImage; -import com.facebook.imageutils.BitmapUtil; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import javax.annotation.Nullable; - -/** - * Allows Sonar to display the contents of Fresco's caches. This is useful for developers to debug - * what images are being held in cache as they navigate through their app. - */ -public class FrescoFlipperPlugin extends BufferingFlipperPlugin - implements ImagePerfDataListener, CloseableReferenceLeakTracker.Listener { - - private static final String FRESCO_EVENT = "events"; - private static final String FRESCO_DEBUGOVERLAY_EVENT = "debug_overlay_event"; - private static final String FRESCO_CLOSEABLE_REFERENCE_LEAK_EVENT = - "closeable_reference_leak_event"; - - private static final int BITMAP_PREVIEW_WIDTH = 150; - private static final int BITMAP_PREVIEW_HEIGHT = 150; - private static final int BITMAP_SCALING_THRESHOLD_WIDTH = 200; - private static final int BITMAP_SCALING_THRESHOLD_HEIGHT = 200; - - /** Helper for clearing cache. */ - private static final Predicate ALWAYS_TRUE_PREDICATE = - new Predicate() { - @Override - public boolean apply(CacheKey cacheKey) { - return true; - } - }; - - private final FlipperImageTracker mFlipperImageTracker; - private final PlatformBitmapFactory mPlatformBitmapFactory; - @Nullable private final FlipperObjectHelper mSonarObjectHelper; - private final DebugMemoryManager mMemoryManager; - private final FlipperPerfLogger mPerfLogger; - @Nullable private final FrescoFlipperDebugPrefHelper mDebugPrefHelper; - private final List mEvents = new ArrayList<>(); - - public FrescoFlipperPlugin( - DebugImageTracker imageTracker, - PlatformBitmapFactory bitmapFactory, - @Nullable FlipperObjectHelper flipperObjectHelper, - DebugMemoryManager memoryManager, - FlipperPerfLogger perfLogger, - @Nullable FrescoFlipperDebugPrefHelper debugPrefHelper, - @Nullable CloseableReferenceLeakTracker closeableReferenceLeakTracker) { - mFlipperImageTracker = - imageTracker instanceof FlipperImageTracker - ? (FlipperImageTracker) imageTracker - : new FlipperImageTracker(); - mPlatformBitmapFactory = bitmapFactory; - mSonarObjectHelper = flipperObjectHelper; - mMemoryManager = memoryManager; - mPerfLogger = perfLogger; - mDebugPrefHelper = debugPrefHelper; - - if (closeableReferenceLeakTracker != null) { - closeableReferenceLeakTracker.setListener(this); - } - } - - public FrescoFlipperPlugin() { - this( - new FlipperImageTracker(), - Fresco.getImagePipelineFactory().getPlatformBitmapFactory(), - null, - new NoOpDebugMemoryManager(), - new NoOpFlipperPerfLogger(), - null, - null); - } - - public FlipperImageTracker getFlipperImageTracker() { - return mFlipperImageTracker; - } - - @Override - public String getId() { - return "Fresco"; - } - - @Override - public void onConnect(FlipperConnection connection) { - super.onConnect(connection); - connection.receive( - "getAllImageEventsInfo", - new FlipperReceiver() { - @Override - public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception { - if (!ensureFrescoInitialized()) { - return; - } - - FlipperArray.Builder arrayBuilder = new FlipperArray.Builder(); - for (FlipperObject obj : mEvents) { - arrayBuilder.put(obj); - } - mEvents.clear(); - - FlipperObject object = - new FlipperObject.Builder().put("events", arrayBuilder.build()).build(); - responder.success(object); - } - }); - - connection.receive( - "listImages", - new FlipperReceiver() { - @Override - public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception { - if (!ensureFrescoInitialized()) { - return; - } - - mPerfLogger.startMarker("Sonar.Fresco.listImages"); - final boolean showDiskImages = params.getBoolean("showDiskImages"); - final ImagePipelineFactory imagePipelineFactory = Fresco.getImagePipelineFactory(); - - final CountingMemoryCacheInspector.DumpInfo bitmapMemoryCache = - new CountingMemoryCacheInspector<>( - imagePipelineFactory.getBitmapCountingMemoryCache()) - .dumpCacheContent(); - final CountingMemoryCacheInspector.DumpInfo encodedMemoryCache = - new CountingMemoryCacheInspector<>( - imagePipelineFactory.getEncodedCountingMemoryCache()) - .dumpCacheContent(); - - try { - responder.success( - getImageList(bitmapMemoryCache, encodedMemoryCache, showDiskImages)); - mPerfLogger.endMarker("Sonar.Fresco.listImages"); - } finally { - bitmapMemoryCache.release(); - encodedMemoryCache.release(); - } - } - }); - - connection.receive( - "getImage", - new FlipperReceiver() { - @Override - public void onReceive(FlipperObject params, final FlipperResponder responder) - throws Exception { - if (!ensureFrescoInitialized()) { - return; - } - - mPerfLogger.startMarker("Sonar.Fresco.getImage"); - final String imageId = params.getString("imageId"); - final CacheKey cacheKey = mFlipperImageTracker.getCacheKey(imageId); - if (cacheKey == null) { - respondError(responder, "ImageId " + imageId + " was evicted from cache"); - mPerfLogger.cancelMarker("Sonar.Fresco.getImage"); - return; - } - - final ImagePipelineFactory imagePipelineFactory = Fresco.getImagePipelineFactory(); - - // try to load from bitmap cache - @Nullable - CloseableImage closeableImage = - imagePipelineFactory.getBitmapCountingMemoryCache().inspect(cacheKey); - if (closeableImage instanceof CloseableBitmap) { - @Nullable Bitmap bitmap = ((CloseableBitmap) closeableImage).getUnderlyingBitmap(); - if (bitmap != null) { - loadFromBitmapCache(bitmap, imageId, cacheKey, responder); - mPerfLogger.endMarker("Sonar.Fresco.getImage"); - return; - } - } - - // try to load from encoded cache - PooledByteBuffer encoded = - imagePipelineFactory.getEncodedCountingMemoryCache().inspect(cacheKey); - if (encoded != null) { - loadFromEncodedCache(encoded, imageId, cacheKey, responder); - mPerfLogger.endMarker("Sonar.Fresco.getImage"); - return; - } - - // try to load from disk - loadFromDisk(imageId, cacheKey, responder); - } - - private void loadFromBitmapCache( - final Bitmap bitmap, - final String imageId, - final CacheKey cacheKey, - final FlipperResponder responder) { - String encodedBitmap = bitmapToBase64Preview(bitmap, mPlatformBitmapFactory); - responder.success( - getImageData( - imageId, - mFlipperImageTracker.getUriString(cacheKey), - bitmap.getWidth(), - bitmap.getHeight(), - BitmapUtil.getSizeInBytes(bitmap), - encodedBitmap)); - } - - private void loadFromEncodedCache( - final PooledByteBuffer encoded, - final String imageId, - final CacheKey cacheKey, - final FlipperResponder responder) - throws Exception { - byte[] encodedArray = ByteStreams.toByteArray(new PooledByteBufferInputStream(encoded)); - Pair dimensions = BitmapUtil.decodeDimensions(encodedArray); - if (dimensions == null) { - respondError(responder, "can not get dimensions withId=" + imageId); - return; - } - - responder.success( - getImageData( - imageId, - mFlipperImageTracker.getUriString(cacheKey), - dimensions.first, - dimensions.second, - encodedArray.length, - dataFromEncodedArray(encodedArray))); - } - - private void loadFromDisk( - final String imageId, final CacheKey cacheKey, final FlipperResponder responder) { - Task t = - Fresco.getImagePipelineFactory() - .getMainBufferedDiskCache() - .get(cacheKey, new AtomicBoolean(false)); - - t.continueWith( - new Continuation() { - public Void then(Task task) throws Exception { - if (task.isCancelled() || task.isFaulted()) { - respondError(responder, "no bitmap withId=" + imageId); - mPerfLogger.cancelMarker("Sonar.Fresco.getImage"); - return null; - } - Preconditions.checkNotNull(task); - final EncodedImage image = task.getResult(); - try { - InputStream stream = Preconditions.checkNotNull(image.getInputStream()); - byte[] encodedArray = ByteStreams.toByteArray(stream); - - responder.success( - getImageData( - imageId, - Preconditions.checkNotNull( - mFlipperImageTracker.getLocalPath(cacheKey)), - image.getWidth(), - image.getHeight(), - encodedArray.length, - dataFromEncodedArray(encodedArray))); - } finally { - EncodedImage.closeSafely(image); - } - mPerfLogger.endMarker("Sonar.Fresco.getImage"); - return null; - } - }); - } - }); - - connection.receive( - "clear", - new FlipperReceiver() { - @Override - public void onReceive(FlipperObject params, FlipperResponder responder) { - if (!ensureFrescoInitialized()) { - return; - } - - mPerfLogger.startMarker("Sonar.Fresco.clear"); - final String type = params.getString("type"); - switch (type) { - case "memory": - final ImagePipelineFactory imagePipelineFactory = Fresco.getImagePipelineFactory(); - imagePipelineFactory.getBitmapMemoryCache().removeAll(ALWAYS_TRUE_PREDICATE); - break; - case "disk": - Fresco.getImagePipeline().clearDiskCaches(); - break; - } - mPerfLogger.endMarker("Sonar.Fresco.clear"); - } - }); - - connection.receive( - "trimMemory", - new FlipperReceiver() { - @Override - public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception { - if (!ensureFrescoInitialized()) { - return; - } - - if (mMemoryManager != null) { - mMemoryManager.trimMemory( - DebugMemoryManager.ON_SYSTEM_LOW_MEMORY_WHILE_APP_IN_FOREGROUND); - } - } - }); - - connection.receive( - "enableDebugOverlay", - new FlipperReceiver() { - @Override - public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception { - if (!ensureFrescoInitialized()) { - return; - } - - final boolean enabled = params.getBoolean("enabled"); - if (mDebugPrefHelper != null) { - mDebugPrefHelper.setDebugOverlayEnabled(enabled); - } - } - }); - - if (mDebugPrefHelper != null) { - mDebugPrefHelper.setDebugOverlayEnabledListener( - new FrescoFlipperDebugPrefHelper.Listener() { - @Override - public void onEnabledStatusChanged(boolean enabled) { - sendDebugOverlayEnabledEvent(enabled); - } - }); - sendDebugOverlayEnabledEvent(mDebugPrefHelper.isDebugOverlayEnabled()); - } - } - - private static String dataFromEncodedArray(byte[] encodedArray) { - return "data:image/jpeg;base64," + Base64.encodeToString(encodedArray, Base64.DEFAULT); - } - - private FlipperObject getImageList( - final CountingMemoryCacheInspector.DumpInfo bitmapMemoryCache, - final CountingMemoryCacheInspector.DumpInfo encodedMemoryCache, - final boolean showDiskImages) - throws IOException { - FlipperArray.Builder levelsBuilder = - new FlipperArray.Builder() - // bitmap - .put(getUsedStats("On screen bitmaps", bitmapMemoryCache)) - .put(getCachedStats("Bitmap memory cache", bitmapMemoryCache)) - // encoded - .put(getUsedStats("Used encoded images", encodedMemoryCache)) - .put(getCachedStats("Cached encoded images", encodedMemoryCache)); - if (showDiskImages) { - levelsBuilder.put( - getDiskStats( - "Disk images", - Fresco.getImagePipelineFactory().getMainFileCache().getDumpInfo().entries)); - } - - return new FlipperObject.Builder().put("levels", levelsBuilder.build()).build(); - } - - private FlipperObject getUsedStats( - final String cacheType, final CountingMemoryCacheInspector.DumpInfo memoryCache) { - return new FlipperObject.Builder() - .put("cacheType", cacheType) - .put("sizeBytes", memoryCache.size - memoryCache.lruSize) - .put("imageIds", buildImageIdList(memoryCache.sharedEntries)) - .build(); - } - - private FlipperObject getCachedStats( - final String cacheType, final CountingMemoryCacheInspector.DumpInfo memoryCache) { - return new FlipperObject.Builder() - .put("cacheType", cacheType) - .put("clearKey", "memory") - .put("sizeBytes", memoryCache.size) - .put("maxSizeBytes", memoryCache.maxSize) - .put("imageIds", buildImageIdList(memoryCache.lruEntries)) - .build(); - } - - private FlipperObject getDiskStats( - final String cacheType, List diskEntries) { - return new FlipperObject.Builder() - .put("cacheType", cacheType) - .put("clearKey", "disk") - .put("sizeBytes", Fresco.getImagePipelineFactory().getMainFileCache().getSize()) - .put("imageIds", buildImageIdListDisk(diskEntries)) - .build(); - } - - private static FlipperObject getImageData( - String imageID, String uriString, int width, int height, int sizeBytes, String data) { - return new FlipperObject.Builder() - .put("imageId", imageID) - .put("uri", uriString) - .put("width", width) - .put("height", height) - .put("sizeBytes", sizeBytes) - .put("data", data) - .build(); - } - - private boolean ensureFrescoInitialized() { - mPerfLogger.startMarker("Sonar.Fresco.ensureFrescoInitialized"); - try { - Fresco.getImagePipelineFactory(); - return true; - } catch (NullPointerException e) { - return false; - } finally { - mPerfLogger.endMarker("Sonar.Fresco.ensureFrescoInitialized"); - } - } - - private FlipperArray buildImageIdList(List> images) { - FlipperArray.Builder builder = new FlipperArray.Builder(); - for (DumpInfoEntry entry : images) { - final FlipperImageTracker.ImageDebugData imageDebugData = - mFlipperImageTracker.getImageDebugData(entry.key); - - if (imageDebugData == null) { - builder.put(mFlipperImageTracker.trackImage(entry.key).getUniqueId()); - } else { - builder.put(imageDebugData.getUniqueId()); - } - } - return builder.build(); - } - - private FlipperArray buildImageIdListDisk(List diskEntries) { - FlipperArray.Builder builder = new FlipperArray.Builder(); - for (DiskStorage.DiskDumpInfoEntry entry : diskEntries) { - final CacheKey entryCacheKey = new SimpleCacheKey(entry.id, true); - final FlipperImageTracker.ImageDebugData imageDebugData = - mFlipperImageTracker.getImageDebugData(entryCacheKey); - - if (imageDebugData == null) { - builder.put(mFlipperImageTracker.trackImage(entry.path, entryCacheKey).getUniqueId()); - } else { - builder.put(imageDebugData.getUniqueId()); - } - } - return builder.build(); - } - - private String bitmapToBase64Preview(Bitmap bitmap, PlatformBitmapFactory bitmapFactory) { - if (bitmap.getWidth() < BITMAP_SCALING_THRESHOLD_WIDTH - && bitmap.getHeight() < BITMAP_SCALING_THRESHOLD_HEIGHT) { - return bitmapToBase64WithoutScaling(bitmap); - } - mPerfLogger.startMarker("Sonar.Fresco.bitmap2base64-resize"); - - // TODO (t19034797): properly load images - CloseableReference scaledBitmapReference = null; - try { - float previewAspectRatio = BITMAP_PREVIEW_WIDTH / BITMAP_PREVIEW_HEIGHT; - float imageAspectRatio = bitmap.getWidth() / bitmap.getHeight(); - - int scaledWidth; - int scaledHeight; - if (previewAspectRatio > imageAspectRatio) { - scaledWidth = bitmap.getWidth() * BITMAP_PREVIEW_HEIGHT / bitmap.getHeight(); - scaledHeight = BITMAP_PREVIEW_HEIGHT; - } else { - scaledWidth = BITMAP_PREVIEW_WIDTH; - scaledHeight = bitmap.getHeight() * BITMAP_PREVIEW_WIDTH / bitmap.getWidth(); - } - scaledBitmapReference = - bitmapFactory.createScaledBitmap(bitmap, scaledWidth, scaledHeight, false); - return bitmapToBase64WithoutScaling(scaledBitmapReference.get()); - } finally { - CloseableReference.closeSafely(scaledBitmapReference); - mPerfLogger.endMarker("Sonar.Fresco.bitmap2base64-resize"); - } - } - - private String bitmapToBase64WithoutScaling(Bitmap bitmap) { - mPerfLogger.startMarker("Sonar.Fresco.bitmap2base64-orig"); - ByteArrayOutputStream byteArrayOutputStream = null; - try { - byteArrayOutputStream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); - - return "data:image/png;base64," - + Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT); - } finally { - if (byteArrayOutputStream != null) { - try { - byteArrayOutputStream.close(); - } catch (IOException e) { - // ignore - } - } - mPerfLogger.endMarker("Sonar.Fresco.bitmap2base64-orig"); - } - } - - public void onImageLoadStatusUpdated( - ImagePerfData imagePerfData, @ImageLoadStatus int imageLoadStatus) { - if (imageLoadStatus != ImageLoadStatus.SUCCESS) { - return; - } - - String requestId = imagePerfData.getRequestId(); - if (requestId == null) { - return; - } - - FlipperImageTracker.ImageDebugData data = - mFlipperImageTracker.getDebugDataForRequestId(requestId); - if (data == null) { - return; - } - - FlipperArray.Builder imageIdsBuilder = new FlipperArray.Builder(); - Set cks = data.getCacheKeys(); - if (cks != null) { - for (CacheKey ck : cks) { - FlipperImageTracker.ImageDebugData d = mFlipperImageTracker.getImageDebugData(ck); - if (d != null) { - imageIdsBuilder.put(d.getUniqueId()); - } - } - } else { - imageIdsBuilder.put(data.getUniqueId()); - } - - FlipperArray attribution; - Object callerContext = imagePerfData.getCallerContext(); - if (callerContext == null) { - attribution = new FlipperArray.Builder().put("unknown").build(); - } else if (mSonarObjectHelper == null) { - attribution = new FlipperArray.Builder().put(callerContext.toString()).build(); - } else { - attribution = mSonarObjectHelper.fromCallerContext(callerContext); - } - - FlipperObject.Builder response = - new FlipperObject.Builder() - .put("imageIds", imageIdsBuilder.build()) - .put("attribution", attribution) - .put("startTime", imagePerfData.getControllerSubmitTimeMs()) - .put("endTime", imagePerfData.getControllerFinalImageSetTimeMs()) - .put("source", ImageOriginUtils.toString(imagePerfData.getImageOrigin())); - - if (!imagePerfData.isPrefetch()) { - response.put( - "viewport", - new FlipperObject.Builder() - // TODO (t31947746): scan times - .put("width", imagePerfData.getOnScreenWidthPx()) - .put("height", imagePerfData.getOnScreenHeightPx()) - .build()); - } - FlipperObject responseObject = response.build(); - mEvents.add(responseObject); - send(FRESCO_EVENT, responseObject); - } - - public void onImageVisibilityUpdated(ImagePerfData imagePerfData, int visibilityState) { - // ignored - } - - public void sendDebugOverlayEnabledEvent(final boolean enabled) { - final FlipperObject.Builder builder = new FlipperObject.Builder().put("enabled", enabled); - send(FRESCO_DEBUGOVERLAY_EVENT, builder.build()); - } - - private static void respondError(FlipperResponder responder, String errorReason) { - responder.error(new FlipperObject.Builder().put("reason", errorReason).build()); - } - - @Override - public void onCloseableReferenceLeak( - SharedReference reference, @Nullable Throwable stacktrace) { - Object object = reference.get(); - Preconditions.checkNotNull(object); - final FlipperObject.Builder builder = - new FlipperObject.Builder() - .put("identityHashCode", System.identityHashCode(reference)) - .put("className", object.getClass().getName()); - if (stacktrace != null) { - builder.put("stacktrace", getStackTraceString(stacktrace)); - } - send(FRESCO_CLOSEABLE_REFERENCE_LEAK_EVENT, builder.build()); - } - - public static String getStackTraceString(Throwable tr) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - tr.printStackTrace(pw); - return sw.toString(); - } -} diff --git a/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/FrescoFlipperRequestListener.java b/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/FrescoFlipperRequestListener.java deleted file mode 100644 index cba60f81a..000000000 --- a/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/FrescoFlipperRequestListener.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.flipper.plugins.fresco; - -import com.facebook.imagepipeline.debug.DebugImageTracker; -import com.facebook.imagepipeline.listener.BaseRequestListener; -import com.facebook.imagepipeline.request.ImageRequest; - -/** Fresco image {@link RequestListener} that logs events for Sonar. */ -public class FrescoFlipperRequestListener extends BaseRequestListener { - - private final DebugImageTracker mDebugImageTracker; - - public FrescoFlipperRequestListener(DebugImageTracker debugImageTracker) { - mDebugImageTracker = debugImageTracker; - } - - @Override - public void onRequestStart( - ImageRequest request, Object callerContext, String requestId, boolean isPrefetch) { - mDebugImageTracker.trackImageRequest(request, requestId); - } -} diff --git a/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/objecthelper/FlipperObjectHelper.java b/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/objecthelper/FlipperObjectHelper.java deleted file mode 100644 index 722193d3b..000000000 --- a/android/plugins/fresco/src/main/java/com/facebook/flipper/plugins/fresco/objecthelper/FlipperObjectHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.flipper.plugins.fresco.objecthelper; - -import static com.facebook.flipper.plugins.inspector.InspectorValue.Type.Color; - -import android.text.TextUtils; -import com.facebook.drawee.backends.pipeline.info.ImageOriginUtils; -import com.facebook.drawee.backends.pipeline.info.ImagePerfData; -import com.facebook.drawee.generic.RoundingParams; -import com.facebook.flipper.core.FlipperArray; -import com.facebook.flipper.core.FlipperObject; -import com.facebook.flipper.plugins.inspector.InspectorValue; -import com.facebook.imagepipeline.common.ImageDecodeOptions; -import com.facebook.imagepipeline.common.ResizeOptions; -import com.facebook.imagepipeline.common.RotationOptions; -import com.facebook.imagepipeline.debug.FlipperImageTracker; -import com.facebook.imagepipeline.image.ImageInfo; -import com.facebook.imagepipeline.image.QualityInfo; -import com.facebook.imagepipeline.request.ImageRequest; -import java.util.Map; -import javax.annotation.Nullable; - -/** Serialization helper to create {@link FlipperObject}s. */ -public abstract class FlipperObjectHelper { - - public FlipperObject keyValuePair(String key, @Nullable String value) { - return new FlipperObject.Builder().put(key, value).build(); - } - - @Nullable - public FlipperObject toFlipperObject(@Nullable Map stringMap) { - if (stringMap == null) { - return null; - } - FlipperObject.Builder optionsJson = new FlipperObject.Builder(); - for (Map.Entry entry : stringMap.entrySet()) { - optionsJson.put(entry.getKey(), entry.getValue()); - } - return optionsJson.build(); - } - - @Nullable - public FlipperObject toFlipperObject(@Nullable ImageRequest imageRequest) { - if (imageRequest == null) { - return null; - } - FlipperObject.Builder optionsJson = new FlipperObject.Builder(); - return addImageRequestProperties(optionsJson, imageRequest).build(); - } - - @Nullable - public FlipperObject toFlipperObject( - @Nullable FlipperImageTracker.ImageDebugData imageDebugData) { - if (imageDebugData == null) { - return null; - } - FlipperObject.Builder optionsJson = new FlipperObject.Builder(); - optionsJson.put("imageId", imageDebugData.getUniqueId()); - optionsJson.put("imageRequest", toFlipperObject(imageDebugData.getImageRequest())); - optionsJson.put( - "requestId", - imageDebugData.getRequestIds() != null - ? TextUtils.join(", ", imageDebugData.getRequestIds()) - : ""); - optionsJson.put("imagePerfData", toFlipperObject(imageDebugData.getImagePerfData())); - return optionsJson.build(); - } - - @Nullable - public FlipperObject toFlipperObject(@Nullable ImageDecodeOptions options) { - if (options == null) { - return null; - } - FlipperObject.Builder optionsJson = new FlipperObject.Builder(); - optionsJson.put("minDecodeIntervalMs", options.minDecodeIntervalMs); - optionsJson.put("decodePreviewFrame", options.decodePreviewFrame); - optionsJson.put("useLastFrameForPreview", options.useLastFrameForPreview); - optionsJson.put("decodeAllFrames", options.decodeAllFrames); - optionsJson.put("forceStaticImage", options.forceStaticImage); - optionsJson.put("bitmapConfig", options.bitmapConfig.name()); - optionsJson.put( - "customImageDecoder", - options.customImageDecoder == null ? "" : options.customImageDecoder.toString()); - return optionsJson.build(); - } - - @Nullable - public FlipperObject toFlipperObject(@Nullable ResizeOptions resizeOptions) { - if (resizeOptions == null) { - return null; - } - FlipperObject.Builder optionsJson = new FlipperObject.Builder(); - optionsJson.put("width", resizeOptions.width); - optionsJson.put("height", resizeOptions.height); - optionsJson.put("maxBitmapSize", resizeOptions.maxBitmapSize); - optionsJson.put("roundUpFraction", resizeOptions.roundUpFraction); - return optionsJson.build(); - } - - @Nullable - public FlipperObject toFlipperObject(@Nullable RotationOptions rotationOptions) { - if (rotationOptions == null) { - return null; - } - FlipperObject.Builder optionsJson = new FlipperObject.Builder(); - optionsJson.put("rotationEnabled", rotationOptions.rotationEnabled()); - optionsJson.put("canDeferUntilRendered", rotationOptions.canDeferUntilRendered()); - optionsJson.put("useImageMetadata", rotationOptions.useImageMetadata()); - if (!rotationOptions.useImageMetadata()) { - optionsJson.put("forcedAngle", rotationOptions.getForcedAngle()); - } - return optionsJson.build(); - } - - @Nullable - public FlipperObject toFlipperObject(@Nullable RoundingParams roundingParams) { - if (roundingParams == null) { - return null; - } - FlipperObject.Builder optionsJson = new FlipperObject.Builder(); - optionsJson.put("borderWidth", roundingParams.getBorderWidth()); - optionsJson.put("cornersRadii", toSonarArray(roundingParams.getCornersRadii())); - optionsJson.put("padding", roundingParams.getPadding()); - optionsJson.put("roundAsCircle", roundingParams.getRoundAsCircle()); - optionsJson.put("roundingMethod", roundingParams.getRoundingMethod()); - optionsJson.put( - "borderColor", InspectorValue.immutable(Color, roundingParams.getBorderColor())); - optionsJson.put( - "overlayColor", InspectorValue.immutable(Color, roundingParams.getOverlayColor())); - return optionsJson.build(); - } - - @Nullable - public FlipperObject toFlipperObject(@Nullable ImagePerfData imagePerfData) { - if (imagePerfData == null) { - return null; - } - FlipperObject.Builder objectJson = new FlipperObject.Builder(); - objectJson.put("requestId", imagePerfData.getRequestId()); - objectJson.put("controllerSubmitTimeMs", imagePerfData.getControllerSubmitTimeMs()); - objectJson.put("controllerFinalTimeMs", imagePerfData.getControllerFinalImageSetTimeMs()); - objectJson.put("imageRequestStartTimeMs", imagePerfData.getImageRequestStartTimeMs()); - objectJson.put("imageRequestEndTimeMs", imagePerfData.getImageRequestEndTimeMs()); - objectJson.put("imageOrigin", ImageOriginUtils.toString(imagePerfData.getImageOrigin())); - objectJson.put("isPrefetch", imagePerfData.isPrefetch()); - objectJson.put("callerContext", imagePerfData.getCallerContext()); - objectJson.put("imageRequest", toFlipperObject(imagePerfData.getImageRequest())); - objectJson.put("imageInfo", toFlipperObject(imagePerfData.getImageInfo())); - return objectJson.build(); - } - - @Nullable - public FlipperObject toFlipperObject(ImageInfo imageInfo) { - if (imageInfo == null) { - return null; - } - FlipperObject.Builder objectJson = new FlipperObject.Builder(); - objectJson.put("imageWidth", imageInfo.getWidth()); - objectJson.put("imageHeight", imageInfo.getHeight()); - objectJson.put("qualityInfo", toFlipperObject(imageInfo.getQualityInfo())); - return objectJson.build(); - } - - @Nullable - public FlipperObject toFlipperObject(QualityInfo qualityInfo) { - if (qualityInfo == null) { - return null; - } - FlipperObject.Builder objectJson = new FlipperObject.Builder(); - objectJson.put("quality", qualityInfo.getQuality()); - objectJson.put("isGoodEnoughQuality", qualityInfo.isOfGoodEnoughQuality()); - objectJson.put("isFullQuality", qualityInfo.isOfFullQuality()); - return objectJson.build(); - } - - public FlipperObject.Builder addImageRequestProperties( - FlipperObject.Builder builder, @Nullable ImageRequest request) { - if (request == null) { - return builder; - } - builder - .put("sourceUri", request.getSourceUri()) - .put("preferredWidth", request.getPreferredWidth()) - .put("preferredHeight", request.getPreferredHeight()) - .put("cacheChoice", request.getCacheChoice()) - .put("diskCacheEnabled", request.isDiskCacheEnabled()) - .put("localThumbnailPreviewsEnabled", request.getLocalThumbnailPreviewsEnabled()) - .put("lowestPermittedRequestLevel", request.getLowestPermittedRequestLevel()) - .put("priority", request.getPriority().name()) - .put("progressiveRenderingEnabled", request.getProgressiveRenderingEnabled()) - .put("postprocessor", String.valueOf(request.getPostprocessor())) - .put("requestListener", String.valueOf(request.getRequestListener())) - .put("imageDecodeOptions", toFlipperObject(request.getImageDecodeOptions())) - .put("bytesRange", request.getBytesRange()) - .put("resizeOptions", toFlipperObject(request.getResizeOptions())) - .put("rotationOptions", toFlipperObject(request.getRotationOptions())); - return builder; - } - - private FlipperArray toSonarArray(float[] floats) { - final FlipperArray.Builder builder = new FlipperArray.Builder(); - for (float f : floats) { - builder.put(f); - } - return builder.build(); - } - - @Nullable - public abstract FlipperArray fromCallerContext(@Nullable Object callerContext); -} diff --git a/android/plugins/fresco/build.gradle b/android/plugins/jetpack-compose/build.gradle similarity index 58% rename from android/plugins/fresco/build.gradle rename to android/plugins/jetpack-compose/build.gradle index 737ae77b1..a4e323712 100644 --- a/android/plugins/fresco/build.gradle +++ b/android/plugins/jetpack-compose/build.gradle @@ -6,8 +6,10 @@ */ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { + namespace 'com.facebook.flipper.plugins.jetpackcompose' compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion @@ -16,19 +18,18 @@ android { targetSdkVersion rootProject.targetSdkVersion } + compileOptions { + targetCompatibility rootProject.javaTargetVersion + sourceCompatibility rootProject.javaTargetVersion + } + dependencies { implementation project(':android') - implementation deps.fresco - implementation deps.frescoFlipper - compileOnly deps.jsr305 - - api deps.boltsTasks - - // Exclude the actual stetho dep as we only want some of the fresco APIs here - implementation(deps.frescoStetho) { - exclude group: 'com.facebook.stetho' - } + implementation 'androidx.compose.ui:ui:1.4.3' + implementation 'androidx.compose.ui:ui-tooling:1.4.3' + implementation 'org.jetbrains.kotlin:kotlin-reflect:1.9.0' } } + apply plugin: 'com.vanniktech.maven.publish' diff --git a/android/plugins/fresco/gradle.properties b/android/plugins/jetpack-compose/gradle.properties similarity index 55% rename from android/plugins/fresco/gradle.properties rename to android/plugins/jetpack-compose/gradle.properties index 47f4e428c..9de462875 100644 --- a/android/plugins/fresco/gradle.properties +++ b/android/plugins/jetpack-compose/gradle.properties @@ -5,8 +5,8 @@ # file in the root directory of this source tree. # -POM_NAME=Flipper Fresco Plugin -POM_DESCRIPTION=Images plugin for Flipper -POM_ARTIFACT_ID=flipper-fresco-plugin +POM_NAME=Flipper Jetpack Compose UIDebugger Plugin +POM_DESCRIPTION=Jetpack Compose Plugin for the Flipper UIDebugger +POM_ARTIFACT_ID=flipper-jetpack-compose-plugin POM_PACKAGING=aar diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt new file mode 100644 index 000000000..55746b67d --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.jetpackcompose + +import androidx.compose.ui.platform.ComposeView +import com.facebook.flipper.plugins.jetpackcompose.descriptors.* +import com.facebook.flipper.plugins.jetpackcompose.model.* +import com.facebook.flipper.plugins.uidebugger.core.UIDContext +import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister + +const val JetpackComposeTag = "JetpackCompose" + +object UIDebuggerComposeSupport { + + fun enable(context: UIDContext) { + addDescriptors(context.descriptorRegister) + } + + private fun addDescriptors(register: DescriptorRegister) { + register.register(ComposeView::class.java, ComposeViewDescriptor) + register.register(ComposeNode::class.java, ComposeNodeDescriptor) + register.register(ComposeInnerViewNode::class.java, ComposeInnerViewDescriptor) + } +} diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeInnerViewDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeInnerViewDescriptor.kt new file mode 100644 index 000000000..8e430d89a --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeInnerViewDescriptor.kt @@ -0,0 +1,81 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.jetpackcompose.descriptors + +import android.graphics.Bitmap +import android.view.ViewGroup +import com.facebook.flipper.plugins.jetpackcompose.model.ComposeInnerViewNode +import com.facebook.flipper.plugins.uidebugger.descriptors.Id +import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor +import com.facebook.flipper.plugins.uidebugger.descriptors.ViewDescriptor +import com.facebook.flipper.plugins.uidebugger.descriptors.ViewGroupDescriptor +import com.facebook.flipper.plugins.uidebugger.model.Bounds +import com.facebook.flipper.plugins.uidebugger.model.InspectableObject +import com.facebook.flipper.plugins.uidebugger.model.MetadataId +import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred +import java.lang.System + +object ComposeInnerViewDescriptor : NodeDescriptor { + + override fun getId(node: ComposeInnerViewNode): Id = System.identityHashCode(node.view) + + override fun getBounds(node: ComposeInnerViewNode): Bounds { + return node.bounds + } + + override fun getName(node: ComposeInnerViewNode): String { + if (node.view is ViewGroup) { + return ViewGroupDescriptor.getName(node.view) + } + return ViewDescriptor.getName(node.view) + } + + override fun getQualifiedName(node: ComposeInnerViewNode): String { + if (node.view is ViewGroup) { + return ViewGroupDescriptor.getQualifiedName(node.view) + } + return ViewDescriptor.getQualifiedName(node.view) + } + + override fun getChildren(node: ComposeInnerViewNode): List { + if (node.view is ViewGroup) { + return ViewGroupDescriptor.getChildren(node.view) + } + return ViewDescriptor.getChildren(node.view) + } + + override fun getSnapshot(node: ComposeInnerViewNode, bitmap: Bitmap?): Bitmap? { + if (node.view is ViewGroup) { + return ViewGroupDescriptor.getSnapshot(node.view, bitmap) + } + return ViewDescriptor.getSnapshot(node.view, bitmap) + } + + override fun getActiveChild(node: ComposeInnerViewNode): Any? { + if (node.view is ViewGroup) { + return ViewGroupDescriptor.getActiveChild(node.view) + } + return ViewDescriptor.getActiveChild(node.view) + } + + override fun getAttributes( + node: ComposeInnerViewNode + ): MaybeDeferred> { + if (node.view is ViewGroup) { + return ViewGroupDescriptor.getAttributes(node.view) + } + return ViewDescriptor.getAttributes(node.view) + } + + override fun getTags(node: ComposeInnerViewNode): Set { + if (node.view is ViewGroup) { + return ViewGroupDescriptor.getTags(node.view) + } + return ViewDescriptor.getTags(node.view) + } +} diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt new file mode 100644 index 000000000..22b5e0046 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt @@ -0,0 +1,140 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.jetpackcompose.descriptors + +import android.graphics.Bitmap +import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode +import com.facebook.flipper.plugins.uidebugger.descriptors.BaseTags +import com.facebook.flipper.plugins.uidebugger.descriptors.Id +import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister +import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor +import com.facebook.flipper.plugins.uidebugger.model.Bounds +import com.facebook.flipper.plugins.uidebugger.model.Coordinate +import com.facebook.flipper.plugins.uidebugger.model.Inspectable +import com.facebook.flipper.plugins.uidebugger.model.InspectableObject +import com.facebook.flipper.plugins.uidebugger.model.InspectableValue +import com.facebook.flipper.plugins.uidebugger.model.MetadataId +import com.facebook.flipper.plugins.uidebugger.util.Immediate +import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred + +object ComposeNodeDescriptor : NodeDescriptor { + + private const val NAMESPACE = "ComposeNode" + + private var SectionId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE) + private val IdAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "id") + private val KeyAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "key") + private val NameAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "name") + private val FilenameAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "filename") + private val PackageHashAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "packageHash") + private val LineNumberAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "lineNumber") + private val OffsetAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "offset") + private val LengthAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_IDENTITY, NAMESPACE, "length") + private val BoxAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "box") + private val BoundsAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "bounds") + private val Bounds0AttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "(x0, y0)") + private val Bounds1AttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "(x1, y1)") + private val Bounds2AttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "(x2, y2)") + private val Bounds3AttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "(x3, y3)") + private val ViewIdAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "viewId") + private val MergedSemanticsAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "mergedSemantics") + private val UnmergedSemanticsAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "unmergedSemantics") + + override fun getName(node: ComposeNode): String = node.inspectorNode.name + + override fun getChildren(node: ComposeNode): List { + return node.children + } + + override fun getAttributes(node: ComposeNode): MaybeDeferred> { + + val builder = mutableMapOf() + val props = mutableMapOf() + + props[IdAttributeId] = InspectableValue.Number(node.inspectorNode.id) + props[ViewIdAttributeId] = InspectableValue.Number(node.inspectorNode.viewId) + props[KeyAttributeId] = InspectableValue.Number(node.inspectorNode.key) + props[NameAttributeId] = InspectableValue.Text(node.inspectorNode.name) + props[FilenameAttributeId] = InspectableValue.Text(node.inspectorNode.fileName) + props[PackageHashAttributeId] = InspectableValue.Number(node.inspectorNode.packageHash) + props[LineNumberAttributeId] = InspectableValue.Number(node.inspectorNode.lineNumber) + props[OffsetAttributeId] = InspectableValue.Number(node.inspectorNode.offset) + props[LengthAttributeId] = InspectableValue.Number(node.inspectorNode.length) + + props[BoxAttributeId] = + InspectableValue.Bounds( + Bounds( + node.inspectorNode.left, + node.inspectorNode.top, + node.inspectorNode.width, + node.inspectorNode.height)) + + node.inspectorNode.bounds?.let { bounds -> + val quadBounds = mutableMapOf() + quadBounds[Bounds0AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x0, bounds.y0)) + quadBounds[Bounds1AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x1, bounds.y1)) + quadBounds[Bounds2AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x2, bounds.y2)) + quadBounds[Bounds3AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x3, bounds.y3)) + props[BoundsAttributeId] = InspectableObject(quadBounds.toMap()) + } + + val mergedSemantics = mutableMapOf() + node.inspectorNode.mergedSemantics.forEach { + val keyAttributeId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, node.inspectorNode.name, it.name) + mergedSemantics[keyAttributeId] = InspectableValue.Text(it.value.toString()) + } + props[MergedSemanticsAttributeId] = InspectableObject(mergedSemantics.toMap()) + + val unmergedSemantics = mutableMapOf() + node.inspectorNode.unmergedSemantics.forEach { + val keyAttributeId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, node.inspectorNode.name, it.name) + mergedSemantics[keyAttributeId] = InspectableValue.Text(it.value.toString()) + } + props[UnmergedSemanticsAttributeId] = InspectableObject(unmergedSemantics.toMap()) + + builder[SectionId] = InspectableObject(props.toMap()) + + return Immediate(builder) + } + + override fun getBounds(node: ComposeNode): Bounds { + return node.bounds + } + + override fun getQualifiedName(node: ComposeNode): String = node.inspectorNode.name + + override fun getSnapshot(node: ComposeNode, bitmap: Bitmap?): Bitmap? = null + + override fun getActiveChild(node: ComposeNode): Any? = null + + override fun getTags(node: ComposeNode): Set = setOf(BaseTags.Android, "Compose") + + override fun getId(node: ComposeNode): Id = node.inspectorNode.id.toInt() +} diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeViewDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeViewDescriptor.kt new file mode 100644 index 000000000..0a4d367aa --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeViewDescriptor.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.jetpackcompose.descriptors + +import android.os.Build +import android.view.View +import androidx.compose.ui.platform.ComposeView +import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode +import com.facebook.flipper.plugins.uidebugger.descriptors.ChainedDescriptor +import facebook.internal.androidx.compose.ui.inspection.inspector.InspectorNode +import facebook.internal.androidx.compose.ui.inspection.inspector.LayoutInspectorTree + +object ComposeViewDescriptor : ChainedDescriptor() { + override fun onGetName(node: ComposeView): String = node.javaClass.simpleName + + private fun transform(view: View, nodes: List): List { + val positionOnScreen = IntArray(2) + view.getLocationOnScreen(positionOnScreen) + + val xOffset = positionOnScreen[0] + val yOffset = positionOnScreen[1] + + return nodes.map { node -> ComposeNode(view, node, xOffset, yOffset) } + } + + override fun onGetChildren(node: ComposeView): List { + val children = mutableListOf() + val count = node.childCount - 1 + for (i in 0..count) { + val child: View = node.getChildAt(i) + children.add(child) + + if (child.javaClass.simpleName.contains("AndroidComposeView") && + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)) { + val layoutInspector = LayoutInspectorTree() + layoutInspector.hideSystemNodes = false + return transform(child, layoutInspector.convert(child)) + } + } + + return children + } +} diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeInnerViewNode.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeInnerViewNode.kt new file mode 100644 index 000000000..b9874f890 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeInnerViewNode.kt @@ -0,0 +1,17 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.jetpackcompose.model + +import android.view.View +import com.facebook.flipper.plugins.uidebugger.model.Bounds + +class ComposeInnerViewNode( + val view: View, +) { + val bounds: Bounds = Bounds(0, 0, view.width, view.height) +} diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt new file mode 100644 index 000000000..b3252a2e8 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.jetpackcompose.model + +import android.os.Build +import android.view.View +import android.view.ViewGroup +import androidx.annotation.RequiresApi +import com.facebook.flipper.plugins.uidebugger.model.* +import facebook.internal.androidx.compose.ui.inspection.inspector.InspectorNode + +class ComposeNode( + private val parentComposeView: View, + val inspectorNode: InspectorNode, + xOffset: Int, + yOffset: Int +) { + val bounds: Bounds = + Bounds( + inspectorNode.left - xOffset, + inspectorNode.top - yOffset, + inspectorNode.width, + inspectorNode.height) + + val children: List = collectChildren() + + private fun collectChildren(): List { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val viewId = inspectorNode.viewId + if (viewId != 0L) { + val view = parentComposeView.findViewByDrawingId(viewId) + if (view != null) { + return listOf(ComposeInnerViewNode(view)) + } + } + } + + return inspectorNode.children.map { child -> + ComposeNode(parentComposeView, child, inspectorNode.left, inspectorNode.top) + } + } + + @RequiresApi(Build.VERSION_CODES.Q) + private fun View.findViewByDrawingId(drawingId: Long): View? { + if (this.uniqueDrawingId == drawingId) { + return this + } + if (this is ViewGroup) { + for (i in 0 until this.childCount) { + val child = this.getChildAt(i) + val foundView = child.findViewByDrawingId(drawingId) + if (foundView != null) { + return foundView + } + } + } + return null + } +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/README.facebook b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/README.facebook new file mode 100644 index 000000000..2c093fe1d --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/README.facebook @@ -0,0 +1,5 @@ +This is a check-in of https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection + +The classes are currently not exported but we rely on them for debug information. + +Tree: 59746be8ea17d5753471bd285b3fbc9cf8ea7c31 diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/InlineClassConverter.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/InlineClassConverter.kt new file mode 100644 index 000000000..6c2152379 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/InlineClassConverter.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.inspector + +/** + * Converter for casting a parameter represented by its primitive value to its inline class type. + * + * For example: an androidx.compose.ui.graphics.Color instance is often represented by a long + */ +internal class InlineClassConverter { + // Map from inline type name to inline class and conversion lambda + private val typeMap = mutableMapOf Any>() + // Return value used in functions + private val notInlineType: (Any) -> Any = { it } + + /** Clear any cached data. */ + fun clear() { + typeMap.clear() + } + + /** + * Cast the specified [value] to a value of type [inlineClassName] if possible. + * + * @param inlineClassName the fully qualified name of the inline class. + * @param value the value to convert to an instance of [inlineClassName]. + */ + fun castParameterValue(inlineClassName: String?, value: Any?): Any? = + if (value != null && inlineClassName != null) typeMapperFor(inlineClassName)(value) else value + + private fun typeMapperFor(typeName: String): (Any) -> (Any) = + typeMap.getOrPut(typeName) { loadTypeMapper(typeName.replace('.', '/')) } + + private fun loadTypeMapper(className: String): (Any) -> Any { + val javaClass = loadClassOrNull(className) ?: return notInlineType + val create = javaClass.declaredConstructors.singleOrNull() ?: return notInlineType + create.isAccessible = true + return { value -> create.newInstance(value) } + } + + private fun loadClassOrNull(className: String): Class<*>? = + try { + javaClass.classLoader!!.loadClass(className) + } catch (ex: Exception) { + null + } +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/InspectorNode.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/InspectorNode.kt new file mode 100644 index 000000000..63eb4e4e6 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/InspectorNode.kt @@ -0,0 +1,237 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.inspector + +import androidx.compose.ui.layout.LayoutInfo +import androidx.compose.ui.unit.IntRect + +internal const val UNDEFINED_ID = 0L + +internal val emptyBox = IntRect(0, 0, 0, 0) +internal val outsideBox = IntRect(Int.MAX_VALUE, Int.MIN_VALUE, Int.MAX_VALUE, Int.MIN_VALUE) + +/** Node representing a Composable for the Layout Inspector. */ +class InspectorNode +internal constructor( + /** The associated render node id or 0. */ + val id: Long, + + /** The associated key for tracking recomposition counts. */ + val key: Int, + + /** + * The id of the associated anchor for tracking recomposition counts. + * + * An Anchor is a mechanism in the compose runtime that can identify a Group in the SlotTable + * that is invariant to SlotTable updates. See [androidx.compose.runtime.Anchor] for more + * information. + */ + val anchorId: Int, + + /** The name of the Composable. */ + val name: String, + + /** The fileName where the Composable was called. */ + val fileName: String, + + /** + * A hash of the package name to help disambiguate duplicate [fileName] values. + * + * This hash is calculated by, + * + * `packageName.fold(0) { hash, current -> hash * 31 + current.toInt() }?.absoluteValue` + * + * where the package name is the dotted name of the package. This can be used to disambiguate + * which file is referenced by [fileName]. This number is -1 if there was no package hash + * information generated such as when the file does not contain a package declaration. + */ + val packageHash: Int, + + /** The line number where the Composable was called. */ + val lineNumber: Int, + + /** The UTF-16 offset in the file where the Composable was called */ + val offset: Int, + + /** The number of UTF-16 code point comprise the Composable call */ + val length: Int, + + /** The bounding box of the Composable. */ + internal val box: IntRect, + + /** The 4 corners of the polygon after transformations of the original rectangle. */ + val bounds: QuadBounds? = null, + + /** True if the code for the Composable was inlined */ + val inlined: Boolean = false, + + /** The parameters of this Composable. */ + val parameters: List, + + /** The id of a android View embedded under this node. */ + val viewId: Long, + + /** The merged semantics information of this Composable. */ + val mergedSemantics: List, + + /** The un-merged semantics information of this Composable. */ + val unmergedSemantics: List, + + /** The children nodes of this Composable. */ + val children: List +) { + /** Left side of the Composable in pixels. */ + val left: Int + get() = box.left + + /** Top of the Composable in pixels. */ + val top: Int + get() = box.top + + /** Width of the Composable in pixels. */ + val width: Int + get() = box.width + + /** Width of the Composable in pixels. */ + val height: Int + get() = box.height + + fun parametersByKind(kind: ParameterKind): List = + when (kind) { + ParameterKind.Normal -> parameters + ParameterKind.MergedSemantics -> mergedSemantics + ParameterKind.UnmergedSemantics -> unmergedSemantics + } +} + +data class QuadBounds( + val x0: Int, + val y0: Int, + val x1: Int, + val y1: Int, + val x2: Int, + val y2: Int, + val x3: Int, + val y3: Int, +) { + val xMin: Int + get() = sequenceOf(x0, x1, x2, x3).minOrNull()!! + + val xMax: Int + get() = sequenceOf(x0, x1, x2, x3).maxOrNull()!! + + val yMin: Int + get() = sequenceOf(y0, y1, y2, y3).minOrNull()!! + + val yMax: Int + get() = sequenceOf(y0, y1, y2, y3).maxOrNull()!! + + val outerBox: IntRect + get() = IntRect(xMin, yMin, xMax, yMax) +} + +/** Parameter definition with a raw value reference. */ +class RawParameter(val name: String, val value: Any?) + +/** Mutable version of [InspectorNode]. */ +internal class MutableInspectorNode { + var id = UNDEFINED_ID + var key = 0 + var anchorId = 0 + val layoutNodes = mutableListOf() + val mergedSemantics = mutableListOf() + val unmergedSemantics = mutableListOf() + var name = "" + var fileName = "" + var packageHash = -1 + var lineNumber = 0 + var offset = 0 + var length = 0 + var box: IntRect = emptyBox + var bounds: QuadBounds? = null + var inlined = false + val parameters = mutableListOf() + var viewId = UNDEFINED_ID + val children = mutableListOf() + var outerBox: IntRect = outsideBox + + fun reset() { + markUnwanted() + id = UNDEFINED_ID + key = 0 + anchorId = 0 + viewId = UNDEFINED_ID + layoutNodes.clear() + mergedSemantics.clear() + unmergedSemantics.clear() + box = emptyBox + bounds = null + inlined = false + outerBox = outsideBox + children.clear() + } + + fun markUnwanted() { + name = "" + fileName = "" + packageHash = -1 + lineNumber = 0 + offset = 0 + length = 0 + parameters.clear() + } + + fun shallowCopy(node: InspectorNode): MutableInspectorNode = apply { + id = node.id + key = node.key + anchorId = node.anchorId + mergedSemantics.addAll(node.mergedSemantics) + unmergedSemantics.addAll(node.unmergedSemantics) + name = node.name + fileName = node.fileName + packageHash = node.packageHash + lineNumber = node.lineNumber + offset = node.offset + length = node.length + box = node.box + bounds = node.bounds + inlined = node.inlined + parameters.addAll(node.parameters) + viewId = node.viewId + children.addAll(node.children) + } + + fun build(withSemantics: Boolean = true): InspectorNode = + InspectorNode( + id, + key, + anchorId, + name, + fileName, + packageHash, + lineNumber, + offset, + length, + box, + bounds, + inlined, + parameters.toList(), + viewId, + if (withSemantics) mergedSemantics.toList() else emptyList(), + if (withSemantics) unmergedSemantics.toList() else emptyList(), + children.toList()) +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt new file mode 100644 index 000000000..867857fa8 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt @@ -0,0 +1,826 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.inspector + +import android.view.View +import androidx.annotation.VisibleForTesting +import androidx.compose.runtime.tooling.CompositionData +import androidx.compose.runtime.tooling.CompositionGroup +import androidx.compose.ui.InternalComposeUiApi +import androidx.compose.ui.R +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.layout.GraphicLayerInfo +import androidx.compose.ui.layout.LayoutInfo +import androidx.compose.ui.node.InteroperableComposeUiNode +import androidx.compose.ui.node.Ref +import androidx.compose.ui.node.RootForTest +import androidx.compose.ui.platform.ViewRootForInspector +import androidx.compose.ui.semantics.getAllSemanticsNodes +import androidx.compose.ui.tooling.data.ContextCache +import androidx.compose.ui.tooling.data.ParameterInformation +import androidx.compose.ui.tooling.data.SourceContext +import androidx.compose.ui.tooling.data.SourceLocation +import androidx.compose.ui.tooling.data.UiToolingDataApi +import androidx.compose.ui.tooling.data.findParameters +import androidx.compose.ui.tooling.data.mapTree +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntRect +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.toSize +import facebook.internal.androidx.compose.ui.inspection.util.AnchorMap +import facebook.internal.androidx.compose.ui.inspection.util.NO_ANCHOR_ID +import java.util.ArrayDeque +import java.util.Collections +import java.util.IdentityHashMap +import kotlin.math.max +import kotlin.math.min +import kotlin.math.roundToInt + +/** + * The [InspectorNode.id] will be populated with: + * - the layerId from a LayoutNode if this exists + * - an id generated from an Anchor instance from the SlotTree if this exists + * - a generated id if none of the above ids are available + * + * The interval -10000..-2 is reserved for the generated ids. + */ +@VisibleForTesting const val RESERVED_FOR_GENERATED_IDS = -10000L +const val PLACEHOLDER_ID = Long.MAX_VALUE + +private val emptySize = IntSize(0, 0) + +private val unwantedCalls = + setOf( + "CompositionLocalProvider", + "Content", + "Inspectable", + "ProvideAndroidCompositionLocals", + "ProvideCommonCompositionLocals", + ) + +/** Generator of a tree for the Layout Inspector. */ +@OptIn(UiToolingDataApi::class) +class LayoutInspectorTree { + @Suppress("MemberVisibilityCanBePrivate") var hideSystemNodes = true + var includeNodesOutsizeOfWindow = true + var includeAllParameters = true + private var foundNode: InspectorNode? = null + private var windowSize = emptySize + private val inlineClassConverter = InlineClassConverter() + private val parameterFactory = ParameterFactory(inlineClassConverter) + private val cache = ArrayDeque() + private var generatedId = -1L + private val subCompositions = SubCompositionRoots() + /** Map from [LayoutInfo] to the nearest [InspectorNode] that contains it */ + private val claimedNodes = IdentityHashMap() + /** Map from parent tree to child trees that are about to be stitched together */ + private val treeMap = IdentityHashMap>() + /** Map from owner node to child trees that are about to be stitched to this owner */ + private val ownerMap = IdentityHashMap>() + /** Map from semantics id to a list of merged semantics information */ + private val semanticsMap = mutableMapOf>() + /* Map of seemantics id to a list of unmerged semantics information */ + private val unmergedSemanticsMap = mutableMapOf>() + /** Set of tree nodes that were stitched into another tree */ + private val stitched = Collections.newSetFromMap(IdentityHashMap()) + private val contextCache = ContextCache() + private val anchorMap = AnchorMap() + + /** Converts the [CompositionData] set held by [view] into a list of root nodes. */ + fun convert(view: View): List { + windowSize = IntSize(view.width, view.height) + parameterFactory.density = Density(view.context) + @Suppress("UNCHECKED_CAST") + val tables = + view.getTag(R.id.inspection_slot_table_set) as? Set ?: return emptyList() + clear() + collectSemantics(view) + val result = convert(tables, view) + clear() + return result + } + + fun findParameters(view: View, anchorId: Int): InspectorNode? { + windowSize = IntSize(view.width, view.height) + parameterFactory.density = Density(view.context) + val identity = anchorMap[anchorId] ?: return null + + @Suppress("UNCHECKED_CAST") + val tables = view.getTag(R.id.inspection_slot_table_set) as? Set ?: return null + val node = newNode().apply { this.anchorId = anchorId } + val group = tables.firstNotNullOfOrNull { it.find(identity) } ?: return null + group.findParameters(contextCache).forEach { + val castedValue = castValue(it) + node.parameters.add(RawParameter(it.name, castedValue)) + } + return buildAndRelease(node) + } + + /** + * Add the roots to sub compositions that may have been collected from a different SlotTree. + * + * See [SubCompositionRoots] for details. + */ + fun addSubCompositionRoots(view: View, nodes: List): List = + subCompositions.addRoot(view, nodes) + + /** + * Extract the merged semantics for this semantics owner such that they can be added to compose + * nodes during the conversion of Group nodes. + */ + private fun collectSemantics(view: View) { + val root = view as? RootForTest ?: return + val nodes = root.semanticsOwner.getAllSemanticsNodes(mergingEnabled = true) + val unmergedNodes = root.semanticsOwner.getAllSemanticsNodes(mergingEnabled = false) + nodes.forEach { node -> + semanticsMap[node.id] = node.config.map { RawParameter(it.key.name, it.value) } + } + unmergedNodes.forEach { node -> + unmergedSemanticsMap[node.id] = node.config.map { RawParameter(it.key.name, it.value) } + } + } + + /** Converts the [RawParameter]s of the [node] into displayable parameters. */ + fun convertParameters( + rootId: Long, + node: InspectorNode, + kind: ParameterKind, + maxRecursions: Int, + maxInitialIterableSize: Int + ): List { + val parameters = node.parametersByKind(kind) + return parameters.mapIndexed { index, parameter -> + parameterFactory.create( + rootId, + node.id, + node.anchorId, + parameter.name, + parameter.value, + kind, + index, + maxRecursions, + maxInitialIterableSize) + } + } + + /** + * Converts a part of the [RawParameter] identified by [reference] into a displayable parameter. + * If the parameter is some sort of a collection then [startIndex] and [maxElements] describes the + * scope of the data returned. + */ + fun expandParameter( + rootId: Long, + node: InspectorNode, + reference: NodeParameterReference, + startIndex: Int, + maxElements: Int, + maxRecursions: Int, + maxInitialIterableSize: Int + ): NodeParameter? { + val parameters = node.parametersByKind(reference.kind) + if (reference.parameterIndex !in parameters.indices) { + return null + } + val parameter = parameters[reference.parameterIndex] + return parameterFactory.expand( + rootId, + node.id, + node.anchorId, + parameter.name, + parameter.value, + reference, + startIndex, + maxElements, + maxRecursions, + maxInitialIterableSize) + } + + /** Reset any state accumulated between windows. */ + @Suppress("unused") + fun resetAccumulativeState() { + subCompositions.resetAccumulativeState() + parameterFactory.clearReferenceCache() + // Reset the generated id. Nodes are assigned an id if there isn't a layout node id present. + generatedId = -1L + } + + private fun clear() { + cache.clear() + inlineClassConverter.clear() + claimedNodes.clear() + treeMap.clear() + ownerMap.clear() + semanticsMap.clear() + unmergedSemanticsMap.clear() + stitched.clear() + subCompositions.clear() + foundNode = null + } + + private fun convert(tables: Set, view: View): List { + val trees = tables.mapNotNull { convert(view, it) } + return when (trees.size) { + 0 -> listOf() + 1 -> addTree(mutableListOf(), trees.single()) + else -> stitchTreesByLayoutInfo(trees) + } + } + + /** + * Stitch separate trees together using the [LayoutInfo]s found in the [CompositionData]s. + * + * Some constructs in Compose (e.g. ModalDrawer) will result is multiple [CompositionData]s. This + * code will attempt to stitch the resulting [InspectorNode] trees together by looking at the + * parent of each [LayoutInfo]. + * + * If this algorithm is successful the result of this function will be a list with a single tree. + */ + private fun stitchTreesByLayoutInfo(trees: List): List { + val layoutToTreeMap = IdentityHashMap() + trees.forEach { tree -> tree.layoutNodes.forEach { layoutToTreeMap[it] = tree } } + trees.forEach { tree -> + val layout = tree.layoutNodes.lastOrNull() + val parentLayout = + generateSequence(layout) { it.parentInfo } + .firstOrNull { + val otherTree = layoutToTreeMap[it] + otherTree != null && otherTree != tree + } + if (parentLayout != null) { + val ownerNode = claimedNodes[parentLayout] + val ownerTree = layoutToTreeMap[parentLayout] + if (ownerNode != null && ownerTree != null) { + ownerMap.getOrPut(ownerNode) { mutableListOf() }.add(tree) + treeMap.getOrPut(ownerTree) { mutableListOf() }.add(tree) + } + } + } + var parentTree = findDeepParentTree() + while (parentTree != null) { + addSubTrees(parentTree) + treeMap.remove(parentTree) + parentTree = findDeepParentTree() + } + val result = mutableListOf() + trees.asSequence().filter { !stitched.contains(it) }.forEach { addTree(result, it) } + return result + } + + /** + * Return a parent tree where the children trees (to be stitched under the parent) are not a + * parent themselves. Do this to avoid rebuilding the same tree more than once. + */ + private fun findDeepParentTree(): MutableInspectorNode? = + treeMap.entries + .asSequence() + .filter { (_, children) -> children.none { treeMap.containsKey(it) } } + .firstOrNull() + ?.key + + private fun addSubTrees(tree: MutableInspectorNode) { + for ((index, child) in tree.children.withIndex()) { + tree.children[index] = addSubTrees(child) ?: child + } + } + + /** + * Rebuild [node] with any possible sub trees added (stitched in). Return the rebuild node, or + * null if no changes were found in this node or its children. Lazily allocate the new node to + * avoid unnecessary allocations. + */ + private fun addSubTrees(node: InspectorNode): InspectorNode? { + var newNode: MutableInspectorNode? = null + for ((index, child) in node.children.withIndex()) { + val newChild = addSubTrees(child) + if (newChild != null) { + val newCopy = newNode ?: newNode(node) + newCopy.children[index] = newChild + newNode = newCopy + } + } + val trees = ownerMap[node] + if (trees == null && newNode == null) { + return null + } + val newCopy = newNode ?: newNode(node) + if (trees != null) { + trees.forEach { addTree(newCopy.children, it) } + stitched.addAll(trees) + } + return buildAndRelease(newCopy) + } + + /** + * Add [tree] to the end of the [out] list. The root nodes of [tree] may be a fake node that hold + * a list of [LayoutInfo]. + */ + private fun addTree( + out: MutableList, + tree: MutableInspectorNode + ): List { + tree.children.forEach { + if (it.name.isNotEmpty()) { + out.add(it) + } else { + out.addAll(it.children) + } + } + return out + } + + private fun convert(view: View, table: CompositionData): MutableInspectorNode? { + val fakeParent = newNode() + val group = table.mapTree(::convert, contextCache) ?: return null + addToParent(fakeParent, listOf(group), buildFakeChildNodes = true) + return if (belongsToView(fakeParent.layoutNodes, view)) fakeParent else null + } + + private fun convert( + group: CompositionGroup, + context: SourceContext, + children: List + ): MutableInspectorNode { + val parent = parse(group, context, children) + subCompositions.captureNode(parent, context) + addToParent(parent, children) + return parent + } + + /** + * Adds the nodes in [input] to the children of [parentNode]. Nodes without a reference to a + * wanted Composable are skipped unless [buildFakeChildNodes]. A single skipped render id and + * layoutNode will be added to [parentNode]. + */ + private fun addToParent( + parentNode: MutableInspectorNode, + input: List, + buildFakeChildNodes: Boolean = false + ) { + // If we're adding an unwanted node from the `input` to the parent node and it has a + // View ID, then assign it to the parent view so that we don't lose the context that we + // found a View as a descendant of the parent node. Most likely, there were one or more + // unwanted intermediate nodes between the node that actually owns the Android View + // and the desired node that the View should be associated with in the inspector. If + // there's more than one input node with a View ID, we skip this step since it's + // unclear how these views would be related. + input + .singleOrNull { it.viewId != UNDEFINED_ID } + ?.takeIf { node -> + // Take if the node has been marked as unwanted + node.id == UNDEFINED_ID + } + ?.let { nodeWithView -> parentNode.viewId = nodeWithView.viewId } + + var id: Long? = null + input.forEach { node -> + if (node.name.isEmpty() && !(buildFakeChildNodes && node.layoutNodes.isNotEmpty())) { + parentNode.children.addAll(node.children) + if (node.id > UNDEFINED_ID) { + // If multiple siblings with a render ids are dropped: + // Ignore them all. And delegate the drawing to a parent in the inspector. + id = if (id == null) node.id else UNDEFINED_ID + } + } else { + node.id = if (node.id != UNDEFINED_ID) node.id else --generatedId + val withSemantics = node.packageHash !in systemPackages + val resultNode = node.build(withSemantics) + // TODO: replace getOrPut with putIfAbsent which requires API level 24 + node.layoutNodes.forEach { claimedNodes.getOrPut(it) { resultNode } } + parentNode.children.add(resultNode) + if (withSemantics) { + node.mergedSemantics.clear() + node.unmergedSemantics.clear() + } + } + if (node.bounds != null && parentNode.box == node.box) { + parentNode.bounds = node.bounds + } + parentNode.layoutNodes.addAll(node.layoutNodes) + parentNode.mergedSemantics.addAll(node.mergedSemantics) + parentNode.unmergedSemantics.addAll(node.unmergedSemantics) + release(node) + } + val nodeId = id + parentNode.id = if (parentNode.id <= UNDEFINED_ID && nodeId != null) nodeId else parentNode.id + } + + @OptIn(InternalComposeUiApi::class) + private fun parse( + group: CompositionGroup, + context: SourceContext, + children: List + ): MutableInspectorNode { + val node = newNode() + node.name = context.name ?: "" + node.key = group.key as? Int ?: 0 + node.inlined = context.isInline + + // If this node is associated with an android View, set the node's viewId to point to + // the hosted view. We use the parent's uniqueDrawingId since the interopView returned here + // will be the view itself, but we want to use the `AndroidViewHolder` that hosts the view + // instead of the view directly. + (group.node as? InteroperableComposeUiNode?)?.getInteropView()?.let { interopView -> + (interopView.parent as? View)?.uniqueDrawingId?.let { viewId -> node.viewId = viewId } + } + + val layoutInfo = group.node as? LayoutInfo + if (layoutInfo != null) { + return parseLayoutInfo(layoutInfo, context, node) + } + if (unwantedOutsideWindow(node, children)) { + return markUnwanted(group, context, node) + } + node.box = context.bounds.emptyCheck() + if (unwantedName(node.name) || (node.box == emptyBox && !subCompositions.capturing)) { + return markUnwanted(group, context, node) + } + parseCallLocation(node, context.location) + if (isHiddenSystemNode(node)) { + return markUnwanted(group, context, node) + } + node.anchorId = anchorMap[group.identity] + node.id = syntheticId(node.anchorId) + if (includeAllParameters) { + addParameters(context, node) + } + return node + } + + private fun IntRect.emptyCheck(): IntRect = if (left >= right && top >= bottom) emptyBox else this + + private fun IntRect.inWindow(): Boolean = + !(left > windowSize.width || right < 0 || top > windowSize.height || bottom < 0) + + private fun IntRect.union(other: IntRect): IntRect { + if (this == outsideBox) return other else if (other == outsideBox) return this + + return IntRect( + left = min(left, other.left), + top = min(top, other.top), + bottom = max(bottom, other.bottom), + right = max(right, other.right)) + } + + private fun parseLayoutInfo( + layoutInfo: LayoutInfo, + context: SourceContext, + node: MutableInspectorNode + ): MutableInspectorNode { + val box = context.bounds + val size = box.size.toSize() + val coordinates = layoutInfo.coordinates + val topLeft = toIntOffset(coordinates.localToWindow(Offset.Zero)) + val topRight = toIntOffset(coordinates.localToWindow(Offset(size.width, 0f))) + val bottomRight = toIntOffset(coordinates.localToWindow(Offset(size.width, size.height))) + val bottomLeft = toIntOffset(coordinates.localToWindow(Offset(0f, size.height))) + var bounds: QuadBounds? = null + + if (topLeft.x != box.left || + topLeft.y != box.top || + topRight.x != box.right || + topRight.y != box.top || + bottomRight.x != box.right || + bottomRight.y != box.bottom || + bottomLeft.x != box.left || + bottomLeft.y != box.bottom) { + bounds = + QuadBounds( + topLeft.x, + topLeft.y, + topRight.x, + topRight.y, + bottomRight.x, + bottomRight.y, + bottomLeft.x, + bottomLeft.y, + ) + } + if (!includeNodesOutsizeOfWindow) { + // Ignore this node if the bounds are completely outside the window + node.outerBox = bounds?.outerBox ?: box + if (!node.outerBox.inWindow()) { + return node + } + } + + node.box = box.emptyCheck() + node.bounds = bounds + node.layoutNodes.add(layoutInfo) + val modifierInfo = layoutInfo.getModifierInfo() + + val unmergedSemantics = unmergedSemanticsMap[layoutInfo.semanticsId] + if (unmergedSemantics != null) { + node.unmergedSemantics.addAll(unmergedSemantics) + } + + val mergedSemantics = semanticsMap[layoutInfo.semanticsId] + if (mergedSemantics != null) { + node.mergedSemantics.addAll(mergedSemantics) + } + + node.id = + modifierInfo + .asSequence() + .map { it.extra } + .filterIsInstance() + .map { it.layerId } + .firstOrNull() ?: UNDEFINED_ID + + return node + } + + private fun syntheticId(anchorId: Int): Long { + if (anchorId == NO_ANCHOR_ID) { + return UNDEFINED_ID + } + // The anchorId is an Int + return anchorId.toLong() - Int.MAX_VALUE.toLong() + RESERVED_FOR_GENERATED_IDS + } + + private fun belongsToView(layoutNodes: List, view: View): Boolean = + layoutNodes + .asSequence() + .flatMap { node -> + node + .getModifierInfo() + .asSequence() + .map { it.extra } + .filterIsInstance() + .map { it.ownerViewId } + } + .contains(view.uniqueDrawingId) + + private fun addParameters(context: SourceContext, node: MutableInspectorNode) { + context.parameters.forEach { + val castedValue = castValue(it) + node.parameters.add(RawParameter(it.name, castedValue)) + } + } + + private fun castValue(parameter: ParameterInformation): Any? { + val value = parameter.value ?: return null + if (parameter.inlineClass == null || !isPrimitive(value.javaClass)) return value + return inlineClassConverter.castParameterValue(parameter.inlineClass, value) + } + + private fun isPrimitive(cls: Class<*>): Boolean = cls.kotlin.javaPrimitiveType != null + + private fun toIntOffset(offset: Offset): IntOffset = + IntOffset(offset.x.roundToInt(), offset.y.roundToInt()) + + private fun markUnwanted( + group: CompositionGroup, + context: SourceContext, + node: MutableInspectorNode + ): MutableInspectorNode = + when (node.name) { + "rememberCompositionContext" -> subCompositions.rememberCompositionContext(node, context) + "remember" -> subCompositions.remember(node, group) + else -> node.apply { markUnwanted() } + } + + private fun parseCallLocation(node: MutableInspectorNode, location: SourceLocation?) { + val fileName = location?.sourceFile ?: return + node.fileName = fileName + node.packageHash = location.packageHash + node.lineNumber = location.lineNumber + node.offset = location.offset + node.length = location.length + } + + private fun isHiddenSystemNode(node: MutableInspectorNode): Boolean = + node.packageHash in systemPackages && hideSystemNodes + + private fun unwantedName(name: String): Boolean = + name.isEmpty() || name.startsWith("remember") || name in unwantedCalls + + private fun unwantedOutsideWindow( + node: MutableInspectorNode, + children: List + ): Boolean { + if (includeNodesOutsizeOfWindow) { + return false + } + node.outerBox = + if (children.isEmpty()) outsideBox + else children.map { g -> g.outerBox }.reduce { acc, box -> box.union(acc) } + return !node.outerBox.inWindow() + } + + private fun newNode(): MutableInspectorNode = + if (cache.isNotEmpty()) cache.pop() else MutableInspectorNode() + + private fun newNode(copyFrom: InspectorNode): MutableInspectorNode = + newNode().shallowCopy(copyFrom) + + private fun release(node: MutableInspectorNode) { + node.reset() + cache.add(node) + } + + private fun buildAndRelease(node: MutableInspectorNode): InspectorNode { + val result = node.build() + release(node) + return result + } + + /** + * Keep track of sub-composition roots. + * + * Examples: + * - Popup, Dialog: When one of these is open an extra Android Window is created with its own + * AndroidComposeView. The contents of the Composable is a sub-composition that will be computed + * by calling convert. + * + * The Popup/Dialog composable itself, and a few helping composables (the root) will not be + * included in the SlotTree with the contents, instead these composables will be found in the + * SlotTree for the main app and they all have empty sizes. The aim is to collect these + * sub-composition roots such that they can be added to the [InspectorNode]s of the contents. + * - AndroidView: When this is used in a compose app we will see a similar pattern in the SlotTree + * except there isn't a sub-composition to stitch in. But we need to collect the view id + * separately from the "AndroidView" node itself. + */ + private inner class SubCompositionRoots { + /** Set to true when the nodes found should be added to a sub-composition root */ + var capturing = false + private set + + /** The `uniqueDrawingId` of the `AndroidComposeView` that owns the root being captured */ + private var ownerView = UNDEFINED_ID + + /** The node that represent the root of the sub-composition */ + private var rootNode: MutableInspectorNode? = null + + /** The depth of the parse tree the [rootNode] was found at */ + private var rootNodeDepth = 0 + + /** Last captured view that is believed to be an embbed View under an AndroidView node */ + private var androidView = UNDEFINED_ID + + /** + * The sub-composition roots found. + * + * Map from View owner to a pair of [InspectorNode] indicating the actual root, and the node + * where the content should be stitched in. + */ + private val found = mutableMapOf() + + /** Call this before converting a SlotTree for an AndroidComposeView */ + fun clear() { + capturing = false + ownerView = UNDEFINED_ID + rootNode?.markUnwanted() + rootNode?.id = UNDEFINED_ID + rootNode = null + rootNodeDepth = 0 + } + + /** Call this when starting converting a new set of windows */ + fun resetAccumulativeState() { + found.clear() + } + + /** + * When a "rememberCompositionContext" is found in the slot tree, it indicates that a + * sub-composition was started. We should capture all parent nodes with an empty size as the + * "root" of the sub-composition. + */ + fun rememberCompositionContext( + node: MutableInspectorNode, + context: SourceContext + ): MutableInspectorNode { + if (capturing) { + save() + } + capturing = true + rootNode = node + rootNodeDepth = context.depth + node.id = PLACEHOLDER_ID + return node + } + + /** + * When "remember" is found in the slot tree and we are currently capturing, the data of the + * [group] may contain the owner of the sub-composition. + */ + fun remember(node: MutableInspectorNode, group: CompositionGroup): MutableInspectorNode { + node.markUnwanted() + if (!capturing) { + return node + } + val root = findSingleRootInGroupData(group) ?: return node + + val view = root.subCompositionView + if (view != null) { + val composeOwner = if (view.childCount == 1) view.getChildAt(0) else return node + ownerView = composeOwner.uniqueDrawingId + } else { + androidView = root.viewRoot?.uniqueDrawingId ?: UNDEFINED_ID + // Store the viewRoot such that we can move the View under the compose node + // in Studio. We do not need to capture the Groups found for this case, so + // we call "reset" here to stop capturing. + clear() + } + return node + } + + private fun findSingleRootInGroupData(group: CompositionGroup): ViewRootForInspector? { + group.data.filterIsInstance().singleOrNull()?.let { + return it + } + val refs = group.data.filterIsInstance>().map { it.value } + return refs.filterIsInstance().singleOrNull() + } + + /** Capture the top node of the sub-composition root until a non empty node is found. */ + fun captureNode(node: MutableInspectorNode, context: SourceContext) { + if (!capturing) { + return + } + if (node.box != emptyBox) { + save() + return + } + val depth = context.depth + if (depth < rootNodeDepth) { + rootNode = node + rootNodeDepth = depth + } + } + + fun latestViewId(): Long { + val id = androidView + androidView = UNDEFINED_ID + return id + } + + /** If a sub-composition root has been captured, save it now. */ + private fun save() { + val node = rootNode + if (node != null && ownerView != UNDEFINED_ID) { + found[ownerView] = node.build() + } + node?.markUnwanted() + node?.id = UNDEFINED_ID + node?.children?.clear() + clear() + } + + /** + * Add the root of the sub-composition to the found tree. + * + * If a root is not found for this [owner] or if the stitching fails just return [nodes]. + */ + fun addRoot(owner: View, nodes: List): List { + val root = found[owner.uniqueDrawingId] ?: return nodes + val box = IntRect(0, 0, owner.width, owner.height) + val info = StitchInfo(nodes, box) + val result = listOf(stitch(root, info)) + return if (info.added) result else nodes + } + + private fun stitch(node: InspectorNode, info: StitchInfo): InspectorNode { + val children = node.children.map { stitch(it, info) } + val index = children.indexOfFirst { it.id == PLACEHOLDER_ID } + val newNode = newNode() + newNode.shallowCopy(node) + newNode.children.clear() + if (index < 0) { + newNode.children.addAll(children) + } else { + newNode.children.addAll(children.subList(0, index)) + newNode.children.addAll(info.nodes) + newNode.children.addAll(children.subList(index + 1, children.size)) + info.added = true + } + newNode.box = info.bounds + return buildAndRelease(newNode) + } + } + + private class StitchInfo( + /** The nodes found that should be stitched into a sub-composition root. */ + val nodes: List, + + /** The bounds of the View containing the sub-composition */ + val bounds: IntRect + ) { + /** Set this to true when the [nodes] have been added to a sub-composition root */ + var added: Boolean = false + } +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameter.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameter.kt new file mode 100644 index 000000000..b2a3ce1a0 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameter.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.inspector + +/** Holds data representing a Composable parameter for the Layout Inspector. */ +class NodeParameter +internal constructor( + /** The name of the parameter. */ + val name: String, + + /** The type of the parameter. */ + val type: ParameterType, + + /** The value of the parameter. */ + val value: Any? +) { + /** Sub elements of the parameter. */ + val elements = mutableListOf() + + /** Reference to value parameter. */ + var reference: NodeParameterReference? = null + + /** The index into the composite parent parameter value. */ + var index = 0 +} + +/** The type of a parameter. */ +enum class ParameterType { + String, + Boolean, + Double, + Float, + Int32, + Int64, + Color, + Resource, + DimensionDp, + DimensionSp, + DimensionEm, + Lambda, + FunctionReference, + Iterable, +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameterReference.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameterReference.kt new file mode 100644 index 000000000..1353413f0 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameterReference.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.inspector + +import facebook.internal.androidx.compose.ui.inspection.util.asIntArray + +/** + * A reference to a parameter to a [NodeParameter] + * + * @param nodeId is the id of the node the parameter belongs to + * @param anchorId is the anchor hash of the node the parameter belongs to + * @param kind is this a reference to a normal, merged, or unmerged semantic parameter. + * @param parameterIndex index into [InspectorNode.parameters], [InspectorNode.mergedSemantics], or + * [InspectorNode.unMergedSemantics] + * @param indices are indices into the composite parameter + */ +class NodeParameterReference( + val nodeId: Long, + val anchorId: Int, + val kind: ParameterKind, + val parameterIndex: Int, + val indices: IntArray +) { + constructor( + nodeId: Long, + anchorId: Int, + kind: ParameterKind, + parameterIndex: Int, + indices: List + ) : this(nodeId, anchorId, kind, parameterIndex, indices.asIntArray()) + + // For testing: + override fun toString(): String { + val suffix = if (indices.isNotEmpty()) ", ${indices.joinToString()}" else "" + return "[$nodeId, $anchorId, $kind, $parameterIndex$suffix]" + } +} + +/** Identifies which kind of parameter the [NodeParameterReference] is a reference to. */ +enum class ParameterKind { + Normal, + MergedSemantics, + UnmergedSemantics +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/PackageHashes.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/PackageHashes.kt new file mode 100644 index 000000000..57a51d030 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/PackageHashes.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// WARNING: DO NOT EDIT THIS FILE MANUALLY. It's automatically generated by running: +// frameworks/support/compose/ui/ui-inspection/generate-packages/generate_compose_packages.py -r +package facebook.internal.androidx.compose.ui.inspection.inspector + +import androidx.annotation.VisibleForTesting +import kotlin.math.absoluteValue + +@VisibleForTesting +fun packageNameHash(packageName: String) = + packageName.fold(0) { hash, char -> hash * 31 + char.code }.absoluteValue + +val systemPackages = + setOf( + -1, + packageNameHash("androidx.compose.animation"), + packageNameHash("androidx.compose.animation.core"), + packageNameHash("androidx.compose.animation.graphics.vector"), + packageNameHash("androidx.compose.desktop"), + packageNameHash("androidx.compose.foundation"), + packageNameHash("androidx.compose.foundation.gestures"), + packageNameHash("androidx.compose.foundation.gestures.snapping"), + packageNameHash("androidx.compose.foundation.interaction"), + packageNameHash("androidx.compose.foundation.layout"), + packageNameHash("androidx.compose.foundation.lazy"), + packageNameHash("androidx.compose.foundation.lazy.grid"), + packageNameHash("androidx.compose.foundation.lazy.layout"), + packageNameHash("androidx.compose.foundation.lazy.staggeredgrid"), + packageNameHash("androidx.compose.foundation.newtext.text"), + packageNameHash("androidx.compose.foundation.newtext.text.copypasta"), + packageNameHash("androidx.compose.foundation.newtext.text.copypasta.selection"), + packageNameHash("androidx.compose.foundation.pager"), + packageNameHash("androidx.compose.foundation.relocation"), + packageNameHash("androidx.compose.foundation.text"), + packageNameHash("androidx.compose.foundation.text.selection"), + packageNameHash("androidx.compose.foundation.window"), + packageNameHash("androidx.compose.material"), + packageNameHash("androidx.compose.material.internal"), + packageNameHash("androidx.compose.material.pullrefresh"), + packageNameHash("androidx.compose.material.ripple"), + packageNameHash("androidx.compose.material3"), + packageNameHash("androidx.compose.material3.internal"), + packageNameHash("androidx.compose.material3.windowsizeclass"), + packageNameHash("androidx.compose.runtime"), + packageNameHash("androidx.compose.runtime.livedata"), + packageNameHash("androidx.compose.runtime.mock"), + packageNameHash("androidx.compose.runtime.reflect"), + packageNameHash("androidx.compose.runtime.rxjava2"), + packageNameHash("androidx.compose.runtime.rxjava3"), + packageNameHash("androidx.compose.runtime.saveable"), + packageNameHash("androidx.compose.ui"), + packageNameHash("androidx.compose.ui.awt"), + packageNameHash("androidx.compose.ui.graphics.benchmark"), + packageNameHash("androidx.compose.ui.graphics.vector"), + packageNameHash("androidx.compose.ui.layout"), + packageNameHash("androidx.compose.ui.platform"), + packageNameHash("androidx.compose.ui.text"), + packageNameHash("androidx.compose.ui.util"), + packageNameHash("androidx.compose.ui.viewinterop"), + packageNameHash("androidx.compose.ui.window"), + ) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt new file mode 100644 index 000000000..e74e49ed4 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt @@ -0,0 +1,1001 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.inspector + +import android.util.Log +import android.view.View +import androidx.compose.runtime.internal.ComposableLambda +import androidx.compose.ui.AbsoluteAlignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.Shadow +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.InspectableModifier +import androidx.compose.ui.platform.InspectableValue +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontListFontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.font.ResourceFont +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.style.BaselineShift +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType +import facebook.internal.androidx.compose.ui.inspection.inspector.ParameterType.DimensionDp +import java.lang.reflect.Field +import java.lang.reflect.Modifier as JavaModifier +import java.util.IdentityHashMap +import kotlin.jvm.internal.FunctionReference +import kotlin.jvm.internal.Lambda +import kotlin.math.abs +import kotlin.reflect.KClass +import kotlin.reflect.KProperty +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.allSuperclasses +import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.javaField +import kotlin.reflect.jvm.javaGetter + +private val reflectionScope: ReflectionScope = ReflectionScope() + +/** + * Factory of [NodeParameter]s. + * + * Each parameter value is converted to a user readable value. + */ +internal class ParameterFactory(private val inlineClassConverter: InlineClassConverter) { + /** A map from known values to a user readable string representation. */ + private val valueLookup = mutableMapOf() + + /** The classes we have loaded constants from. */ + private val valuesLoaded = mutableSetOf>() + + /** + * Do not load constant names from instances of these classes. We prefer showing the raw values of + * Color and Dimensions. + */ + private val ignoredClasses = listOf(Color::class.java, Dp::class.java) + private var creatorCache: ParameterCreator? = null + + /** + * Do not decompose instances or lookup constants from these package prefixes + * + * The following instances are known to contain self recursion: + * - kotlinx.coroutines.flow.StateFlowImpl + * - androidx.compose.ui.node.LayoutNode + */ + private val ignoredPackagePrefixes = + listOf("android.", "java.", "javax.", "kotlinx.", "androidx.compose.ui.node.") + + var density = Density(1.0f) + + init { + val textDecorationCombination = + TextDecoration.combine(listOf(TextDecoration.LineThrough, TextDecoration.Underline)) + valueLookup[textDecorationCombination] = "LineThrough+Underline" + valueLookup[Color.Unspecified] = "Unspecified" + valueLookup[RectangleShape] = "RectangleShape" + valuesLoaded.add(Enum::class.java) + valuesLoaded.add(Any::class.java) + + // AbsoluteAlignment is not found from an instance of BiasAbsoluteAlignment, + // because Alignment has no file level class. + reflectionScope.withReflectiveAccess { + loadConstantsFromEnclosedClasses(AbsoluteAlignment::class.java) + } + } + + /** + * Create a [NodeParameter] from the specified parameter [name] and [value]. + * + * Attempt to convert the value to a user readable value. For now: return null when a conversion + * is not possible/found. + */ + fun create( + rootId: Long, + nodeId: Long, + anchorId: Int, + name: String, + value: Any?, + kind: ParameterKind, + parameterIndex: Int, + maxRecursions: Int, + maxInitialIterableSize: Int + ): NodeParameter { + val creator = creatorCache ?: ParameterCreator() + try { + return reflectionScope.withReflectiveAccess { + creator.create( + rootId, + nodeId, + anchorId, + name, + value, + kind, + parameterIndex, + maxRecursions, + maxInitialIterableSize) + } + } finally { + creatorCache = creator + } + } + + /** + * Create/expand the [NodeParameter] specified by [reference]. + * + * @param rootId is the root id of the specified [nodeId]. + * @param nodeId is the [InspectorNode.id] of the node the parameter belongs to. + * @param anchorId is the [InspectorNode.anchorId] of the node the parameter belongs to. + * @param name is the name of the [reference].parameterIndex'th parameter of the node. + * @param value is the value of the [reference].parameterIndex'th parameter of the node. + * @param startIndex is the index of the 1st wanted element of a List/Array. + * @param maxElements is the max number of elements wanted from a List/Array. + * @param maxRecursions is the max recursion into composite types starting from reference. + * @param maxInitialIterableSize is the max number of elements wanted in new List/Array values. + */ + fun expand( + rootId: Long, + nodeId: Long, + anchorId: Int, + name: String, + value: Any?, + reference: NodeParameterReference, + startIndex: Int, + maxElements: Int, + maxRecursions: Int, + maxInitialIterableSize: Int + ): NodeParameter? { + val creator = creatorCache ?: ParameterCreator() + try { + return reflectionScope.withReflectiveAccess { + creator.expand( + rootId, + nodeId, + anchorId, + name, + value, + reference, + startIndex, + maxElements, + maxRecursions, + maxInitialIterableSize) + } + } finally { + creatorCache = creator + } + } + + fun clearReferenceCache() { + val creator = creatorCache ?: return + creator.clearReferenceCache() + } + + private fun loadConstantsFrom(javaClass: Class<*>) { + if (valuesLoaded.contains(javaClass) || + ignoredPackagePrefixes.any { javaClass.name.startsWith(it) }) { + return + } + val related = generateSequence(javaClass) { it.superclass }.plus(javaClass.interfaces) + related.forEach { aClass -> + val topClass = generateSequence(aClass) { safeEnclosingClass(it) }.last() + loadConstantsFromEnclosedClasses(topClass) + findPackageLevelClass(topClass)?.let { loadConstantsFromStaticFinal(it) } + } + } + + private fun safeEnclosingClass(klass: Class<*>): Class<*>? = + try { + klass.enclosingClass + } catch (_: Error) { + // Exceptions seen on API 23... + null + } + + private fun findPackageLevelClass(javaClass: Class<*>): Class<*>? = + try { + // Note: This doesn't work when @file.JvmName is specified + Class.forName("${javaClass.name}Kt") + } catch (ex: Throwable) { + null + } + + private fun loadConstantsFromEnclosedClasses(javaClass: Class<*>) { + if (valuesLoaded.contains(javaClass)) { + return + } + loadConstantsFromObjectInstance(javaClass.kotlin) + loadConstantsFromStaticFinal(javaClass) + valuesLoaded.add(javaClass) + javaClass.declaredClasses.forEach { loadConstantsFromEnclosedClasses(it) } + } + + /** + * Load all constants from companion objects and singletons + * + * Exclude: primary types and types of ignoredClasses, open and lateinit vals. + */ + private fun loadConstantsFromObjectInstance(kClass: KClass<*>) { + try { + val instance = kClass.objectInstance ?: return + kClass.declaredMemberProperties + .asSequence() + .filter { it.isFinal && !it.isLateinit } + .mapNotNull { constantValueOf(it, instance)?.let { key -> Pair(key, it.name) } } + .filter { !ignoredValue(it.first) } + .toMap(valueLookup) + } catch (_: Throwable) { + // KT-16479 : kotlin reflection does currently not support packages and files. + // We load top level values using Java reflection instead. + // Ignore other reflection errors as well + } + } + + /** + * Load all constants from top level values from Java. + * + * Exclude: primary types and types of ignoredClasses. Since this is Java, inline types will also + * (unfortunately) be excluded. + */ + private fun loadConstantsFromStaticFinal(javaClass: Class<*>) { + try { + javaClass.declaredMethods + .asSequence() + .filter { + it.returnType != Void.TYPE && + JavaModifier.isStatic(it.modifiers) && + JavaModifier.isFinal(it.modifiers) && + !it.returnType.isPrimitive && + it.parameterTypes.isEmpty() && + it.name.startsWith("get") + } + .mapNotNull { javaClass.getDeclaredField(it.name.substring(3)) } + .mapNotNull { constantValueOf(it)?.let { key -> Pair(key, it.name) } } + .filter { !ignoredValue(it.first) } + .toMap(valueLookup) + } catch (_: ReflectiveOperationException) { + // ignore reflection errors + } catch (_: NoClassDefFoundError) { + // ignore missing classes on lower level SDKs + } + } + + private fun constantValueOf(field: Field?): Any? = + try { + field?.isAccessible = true + field?.get(null) + } catch (_: ReflectiveOperationException) { + // ignore reflection errors + null + } + + private fun constantValueOf(property: KProperty1, instance: Any): Any? = + try { + val field = property.javaField + field?.isAccessible = true + inlineClassConverter.castParameterValue(inlineResultClass(property), field?.get(instance)) + } catch (_: ReflectiveOperationException) { + // ignore reflection errors + null + } + + private fun inlineResultClass(property: KProperty1): String? { + // The Java getter name will be mangled if it contains parameters of an inline class. + // The mangled part starts with a '-'. + if (property.javaGetter?.name?.contains('-') == true) { + return property.returnType.toString() + } + return null + } + + private fun ignoredValue(value: Any?): Boolean = + value == null || + ignoredClasses.any { ignored -> ignored.isInstance(value) } || + value::class.java.isPrimitive + + /** Convenience class for building [NodeParameter]s. */ + private inner class ParameterCreator { + private var rootId = 0L + private var nodeId = 0L + private var anchorId = 0 + private var kind: ParameterKind = ParameterKind.Normal + private var parameterIndex = 0 + private var maxRecursions = 0 + private var maxInitialIterableSize = 0 + private var recursions = 0 + private val valueIndex = mutableListOf() + private val valueLazyReferenceMap = IdentityHashMap>() + private val rootValueIndexCache = + mutableMapOf>() + private var valueIndexMap = IdentityHashMap() + + fun create( + rootId: Long, + nodeId: Long, + anchorId: Int, + name: String, + value: Any?, + kind: ParameterKind, + parameterIndex: Int, + maxRecursions: Int, + maxInitialIterableSize: Int + ): NodeParameter = + try { + setup( + rootId, nodeId, anchorId, kind, parameterIndex, maxRecursions, maxInitialIterableSize) + create(name, value, null) ?: createEmptyParameter(name) + } finally { + setup() + } + + fun expand( + rootId: Long, + nodeId: Long, + anchorId: Int, + name: String, + value: Any?, + reference: NodeParameterReference, + startIndex: Int, + maxElements: Int, + maxRecursions: Int, + maxInitialIterableSize: Int + ): NodeParameter? { + setup( + rootId, + nodeId, + anchorId, + reference.kind, + reference.parameterIndex, + maxRecursions, + maxInitialIterableSize) + var parent: Pair? = null + var new = Pair(name, value) + for (i in reference.indices) { + parent = new + new = find(new.first, new.second, i) ?: return null + } + recursions = 0 + valueIndex.addAll(reference.indices.asSequence()) + val parameter = + if (startIndex == 0) { + create(new.first, new.second, parent?.second) + } else { + createFromCompositeValue(new.first, new.second, parent?.second, startIndex, maxElements) + } + if (parameter == null && reference.indices.isEmpty()) { + return createEmptyParameter(name) + } + return parameter + } + + fun clearReferenceCache() { + rootValueIndexCache.clear() + } + + private fun setup( + newRootId: Long = 0, + newNodeId: Long = 0, + newAnchorId: Int = 0, + newKind: ParameterKind = ParameterKind.Normal, + newParameterIndex: Int = 0, + maxRecursions: Int = 0, + maxInitialIterableSize: Int = 0 + ) { + rootId = newRootId + nodeId = newNodeId + anchorId = newAnchorId + kind = newKind + parameterIndex = newParameterIndex + this.maxRecursions = maxRecursions + this.maxInitialIterableSize = maxInitialIterableSize + recursions = 0 + valueIndex.clear() + valueLazyReferenceMap.clear() + valueIndexMap = rootValueIndexCache.getOrPut(newRootId) { IdentityHashMap() } + } + + private fun create(name: String, value: Any?, parentValue: Any?): NodeParameter? { + if (value == null) { + return null + } + createFromSimpleValue(name, value)?.let { + return it + } + + val existing = + valueIndexMap[value] ?: return createFromCompositeValue(name, value, parentValue) + + // Do not decompose an instance we already decomposed. + // Instead reference the data that was already decomposed. + return createReferenceToExistingValue(name, value, parentValue, existing) + } + + private fun create( + name: String, + value: Any?, + parentValue: Any?, + specifiedIndex: Int = 0 + ): NodeParameter? = create(name, value, parentValue)?.apply { index = specifiedIndex } + + private fun createFromSimpleValue(name: String, value: Any?): NodeParameter? { + if (value == null) { + return null + } + createFromConstant(name, value)?.let { + return it + } + return when (value) { + is AnnotatedString -> NodeParameter(name, ParameterType.String, value.text) + is BaselineShift -> createFromBaselineShift(name, value) + is Boolean -> NodeParameter(name, ParameterType.Boolean, value) + is ComposableLambda -> createFromCLambda(name, value) + is Color -> NodeParameter(name, ParameterType.Color, value.toArgb()) + is Double -> NodeParameter(name, ParameterType.Double, value) + is Dp -> NodeParameter(name, DimensionDp, value.value) + is Enum<*> -> NodeParameter(name, ParameterType.String, value.toString()) + is Float -> NodeParameter(name, ParameterType.Float, value) + is FunctionReference -> createFromFunctionReference(name, value) + is FontListFontFamily -> createFromFontListFamily(name, value) + is FontWeight -> NodeParameter(name, ParameterType.Int32, value.weight) + is Int -> NodeParameter(name, ParameterType.Int32, value) + is Lambda<*> -> createFromLambda(name, value) + is Locale -> NodeParameter(name, ParameterType.String, value.toString()) + is Long -> NodeParameter(name, ParameterType.Int64, value) + is SolidColor -> NodeParameter(name, ParameterType.Color, value.value.toArgb()) + is String -> NodeParameter(name, ParameterType.String, value) + is TextUnit -> createFromTextUnit(name, value) + is ImageVector -> createFromImageVector(name, value) + is View -> NodeParameter(name, ParameterType.String, value.javaClass.simpleName) + else -> null + } + } + + private fun createFromCompositeValue( + name: String, + value: Any?, + parentValue: Any?, + startIndex: Int = 0, + maxElements: Int = maxInitialIterableSize + ): NodeParameter? = + when { + value == null -> null + value is Modifier -> createFromModifier(name, value) + value is InspectableValue -> createFromInspectableValue(name, value) + value is Sequence<*> -> createFromSequence(name, value, value, startIndex, maxElements) + value is Map<*, *> -> + createFromSequence(name, value, value.asSequence(), startIndex, maxElements) + value is Map.Entry<*, *> -> createFromMapEntry(name, value, parentValue) + value is Iterable<*> -> + createFromSequence(name, value, value.asSequence(), startIndex, maxElements) + value.javaClass.isArray -> createFromArray(name, value, startIndex, maxElements) + value is Offset -> createFromOffset(name, value) + value is Shadow -> createFromShadow(name, value) + value is TextStyle -> createFromTextStyle(name, value) + else -> createFromKotlinReflection(name, value) + } + + private fun find(name: String, value: Any?, index: Int): Pair? = + when { + value == null -> null + value is Modifier -> findFromModifier(name, value, index) + value is InspectableValue -> findFromInspectableValue(value, index) + value is Sequence<*> -> findFromSequence(value, index) + value is Map<*, *> -> findFromSequence(value.asSequence(), index) + value is Map.Entry<*, *> -> findFromMapEntry(value, index) + value is Iterable<*> -> findFromSequence(value.asSequence(), index) + value.javaClass.isArray -> findFromArray(value, index) + value is Offset -> findFromOffset(value, index) + value is Shadow -> findFromShadow(value, index) + value is TextStyle -> findFromTextStyle(value, index) + else -> findFromKotlinReflection(value, index) + } + + private fun createRecursively( + name: String, + value: Any?, + parentValue: Any?, + index: Int + ): NodeParameter? { + valueIndex.add(index) + recursions++ + val parameter = create(name, value, parentValue)?.apply { this.index = index } + recursions-- + valueIndex.removeLast() + return parameter + } + + private fun shouldRecurseDeeper(): Boolean = recursions < maxRecursions + + /** + * Create a [NodeParameter] as a reference to a previously created parameter. + * + * Use [createFromCompositeValue] to compute the data type and top value, however no children + * will be created. Instead a reference to the previously created parameter is specified. + */ + private fun createReferenceToExistingValue( + name: String, + value: Any?, + parentValue: Any?, + ref: NodeParameterReference + ): NodeParameter? { + val remember = recursions + recursions = maxRecursions + val parameter = createFromCompositeValue(name, value, parentValue)?.apply { reference = ref } + recursions = remember + return parameter + } + + /** + * Returns `true` if the value can be mapped to a [NodeParameter]. + * + * Composite values should NOT be added to the [valueIndexMap] since we do not intend to include + * this parameter in the response. + */ + private fun hasMappableValue(value: Any?): Boolean { + if (value == null) { + return false + } + if (valueIndexMap.containsKey(value)) { + return true + } + val remember = recursions + recursions = maxRecursions + val parameter = create("p", value, null) + recursions = remember + valueIndexMap.remove(value) + return parameter != null + } + + /** + * Store the reference of this [NodeParameter] by its [value] + * + * If the value is seen in other parameter values again, there is no need to create child + * parameters a second time. + */ + private fun NodeParameter.store(value: Any?): NodeParameter { + if (value != null) { + val index = valueIndexToReference() + valueIndexMap[value] = index + } + return this + } + + /** Remove the [value] of this [NodeParameter] if there are no child elements. */ + private fun NodeParameter.removeIfEmpty(value: Any?): NodeParameter { + if (value != null) { + if (elements.isEmpty()) { + valueIndexMap.remove(value) + } + val reference = valueIndexMap[value] + valueLazyReferenceMap.remove(value)?.forEach { it.reference = reference } + } + return this + } + + /** + * Delay the creation of all child parameters of this composite parameter. + * + * If the child parameters are omitted because of [maxRecursions], store the parameter itself + * such that its reference can be updated if it turns out that child [NodeParameter]s need to be + * generated later. + */ + private fun NodeParameter.withChildReference(value: Any): NodeParameter { + valueLazyReferenceMap.getOrPut(value, { mutableListOf() }).add(this) + reference = valueIndexToReference() + return this + } + + private fun valueIndexToReference(): NodeParameterReference = + NodeParameterReference(nodeId, anchorId, kind, parameterIndex, valueIndex) + + private fun createEmptyParameter(name: String): NodeParameter = + NodeParameter(name, ParameterType.String, "") + + private fun createFromArray( + name: String, + value: Any, + startIndex: Int, + maxElements: Int + ): NodeParameter? { + val sequence = arrayToSequence(value) ?: return null + return createFromSequence(name, value, sequence, startIndex, maxElements) + } + + private fun findFromArray(value: Any, index: Int): Pair? { + val sequence = arrayToSequence(value) ?: return null + return findFromSequence(sequence, index) + } + + private fun arrayToSequence(value: Any): Sequence<*>? = + when (value) { + is Array<*> -> value.asSequence() + is ByteArray -> value.asSequence() + is IntArray -> value.asSequence() + is LongArray -> value.asSequence() + is FloatArray -> value.asSequence() + is DoubleArray -> value.asSequence() + is BooleanArray -> value.asSequence() + is CharArray -> value.asSequence() + else -> null + } + + private fun createFromBaselineShift(name: String, value: BaselineShift): NodeParameter { + val converted = + when (value.multiplier) { + BaselineShift.None.multiplier -> "None" + BaselineShift.Subscript.multiplier -> "Subscript" + BaselineShift.Superscript.multiplier -> "Superscript" + else -> return NodeParameter(name, ParameterType.Float, value.multiplier) + } + return NodeParameter(name, ParameterType.String, converted) + } + + private fun createFromCLambda(name: String, value: ComposableLambda): NodeParameter? = + try { + val lambda = + value.javaClass.getDeclaredField("_block").apply { isAccessible = true }.get(value) + NodeParameter(name, ParameterType.Lambda, arrayOf(lambda)) + } catch (_: Throwable) { + null + } + + private fun createFromConstant(name: String, value: Any): NodeParameter? { + loadConstantsFrom(value.javaClass) + return valueLookup[value]?.let { NodeParameter(name, ParameterType.String, it) } + } + + // For now: select ResourceFontFont closest to W400 and Normal, and return the resId + private fun createFromFontListFamily(name: String, value: FontListFontFamily): NodeParameter? = + findBestResourceFont(value)?.let { NodeParameter(name, ParameterType.Resource, it.resId) } + + private fun createFromFunctionReference(name: String, value: FunctionReference): NodeParameter = + NodeParameter(name, ParameterType.FunctionReference, arrayOf(value, value.name)) + + private fun createFromKotlinReflection(name: String, value: Any): NodeParameter? { + val simpleName = value::class.simpleName + val properties = lookup(value) ?: return null + val parameter = NodeParameter(name, ParameterType.String, simpleName) + return when { + properties.isEmpty() -> parameter + !shouldRecurseDeeper() -> parameter.withChildReference(value) + else -> { + val elements = parameter.store(value).elements + properties.values.mapIndexedNotNullTo(elements) { index, part -> + createRecursively(part.name, valueOf(part, value), value, index) + } + parameter.removeIfEmpty(value) + } + } + } + + private fun findFromKotlinReflection(value: Any, index: Int): Pair? { + val properties = lookup(value)?.entries?.iterator()?.asSequence() ?: return null + val element = properties.elementAtOrNull(index)?.value ?: return null + return Pair(element.name, valueOf(element, value)) + } + + private fun lookup(value: Any): Map>? { + val kClass = value::class + val simpleName = kClass.simpleName + val qualifiedName = kClass.qualifiedName + if (simpleName == null || + qualifiedName == null || + ignoredPackagePrefixes.any { qualifiedName.startsWith(it) }) { + // Exit without creating a parameter for: + // - internal synthetic classes + // - certain android packages + return null + } + return try { + sequenceOf(kClass) + .plus(kClass.allSuperclasses.asSequence()) + .flatMap { it.declaredMemberProperties.asSequence() } + .associateBy { it.name } + } catch (ex: Throwable) { + Log.w("Compose", "Could not decompose ${kClass.simpleName}", ex) + null + } + } + + private fun valueOf(property: KProperty<*>, instance: Any): Any? = + try { + property.isAccessible = true + // Bug in kotlin reflection API: if the type is a nullable inline type with a null + // value, we get an IllegalArgumentException in this line: + property.getter.call(instance) + } catch (ex: Throwable) { + // TODO: Remove this warning since this is expected with nullable inline types + Log.w("Compose", "Could not get value of ${property.name}") + null + } + + private fun createFromInspectableValue(name: String, value: InspectableValue): NodeParameter { + val tempValue = value.valueOverride ?: "" + val parameterName = name.ifEmpty { value.nameFallback } ?: "element" + val parameterValue = if (tempValue is InspectableValue) "" else tempValue + val parameter = + createFromSimpleValue(parameterName, parameterValue) + ?: NodeParameter(parameterName, ParameterType.String, "") + if (!shouldRecurseDeeper()) { + return parameter.withChildReference(value) + } + val elements = parameter.store(value).elements + value.inspectableElements.mapIndexedNotNullTo(elements) { index, element -> + createRecursively(element.name, element.value, value, index) + } + return parameter.removeIfEmpty(value) + } + + private fun findFromInspectableValue(value: InspectableValue, index: Int): Pair? { + val elements = value.inspectableElements.toList() + if (index !in elements.indices) { + return null + } + val element = elements[index] + return Pair(element.name, element.value) + } + + private fun createFromMapEntry( + name: String, + entry: Map.Entry<*, *>, + parentValue: Any? + ): NodeParameter? { + val key = createRecursively("key", entry.key, entry, 0) ?: return null + val value = createRecursively("value", entry.value, entry, 1) ?: return null + val keyName = (key.value?.toString() ?: "").ifEmpty { "entry" } + val valueName = value.value?.toString()?.ifEmpty { null } + val nodeName = if (parentValue is Map<*, *>) "[$keyName]" else name + return NodeParameter(nodeName, ParameterType.String, valueName).apply { + elements.add(key) + elements.add(value) + } + } + + private fun findFromMapEntry(entry: Map.Entry<*, *>, index: Int): Pair? = + when (index) { + 0 -> Pair("key", entry.key) + 1 -> Pair("value", entry.value) + else -> null + } + + private fun createFromSequence( + name: String, + value: Any, + sequence: Sequence<*>, + startIndex: Int, + maxElements: Int + ): NodeParameter { + val parameter = NodeParameter(name, ParameterType.Iterable, sequenceName(value)) + return when { + !sequence.any() -> parameter + !shouldRecurseDeeper() -> parameter.withChildReference(value) + else -> { + val elements = parameter.store(value).elements + val rest = sequence.drop(startIndex).iterator() + var index = startIndex + while (rest.hasNext() && elements.size < maxElements) { + createRecursively("[$index]", rest.next(), value, index)?.let { elements.add(it) } + index++ + } + while (rest.hasNext()) { + if (hasMappableValue(rest.next())) { + parameter.withChildReference(value) + break + } + } + parameter.removeIfEmpty(value) + } + } + } + + private fun findFromSequence(value: Sequence<*>, index: Int): Pair? { + val element = value.elementAtOrNull(index) ?: return null + return Pair("[$index]", element) + } + + private fun sequenceName(value: Any): String = + when (value) { + is Array<*> -> "Array[${value.size}]" + is ByteArray -> "ByteArray[${value.size}]" + is IntArray -> "IntArray[${value.size}]" + is LongArray -> "LongArray[${value.size}]" + is FloatArray -> "FloatArray[${value.size}]" + is DoubleArray -> "DoubleArray[${value.size}]" + is BooleanArray -> "BooleanArray[${value.size}]" + is CharArray -> "CharArray[${value.size}]" + is List<*> -> "List[${value.size}]" + is Set<*> -> "Set[${value.size}]" + is Map<*, *> -> "Map[${value.size}]" + is Collection<*> -> "Collection[${value.size}]" + is Iterable<*> -> "Iterable" + else -> "Sequence" + } + + private fun createFromLambda(name: String, value: Lambda<*>): NodeParameter = + NodeParameter(name, ParameterType.Lambda, arrayOf(value)) + + private fun createFromModifier(name: String, value: Modifier): NodeParameter? = + when { + name.isNotEmpty() -> { + val parameter = NodeParameter(name, ParameterType.String, "") + val modifiers = unwrap(value) + when { + modifiers.isEmpty() -> parameter + !shouldRecurseDeeper() -> parameter.withChildReference(value) + else -> { + val elements = parameter.elements + modifiers.mapIndexedNotNullTo(elements) { index, element -> + createRecursively("", element, value, index) + } + parameter.store(value).removeIfEmpty(value) + } + } + } + value is InspectableValue -> createFromInspectableValue(name, value) + else -> null + } + + private fun unwrap(value: Modifier): List { + val collector = ModifierCollector() + value.foldIn(collector) { acc, m -> acc.apply { add(m) } } + return collector.modifiers + } + + private fun findFromModifier(name: String, value: Modifier, index: Int): Pair? = + when { + name.isNotEmpty() -> { + val modifiers = unwrap(value) + if (index in modifiers.indices) Pair("", modifiers[index]) else null + } + value is InspectableValue -> findFromInspectableValue(value, index) + else -> null + } + + private fun createFromOffset(name: String, value: Offset): NodeParameter { + val parameter = NodeParameter(name, ParameterType.String, Offset::class.java.simpleName) + val elements = parameter.elements + val x = with(density) { value.x.toDp().value } + val y = with(density) { value.y.toDp().value } + elements.add(NodeParameter("x", DimensionDp, x)) + elements.add(NodeParameter("y", DimensionDp, y).apply { index = 1 }) + return parameter + } + + private fun findFromOffset(value: Offset, index: Int): Pair? = + when (index) { + 0 -> Pair("x", with(density) { value.x.toDp() }) + 1 -> Pair("y", with(density) { value.y.toDp() }) + else -> null + } + + // Special handling of blurRadius: convert to dp: + private fun createFromShadow(name: String, value: Shadow): NodeParameter? { + val parameter = createFromKotlinReflection(name, value) ?: return null + val elements = parameter.elements + val index = elements.indexOfFirst { it.name == "blurRadius" } + if (index >= 0) { + val existing = elements[index] + val blurRadius = with(density) { value.blurRadius.toDp().value } + elements[index] = NodeParameter("blurRadius", DimensionDp, blurRadius) + elements[index].index = existing.index + } + return parameter + } + + private fun findFromShadow(value: Shadow, index: Int): Pair? { + val result = findFromKotlinReflection(value, index) + if (result == null || result.first != "blurRadius") { + return result + } + return Pair("blurRadius", with(density) { value.blurRadius.toDp() }) + } + + // Temporary handling of TextStyle: remove when TextStyle implements InspectableValue + // Hide: paragraphStyle, spanStyle, platformStyle, lineHeightStyle + private fun createFromTextStyle(name: String, value: TextStyle): NodeParameter? { + val parameter = NodeParameter(name, ParameterType.String, TextStyle::class.java.simpleName) + val elements = parameter.elements + create("color", value.color, value)?.let { elements.add(it) } + create("fontSize", value.fontSize, value, 1)?.let { elements.add(it) } + create("fontWeight", value.fontWeight, value, 2)?.let { elements.add(it) } + create("fontStyle", value.fontStyle, value, 3)?.let { elements.add(it) } + create("fontSynthesis", value.fontSynthesis, value, 4)?.let { elements.add(it) } + create("fontFamily", value.fontFamily, value, 5)?.let { elements.add(it) } + create("fontFeatureSettings", value.fontFeatureSettings, value, 6)?.let { elements.add(it) } + create("letterSpacing", value.letterSpacing, value, 7)?.let { elements.add(it) } + create("baselineShift", value.baselineShift, value, 8)?.let { elements.add(it) } + create("textGeometricTransform", value.textGeometricTransform, value, 9)?.let { + elements.add(it) + } + create("localeList", value.localeList, value, 10)?.let { elements.add(it) } + create("background", value.background, value, 11)?.let { elements.add(it) } + create("textDecoration", value.textDecoration, value, 12)?.let { elements.add(it) } + create("shadow", value.shadow, value, 13)?.let { elements.add(it) } + create("textDirection", value.textDirection, value, 14)?.let { elements.add(it) } + create("lineHeight", value.lineHeight, value, 15)?.let { elements.add(it) } + create("textIndent", value.textIndent, value, 16)?.let { elements.add(it) } + return parameter + } + + private fun findFromTextStyle(value: TextStyle, index: Int): Pair? = + when (index) { + 0 -> Pair("color", value.color) + 1 -> Pair("fontSize", value.fontSize) + 2 -> Pair("fontWeight", value.fontWeight) + 3 -> Pair("fontStyle", value.fontStyle) + 4 -> Pair("fontSynthesis", value.fontSynthesis) + 5 -> Pair("fontFamily", value.fontFamily) + 6 -> Pair("fontFeatureSettings", value.fontFeatureSettings) + 7 -> Pair("letterSpacing", value.letterSpacing) + 8 -> Pair("baselineShift", value.baselineShift) + 9 -> Pair("textGeometricTransform", value.textGeometricTransform) + 10 -> Pair("localeList", value.localeList) + 11 -> Pair("background", value.background) + 12 -> Pair("textDecoration", value.textDecoration) + 13 -> Pair("shadow", value.shadow) + 14 -> Pair("textDirection", value.textDirection) + 15 -> Pair("lineHeight", value.lineHeight) + 16 -> Pair("textIndent", value.textIndent) + else -> null + } + + @Suppress("DEPRECATION") + private fun createFromTextUnit(name: String, value: TextUnit): NodeParameter = + when (value.type) { + TextUnitType.Sp -> NodeParameter(name, ParameterType.DimensionSp, value.value) + TextUnitType.Em -> NodeParameter(name, ParameterType.DimensionEm, value.value) + else -> NodeParameter(name, ParameterType.String, "Unspecified") + } + + private fun createFromImageVector(name: String, value: ImageVector): NodeParameter = + NodeParameter(name, ParameterType.String, value.name) + + /** + * Select a resource font among the font in the family to represent the font + * + * Prefer the font closest to [FontWeight.Normal] and [FontStyle.Normal] + */ + private fun findBestResourceFont(value: FontListFontFamily): ResourceFont? = + value.fonts.asSequence().filterIsInstance().minByOrNull { + abs(it.weight.weight - FontWeight.Normal.weight) + it.style.value + } + } + + private class ModifierCollector { + val modifiers = mutableListOf() + var start: InspectableModifier? = null + + fun add(element: Modifier.Element) = + when { + element == start?.end -> start = null + start != null -> {} + else -> { + modifiers.add(element) + start = element as? InspectableModifier + } + } + } +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt new file mode 100644 index 000000000..8deae6a1b --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt @@ -0,0 +1,215 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.inspector + +import android.annotation.SuppressLint +import java.lang.reflect.Field +import java.lang.reflect.Modifier +import kotlin.jvm.internal.FunctionBase +import kotlin.jvm.internal.FunctionReference +import kotlin.jvm.internal.Lambda +import kotlin.jvm.internal.MutablePropertyReference0 +import kotlin.jvm.internal.MutablePropertyReference1 +import kotlin.jvm.internal.MutablePropertyReference2 +import kotlin.jvm.internal.PropertyReference0 +import kotlin.jvm.internal.PropertyReference1 +import kotlin.jvm.internal.PropertyReference2 +import kotlin.jvm.internal.Reflection +import kotlin.jvm.internal.ReflectionFactory +import kotlin.reflect.KClass +import kotlin.reflect.KClassifier +import kotlin.reflect.KDeclarationContainer +import kotlin.reflect.KFunction +import kotlin.reflect.KMutableProperty0 +import kotlin.reflect.KMutableProperty1 +import kotlin.reflect.KMutableProperty2 +import kotlin.reflect.KProperty0 +import kotlin.reflect.KProperty1 +import kotlin.reflect.KProperty2 +import kotlin.reflect.KType +import kotlin.reflect.KTypeParameter +import kotlin.reflect.KTypeProjection +import kotlin.reflect.KVariance +import kotlin.reflect.jvm.internal.ReflectionFactoryImpl + +/** + * Scope that allows to use jarjar-ed kotlin-reflect artifact that is shipped with inspector itself. + * + * Issue with kotlin-reflect. Many of reflective calls such as "foo::class" rely on static functions + * defined in kotlin-stdlib's Reflection.java that delegate to ReflectionFactory. In order to + * initialize that factory kotlin-stdlib statically detects presence or absence of kotlin-reflect in + * classloader and chooses a factory accordingly. If there is no kotlin-reflect, very limited + * version of ReflectionFactory is used. + * + * It is an issue for inspectors because they could be loaded after that factory is initialised, and + * even if they are loaded before, they live in a separate child classloader, thus kotlin-reflect in + * inspector wouldn't exist for kotlin-stdlib in app. + * + * First step to avoid the issue is using ReflectionFactoryImpl that is bundled with inspector. Code + * for that would be fairly simple, for example instead of directly calling + * `kClass.declaredMemberProperties`, correct instance of kClass should be obtained from factory: + * `factory.getOrCreateKotlinClass(kClass.java).declaredMemberProperties`. + * + * That would work if code that works with correct KClass full implementation would never try to + * access a default factory installed in Reflection.java. Unfortunately it is not true, it + * eventually calls `CallableReference.getOwner()` in stdlib that uses default factory. + * + * As a result we have to replace the factory in Reflection.java. To avoid issues with user's code + * factory that we setup is smart, by default it simply delegates to a factory that was previously + * installed. Only within `reflectionScope.withReflectiveAccess{ }` factory from kotlin-reflect is + * used. + */ +@SuppressLint("BanUncheckedReflection") +class ReflectionScope { + + companion object { + init { + allowHiddenApi() + } + } + + private val scopedReflectionFactory = installScopedReflectionFactory() + + /** Runs `block` with access to kotlin-reflect. */ + fun withReflectiveAccess(block: () -> T): T { + return scopedReflectionFactory.withMainFactory(block) + } + + private fun installScopedReflectionFactory(): ScopedReflectionFactory { + val factoryField = Reflection::class.java.getDeclaredField("factory") + factoryField.isAccessible = true + val original: ReflectionFactory = factoryField.get(null) as ReflectionFactory + val modifiersField: Field = Field::class.java.getDeclaredField("accessFlags") + modifiersField.isAccessible = true + // make field non-final 😅 b/179685774 https://youtrack.jetbrains.com/issue/KT-44795 + modifiersField.setInt(factoryField, factoryField.modifiers and Modifier.FINAL.inv()) + val scopedReflectionFactory = ScopedReflectionFactory(original) + factoryField.set(null, scopedReflectionFactory) + return scopedReflectionFactory + } +} + +@SuppressLint("BanUncheckedReflection") +private fun allowHiddenApi() { + try { + val vmDebug = Class.forName("dalvik.system.VMDebug") + val allowHiddenApiReflectionFrom = + vmDebug.getDeclaredMethod("allowHiddenApiReflectionFrom", Class::class.java) + allowHiddenApiReflectionFrom.invoke(null, ReflectionScope::class.java) + } catch (e: Throwable) { + // ignore failure, let's try to proceed without it + } +} + +private class ScopedReflectionFactory( + private val original: ReflectionFactory, +) : ReflectionFactory() { + private val mainFactory = ReflectionFactoryImpl() + private val threadLocalFactory = ThreadLocal() + + fun withMainFactory(block: () -> T): T { + threadLocalFactory.set(mainFactory) + try { + return block() + } finally { + threadLocalFactory.set(null) + } + } + + val factory: ReflectionFactory + get() = threadLocalFactory.get() ?: original + + override fun createKotlinClass(javaClass: Class<*>?): KClass<*> { + return factory.createKotlinClass(javaClass) + } + + override fun createKotlinClass(javaClass: Class<*>?, internalName: String?): KClass<*> { + return factory.createKotlinClass(javaClass, internalName) + } + + override fun getOrCreateKotlinPackage( + javaClass: Class<*>?, + moduleName: String? + ): KDeclarationContainer { + return factory.getOrCreateKotlinPackage(javaClass, moduleName) + } + + override fun getOrCreateKotlinClass(javaClass: Class<*>?): KClass<*> { + return factory.getOrCreateKotlinClass(javaClass) + } + + override fun getOrCreateKotlinClass(javaClass: Class<*>?, internalName: String?): KClass<*> { + return factory.getOrCreateKotlinClass(javaClass, internalName) + } + + override fun renderLambdaToString(lambda: Lambda<*>?): String { + return factory.renderLambdaToString(lambda) + } + + override fun renderLambdaToString(lambda: FunctionBase<*>?): String { + return factory.renderLambdaToString(lambda) + } + + override fun function(f: FunctionReference?): KFunction<*> { + return factory.function(f) + } + + override fun property0(p: PropertyReference0?): KProperty0<*> { + return factory.property0(p) + } + + override fun mutableProperty0(p: MutablePropertyReference0?): KMutableProperty0<*> { + return factory.mutableProperty0(p) + } + + override fun property1(p: PropertyReference1?): KProperty1<*, *> { + return factory.property1(p) + } + + override fun mutableProperty1(p: MutablePropertyReference1?): KMutableProperty1<*, *> { + return factory.mutableProperty1(p) + } + + override fun property2(p: PropertyReference2?): KProperty2<*, *, *> { + return factory.property2(p) + } + + override fun mutableProperty2(p: MutablePropertyReference2?): KMutableProperty2<*, *, *> { + return factory.mutableProperty2(p) + } + + override fun typeOf( + klass: KClassifier?, + arguments: MutableList?, + isMarkedNullable: Boolean + ): KType { + return factory.typeOf(klass, arguments, isMarkedNullable) + } + + override fun typeParameter( + container: Any?, + name: String?, + variance: KVariance?, + isReified: Boolean + ): KTypeParameter { + return factory.typeParameter(container, name, variance, isReified) + } + + override fun setUpperBounds(typeParameter: KTypeParameter?, bounds: MutableList?) { + factory.setUpperBounds(typeParameter, bounds) + } +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/AnchorMap.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/AnchorMap.kt new file mode 100644 index 000000000..b7af6df91 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/AnchorMap.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.util + +import java.util.IdentityHashMap + +const val NO_ANCHOR_ID = 0 + +/** A map of anchors with a unique id generator. */ +class AnchorMap { + private val anchorLookup = mutableMapOf() + private val idLookup = IdentityHashMap() + + /** Return a unique id for the specified [anchor] instance. */ + operator fun get(anchor: Any?): Int = + anchor?.let { idLookup.getOrPut(it) { generateUniqueId(it) } } ?: NO_ANCHOR_ID + + /** Return the anchor associated with a given unique anchor [id]. */ + operator fun get(id: Int): Any? = anchorLookup[id] + + private fun generateUniqueId(anchor: Any): Int { + var id = anchor.hashCode() + while (id == NO_ANCHOR_ID || anchorLookup.containsKey(id)) { + id++ + } + anchorLookup[id] = anchor + return id + } +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/IntArray.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/IntArray.kt new file mode 100644 index 000000000..8287eb7a3 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/IntArray.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.util + +private val EMPTY_INT_ARRAY = intArrayOf() + +fun List.asIntArray() = if (isNotEmpty()) toIntArray() else EMPTY_INT_ARRAY diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt new file mode 100644 index 000000000..80320d9f7 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.util + +import android.os.Handler +import android.os.Looper +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Future + +object ThreadUtils { + fun assertOnMainThread() { + if (!Looper.getMainLooper().isCurrentThread) { + error("This work is required on the main thread") + } + } + + fun assertOffMainThread() { + if (Looper.getMainLooper().isCurrentThread) { + error("This work is required off the main thread") + } + } + + /** + * Run some logic on the main thread, returning a future that will contain any data computed by + * and returned from the block. + * + * If this method is called from the main thread, it will run immediately. + */ + fun runOnMainThread(block: () -> T): Future { + return if (!Looper.getMainLooper().isCurrentThread) { + val future = CompletableFuture() + Handler.createAsync(Looper.getMainLooper()).post { future.complete(block()) } + future + } else { + CompletableFuture.completedFuture(block()) + } + } +} diff --git a/android/plugins/leakcanary/build.gradle b/android/plugins/leakcanary/build.gradle index 0c6388fb3..12bd46933 100644 --- a/android/plugins/leakcanary/build.gradle +++ b/android/plugins/leakcanary/build.gradle @@ -8,6 +8,7 @@ apply plugin: 'com.android.library' android { + namespace 'com.facebook.flipper.plugins.leakcanary' compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion diff --git a/android/plugins/leakcanary2/build.gradle b/android/plugins/leakcanary2/build.gradle index bfc6c47a2..def2cf57d 100644 --- a/android/plugins/leakcanary2/build.gradle +++ b/android/plugins/leakcanary2/build.gradle @@ -7,10 +7,10 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android { + namespace 'com.facebook.flipper.plugins.leakcanary2' compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion @@ -19,8 +19,13 @@ android { targetSdkVersion rootProject.targetSdkVersion } + compileOptions { + targetCompatibility rootProject.javaTargetVersion + sourceCompatibility rootProject.javaTargetVersion + } + dependencies { - compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION" + compileOnly "org.jetbrains.kotlin:kotlin-stdlib:$KOTLIN_VERSION" implementation project(':android') compileOnly deps.leakcanary2 compileOnly deps.jsr305 diff --git a/android/plugins/leakcanary2/src/main/java/com/facebook/flipper/plugins/leakcanary2/FlipperLeakEventListener.kt b/android/plugins/leakcanary2/src/main/java/com/facebook/flipper/plugins/leakcanary2/FlipperLeakEventListener.kt new file mode 100644 index 000000000..3eeae988a --- /dev/null +++ b/android/plugins/leakcanary2/src/main/java/com/facebook/flipper/plugins/leakcanary2/FlipperLeakEventListener.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.leakcanary2 + +import com.facebook.flipper.android.AndroidFlipperClient +import leakcanary.EventListener +import shark.HeapAnalysis +import shark.HeapAnalysisSuccess + +class FlipperLeakEventListener : EventListener { + private val leaks: MutableList = mutableListOf() + + override fun onEvent(event: EventListener.Event) { + if (event is EventListener.Event.HeapAnalysisDone.HeapAnalysisSucceeded) { + val heapAnalysis = event.heapAnalysis + leaks.addAll(heapAnalysis.toLeakList()) + + AndroidFlipperClient.getInstanceIfInitialized()?.let { client -> + (client.getPlugin(LeakCanary2FlipperPlugin.ID) as? LeakCanary2FlipperPlugin)?.reportLeaks( + leaks) + } + } + } + + private fun HeapAnalysis.toLeakList(): List { + return if (this is HeapAnalysisSuccess) { + allLeaks + .mapNotNull { + if (it.leakTraces.isNotEmpty()) { + it.leakTraces[0].toLeak(it.shortDescription) + } else { + null + } + } + .toList() + } else { + emptyList() + } + } +} diff --git a/android/plugins/leakcanary2/src/main/java/com/facebook/flipper/plugins/leakcanary2/FlipperLeakListener.kt b/android/plugins/leakcanary2/src/main/java/com/facebook/flipper/plugins/leakcanary2/FlipperLeakListener.kt index 660edc7e2..ffc2c3df7 100644 --- a/android/plugins/leakcanary2/src/main/java/com/facebook/flipper/plugins/leakcanary2/FlipperLeakListener.kt +++ b/android/plugins/leakcanary2/src/main/java/com/facebook/flipper/plugins/leakcanary2/FlipperLeakListener.kt @@ -13,6 +13,7 @@ import leakcanary.OnHeapAnalyzedListener import shark.HeapAnalysis import shark.HeapAnalysisSuccess +@Deprecated("Use FlipperLeakEventListener add to LeakCanary.config.eventListeners instead") class FlipperLeakListener : OnHeapAnalyzedListener { private val leaks: MutableList = mutableListOf() diff --git a/android/plugins/litho/build.gradle b/android/plugins/litho/build.gradle index e9f636e3c..4ca8b0e45 100644 --- a/android/plugins/litho/build.gradle +++ b/android/plugins/litho/build.gradle @@ -6,8 +6,10 @@ */ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { + namespace 'com.facebook.flipper.plugins.litho' compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion @@ -16,9 +18,15 @@ android { targetSdkVersion rootProject.targetSdkVersion } + compileOptions { + targetCompatibility rootProject.javaTargetVersion + sourceCompatibility rootProject.javaTargetVersion + } + dependencies { compileOnly deps.lithoAnnotations implementation project(':android') + implementation deps.kotlinCoroutinesAndroid implementation deps.lithoCore api deps.lithoEditorCore api(deps.lithoEditorFlipper) { diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupport.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupport.kt new file mode 100644 index 000000000..0101dc728 --- /dev/null +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupport.kt @@ -0,0 +1,126 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.litho + +import com.facebook.flipper.plugins.uidebugger.core.ConnectionListener +import com.facebook.flipper.plugins.uidebugger.core.UIDContext +import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister +import com.facebook.flipper.plugins.uidebugger.litho.descriptors.* +import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent +import com.facebook.flipper.plugins.uidebugger.model.FrameworkEventMetadata +import com.facebook.litho.ComponentTree +import com.facebook.litho.DebugComponent +import com.facebook.litho.LithoView +import com.facebook.litho.MatrixDrawable +import com.facebook.litho.debug.LithoDebugEvent +import com.facebook.litho.widget.TextDrawable +import com.facebook.rendercore.debug.DebugEvent +import com.facebook.rendercore.debug.DebugEventBus +import com.facebook.rendercore.debug.DebugEventSubscriber +import com.facebook.rendercore.debug.DebugMarkerEvent +import com.facebook.rendercore.debug.DebugProcessEvent +import com.facebook.rendercore.debug.Duration + +const val LithoTag = "Litho" +const val LithoMountableTag = "LithoMountable" + +object UIDebuggerLithoSupport { + + fun enable(context: UIDContext) { + addDescriptors(context.descriptorRegister) + + val eventMeta = + listOf( + // Litho + FrameworkEventMetadata( + LithoDebugEvent.StateUpdateEnqueued, + "Set state was called, this will trigger resolve and then possibly layout and mount"), + FrameworkEventMetadata( + LithoDebugEvent.RenderRequest, + "A request to render the component tree again. It can be requested due to 1) set root 2) state update 3) size change or measurement"), + FrameworkEventMetadata( + LithoDebugEvent.ComponentTreeResolve, + "ComponentTree resolved the hierarchy into a LayoutState, non layout nodes are removed, see attributes for source of execution"), + FrameworkEventMetadata( + LithoDebugEvent.LayoutCommitted, + "A new layout state created (resolved and measured result) being committed; this layout state could get mounted next."), + + // RenderCore + + FrameworkEventMetadata( + DebugEvent.RenderTreeMounted, "The mount phase for the entire render tree"), + FrameworkEventMetadata( + DebugEvent.RenderUnitMounted, + "Component was added into the view hierarchy (this doesn't mean it is visible)"), + FrameworkEventMetadata( + DebugEvent.RenderUnitUpdated, + "The properties of a component's content were were rebinded"), + FrameworkEventMetadata( + DebugEvent.RenderUnitUnmounted, "Component was removed from the view hierarchy"), + FrameworkEventMetadata(DebugEvent.RenderUnitOnVisible, "Component became visible"), + FrameworkEventMetadata(DebugEvent.RenderUnitOnInvisible, "Component became invisible"), + ) + + val eventForwarder = + object : DebugEventSubscriber(*eventMeta.map { it.type }.toTypedArray()) { + override fun onEvent(event: DebugEvent) { + val timestamp = + when (event) { + is DebugMarkerEvent -> event.timestamp + is DebugProcessEvent -> event.timestamp + } + val treeId = event.renderStateId.toIntOrNull() ?: -1 + + val globalKey = + event.attributeOrNull("key")?.let { + DebugComponent.generateGlobalKey(treeId, it).hashCode() + } + val duration = event.attributeOrNull("duration") + + val attributes = mutableMapOf() + val source = + event.attributeOrNull( + "source") // todo replace magic strings with DebugEventAttribute.Source once + // litho open source is released + if (source != null) { + attributes["source"] = source + } + context.addFrameworkEvent( + FrameworkEvent( + treeId, + globalKey ?: treeId, + event.type, + timestamp, + duration?.value, + event.threadName, + attributes)) + } + } + + context.connectionListeners.add( + object : ConnectionListener { + override fun onConnect() { + DebugEventBus.subscribe(eventForwarder) + } + + override fun onDisconnect() { + DebugEventBus.unsubscribe(eventForwarder) + } + }) + + context.frameworkEventMetadata.addAll(eventMeta) + } + + private fun addDescriptors(register: DescriptorRegister) { + register.register(LithoView::class.java, LithoViewDescriptor) + register.register(DebugComponent::class.java, DebugComponentDescriptor(register)) + register.register(TextDrawable::class.java, TextDrawableDescriptor) + register.register(MatrixDrawable::class.java, MatrixDrawableDescriptor) + register.register(ComponentTree::class.java, ComponentTreeDescriptor(register)) + } +} diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/ComponentTreeDescriptor.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/ComponentTreeDescriptor.kt new file mode 100644 index 000000000..22bd068a1 --- /dev/null +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/ComponentTreeDescriptor.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.litho.descriptors + +import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister +import com.facebook.flipper.plugins.uidebugger.descriptors.Id +import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor +import com.facebook.flipper.plugins.uidebugger.descriptors.OffsetChild +import com.facebook.flipper.plugins.uidebugger.litho.LithoTag +import com.facebook.flipper.plugins.uidebugger.model.Bounds +import com.facebook.flipper.plugins.uidebugger.model.InspectableObject +import com.facebook.flipper.plugins.uidebugger.model.MetadataId +import com.facebook.flipper.plugins.uidebugger.util.Immediate +import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred +import com.facebook.litho.ComponentTree +import com.facebook.litho.DebugComponent + +class ComponentTreeDescriptor(val register: DescriptorRegister) : NodeDescriptor { + + private val qualifiedName = ComponentTree::class.qualifiedName ?: "" + + override fun getId(node: ComponentTree): Id = node.id + + override fun getBounds(node: ComponentTree): Bounds { + val rootComponent = DebugComponent.getRootInstance(node) + return if (rootComponent != null) { + Bounds.fromRect(rootComponent.boundsInParentDebugComponent) + } else { + Bounds(0, 0, 0, 0) + } + } + + override fun getName(node: ComponentTree): String = "ComponentTree" + + override fun getQualifiedName(node: ComponentTree): String = qualifiedName + + override fun getChildren(node: ComponentTree): List { + val result = mutableListOf() + val debugComponent = DebugComponent.getRootInstance(node) + if (debugComponent != null) { + result.add( + // we want the component tree to take the size and any offset so we reset this one + OffsetChild.zero( + debugComponent, register.descriptorForClassUnsafe(debugComponent.javaClass))) + } + return result + } + + override fun getActiveChild(node: ComponentTree): Any? = null + + override fun getAttributes( + node: ComponentTree + ): MaybeDeferred> { + return Immediate(mapOf()) + } + + override fun getTags(node: ComponentTree): Set = setOf(LithoTag, "TreeRoot") +} diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/DebugComponentDescriptor.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/DebugComponentDescriptor.kt new file mode 100644 index 000000000..6de5e232d --- /dev/null +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/DebugComponentDescriptor.kt @@ -0,0 +1,188 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.litho.descriptors + +import android.graphics.Bitmap +import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister +import com.facebook.flipper.plugins.uidebugger.descriptors.Id +import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister +import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor +import com.facebook.flipper.plugins.uidebugger.descriptors.OffsetChild +import com.facebook.flipper.plugins.uidebugger.litho.LithoMountableTag +import com.facebook.flipper.plugins.uidebugger.litho.LithoTag +import com.facebook.flipper.plugins.uidebugger.litho.descriptors.props.ComponentDataExtractor +import com.facebook.flipper.plugins.uidebugger.litho.descriptors.props.LayoutPropExtractor +import com.facebook.flipper.plugins.uidebugger.model.Bounds +import com.facebook.flipper.plugins.uidebugger.model.Inspectable +import com.facebook.flipper.plugins.uidebugger.model.InspectableObject +import com.facebook.flipper.plugins.uidebugger.model.InspectableValue +import com.facebook.flipper.plugins.uidebugger.model.MetadataId +import com.facebook.flipper.plugins.uidebugger.util.Deferred +import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred +import com.facebook.litho.Component +import com.facebook.litho.DebugComponent +import com.facebook.rendercore.FastMath +import com.facebook.yoga.YogaEdge + +class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescriptor { + private val NAMESPACE = "DebugComponent" + + /* + * Debug component is generated on the fly so use the underlying component instance which is + * immutable + */ + override fun getId(node: DebugComponent): Id = node.globalKey.hashCode() + + override fun getName(node: DebugComponent): String = node.component.simpleName + + override fun getQualifiedName(node: com.facebook.litho.DebugComponent): String = + node.component::class.qualifiedName ?: "" + + override fun getChildren(node: DebugComponent): List { + val result = mutableListOf() + + val mountedContent = node.mountedContent + + if (mountedContent == null) { + for (child in node.childComponents) { + result.add(child) + } + } else { + + val layoutNode = node.layoutNode + val descriptor: NodeDescriptor = + register.descriptorForClassUnsafe(mountedContent.javaClass) + // mountables are always layout nodes + if (layoutNode != null) { + /** + * We need to override the mounted contents offset since the mounted contents android bounds + * are w.r.t its native parent but we want it w.r.t to the mountable. + * + * However padding on a mountable means that the content is inset within the mountables + * bounds so we need to adjust for this + */ + result.add( + OffsetChild( + child = mountedContent, + descriptor = descriptor, + x = layoutNode.getLayoutPadding(YogaEdge.LEFT).let { FastMath.round(it) }, + y = layoutNode.getLayoutPadding(YogaEdge.TOP).let { FastMath.round(it) }, + )) + } + } + + return result + } + + override fun getActiveChild(node: DebugComponent): Any? = null + + private val LayoutId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "Litho Layout") + + private val UserPropsId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "Litho Props") + + private val StateId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "Litho State") + + private val MountingDataId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "Mount State") + + private val isMountedAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "mounted") + + private val isVisibleAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "visible") + + override fun getAttributes( + node: DebugComponent + ): MaybeDeferred> { + return Deferred { + val attributeSections = mutableMapOf() + + val mountingData = getMountingData(node) + attributeSections[MountingDataId] = InspectableObject(mountingData) + + val layoutProps = LayoutPropExtractor.getProps(node) + attributeSections[LayoutId] = InspectableObject(layoutProps.toMap()) + + if (!node.canResolve()) { + val stateContainer = node.stateContainer + if (stateContainer != null) { + attributeSections[StateId] = + ComponentDataExtractor.getState(stateContainer, node.component.simpleName) + } + + val props = ComponentDataExtractor.getProps(node.component) + + attributeSections[UserPropsId] = InspectableObject(props.toMap()) + } + + attributeSections + } + } + + override fun getBounds(node: DebugComponent): Bounds = + Bounds.fromRect(node.boundsInParentDebugComponent) + + override fun getTags(node: DebugComponent): Set { + val tags = mutableSetOf(LithoTag) + + if (node.component.mountType != Component.MountType.NONE) { + tags.add(LithoMountableTag) + } + return tags + } + + override fun getSnapshot(node: DebugComponent, bitmap: Bitmap?): Bitmap? = null + + override fun getInlineAttributes(node: DebugComponent): Map { + val attributes = mutableMapOf() + val key = node.key + val testKey = node.testKey + if (key != null && key.trim { it <= ' ' }.length > 0) { + attributes["key"] = key + } + if (testKey != null && testKey.trim { it <= ' ' }.length > 0) { + attributes["testKey"] = testKey + } + return attributes + } + + private fun getMountingData(node: DebugComponent): Map { + + val lithoView = node.lithoView + val mountingData = mutableMapOf() + + if (lithoView == null) { + return mountingData + } + + val mountState = lithoView.mountDelegateTarget ?: return mountingData + val componentTree = lithoView.componentTree ?: return mountingData + + val component = node.component + + if (component.mountType != Component.MountType.NONE) { + val renderUnit = DebugComponent.getRenderUnit(node, componentTree) + if (renderUnit != null) { + val renderUnitId = renderUnit.id + val isMounted = mountState.getContentById(renderUnitId) != null + mountingData[isMountedAttributeId] = InspectableValue.Boolean(isMounted) + } + } + + val visibilityOutput = DebugComponent.getVisibilityOutput(node, componentTree) + if (visibilityOutput != null) { + val isVisible = DebugComponent.isVisible(node, lithoView) + mountingData[isVisibleAttributeId] = InspectableValue.Boolean(isVisible) + } + + return mountingData + } +} diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/LithoViewDescriptor.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/LithoViewDescriptor.kt new file mode 100644 index 000000000..26ffe7b36 --- /dev/null +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/LithoViewDescriptor.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.litho.descriptors + +import com.facebook.flipper.plugins.uidebugger.descriptors.ChainedDescriptor +import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister +import com.facebook.flipper.plugins.uidebugger.model.InspectableObject +import com.facebook.flipper.plugins.uidebugger.model.InspectableValue +import com.facebook.flipper.plugins.uidebugger.model.MetadataId +import com.facebook.litho.LithoView + +object LithoViewDescriptor : ChainedDescriptor() { + + private const val NAMESPACE = "LithoView" + private val SectionId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE) + + override fun onGetName(node: LithoView): String = node.javaClass.simpleName + + override fun onGetChildren(node: LithoView): List { + val componentTree = node.componentTree + if (componentTree != null) { + return listOf(componentTree) + } + + return listOf() + } + + private val IsIncrementalMountEnabledAttributeId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "isIncrementalMountEnabled") + + override fun onGetAttributes( + node: LithoView, + attributeSections: MutableMap + ) { + attributeSections[SectionId] = + InspectableObject( + mapOf( + IsIncrementalMountEnabledAttributeId to + InspectableValue.Boolean(node.isIncrementalMountEnabled))) + } +} diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/MatrixDrawableDescriptor.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/MatrixDrawableDescriptor.kt new file mode 100644 index 000000000..d8f9117ab --- /dev/null +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/MatrixDrawableDescriptor.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.litho.descriptors + +import com.facebook.flipper.plugins.uidebugger.descriptors.ChainedDescriptor +import com.facebook.litho.MatrixDrawable + +object MatrixDrawableDescriptor : ChainedDescriptor>() { + + override fun onGetChildren(node: MatrixDrawable<*>): List? { + val mountedDrawable = node.mountedDrawable + return if (mountedDrawable != null) { + listOf(mountedDrawable) + } else { + listOf() + } + } + + override fun onGetName(node: MatrixDrawable<*>): String = node.javaClass.simpleName +} diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/TextDrawableDescriptor.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/TextDrawableDescriptor.kt new file mode 100644 index 000000000..0ec132b6e --- /dev/null +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/TextDrawableDescriptor.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.litho.descriptors + +import com.facebook.flipper.plugins.uidebugger.descriptors.ChainedDescriptor +import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister +import com.facebook.flipper.plugins.uidebugger.model.Inspectable +import com.facebook.flipper.plugins.uidebugger.model.InspectableObject +import com.facebook.flipper.plugins.uidebugger.model.InspectableValue +import com.facebook.flipper.plugins.uidebugger.model.MetadataId +import com.facebook.litho.widget.TextDrawable + +object TextDrawableDescriptor : ChainedDescriptor() { + + private const val NAMESPACE = "TextDrawable" + private val SectionId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, NAMESPACE) + + override fun onGetName(node: TextDrawable): String = node.javaClass.simpleName + + private val TextAttributeId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "text") + + override fun onGetAttributes( + node: TextDrawable, + attributeSections: MutableMap + ) { + val props = + mapOf(TextAttributeId to InspectableValue.Text(node.text.toString())) + + attributeSections[SectionId] = InspectableObject(props) + } +} diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/props/ComponentDataExtractor.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/props/ComponentDataExtractor.kt new file mode 100644 index 000000000..a07286d0e --- /dev/null +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/props/ComponentDataExtractor.kt @@ -0,0 +1,187 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.litho.descriptors.props + +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.util.Log +import com.facebook.flipper.plugins.uidebugger.LogTag +import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister +import com.facebook.flipper.plugins.uidebugger.model.* +import com.facebook.litho.Component +import com.facebook.litho.SpecGeneratedComponent +import com.facebook.litho.StateContainer +import com.facebook.litho.annotations.Prop +import com.facebook.litho.annotations.ResType +import com.facebook.litho.annotations.State +import com.facebook.litho.editor.EditorRegistry +import com.facebook.litho.editor.model.EditorArray +import com.facebook.litho.editor.model.EditorBool +import com.facebook.litho.editor.model.EditorColor +import com.facebook.litho.editor.model.EditorNumber +import com.facebook.litho.editor.model.EditorPick +import com.facebook.litho.editor.model.EditorShape +import com.facebook.litho.editor.model.EditorString +import com.facebook.litho.editor.model.EditorValue +import com.facebook.litho.editor.model.EditorValue.EditorVisitor + +object ComponentDataExtractor { + + fun getProps(component: Component): Map { + val props = mutableMapOf() + + val isSpecComponent = component is SpecGeneratedComponent + + for (declaredField in component.javaClass.declaredFields) { + declaredField.isAccessible = true + + val name = declaredField.name + val declaredFieldAnnotation = declaredField.getAnnotation(Prop::class.java) + + // Only expose `@Prop` annotated fields for Spec components + if (isSpecComponent && declaredFieldAnnotation == null) { + continue + } + + val prop = + try { + declaredField[component] + } catch (e: IllegalAccessException) { + continue + } + + if (declaredFieldAnnotation != null) { + val resType = declaredFieldAnnotation.resType + if (resType == ResType.COLOR) { + if (prop != null) { + val identifier = getMetadataId(component.simpleName, name) + props[identifier] = InspectableValue.Color(Color.fromColor(prop as Int)) + } + continue + } else if (resType == ResType.DRAWABLE) { + val identifier = getMetadataId(component.simpleName, name) + props[identifier] = fromDrawable(prop as Drawable?) + continue + } + } + + val editorValue = + try { + EditorRegistry.read(declaredField.type, declaredField, component) + } catch (e: Exception) { + Log.d( + LogTag, + "Unable to retrieve prop ${declaredField.name} on type ${component.simpleName}") + EditorString("error fetching prop") + } + + if (editorValue != null) { + addProp(props, component.simpleName, name, editorValue) + } + } + + return props + } + + fun getState(stateContainer: StateContainer, componentName: String): InspectableObject { + + val stateFields = mutableMapOf() + for (field in stateContainer.javaClass.declaredFields) { + field.isAccessible = true + val stateAnnotation = field.getAnnotation(State::class.java) + val isKStateField = field.name == "states" + if (stateAnnotation != null || isKStateField) { + val id = getMetadataId(componentName, field.name) + val editorValue: EditorValue? = EditorRegistry.read(field.type, field, stateContainer) + if (editorValue != null) { + stateFields[id] = toInspectable(field.name, editorValue) + } + } + } + return InspectableObject(stateFields) + } + + private fun getMetadataId( + namespace: String, + key: String, + mutable: Boolean = false, + possibleValues: Set? = emptySet() + ): MetadataId { + val metadata = MetadataRegister.get(namespace, key) + val identifier = + metadata?.id + ?: MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, namespace, key, mutable, possibleValues) + return identifier + } + + private fun addProp( + props: MutableMap, + namespace: String, + name: String, + value: EditorValue + ) { + var possibleValues: MutableSet? = null + if (value is EditorPick) { + possibleValues = mutableSetOf() + value.values.forEach { possibleValues.add(InspectableValue.Text(it)) } + } + + val identifier = getMetadataId(namespace, name, false, possibleValues) + props[identifier] = toInspectable(name, value) + } + + private fun toInspectable(name: String, editorValue: EditorValue): Inspectable { + return editorValue.`when`( + object : EditorVisitor { + override fun isShape(shape: EditorShape): Inspectable { + + val fields = mutableMapOf() + shape.value.entries.forEach { entry -> + val value = toInspectable(entry.key, entry.value) + + val shapeEditorValue = entry.value + var possibleValues: MutableSet? = null + if (shapeEditorValue is EditorPick) { + possibleValues = mutableSetOf() + shapeEditorValue.values.forEach { possibleValues.add(InspectableValue.Text(it)) } + } + + val identifier = getMetadataId(name, entry.key, false, possibleValues) + fields[identifier] = value + } + + return InspectableObject(fields) + } + + override fun isArray(array: EditorArray?): Inspectable { + val values = array?.value?.map { value -> toInspectable(name, value) } + return InspectableArray(values ?: listOf()) + } + + override fun isPick(pick: EditorPick): Inspectable = InspectableValue.Enum(pick.selected) + + override fun isNumber(number: EditorNumber): Inspectable = + InspectableValue.Number(number.value) + + override fun isColor(number: EditorColor): Inspectable = + InspectableValue.Color(number.value.toInt().let { Color.fromColor(it) }) + + override fun isString(string: EditorString): Inspectable = + InspectableValue.Text(string.value ?: "") + + override fun isBool(bool: EditorBool): Inspectable = InspectableValue.Boolean(bool.value) + }) + } + + private fun fromDrawable(d: Drawable?): Inspectable = + when (d) { + is ColorDrawable -> InspectableValue.Color(Color.fromColor(d.color)) + else -> InspectableValue.Unknown(d.toString()) + } +} diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/props/LayoutPropExtractor.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/props/LayoutPropExtractor.kt new file mode 100644 index 000000000..a2f2ef651 --- /dev/null +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/props/LayoutPropExtractor.kt @@ -0,0 +1,432 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.litho.descriptors.props + +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import com.facebook.flipper.plugins.uidebugger.common.enumToInspectableSet +import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister +import com.facebook.flipper.plugins.uidebugger.model.* +import com.facebook.litho.DebugComponent +import com.facebook.yoga.* + +object LayoutPropExtractor { + private const val NAMESPACE = "LayoutPropExtractor" + + private var BackgroundId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "background") + private var ForegroundId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "foreground") + + private val DirectionId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, + NAMESPACE, + "direction", + false, + enumToInspectableSet()) + private val FlexDirectionId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, + NAMESPACE, + "flexDirection", + false, + enumToInspectableSet()) + private val JustifyContentId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, + NAMESPACE, + "justifyContent", + false, + enumToInspectableSet()) + private val AlignItemsId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, + NAMESPACE, + "alignItems", + false, + enumToInspectableSet()) + private val AlignSelfId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, + NAMESPACE, + "alignSelf", + false, + enumToInspectableSet()) + private val AlignContentId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, + NAMESPACE, + "alignContent", + false, + enumToInspectableSet()) + private val PositionTypeId = + MetadataRegister.register( + MetadataRegister.TYPE_ATTRIBUTE, + NAMESPACE, + "positionType", + false, + enumToInspectableSet()) + + private val FlexGrowId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "flexGrow") + private val FlexShrinkId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "flexShrink") + private val FlexBasisId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "flexBasis") + private val WidthId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "width") + private val HeightId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "height") + private val MinWidthId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "minWidth") + private val MinHeightId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "minHeight") + private val MaxWidthId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "maxWidth") + private val MaxHeightId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "maxHeight") + private val AspectRatioId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "aspectRatio") + + private val MarginId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "margin") + private val PaddingId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "padding") + private val BorderId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "border") + private val PositionId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "position") + + private val LeftId = MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "left") + private val TopId = MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "top") + private val RightId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "right") + private val BottomId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "bottom") + private val StartId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "start") + private val EndId = MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "end") + private val HorizontalId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "horizontal") + private val VerticalId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "vertical") + private val AllId = MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "all") + + private val HasViewOutputId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "hasViewOutput") + private val AlphaId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "alpha") + private val ScaleId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "scale") + private val RotationId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "rotation") + + private val EmptyId = MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "") + private val NoneId = MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "none") + private val SizeId = MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "size") + private val ViewOutputId = + MetadataRegister.register(MetadataRegister.TYPE_ATTRIBUTE, NAMESPACE, "viewOutput") + + fun getInspectableBox( + left: YogaValue?, + top: YogaValue?, + right: YogaValue?, + bottom: YogaValue?, + horizontal: YogaValue?, + vertical: YogaValue?, + all: YogaValue?, + start: YogaValue?, + end: YogaValue? + ): InspectableObject { + val props = mutableMapOf() + + var actualLeft = 0 + var actualTop = 0 + var actualRight = 0 + var actualBottom = 0 + + all?.let { yogaValue -> + if (yogaValue.unit != YogaUnit.UNDEFINED) { + if (yogaValue.unit == YogaUnit.POINT || yogaValue.unit == YogaUnit.PERCENT) { + val intValue = yogaValue.value.toInt() + actualLeft = intValue + actualTop = intValue + actualRight = intValue + actualBottom = intValue + } + + props[AllId] = InspectableValue.Text(yogaValue.toString()) + } + } + + horizontal?.let { yogaValue -> + if (yogaValue.unit != YogaUnit.UNDEFINED) { + if (yogaValue.unit == YogaUnit.POINT || yogaValue.unit == YogaUnit.PERCENT) { + val intValue = yogaValue.value.toInt() + actualLeft = intValue + actualRight = intValue + } + + props[HorizontalId] = InspectableValue.Text(yogaValue.toString()) + } + } + + vertical?.let { yogaValue -> + if (yogaValue.unit != YogaUnit.UNDEFINED) { + if (yogaValue.unit == YogaUnit.POINT || yogaValue.unit == YogaUnit.PERCENT) { + val intValue = yogaValue.value.toInt() + actualTop = intValue + actualBottom = intValue + } + + props[VerticalId] = InspectableValue.Text(yogaValue.toString()) + } + } + + left?.let { yogaValue -> + if (yogaValue.unit != YogaUnit.UNDEFINED) { + if (yogaValue.unit == YogaUnit.POINT || yogaValue.unit == YogaUnit.PERCENT) { + val intValue = yogaValue.value.toInt() + actualLeft = intValue + } + + props[LeftId] = InspectableValue.Text(yogaValue.toString()) + } + } + + right?.let { yogaValue -> + if (yogaValue.unit != YogaUnit.UNDEFINED) { + if (yogaValue.unit == YogaUnit.POINT || yogaValue.unit == YogaUnit.PERCENT) { + val intValue = yogaValue.value.toInt() + actualRight = intValue + } + + props[RightId] = InspectableValue.Text(yogaValue.toString()) + } + } + + top?.let { yogaValue -> + if (yogaValue.unit != YogaUnit.UNDEFINED) { + if (yogaValue.unit == YogaUnit.POINT || yogaValue.unit == YogaUnit.PERCENT) { + val intValue = yogaValue.value.toInt() + actualTop = intValue + } + + props[TopId] = InspectableValue.Text(yogaValue.toString()) + } + } + + bottom?.let { yogaValue -> + if (yogaValue.unit != YogaUnit.UNDEFINED) { + if (yogaValue.unit == YogaUnit.POINT || yogaValue.unit == YogaUnit.PERCENT) { + val intValue = yogaValue.value.toInt() + actualBottom = intValue + } + + props[BottomId] = InspectableValue.Text(yogaValue.toString()) + } + } + + props[EmptyId] = + InspectableValue.SpaceBox(SpaceBox(actualTop, actualRight, actualBottom, actualLeft)) + + return InspectableObject(props) + } + + fun getInspectableBoxRaw( + left: Float?, + top: Float?, + right: Float?, + bottom: Float?, + horizontal: Float?, + vertical: Float?, + all: Float?, + start: Float?, + end: Float? + ): InspectableObject { + val props = mutableMapOf() + + var actualLeft = 0 + var actualTop = 0 + var actualRight = 0 + var actualBottom = 0 + + all?.let { value -> + if (!value.isNaN()) { + val intValue = value.toInt() + actualLeft = intValue + actualTop = intValue + actualRight = intValue + actualBottom = intValue + props[AllId] = InspectableValue.Number(value) + } + } + + horizontal?.let { value -> + if (!value.isNaN()) { + val intValue = value.toInt() + actualLeft = intValue + actualRight = intValue + props[HorizontalId] = InspectableValue.Number(value) + } + } + + vertical?.let { value -> + if (!value.isNaN()) { + val intValue = value.toInt() + actualTop = intValue + actualBottom = intValue + props[VerticalId] = InspectableValue.Number(value) + } + } + + left?.let { value -> + if (!value.isNaN()) { + val intValue = value.toInt() + actualLeft = intValue + props[LeftId] = InspectableValue.Number(value) + } + } + + right?.let { value -> + if (!value.isNaN()) { + val intValue = value.toInt() + actualRight = intValue + props[RightId] = InspectableValue.Number(value) + } + } + + top?.let { value -> + if (!value.isNaN()) { + val intValue = value.toInt() + actualTop = intValue + props[TopId] = InspectableValue.Number(value) + } + } + + bottom?.let { value -> + if (!value.isNaN()) { + val intValue = value.toInt() + actualBottom = intValue + props[BottomId] = InspectableValue.Number(value) + } + } + + props[EmptyId] = + InspectableValue.SpaceBox(SpaceBox(actualTop, actualRight, actualBottom, actualLeft)) + + return InspectableObject(props) + } + + fun getProps(component: DebugComponent): Map { + val props = mutableMapOf() + + val layout = component.layoutNode ?: return props + + props[AlignItemsId] = InspectableValue.Enum(layout.alignItems.name) + props[AlignSelfId] = InspectableValue.Enum(layout.alignSelf.name) + props[AlignContentId] = InspectableValue.Enum(layout.alignContent.name) + + props[AspectRatioId] = InspectableValue.Text(layout.aspectRatio.toString()) + + layout.background?.let { drawable -> props[BackgroundId] = fromDrawable(drawable) } + + props[DirectionId] = InspectableValue.Enum(layout.layoutDirection.name) + + props[FlexBasisId] = InspectableValue.Text(layout.flexBasis.toString()) + props[FlexDirectionId] = InspectableValue.Enum(layout.flexDirection.name) + props[FlexGrowId] = InspectableValue.Text(layout.flexGrow.toString()) + props[FlexShrinkId] = InspectableValue.Text(layout.flexShrink.toString()) + + layout.foreground?.let { drawable -> props[ForegroundId] = fromDrawable(drawable) } + + props[JustifyContentId] = InspectableValue.Enum(layout.justifyContent.name) + + props[PositionTypeId] = InspectableValue.Enum(layout.positionType.name) + + val size: MutableMap = mutableMapOf() + size[WidthId] = InspectableValue.Text(layout.width.toString()) + if (layout.minWidth.unit != YogaUnit.UNDEFINED) + size[MinWidthId] = InspectableValue.Text(layout.minWidth.toString()) + if (layout.maxWidth.unit != YogaUnit.UNDEFINED) + size[MaxWidthId] = InspectableValue.Text(layout.maxWidth.toString()) + size[HeightId] = InspectableValue.Text(layout.height.toString()) + if (layout.minHeight.unit != YogaUnit.UNDEFINED) + size[MinHeightId] = InspectableValue.Text(layout.minHeight.toString()) + if (layout.maxHeight.unit != YogaUnit.UNDEFINED) + size[MaxHeightId] = InspectableValue.Text(layout.maxHeight.toString()) + + props[SizeId] = InspectableObject(size) + + props[MarginId] = + getInspectableBox( + layout.getMargin(YogaEdge.LEFT), + layout.getMargin(YogaEdge.TOP), + layout.getMargin(YogaEdge.RIGHT), + layout.getMargin(YogaEdge.BOTTOM), + layout.getMargin(YogaEdge.HORIZONTAL), + layout.getMargin(YogaEdge.VERTICAL), + layout.getMargin(YogaEdge.ALL), + layout.getMargin(YogaEdge.START), + layout.getMargin(YogaEdge.END)) + + props[PaddingId] = + getInspectableBox( + layout.getPadding(YogaEdge.LEFT), + layout.getPadding(YogaEdge.TOP), + layout.getPadding(YogaEdge.RIGHT), + layout.getPadding(YogaEdge.BOTTOM), + layout.getPadding(YogaEdge.HORIZONTAL), + layout.getPadding(YogaEdge.VERTICAL), + layout.getPadding(YogaEdge.ALL), + layout.getPadding(YogaEdge.START), + layout.getPadding(YogaEdge.END)) + + props[BorderId] = + getInspectableBoxRaw( + layout.getBorderWidth(YogaEdge.LEFT), + layout.getBorderWidth(YogaEdge.TOP), + layout.getBorderWidth(YogaEdge.RIGHT), + layout.getBorderWidth(YogaEdge.BOTTOM), + layout.getBorderWidth(YogaEdge.HORIZONTAL), + layout.getBorderWidth(YogaEdge.VERTICAL), + layout.getBorderWidth(YogaEdge.ALL), + layout.getBorderWidth(YogaEdge.START), + layout.getBorderWidth(YogaEdge.END)) + + props[PositionId] = + getInspectableBox( + layout.getPosition(YogaEdge.LEFT), + layout.getPosition(YogaEdge.TOP), + layout.getPosition(YogaEdge.RIGHT), + layout.getPosition(YogaEdge.BOTTOM), + layout.getPosition(YogaEdge.HORIZONTAL), + layout.getPosition(YogaEdge.VERTICAL), + layout.getPosition(YogaEdge.ALL), + layout.getPosition(YogaEdge.START), + layout.getPosition(YogaEdge.END)) + + val viewOutput: MutableMap = mutableMapOf() + viewOutput[HasViewOutputId] = InspectableValue.Boolean(layout.hasViewOutput()) + if (layout.hasViewOutput()) { + viewOutput[AlphaId] = InspectableValue.Number(layout.alpha) + viewOutput[RotationId] = InspectableValue.Number(layout.rotation) + viewOutput[ScaleId] = InspectableValue.Number(layout.scale) + } + props[ViewOutputId] = InspectableObject(viewOutput) + + return props + } + + private fun fromDrawable(d: Drawable?): Inspectable = + when (d) { + is ColorDrawable -> InspectableValue.Color(Color.fromColor(d.color)) + else -> InspectableValue.Unknown(d.toString()) + } +} diff --git a/android/plugins/litho/src/test/java/com/facebook/flipper/plugins/uidebugger/litho/KStateContainerExtractionTest.kt b/android/plugins/litho/src/test/java/com/facebook/flipper/plugins/uidebugger/litho/KStateContainerExtractionTest.kt new file mode 100644 index 000000000..b87333700 --- /dev/null +++ b/android/plugins/litho/src/test/java/com/facebook/flipper/plugins/uidebugger/litho/KStateContainerExtractionTest.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import com.facebook.flipper.plugins.uidebugger.litho.descriptors.props.ComponentDataExtractor +import com.facebook.flipper.plugins.uidebugger.model.InspectableArray +import com.facebook.flipper.plugins.uidebugger.model.InspectableObject +import com.facebook.flipper.plugins.uidebugger.model.InspectableValue +import com.facebook.litho.KStateContainer +import junit.framework.Assert.assertEquals +import org.junit.Test + +class KStateContainerExtractionTest { + @Test + @Throws(Exception::class) + fun testCanExtractKState() { + + // this test ensures that our reflection based extraction doesn't break if the KState class + // structure changes + val stateContainer = KStateContainer.withNewState(null, "foo") + + val result = ComponentDataExtractor.getState(stateContainer, "Comp1") + + assertEquals( + result, + InspectableObject(mapOf(1 to InspectableArray(listOf(InspectableValue.Text("foo")))))) + } +} diff --git a/android/plugins/network/build.gradle b/android/plugins/network/build.gradle index c4a3cd27a..f2e02bdd7 100644 --- a/android/plugins/network/build.gradle +++ b/android/plugins/network/build.gradle @@ -8,6 +8,7 @@ apply plugin: 'com.android.library' android { + namespace 'com.facebook.flipper.plugins.network' compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion diff --git a/android/plugins/retrofit2-protobuf/build.gradle b/android/plugins/retrofit2-protobuf/build.gradle index e0efdc12c..4a4c383e2 100644 --- a/android/plugins/retrofit2-protobuf/build.gradle +++ b/android/plugins/retrofit2-protobuf/build.gradle @@ -7,10 +7,10 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android { + namespace 'com.facebook.flipper.plugins.retrofit2protobuf' compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion @@ -19,8 +19,13 @@ android { targetSdkVersion rootProject.targetSdkVersion } + compileOptions { + targetCompatibility rootProject.javaTargetVersion + sourceCompatibility rootProject.javaTargetVersion + } + dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$KOTLIN_VERSION" implementation project(':android') implementation project(':network-plugin') implementation deps.protobuf diff --git a/android/sample/AndroidManifest.xml b/android/sample/AndroidManifest.xml index c7e79a8dd..54f0a453d 100644 --- a/android/sample/AndroidManifest.xml +++ b/android/sample/AndroidManifest.xml @@ -11,6 +11,8 @@ xmlns:tools="http://schemas.android.com/tools" package="com.facebook.flipper.sample"> + - @@ -87,6 +89,15 @@ + + + + + + + + { + ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 300); + valueAnimator.addUpdateListener( + animator -> txtValueAnimator.setTranslationX((Float) animator.getAnimatedValue())); + valueAnimator.setInterpolator(new LinearInterpolator()); + valueAnimator.setDuration(10000); + valueAnimator.start(); + }); - animBlink = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.blink); - // blink btnBlink.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - txtBlink.setVisibility(View.VISIBLE); - txtBlink.startAnimation(animBlink); - } + v -> { + txtBlink.setVisibility(View.VISIBLE); + txtBlink.startAnimation(animBlink); }); animRotate = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.rotate); - // Rotate - btnRotate.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - txtRotate.startAnimation(animRotate); - } - }); + btnRotate.setOnClickListener(v -> txtRotate.startAnimation(animRotate)); animMove = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.move); - // Move - btnMove.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - txtMove.startAnimation(animMove); - } - }); + + btnMove.setOnClickListener(v -> txtMove.startAnimation(animMove)); animBounce = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.bounce); - // Slide Down - btnBounce.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - txtBounce.startAnimation(animBounce); - } - }); - animSequential = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.sequential); - // Sequential - btnSequential.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - txtSeq.startAnimation(animSequential); - } - }); + btnBounce.setOnClickListener(v -> txtBounce.startAnimation(animBounce)); + animSequential = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.sequential); + + btnSequential.setOnClickListener(v -> txtSeq.startAnimation(animSequential)); } } diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/ButtonsActivity.java b/android/sample/src/main/java/com/facebook/flipper/sample/ButtonsActivity.java new file mode 100644 index 000000000..74a6fcb27 --- /dev/null +++ b/android/sample/src/main/java/com/facebook/flipper/sample/ButtonsActivity.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.sample; + +import android.os.Bundle; +import android.widget.Button; +import android.widget.TextView; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; + +public class ButtonsActivity extends FragmentActivity { + + int count = 0; + TextView text; + Button button; + Button dialogOld; + Button dialogFragment; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_buttons); + + text = findViewById(R.id.count); + + button = findViewById(com.facebook.flipper.sample.R.id.btn_inc); + dialogOld = findViewById(R.id.dialog_old_api); + dialogFragment = findViewById(R.id.dialog_fragment); + button.setOnClickListener(view -> ButtonsActivity.this.text.setText(String.valueOf(++count))); + + dialogFragment.setOnClickListener( + btn -> { + TestDialogFragment startGameDialogFragment = new TestDialogFragment(); + startGameDialogFragment.show(getSupportFragmentManager(), "dialog"); + }); + + dialogOld.setOnClickListener( + btn -> { + new AlertDialog.Builder(this) + .setTitle("Old Dialog") + .setMessage("This is an old dialog") + .setNegativeButton(android.R.string.no, null) + .setIcon(android.R.drawable.ic_dialog_alert) + .show(); + }); + } +} diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/FlipperSampleApplication.java b/android/sample/src/main/java/com/facebook/flipper/sample/FlipperSampleApplication.java index 63ded5e97..0a1076f07 100644 --- a/android/sample/src/main/java/com/facebook/flipper/sample/FlipperSampleApplication.java +++ b/android/sample/src/main/java/com/facebook/flipper/sample/FlipperSampleApplication.java @@ -14,6 +14,7 @@ import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.flipper.android.AndroidFlipperClient; import com.facebook.flipper.core.FlipperClient; import com.facebook.flipper.sample.network.NetworkClient; +import com.facebook.fresco.vito.init.FrescoVito; import com.facebook.soloader.SoLoader; public class FlipperSampleApplication extends Application { @@ -22,9 +23,12 @@ public class FlipperSampleApplication extends Application { super.onCreate(); SoLoader.init(this, false); Fresco.initialize(this); + FrescoVito.initialize(); final FlipperClient client = AndroidFlipperClient.getInstance(this); - final FlipperInitializer.IntializationResult initializationResult = + assert client != null; + + final FlipperInitializer.InitializationResult initializationResult = FlipperInitializer.initFlipperPlugins(this, client); NetworkClient.getInstance().setOkHttpClient(initializationResult.getOkHttpClient()); diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/FragmentTestFragment.java b/android/sample/src/main/java/com/facebook/flipper/sample/FragmentTestFragment.java index 4bd31be23..cfccc9d52 100644 --- a/android/sample/src/main/java/com/facebook/flipper/sample/FragmentTestFragment.java +++ b/android/sample/src/main/java/com/facebook/flipper/sample/FragmentTestFragment.java @@ -15,39 +15,39 @@ import android.widget.TextView; import androidx.fragment.app.Fragment; public class FragmentTestFragment extends Fragment { - View mView; - int mTicker; + View view; + int ticker; public FragmentTestFragment() { - mTicker = 0; + ticker = 0; } private void updateTicker() { try { - ViewGroup viewGroup = (ViewGroup) mView; + ViewGroup viewGroup = (ViewGroup) view; TextView textView = (TextView) viewGroup.getChildAt(1); - String text = String.valueOf(mTicker++); + String text = String.valueOf(ticker++); textView.setText(text); } finally { // 100% guarantee that this always happens, even if // your update method throws an exception - mView.postDelayed( + view.postDelayed( new Runnable() { @Override public void run() { updateTicker(); } }, - 10000); + 1000); } } @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - mView = inflater.inflate(R.layout.fragment_test, container, false); - mView.postDelayed( + view = inflater.inflate(R.layout.fragment_test, container, false); + view.postDelayed( new Runnable() { @Override public void run() { @@ -56,6 +56,6 @@ public class FragmentTestFragment extends Fragment { }, 1000); - return mView; + return view; } } diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/IncrementActivity.java b/android/sample/src/main/java/com/facebook/flipper/sample/IncrementActivity.java deleted file mode 100644 index 7e35ac7b3..000000000 --- a/android/sample/src/main/java/com/facebook/flipper/sample/IncrementActivity.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.flipper.sample; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; - -public class IncrementActivity extends Activity { - - int count = 0; - TextView text; - Button button; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_increment); - - text = (TextView) findViewById(R.id.count); - - button = (Button) findViewById(com.facebook.flipper.sample.R.id.btn_inc); - button.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - IncrementActivity.this.text.setText(String.valueOf(++count)); - } - }); - } -} diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/JetpackComposeActivity.kt b/android/sample/src/main/java/com/facebook/flipper/sample/JetpackComposeActivity.kt new file mode 100644 index 000000000..8498774b0 --- /dev/null +++ b/android/sample/src/main/java/com/facebook/flipper/sample/JetpackComposeActivity.kt @@ -0,0 +1,30 @@ +package com.facebook.flipper.sample + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +@Composable +fun MessageCard(name: String) { + Row(modifier = Modifier.padding(all = 8.dp)) { Text(text = "Hello $name!") } +} + +@Preview +@Composable +fun PreviewMessageCard() { + MessageCard("Android") +} + +class JetpackComposeActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { MessageCard("Flipper") } + } +} diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/ListActivity.java b/android/sample/src/main/java/com/facebook/flipper/sample/ListActivity.java index 0f65041c3..35bd952c6 100644 --- a/android/sample/src/main/java/com/facebook/flipper/sample/ListActivity.java +++ b/android/sample/src/main/java/com/facebook/flipper/sample/ListActivity.java @@ -24,7 +24,7 @@ public class ListActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list); - listView = (ListView) findViewById(R.id.list); + listView = findViewById(R.id.list); list = new ArrayList<>(); list.add("Apple"); @@ -33,7 +33,7 @@ public class ListActivity extends Activity { list.add("Orange"); list.add("Lychee"); list.add("Guava"); - list.add("Peech"); + list.add("Peach"); list.add("Melon"); list.add("Watermelon"); list.add("Papaya"); diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/MainActivity.java b/android/sample/src/main/java/com/facebook/flipper/sample/MainActivity.java index bf558c524..c5c4c17f1 100644 --- a/android/sample/src/main/java/com/facebook/flipper/sample/MainActivity.java +++ b/android/sample/src/main/java/com/facebook/flipper/sample/MainActivity.java @@ -29,7 +29,9 @@ public class MainActivity extends AppCompatActivity { final FlipperClient client = AndroidFlipperClient.getInstanceIfInitialized(); if (client != null) { final ExampleFlipperPlugin samplePlugin = client.getPluginByClass(ExampleFlipperPlugin.class); - samplePlugin.setActivity(this); + if (samplePlugin != null) { + samplePlugin.setActivity(this); + } } } } diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/RootComponentSpec.java b/android/sample/src/main/java/com/facebook/flipper/sample/RootComponentSpec.java index af0fb1ec2..09c7eb9c4 100644 --- a/android/sample/src/main/java/com/facebook/flipper/sample/RootComponentSpec.java +++ b/android/sample/src/main/java/com/facebook/flipper/sample/RootComponentSpec.java @@ -8,10 +8,10 @@ package com.facebook.flipper.sample; import android.content.Intent; -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.drawee.interfaces.DraweeController; +import android.net.Uri; import com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity; import com.facebook.flipper.sample.network.NetworkClient; +import com.facebook.fresco.vito.litho.FrescoVitoImage2; import com.facebook.litho.ClickEvent; import com.facebook.litho.Column; import com.facebook.litho.Component; @@ -22,7 +22,6 @@ import com.facebook.litho.annotations.OnCreateLayout; import com.facebook.litho.annotations.OnEvent; import com.facebook.litho.annotations.OnUpdateState; import com.facebook.litho.annotations.State; -import com.facebook.litho.fresco.FrescoImage; import com.facebook.litho.widget.Text; import com.facebook.litho.widget.VerticalScroll; import com.facebook.yoga.YogaEdge; @@ -32,9 +31,6 @@ public class RootComponentSpec { @OnCreateLayout static Component onCreateLayout(final ComponentContext c, @State boolean displayImage) { - final DraweeController controller = - Fresco.newDraweeControllerBuilder().setUri("https://fbflipper.com/img/icon.png").build(); - Column col = Column.create(c) .child( @@ -109,24 +105,33 @@ public class RootComponentSpec { .clickHandler(RootComponent.openAnimationsActivity(c))) .child( Text.create(c) - .text("Navigate to increment activity") + .text("Navigate to buttons activity") .key("11") .marginDip(YogaEdge.ALL, 10) .textSizeSp(20) .clickHandler(RootComponent.openIncrementActivity(c))) .child( Text.create(c) - .text("Crash this app") + .text("Navigate to Jetpack Compose activity") .key("12") .marginDip(YogaEdge.ALL, 10) .textSizeSp(20) + .clickHandler(RootComponent.openJetpackComposeActivity(c))) + .child( + Text.create(c) + .text("Crash this app") + .key("13") + .marginDip(YogaEdge.ALL, 10) + .textSizeSp(20) .clickHandler(RootComponent.triggerCrash(c))) .child( - FrescoImage.create(c) - .controller(controller) - .marginDip(YogaEdge.ALL, 10) - .widthDip(150) - .heightDip(150)) + displayImage + ? FrescoVitoImage2.create(c) + .uri(Uri.parse("https://fbflipper.com/img/icon.png")) + .marginDip(YogaEdge.ALL, 10) + .widthDip(150) + .heightDip(150) + : null) .build(); return VerticalScroll.create(c).childComponent(col).build(); @@ -200,7 +205,13 @@ public class RootComponentSpec { @OnEvent(ClickEvent.class) static void openIncrementActivity(final ComponentContext c) { - final Intent intent = new Intent(c.getAndroidContext(), IncrementActivity.class); + final Intent intent = new Intent(c.getAndroidContext(), ButtonsActivity.class); + c.getAndroidContext().startActivity(intent); + } + + @OnEvent(ClickEvent.class) + static void openJetpackComposeActivity(final ComponentContext c) { + final Intent intent = new Intent(c.getAndroidContext(), JetpackComposeActivity.class); c.getAndroidContext().startActivity(intent); } } diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/TestDialogFragment.java b/android/sample/src/main/java/com/facebook/flipper/sample/TestDialogFragment.java new file mode 100644 index 000000000..cf39d498b --- /dev/null +++ b/android/sample/src/main/java/com/facebook/flipper/sample/TestDialogFragment.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.sample; + +import android.app.Dialog; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; + +public class TestDialogFragment extends DialogFragment { + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Use the Builder class for convenient dialog construction + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage("This is a dialog fragment").setPositiveButton("Yes", (dialog, id) -> {}); + + return builder.create(); + } +} diff --git a/android/sample/src/main/res/layout/activity_animations.xml b/android/sample/src/main/res/layout/activity_animations.xml index e53b67722..e9be343b6 100644 --- a/android/sample/src/main/res/layout/activity_animations.xml +++ b/android/sample/src/main/res/layout/activity_animations.xml @@ -12,6 +12,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + )} + {connected && enableClear && ( + + )} + + } + actionsTop={actionsTop} + extraActions={extraActions} + /> + ); + + switch (sidebarPosition!) { + case 'main': + return ( + + {table} + {sidebar} + + ); + case 'right': + return ( + + {table} + {sidebar} + + ); + case 'bottom': + return ( + + {table} + {sidebar} + + ); + case 'overlay': + return ( + + {table} + {sidebar} + + ); + case 'none': + return table; + } +} + +MasterDetailWithPowerSearch.defaultProps = { + sidebarPosition: 'main', + sidebarSize: 400, + sidebarComponent: DefaultRenderSidebar, +} as Partial>; + +function DefaultRenderSidebar({record}: {record: T}) { + return ( + + + + ); +} diff --git a/desktop/flipper-plugin/src/ui/NUX.tsx b/desktop/flipper-plugin/src/ui/NUX.tsx index 6682e2e41..b01b62f7b 100644 --- a/desktop/flipper-plugin/src/ui/NUX.tsx +++ b/desktop/flipper-plugin/src/ui/NUX.tsx @@ -19,12 +19,15 @@ import styled from '@emotion/styled'; import {keyframes} from '@emotion/css'; import reactElementToJSXString from 'react-element-to-jsx-string'; import {SandyPluginContext} from '../plugin/PluginContext'; -import {createState, useValue} from '../state/atom'; -import {SandyDevicePluginInstance} from '../plugin/DevicePlugin'; +import {createState} from 'flipper-plugin-core'; +import {useValue} from '../state/atom'; +import {_SandyDevicePluginInstance} from 'flipper-plugin-core'; import {Layout} from './Layout'; import {BulbTwoTone} from '@ant-design/icons'; +// This import is OK since it is a type-only import +// eslint-disable-next-line no-restricted-imports import type {TooltipPlacement} from 'antd/lib/tooltip'; -import {SandyPluginInstance} from '../plugin/Plugin'; +import {_SandyPluginInstance} from 'flipper-plugin-core'; import {theme} from './theme'; import {Tracked} from './Tracked'; import {sha256} from '../utils/sha256'; @@ -37,7 +40,7 @@ const storageKey = `FLIPPER_NUX_STATE`; export async function getNuxKey( elem: React.ReactNode, - currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance, + currentPlugin?: _SandyPluginInstance | _SandyDevicePluginInstance, ): Promise { const hash = await sha256(reactElementToJSXString(elem)); return `${currentPlugin?.definition.id ?? 'flipper'}:${hash}`; @@ -59,14 +62,14 @@ export function createNuxManager() { return { async markRead( elem: React.ReactNode, - currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance, + currentPlugin?: _SandyPluginInstance | _SandyDevicePluginInstance, ): Promise { readMap[await getNuxKey(elem, currentPlugin)] = true; save(); }, async isRead( elem: React.ReactNode, - currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance, + currentPlugin?: _SandyPluginInstance | _SandyDevicePluginInstance, ): Promise { return !!readMap[await getNuxKey(elem, currentPlugin)]; }, diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchAbsoluteDateTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchAbsoluteDateTerm.tsx new file mode 100644 index 000000000..3dc427f50 --- /dev/null +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchAbsoluteDateTerm.tsx @@ -0,0 +1,97 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Button, DatePicker, DatePickerProps} from 'antd'; +import dayjs from 'dayjs'; +import React from 'react'; +// Use this exact version of moment to match what antd has +// eslint-disable-next-line no-restricted-imports +import moment from 'antd/node_modules/moment'; + +type PowerSearchAbsoluteTermProps = { + onCancel: () => void; + onChange: (value: Date) => void; + dateOnly?: boolean; + minValue?: Date; + maxValue?: Date; + defaultValue?: Date; +}; + +export const DATE_ONLY_FORMAT = 'YYYY-MM-DD'; +export const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; + +export const PowerSearchAbsoluteDateTerm: React.FC< + PowerSearchAbsoluteTermProps +> = ({onCancel, onChange, dateOnly, minValue, maxValue, defaultValue}) => { + const [editing, setEditing] = React.useState(!defaultValue); + + const disabledDate: DatePickerProps['disabledDate'] = React.useCallback( + (date) => { + if (minValue !== undefined && date < minValue) { + return true; + } + if (maxValue !== undefined && date > maxValue) { + return true; + } + return false; + }, + [minValue, maxValue], + ); + + const format = dateOnly ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'; + + const valueRef = React.useRef(); + if (defaultValue && !valueRef.current) { + valueRef.current = defaultValue; + } + + if (editing) { + return ( + { + if (!newValue) { + onCancel(); + return; + } + + const newDate = newValue.toDate(); + valueRef.current = newDate; + onChange(newDate); + }} + onKeyDown={(event) => { + if (event.key === 'Escape') { + onCancel(); + } + }} + onBlur={() => { + if (!valueRef.current) { + onCancel(); + } + setEditing(false); + }} + disabledDate={disabledDate} + showTime={!dateOnly} + defaultOpen + defaultValue={defaultValue ? moment(defaultValue) : undefined} + /> + ); + } + + return ( + + ); +}; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx new file mode 100644 index 000000000..f2775d0b1 --- /dev/null +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchConfig.tsx @@ -0,0 +1,92 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +// Mostly matches https://www.internalfb.com/code/www/html/intern/js/ui/PowerSearch/PowerSearchExampleConfig.js + +export type NullishFilterValueType = 'NO_VALUE'; + +export type StringFilterValueType = 'STRING'; + +export type StringSetFilterValueType = 'STRING_SET'; + +export type IntegerFilterValueType = 'INTEGER'; + +export type FloatFilterValueType = 'FLOAT'; + +export type EnumFilterValueType = 'ENUM' | 'ENUM_SET'; + +export type AbsoluteDateFilterValueType = 'ABSOLUTE_DATE'; + +export type NullishOperatorConfig = { + valueType: NullishFilterValueType; + key: string; + label: string; +}; + +export type StringOperatorConfig = { + valueType: StringFilterValueType; + key: string; + label: string; + handleUnknownValues?: boolean; +}; + +export type StringSetOperatorConfig = { + valueType: StringSetFilterValueType; + key: string; + label: string; +}; + +export type IntegerOperatorConfig = { + valueType: IntegerFilterValueType; + key: string; + label: string; +}; + +export type FloatOperatorConfig = { + valueType: FloatFilterValueType; + key: string; + label: string; + precision?: number; +}; + +export type EnumOperatorConfig = { + valueType: EnumFilterValueType; + key: string; + label: string; + enumLabels: {[key: string]: string}; +}; + +export type AbsoluteDateOperatorConfig = { + valueType: AbsoluteDateFilterValueType; + key: string; + label: string; + dateOnly?: boolean; + minValue?: Date; + maxValue?: Date; +}; + +export type OperatorConfig = + | NullishOperatorConfig + | StringOperatorConfig + | StringSetOperatorConfig + | IntegerOperatorConfig + | FloatOperatorConfig + | EnumOperatorConfig + | AbsoluteDateOperatorConfig; + +export type FieldConfig = { + key: string; + label: string; + operators: {[key: string]: OperatorConfig}; + useWholeRow?: boolean; +}; + +export type PowerSearchConfig = { + fields: {[key: string]: FieldConfig}; +}; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx new file mode 100644 index 000000000..8645cc38e --- /dev/null +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchContainer.tsx @@ -0,0 +1,33 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import * as React from 'react'; +import {css} from '@emotion/css'; +import {theme} from '../theme'; + +const containerStyle = css` + flex: 1 0 auto; + background-color: ${theme.white}; + display: flex; + flex-direction: row; + border-radius: ${theme.borderRadius}; + border: 1px solid ${theme.borderColor}; + transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); + padding: 0 ${theme.space.tiny}px; + + &:focus-within, + &:hover { + border-color: ${theme.primaryColor}; + box-shadow: 0 0 0 2px rgba(114, 46, 209, 0.2); + } +`; + +export const PowerSearchContainer: React.FC = ({children}) => { + return
{children}
; +}; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx new file mode 100644 index 000000000..3aad64df0 --- /dev/null +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchEnumSetTerm.tsx @@ -0,0 +1,68 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Select} from 'antd'; +import React from 'react'; + +type PowerSearchEnumSetTermProps = { + onCancel: () => void; + onChange: (value: string[]) => void; + enumLabels: {[key: string]: string}; + defaultValue?: string[]; +}; + +export const PowerSearchEnumSetTerm: React.FC = ({ + onCancel, + onChange, + enumLabels, + defaultValue, +}) => { + const options = React.useMemo(() => { + return Object.entries(enumLabels).map(([key, label]) => ({ + label, + value: key, + })); + }, [enumLabels]); + + const selectValueRef = React.useRef(); + if (defaultValue && !selectValueRef.current) { + selectValueRef.current = defaultValue; + } + + return ( + { + if (!selectValueRef.current) { + onCancel(); + } + setEditing(false); + }} + onSelect={(value) => { + selectValueRef.current = value; + onChange(value); + }} + onKeyDown={(event) => { + if (event.key === 'Enter' || event.key === 'Escape') { + event.currentTarget.blur(); + } + }} + defaultValue={defaultValue} + /> + ); + } + + return ( + + ); +}; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchExampleConfig.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchExampleConfig.tsx new file mode 100644 index 000000000..f647850fb --- /dev/null +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchExampleConfig.tsx @@ -0,0 +1,182 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {OperatorConfig, PowerSearchConfig} from './PowerSearchConfig'; + +const MyMacroEnum = { + SURE_WHY_NOT: 'surewhynot', + DOGSCIENCE: 'dogscience', + TEST_IN_PROD: 'testinproduction', +}; + +const operators = { + contain: { + label: 'contains', + key: 'contain', + valueType: 'STRING', + }, + not_contain: { + label: 'does not contain', + key: 'not_contain', + valueType: 'STRING', + }, + contains_any_of: { + label: 'contains any of', + key: 'contains_any_of', + valueType: 'STRING_SET', + }, + greater_than: { + label: '>', + key: 'greater_than', + valueType: 'INTEGER', + }, + greater_than_float: { + label: '>', + key: 'greater_than_float', + valueType: 'FLOAT', + }, + less_than: { + label: '<', + key: 'less_than', + valueType: 'INTEGER', + }, + less_than_float: { + label: '<', + key: 'less_than_float', + valueType: 'FLOAT', + }, + caller_is: { + label: 'is', + key: 'caller_is', + valueType: 'STRING', + }, + macro_is: { + label: 'is', + key: 'macro_is', + valueType: 'ENUM', + enumLabels: MyMacroEnum, + }, + macro_is_not: { + label: 'is not', + key: 'macro_is_not', + valueType: 'ENUM', + enumLabels: MyMacroEnum, + }, + macro_is_any_of: { + label: 'is any of', + key: 'macro_is_any_of', + valueType: 'ENUM_SET', + enumLabels: MyMacroEnum, + }, + predictive_contain: { + label: 'contains', + key: 'predictive_contain', + valueType: 'STRING', + }, + predictive_not_contain: { + label: 'does not contain', + key: 'predictive_not_contain', + valueType: 'STRING', + }, + newer_than_absolute_date: { + key: 'newer_than_absolute_date', + label: 'is after', + valueType: 'ABSOLUTE_DATE', + dateOnly: false, + }, + newer_than_absolute_date_no_time: { + key: 'newer_than_absolute_date_no_time', + label: 'is after the day', + valueType: 'ABSOLUTE_DATE', + dateOnly: true, + }, + unread: { + key: 'unread', + label: '', + valueType: 'NO_VALUE', + }, +} satisfies {[key: string]: OperatorConfig}; + +export const powerSearchExampleConfig: PowerSearchConfig = { + fields: { + title: { + key: 'title', + label: 'Title', + operators: { + contain: operators.contain, + not_contain: operators.not_contain, + }, + }, + description: { + key: 'description', + label: 'Description', + operators: { + contain: operators.contain, + not_contain: operators.not_contain, + contains_any_of: operators.contains_any_of, + }, + }, + placeholder: { + key: 'placeholder', + label: 'Placeholder', + operators: { + predictive_contain: operators.predictive_contain, + predictive_not_contain: operators.predictive_not_contain, + }, + }, + lines: { + key: 'lines', + label: 'Line count', + operators: { + greater_than: operators.greater_than, + less_than: operators.less_than, + }, + }, + cost: { + key: 'cost', + label: 'Cost', + operators: { + greater_than_float: operators.greater_than_float, + less_than_float: operators.less_than_float, + }, + }, + date: { + key: 'date', + label: 'Date', + operators: { + newer_than_absolute_date_no_time: + operators.newer_than_absolute_date_no_time, + newer_than_absolute_date: operators.newer_than_absolute_date, + }, + }, + caller: { + key: 'caller', + label: 'Caller', + operators: { + caller_is: operators.caller_is, + }, + }, + macro: { + key: 'macro', + label: 'Macro', + operators: { + macro_is: operators.macro_is, + macro_is_not: operators.macro_is_not, + macro_is_any_of: operators.macro_is_any_of, + }, + }, + unread_only: { + key: 'unread_only', + label: 'Unread Only', + operators: { + unread: operators.unread, + }, + }, + }, +}; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchFloatTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchFloatTerm.tsx new file mode 100644 index 000000000..69d6d779f --- /dev/null +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchFloatTerm.tsx @@ -0,0 +1,58 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Button, Input} from 'antd'; +import React from 'react'; + +type PowerSearchFloatTermProps = { + onCancel: () => void; + onChange: (value: number) => void; + defaultValue?: number; +}; + +export const PowerSearchFloatTerm: React.FC = ({ + onCancel, + onChange, + defaultValue, +}) => { + const [editing, setEditing] = React.useState(!defaultValue); + + if (editing) { + return ( + { + const newValue = event.target.value; + + setEditing(false); + + if (!newValue) { + onCancel(); + return; + } + + const normalizedValue = parseFloat(newValue); + onChange(normalizedValue); + }} + onKeyDown={(event) => { + if (event.key === 'Enter' || event.key === 'Escape') { + event.currentTarget.blur(); + } + }} + type="number" + step={0.1} + defaultValue={defaultValue} + /> + ); + } + + return ; +}; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchIntegerTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchIntegerTerm.tsx new file mode 100644 index 000000000..3edf7f50a --- /dev/null +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchIntegerTerm.tsx @@ -0,0 +1,67 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Button, Input} from 'antd'; +import React from 'react'; + +type PowerSearchIntegerTermProps = { + onCancel: () => void; + onChange: (value: number) => void; + defaultValue?: number; +}; + +export const PowerSearchIntegerTerm: React.FC = ({ + onCancel, + onChange, + defaultValue, +}) => { + const [editing, setEditing] = React.useState(!defaultValue); + + if (editing) { + return ( + { + const newValue = event.target.value; + + const normalizedValue = parseInt(newValue, 10); + + if (normalizedValue.toString() !== newValue) { + event.target.value = normalizedValue.toString(); + } + }} + onBlur={(event) => { + const newValue = event.target.value; + + setEditing(false); + + if (!newValue) { + onCancel(); + return; + } + + const normalizedValue = parseInt(newValue, 10); + onChange(normalizedValue); + }} + onKeyDown={(event) => { + if (event.key === 'Enter' || event.key === 'Escape') { + event.currentTarget.blur(); + } + }} + type="number" + step={1} + defaultValue={defaultValue} + /> + ); + } + + return ; +}; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchStringSetTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchStringSetTerm.tsx new file mode 100644 index 000000000..73bcfd80e --- /dev/null +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchStringSetTerm.tsx @@ -0,0 +1,56 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Select} from 'antd'; +import React from 'react'; + +type PowerSearchStringSetTermProps = { + onCancel: () => void; + onChange: (value: string[]) => void; + defaultValue?: string[]; +}; + +export const PowerSearchStringSetTerm: React.FC< + PowerSearchStringSetTermProps +> = ({onCancel, onChange, defaultValue}) => { + const selectValueRef = React.useRef(); + if (defaultValue && !selectValueRef.current) { + selectValueRef.current = defaultValue; + } + + return ( + { + const newValue = event.target.value; + + setEditing(false); + + if (!newValue) { + onCancel(); + return; + } + + onChange(newValue); + }} + onKeyDown={(event) => { + if (event.key === 'Enter' || event.key === 'Escape') { + event.currentTarget.blur(); + } + }} + defaultValue={defaultValue} + /> + ); + } + + return ; +}; diff --git a/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx new file mode 100644 index 000000000..bef2230bf --- /dev/null +++ b/desktop/flipper-plugin/src/ui/PowerSearch/PowerSearchTerm.tsx @@ -0,0 +1,187 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {CloseOutlined} from '@ant-design/icons'; +import {Button, Space} from 'antd'; +import * as React from 'react'; +import {PowerSearchAbsoluteDateTerm} from './PowerSearchAbsoluteDateTerm'; +import {OperatorConfig} from './PowerSearchConfig'; +import {PowerSearchEnumSetTerm} from './PowerSearchEnumSetTerm'; +import {PowerSearchEnumTerm} from './PowerSearchEnumTerm'; +import {PowerSearchFloatTerm} from './PowerSearchFloatTerm'; +import {PowerSearchIntegerTerm} from './PowerSearchIntegerTerm'; +import {PowerSearchStringSetTerm} from './PowerSearchStringSetTerm'; +import {PowerSearchStringTerm} from './PowerSearchStringTerm'; + +export type IncompleteSearchExpressionTerm = { + field: {key: string; label: string; useWholeRow?: boolean}; + operator: OperatorConfig; + searchValue?: any; +}; +export type SearchExpressionTerm = Required; + +type PowerSearchTermProps = { + searchTerm: IncompleteSearchExpressionTerm; + onCancel: () => void; + onFinalize: (completeSearchTerm: SearchExpressionTerm) => void; +}; + +export const PowerSearchTerm: React.FC = ({ + searchTerm, + onCancel, + onFinalize, +}) => { + let searchValueComponent: React.ReactNode = null; + switch (searchTerm.operator.valueType) { + case 'STRING': { + searchValueComponent = ( + { + onFinalize({ + ...searchTerm, + searchValue: newValue, + }); + }} + defaultValue={searchTerm.searchValue} + /> + ); + break; + } + case 'STRING_SET': { + searchValueComponent = ( + { + onFinalize({ + ...searchTerm, + searchValue: newValue, + }); + }} + defaultValue={searchTerm.searchValue} + /> + ); + break; + } + case 'INTEGER': { + searchValueComponent = ( + { + onFinalize({ + ...searchTerm, + searchValue: newValue, + }); + }} + defaultValue={searchTerm.searchValue} + /> + ); + break; + } + case 'FLOAT': { + searchValueComponent = ( + { + onFinalize({ + ...searchTerm, + searchValue: newValue, + }); + }} + defaultValue={searchTerm.searchValue} + /> + ); + break; + } + case 'NO_VALUE': { + // Nothing needs to be done. It should never be the case. + searchValueComponent = null; + break; + } + case 'ENUM': { + searchValueComponent = ( + { + onFinalize({ + ...searchTerm, + searchValue: newValue, + }); + }} + enumLabels={searchTerm.operator.enumLabels} + defaultValue={searchTerm.searchValue} + /> + ); + break; + } + case 'ENUM_SET': { + searchValueComponent = ( + { + onFinalize({ + ...searchTerm, + searchValue: newValue, + }); + }} + enumLabels={searchTerm.operator.enumLabels} + defaultValue={searchTerm.searchValue} + /> + ); + break; + } + case 'ABSOLUTE_DATE': { + searchValueComponent = ( + { + onFinalize({ + ...searchTerm, + searchValue: newValue, + }); + }} + minValue={searchTerm.operator.minValue} + maxValue={searchTerm.operator.maxValue} + dateOnly={searchTerm.operator.dateOnly} + defaultValue={searchTerm.searchValue} + /> + ); + break; + } + default: { + // Compilation is going to fail if switch-case is not exhaustive (i.e. we did not cover all possible cases) + const exhaustiveCheck: never = searchTerm.operator; + console.error( + 'PowerSearchTerm -> unknown operator.valueType', + searchTerm, + exhaustiveCheck, + ); + } + } + + return ( + + + {searchTerm.operator.label ? ( + + ) : null} + {searchValueComponent} + + ); + + notification.open({ + message: `${legacyPluginName} plugin is being deprecated.`, + description: `The new replacement plugin, ${newPluginName}, is available for use now. + Find it on the left panel`, + duration: 30, + type: 'warning', + btn, + key, + }); +}; diff --git a/desktop/flipper-plugin/src/ui/Tabs.tsx b/desktop/flipper-plugin/src/ui/Tabs.tsx index d5a5ad685..60730f523 100644 --- a/desktop/flipper-plugin/src/ui/Tabs.tsx +++ b/desktop/flipper-plugin/src/ui/Tabs.tsx @@ -22,8 +22,9 @@ export function Tabs({ grow, children, className, + localStorageKeyOverride, //set this if you need to have a dynamic number of tabs, you do *not* need to namespace with the plugin name ...baseProps -}: {grow?: boolean} & TabsProps) { +}: {grow?: boolean; localStorageKeyOverride?: string} & TabsProps) { const keys: string[] = []; const keyedChildren = Children.map(children, (child: any, idx) => { if (!child || typeof child !== 'object') { @@ -52,7 +53,7 @@ export function Tabs({ }); const [activeTab, setActiveTab] = useLocalStorageState( - 'Tabs:' + keys.join(','), + 'Tabs:' + localStorageKeyOverride ?? keys.join(','), undefined, ); @@ -104,7 +105,9 @@ const growingTabs = css` & .ant-tabs-content { height: 100%; } - & .ant-tabs-tabpane { + & .ant-tabs-tabpane:not(.ant-tabs-tabpane-hidden) { display: flex; + flex-direction: column; + height: 100%; } `; diff --git a/desktop/flipper-plugin/src/ui/__tests__/DataFormatter.node.tsx b/desktop/flipper-plugin/src/ui/__tests__/DataFormatter.node.tsx index 5fc1818d5..d52574492 100644 --- a/desktop/flipper-plugin/src/ui/__tests__/DataFormatter.node.tsx +++ b/desktop/flipper-plugin/src/ui/__tests__/DataFormatter.node.tsx @@ -25,13 +25,13 @@ test('default formatter', () => { expect(DataFormatter.format({hello: 'world'})).toMatchInlineSnapshot(` "{ - \\"hello\\": \\"world\\" + "hello": "world" }" `); expect(DataFormatter.format({hello: ['world']})).toMatchInlineSnapshot(` "{ - \\"hello\\": [ - \\"world\\" + "hello": [ + "world" ] }" `); @@ -39,8 +39,8 @@ test('default formatter', () => { .toMatchInlineSnapshot(` "[ [ - \\"hello\\", - \\"world\\" + "hello", + "world" ] ]" `); @@ -48,8 +48,8 @@ test('default formatter', () => { .toMatchInlineSnapshot(` "[ [ - \\"hello\\", - \\"world\\" + "hello", + "world" ] ]" `); @@ -71,6 +71,13 @@ test('default formatter', () => { `); }); +test.unix('date formatter', () => { + // dates on windows don't support changed timezones + expect( + DataFormatter.format(new Date(1668609938.068577 * 1000)), + ).toMatchInlineSnapshot(`"01:45:38.068"`); +}); + test('linkify formatter', () => { const linkify = (value: any) => DataFormatter.format(value, DataFormatter.linkify); @@ -78,18 +85,18 @@ test('linkify formatter', () => { // verify fallback expect(linkify({hello: 'world'})).toMatchInlineSnapshot(` "{ - \\"hello\\": \\"world\\" + "hello": "world" }" `); expect(linkify('hi there!')).toMatchInlineSnapshot(`"hi there!"`); expect(linkify('https://www.google.com')).toMatchInlineSnapshot(` - https://www.google.com - + `); @@ -100,11 +107,11 @@ test('linkify formatter', () => { expect(linkify('test https://www.google.com test')).toMatchInlineSnapshot(` test - https://www.google.com - + test `); @@ -112,17 +119,17 @@ test('linkify formatter', () => { .toMatchInlineSnapshot(` - https://www.google.com - + test - http://fb.com - + `); @@ -134,14 +141,14 @@ test('jsonify formatter', () => { DataFormatter.format(value, DataFormatter.prettyPrintJson); expect(jsonify({hello: 'world'})).toMatchInlineSnapshot(` - "{ - \\"hello\\": \\"world\\" - }" - `); + "{ + "hello": "world" + }" + `); expect(jsonify([{hello: 'world'}])).toMatchInlineSnapshot(` "[ { - \\"hello\\": \\"world\\" + "hello": "world" } ]" `); @@ -155,11 +162,11 @@ test('jsonify formatter', () => { { "hello": " - http://facebook.com - + " } @@ -174,7 +181,7 @@ test("jsonify doesn't process react elements", () => { expect(jsonify('{ a: 1 }')).toMatchInlineSnapshot(`"{ a: 1 }"`); expect(jsonify({a: 1})).toMatchInlineSnapshot(` "{ - \\"a\\": 1 + "a": 1 }" `); expect(jsonify(hi)).toMatchInlineSnapshot(` @@ -190,7 +197,7 @@ test('truncate formatter', () => { expect(truncate({test: true})).toMatchInlineSnapshot(` "{ - \\"test\\": true + "test": true }" `); expect(truncate('abcde')).toEqual('abcde'); diff --git a/desktop/flipper-plugin/src/ui/__tests__/__snapshots__/MarkerTimeline.node.tsx.snap b/desktop/flipper-plugin/src/ui/__tests__/__snapshots__/MarkerTimeline.node.tsx.snap index 317353cff..82395fc9e 100644 --- a/desktop/flipper-plugin/src/ui/__tests__/__snapshots__/MarkerTimeline.node.tsx.snap +++ b/desktop/flipper-plugin/src/ui/__tests__/__snapshots__/MarkerTimeline.node.tsx.snap @@ -1,14 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`handles single point 1`] = ` -Array [ - Object { +[ + { "color": "var(--flipper-background-default)", "isCut": false, - "markerKeys": Array [ + "markerKeys": [ "1", ], - "markerNames": Array [ + "markerNames": [ "single point", ], "positionY": 0, @@ -17,4 +17,4 @@ Array [ ] `; -exports[`no points 1`] = `Array []`; +exports[`no points 1`] = `[]`; diff --git a/desktop/flipper-plugin/src/ui/__tests__/useLocalStorage.node.tsx b/desktop/flipper-plugin/src/ui/__tests__/useLocalStorage.node.tsx index e144dcf9e..277bcd2b0 100644 --- a/desktop/flipper-plugin/src/ui/__tests__/useLocalStorage.node.tsx +++ b/desktop/flipper-plugin/src/ui/__tests__/useLocalStorage.node.tsx @@ -66,7 +66,7 @@ test('it can store values', async () => { expect((await res.findByTestId('value')).textContent).toEqual('2'); expect(storage).toMatchInlineSnapshot(` - Object { + { "[useLocalStorage][Flipper]x": "2", } `); @@ -83,7 +83,7 @@ test('it can read default from storage', async () => { expect((await res.findByTestId('value')).textContent).toEqual('4'); expect(storage).toMatchInlineSnapshot(` - Object { + { "[useLocalStorage][Flipper]x": "4", } `); @@ -102,6 +102,6 @@ test('it does not allow changing key', async () => { console.error = orig; } }).toThrowErrorMatchingInlineSnapshot( - `"[useAssertStableRef] An unstable reference was passed to this component as property 'key'. For optimization purposes we expect that this prop doesn't change over time. You might want to create the value passed to this prop outside the render closure, store it in useCallback / useMemo / useState, or set a key on the parent component"`, + `"[useAssertStableRef] An unstable reference was passed to this component as property 'key'. For optimization purposes we expect that this prop doesn't change over time. You might want to create the value passed to this prop outside the render closure, store it in useCallback / useMemo / useState, or set a key on the parent component. Prev value: x. New value: y"`, ); }); diff --git a/desktop/flipper-plugin/src/ui/data-inspector/DataDescription.tsx b/desktop/flipper-plugin/src/ui/data-inspector/DataDescription.tsx index e6374f0e3..d284aa3c9 100644 --- a/desktop/flipper-plugin/src/ui/data-inspector/DataDescription.tsx +++ b/desktop/flipper-plugin/src/ui/data-inspector/DataDescription.tsx @@ -18,6 +18,8 @@ import {parseColor} from '../../utils/parseColor'; import {TimelineDataDescription} from './TimelineDataDescription'; import {theme} from '../theme'; import {EditOutlined} from '@ant-design/icons'; +// This import is OK since it is a type-only import +// eslint-disable-next-line no-restricted-imports import type {CheckboxChangeEvent} from 'antd/lib/checkbox'; const {Link} = Typography; @@ -571,7 +573,8 @@ class DataDescriptionContainer extends PureComponent<{ case 'number': return {+val}; - + case 'bigint': + return {val.toString()}; case 'color': { const colorInfo = parseColor(val); if (typeof val === 'number' && val === 0) { diff --git a/desktop/flipper-plugin/src/ui/data-inspector/DataInspector.tsx b/desktop/flipper-plugin/src/ui/data-inspector/DataInspector.tsx index 39f4c7721..396325b33 100644 --- a/desktop/flipper-plugin/src/ui/data-inspector/DataInspector.tsx +++ b/desktop/flipper-plugin/src/ui/data-inspector/DataInspector.tsx @@ -67,6 +67,11 @@ export type DataInspectorProps = { */ filter?: string; + /** + * Highlight color of the search text + */ + highlightColor?: string; + /** * these should be ant design Menu.Item's */ @@ -82,6 +87,7 @@ type DataInspectorState = { filterExpanded: DataInspectorExpanded; userExpanded: DataInspectorExpanded; filter: string; + hoveredNodePath: string | undefined; }; const MAX_RESULTS = 50; @@ -102,6 +108,7 @@ export class DataInspector extends PureComponent< userExpanded: {}, filterExpanded: {}, filter: '', + hoveredNodePath: undefined, }; static getDerivedStateFromProps( @@ -181,6 +188,16 @@ export class DataInspector extends PureComponent< }); }; + setHoveredNodePath = (path?: string) => { + this.setState({ + hoveredNodePath: path, + }); + }; + + removeHover = () => { + this.setHoveredNodePath(undefined); + }; + // make sure this fn is a stable ref to not invalidate the whole tree on new data getRootData = () => { return this.props.data; @@ -188,10 +205,14 @@ export class DataInspector extends PureComponent< render() { return ( - + - + ReactElement[]; - onMouseEnter?: () => void; + hoveredNodePath?: string; - onMouseExit?: () => void; + setHoveredNodePath: (path: string) => void; }; const defaultValueExtractor: DataValueExtractor = (value: any) => { @@ -315,14 +315,13 @@ export const DataInspectorNode: React.FC = memo( tooltips, setValue: setValueProp, additionalContextMenuItems, - onMouseEnter, - onMouseExit, + hoveredNodePath, + setHoveredNodePath, }) { const highlighter = useHighlighter(); const getRoot = useContext(RootDataContext); const isUnitTest = useInUnitTest(); - const [hover, setHover] = useState(false); const shouldExpand = useRef(false); const expandHandle = useRef(undefined as any); const [renderExpanded, setRenderExpanded] = useState(false); @@ -428,37 +427,6 @@ export const DataInspectorNode: React.FC = memo( [onDelete], ); - const thisOnMouseEnter = useCallback( - (e: SyntheticEvent) => { - onMouseEnter?.(); - e.stopPropagation(); - setHover(true); - }, - [onMouseEnter], - ); - - const thisOnMouseLeave = useCallback( - (e: SyntheticEvent) => { - onMouseExit?.(); - e.stopPropagation(); - setHover(false); - }, - [onMouseExit], - ); - - const childOnMouseEnter = useCallback(() => { - //when a child is hovered we are in both child and parents bounds so - //manually disable our own hover state so we focus on child - setHover(false); - }, []); - - const childOnMouseExit = useCallback(() => { - //If a child has been unhovered then it means we have left the childs bounds and mouse should still be in parents - //(current element's) bounds. However the mouse enter callback wont fire again in this element since we never left the bounds. - //Therefore we manually set hovered back to true - setHover(true); - }, []); - /** * RENDERING */ @@ -502,8 +470,8 @@ export const DataInspectorNode: React.FC = memo( const metaKey = key + index; const dataInspectorNode = ( = memo( } function getContextMenu() { - const lib = tryGetFlipperLibImplementation(); + const lib = _tryGetFlipperLibImplementation(); const extraItems = additionalContextMenuItems ? [ additionalContextMenuItems(parentPath, value, name), @@ -662,13 +630,19 @@ export const DataInspectorNode: React.FC = memo( ); } + const nodePath = path.join('.'); + return ( { + setHoveredNodePath(nodePath); + }} + onMouseLeave={() => { + setHoveredNodePath(parentPath.join('.')); + }} depth={depth} disabled={!!setValueProp && !!setValue === false}> @@ -744,6 +718,18 @@ function dataInspectorPropsAreEqual( } } + const nodePath = ( + props.name === undefined + ? props.parentPath + : props.parentPath.concat([props.name]) + ).join('.'); + + //if the node is a prefix then we of the hovered path(s) then we *should* render this branch of the tree + //Otherwise we don't need to rerender since this node is not changing hover state + const nodePathIsPrefixOfCurrentOrNextHoverPath = + nextProps.hoveredNodePath?.startsWith(nodePath) || + props.hoveredNodePath?.startsWith(nodePath); + // basic equality checks for the rest return ( nextProps.data === props.data && @@ -755,7 +741,8 @@ function dataInspectorPropsAreEqual( nextProps.onDelete === props.onDelete && nextProps.setValue === props.setValue && nextProps.collapsed === props.collapsed && - nextProps.expandRoot === props.expandRoot + nextProps.expandRoot === props.expandRoot && + !nodePathIsPrefixOfCurrentOrNextHoverPath ); } diff --git a/desktop/flipper-plugin/src/ui/data-inspector/TimelineDataDescription.tsx b/desktop/flipper-plugin/src/ui/data-inspector/TimelineDataDescription.tsx index c3bc1c3f8..da185ea6b 100644 --- a/desktop/flipper-plugin/src/ui/data-inspector/TimelineDataDescription.tsx +++ b/desktop/flipper-plugin/src/ui/data-inspector/TimelineDataDescription.tsx @@ -19,7 +19,7 @@ type TimePoint = { display: string; color: string; key: string; - properties: {[key: string]: string}; + properties?: any; }; type Timeline = { @@ -30,7 +30,7 @@ type Timeline = { type Props = { canSetCurrent?: boolean; timeline: Timeline; - onClick: (selected: string) => void; + onClick?: (selected: string) => void; }; type State = { @@ -45,6 +45,9 @@ export class TimelineDataDescription extends Component { render(): ReactNode { const moments = Object.values(this.props.timeline.time); + if (moments == null || moments.length === 0) { + return null; + } const firstMoment = moments[0].moment; const points = moments.map((value) => ({ label: value.display, @@ -54,34 +57,33 @@ export class TimelineDataDescription extends Component { value.color, key: value.key, })); + const properties = this.props.timeline.time.find( + (value) => value.key === this.state.selected, + )?.properties; + return ( <> {this.props.canSetCurrent && (
)} -
+
this.setState({selected: ids[0]})} + onClick={(ids) => { + this.setState({selected: ids[0]}); + this.props.onClick?.(ids[0]); + }} maxGap={50} selected={this.state.selected} />
-
- value.key === this.state.selected, - )?.properties ?? {} - } - /> -
+ {properties && } ); } diff --git a/desktop/flipper-plugin/src/ui/data-table/ColumnFilter.tsx b/desktop/flipper-plugin/src/ui/data-table/ColumnFilter.tsx index 06f9def16..c5384d50f 100644 --- a/desktop/flipper-plugin/src/ui/data-table/ColumnFilter.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/ColumnFilter.tsx @@ -20,6 +20,7 @@ import { Switch, } from 'antd'; import { + FilterFilled, FilterOutlined, MinusCircleOutlined, PlusCircleOutlined, @@ -49,6 +50,7 @@ export function FilterIcon({ type: 'addColumnFilter', column: column.key, value: input, + options: {}, }); setInput(''); }; @@ -198,7 +200,7 @@ export function FilterIcon({ return ( - + {isActive ? : } ); @@ -207,7 +209,7 @@ export function FilterIcon({ export const FilterButton = styled.div<{isActive?: boolean}>(({isActive}) => ({ backgroundColor: theme.backgroundWash, visibility: isActive ? 'visible' : 'hidden', - color: isActive ? theme.textColorActive : theme.disabledColor, + color: isActive ? theme.primaryColor : theme.disabledColor, cursor: 'pointer', marginRight: 4, zIndex: 1, diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTable.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTable.tsx index a9d674f5b..34b918f36 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTable.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTable.tsx @@ -26,8 +26,6 @@ import {Percentage} from '../../utils/widthUtils'; import { DataSourceRendererVirtual, DataSourceRendererStatic, - DataSource, - DataSourceView, DataSourceVirtualizer, } from '../../data-source/index'; import { @@ -53,7 +51,11 @@ import {Formatter} from '../DataFormatter'; import {usePluginInstanceMaybe} from '../../plugin/PluginContext'; import {debounce} from 'lodash'; import {useInUnitTest} from '../../utils/useInUnitTest'; -import {createDataSource} from '../../state/createDataSource'; +import { + createDataSource, + DataSource, + _DataSourceView, +} from 'flipper-plugin-core'; import {HighlightProvider} from '../Highlight'; import {useLatestRef} from '../../utils/useLatestRef'; @@ -73,11 +75,12 @@ type DataTableBaseProps = { onSelect?(record: T | undefined, records: T[]): void; onRowStyle?(record: T): CSSProperties | undefined; tableManagerRef?: RefObject | undefined>; // Actually we want a MutableRefObject, but that is not what React.createRef() returns, and we don't want to put the burden on the plugin dev to cast it... + virtualizerRef?: RefObject; onCopyRows?(records: T[]): string; onContextMenu?: (selection: undefined | T) => React.ReactElement; onRenderEmpty?: | null - | ((dataView?: DataSourceView) => React.ReactElement); + | ((dataView?: _DataSourceView) => React.ReactElement); }; export type ItemRenderer = ( @@ -116,8 +119,11 @@ export type DataTableColumn = { value: string; enabled: boolean; predefined?: boolean; + strict?: boolean; + exact?: boolean; }[]; inversed?: boolean; + sortable?: boolean; }; export interface TableRowRenderContext { @@ -157,7 +163,12 @@ export function DataTable( // eslint-disable-next-line const scope = isUnitTest ? '' : usePluginInstanceMaybe()?.definition.id ?? ''; - const virtualizerRef = useRef(); + let virtualizerRef = useRef(); + if (props.virtualizerRef) { + virtualizerRef = props.virtualizerRef as React.MutableRefObject< + DataSourceVirtualizer | undefined + >; + } const [tableState, dispatch] = useReducer( dataTableManagerReducer as DataTableReducer, undefined, @@ -381,6 +392,10 @@ export function DataTable( tableState.columns, ), ); + dataView.setFilterExpections( + tableState.filterExceptions as T[keyof T][] | undefined, + ); + // TODO: in the future setFilter effects could be async, at the moment it isn't, // so we can safely assume the internal state of the dataView is updated with the // filter changes and try to find the same entry back again @@ -427,6 +442,7 @@ export function DataTable( ...tableState.columns.map((c) => c.filters), // eslint-disable-next-line react-hooks/exhaustive-deps ...tableState.columns.map((c) => c.inversed), + tableState.filterExceptions, ], ); @@ -771,7 +787,7 @@ function syncRecordsToDataSource( } function createDefaultEmptyRenderer(dataTableManager?: DataTableManager) { - return (dataView?: DataSourceView) => ( + return (dataView?: _DataSourceView) => ( ); } @@ -780,7 +796,7 @@ function EmptyTable({ dataView, dataManager, }: { - dataView?: DataSourceView; + dataView?: _DataSourceView; dataManager?: DataTableManager; }) { const resetFilters = useCallback(() => { diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx new file mode 100644 index 000000000..3641c88b6 --- /dev/null +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableDefaultPowerSearchOperators.tsx @@ -0,0 +1,348 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import dayjs from 'dayjs'; +import {getFlipperLib} from 'flipper-plugin-core'; +import {OperatorConfig} from '../PowerSearch'; +import { + FloatOperatorConfig, + StringOperatorConfig, +} from '../PowerSearch/PowerSearchConfig'; + +export type PowerSearchOperatorProcessor = ( + powerSearchOperatorConfig: OperatorConfig, + searchValue: any, + value: any, +) => boolean; + +export const dataTablePowerSearchOperators = { + string_contains: (handleUnknownValues?: boolean) => ({ + label: 'contains', + key: 'string_contains', + valueType: 'STRING', + handleUnknownValues, + }), + string_not_contains: (handleUnknownValues?: boolean) => ({ + label: 'does not contain', + key: 'string_not_contains', + valueType: 'STRING', + handleUnknownValues, + }), + string_matches_exactly: (handleUnknownValues?: boolean) => ({ + label: 'is', + key: 'string_matches_exactly', + valueType: 'STRING', + handleUnknownValues, + }), + string_not_matches_exactly: (handleUnknownValues?: boolean) => ({ + label: 'is not', + key: 'string_not_matches_exactly', + valueType: 'STRING', + handleUnknownValues, + }), + searializable_object_contains: () => ({ + label: 'contains', + key: 'searializable_object_contains', + valueType: 'STRING', + }), + searializable_object_not_contains: () => ({ + label: 'does not contain', + key: 'searializable_object_not_contains', + valueType: 'STRING', + }), + string_set_contains_any_of: () => ({ + label: 'contains any of', + key: 'string_set_contains_any_of', + valueType: 'STRING_SET', + }), + string_set_contains_none_of: () => ({ + label: 'contains none of', + key: 'string_set_contains_none_of', + valueType: 'STRING_SET', + }), + int_equals: () => ({ + label: '=', + key: 'int_equals', + valueType: 'INTEGER', + }), + int_greater_than: () => ({ + label: '>', + key: 'int_greater_than', + valueType: 'INTEGER', + }), + int_greater_or_equal: () => ({ + label: '>=', + key: 'int_greater_or_equal', + valueType: 'INTEGER', + }), + int_less_than: () => ({ + label: '<', + key: 'int_less_than', + valueType: 'INTEGER', + }), + int_less_or_equal: () => ({ + label: '<=', + key: 'int_less_or_equal', + valueType: 'INTEGER', + }), + float_equals: (precision?: number) => ({ + label: '=', + key: 'float_equals', + valueType: 'FLOAT', + precision, + }), + float_greater_than: () => ({ + label: '>', + key: 'float_greater_than', + valueType: 'FLOAT', + }), + float_greater_or_equal: () => ({ + label: '>=', + key: 'float_greater_or_equal', + valueType: 'FLOAT', + }), + float_less_than: () => ({ + label: '<', + key: 'float_less_than', + valueType: 'FLOAT', + }), + float_less_or_equal: () => ({ + label: '<=', + key: 'float_less_or_equal', + valueType: 'FLOAT', + }), + // { [enumValue]: enumLabel } + enum_is: (enumLabels: Record) => ({ + label: 'is', + key: 'enum_is', + valueType: 'ENUM', + enumLabels, + }), + enum_is_nullish_or: (enumLabels: Record) => ({ + label: 'is nullish or', + key: 'enum_is_nullish_or', + valueType: 'ENUM', + enumLabels, + }), + enum_is_not: (enumLabels: Record) => ({ + label: 'is not', + key: 'enum_is_not', + valueType: 'ENUM', + enumLabels, + }), + // TODO: Support logical operations (AND, OR, NOT) to combine primitive operators instead of adding new complex operators! + enum_set_is_nullish_or_any_of: (enumLabels: Record) => ({ + label: 'is nullish or any of', + key: 'enum_set_is_nullish_or_any_of', + valueType: 'ENUM_SET', + enumLabels, + }), + enum_set_is_any_of: (enumLabels: Record) => ({ + label: 'is any of', + key: 'enum_set_is_any_of', + valueType: 'ENUM_SET', + enumLabels, + }), + enum_set_is_none_of: (enumLabels: Record) => ({ + label: 'is none of', + key: 'enum_set_is_none_of', + valueType: 'ENUM_SET', + enumLabels, + }), + is_nullish: () => ({ + label: 'is nullish', + key: 'is_nullish', + valueType: 'NO_VALUE', + }), + newer_than_absolute_date: () => ({ + key: 'newer_than_absolute_date', + label: 'is after', + valueType: 'ABSOLUTE_DATE', + dateOnly: false, + }), + newer_than_absolute_date_no_time: () => ({ + key: 'newer_than_absolute_date_no_time', + label: 'is after the day', + valueType: 'ABSOLUTE_DATE', + dateOnly: true, + }), + older_than_absolute_date: () => ({ + key: 'older_than_absolute_date', + label: 'is before', + valueType: 'ABSOLUTE_DATE', + dateOnly: false, + }), + older_than_absolute_date_no_time: () => ({ + key: 'older_than_absolute_date_no_time', + label: 'is before the day', + valueType: 'ABSOLUTE_DATE', + dateOnly: true, + }), + same_as_absolute_date_no_time: () => ({ + key: 'same_as_absolute_date_no_time', + label: 'is', + valueType: 'ABSOLUTE_DATE', + dateOnly: true, + }), +} satisfies { + [key: string]: (...args: any[]) => OperatorConfig; +}; + +export type PowerSearchOperatorProcessorConfig = { + [K in keyof typeof dataTablePowerSearchOperators]: PowerSearchOperatorProcessor; +}; + +const tryConvertingUnknownToString = (value: unknown): string | null => { + try { + if (value == null) { + return null; + } + if (typeof value === 'object') { + return JSON.stringify(value); + } + if (typeof value === 'number') { + return value.toString(); + } + if (typeof value === 'string') { + return value; + } + throw value; + } catch (e) { + console.warn( + 'tryConvertingUnknownToString -> you tried to use power search for some weird data type. Please, configure your MasterDetail component to handle it correctly. See https://fburl.com/workplace/i2n0z6sm', + e, + ); + return null; + } +}; + +export const dataTablePowerSearchOperatorProcessorConfig = { + string_contains: (operator, searchValue: string, value: string) => + !!( + (operator as StringOperatorConfig).handleUnknownValues && + getFlipperLib().GK('flipper_power_search_auto_json_stringify') + ? tryConvertingUnknownToString(value) + : value + ) + ?.toLowerCase() + .includes(searchValue.toLowerCase()), + string_not_contains: (operator, searchValue: string, value: string) => + !( + (operator as StringOperatorConfig).handleUnknownValues && + getFlipperLib().GK('flipper_power_search_auto_json_stringify') + ? tryConvertingUnknownToString(value) + : value + ) + ?.toLowerCase() + .includes(searchValue.toLowerCase()), + searializable_object_contains: ( + _operator, + searchValue: string, + value: object, + ) => JSON.stringify(value).toLowerCase().includes(searchValue.toLowerCase()), + searializable_object_not_contains: ( + _operator, + searchValue: string, + value: object, + ) => !JSON.stringify(value).toLowerCase().includes(searchValue.toLowerCase()), + string_matches_exactly: (operator, searchValue: string, value: string) => + ((operator as StringOperatorConfig).handleUnknownValues && + getFlipperLib().GK('flipper_power_search_auto_json_stringify') + ? tryConvertingUnknownToString(value) + : value) === searchValue, + string_not_matches_exactly: (operator, searchValue: string, value: string) => + ((operator as StringOperatorConfig).handleUnknownValues && + getFlipperLib().GK('flipper_power_search_auto_json_stringify') + ? tryConvertingUnknownToString(value) + : value) !== searchValue, + // See PowerSearchStringSetTerm + string_set_contains_any_of: ( + _operator, + searchValue: string[], + value: string, + ) => + searchValue.some((item) => + value.toLowerCase().includes(item.toLowerCase()), + ), + string_set_contains_none_of: ( + _operator, + searchValue: string[], + value: string, + ) => + !searchValue.some((item) => + value.toLowerCase().includes(item.toLowerCase()), + ), + int_equals: (_operator, searchValue: number, value: number) => + value === searchValue, + int_greater_than: (_operator, searchValue: number, value: number) => + value > searchValue, + int_greater_or_equal: (_operator, searchValue: number, value: number) => + value >= searchValue, + int_less_than: (_operator, searchValue: number, value: number) => + value < searchValue, + int_less_or_equal: (_operator, searchValue: number, value: number) => + value <= searchValue, + float_equals: (operator, searchValue: number, value: number) => { + const precision = (operator as FloatOperatorConfig).precision ?? 0.01; + return value <= searchValue + precision && value >= searchValue - precision; + }, + float_greater_than: (_operator, searchValue: number, value: number) => + value > searchValue, + float_greater_or_equal: (_operator, searchValue: number, value: number) => + value >= searchValue, + float_less_than: (_operator, searchValue: number, value: number) => + value < searchValue, + float_less_or_equal: (_operator, searchValue: number, value: number) => + value <= searchValue, + enum_is: (_operator, searchValue: string, value: string) => + searchValue === value, + enum_is_nullish_or: (_operator, searchValue: string, value?: string | null) => + value == null || searchValue === value, + enum_is_not: (_operator, searchValue: string, value: string) => + searchValue !== value, + enum_set_is_nullish_or_any_of: ( + _operator, + searchValue: string[], + value?: string | null, + ) => value == null || searchValue.some((item) => value === item), + enum_set_is_any_of: (_operator, searchValue: string[], value: string) => + searchValue.some((item) => value === item), + enum_set_is_none_of: (_operator, searchValue: string[], value: string) => + !searchValue.some((item) => value === item), + is_nullish: (_operator, _searchValue, value) => value == null, + // See PowerSearchAbsoluteDateTerm + newer_than_absolute_date: (_operator, searchValue: Date, value: any) => { + const valueNormalized = dayjs(value); + return valueNormalized.isAfter(searchValue); + }, + newer_than_absolute_date_no_time: ( + _operator, + searchValue: Date, + value: any, + ) => { + const valueNormalized = dayjs(value); + return valueNormalized.isAfter(searchValue); + }, + older_than_absolute_date: (_operator, searchValue: Date, value: any) => { + const valueNormalized = dayjs(value); + return valueNormalized.isBefore(searchValue); + }, + older_than_absolute_date_no_time: ( + _operator, + searchValue: Date, + value: any, + ) => { + const valueNormalized = dayjs(value); + return valueNormalized.isBefore(searchValue); + }, + same_as_absolute_date_no_time: (_operator, searchValue: Date, value: any) => { + const valueNormalized = dayjs(value); + return valueNormalized.isSame(searchValue, 'day'); + }, +} satisfies PowerSearchOperatorProcessorConfig; diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableManager.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableManager.tsx index ab5719149..7c659c7c5 100644 --- a/desktop/flipper-plugin/src/ui/data-table/DataTableManager.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableManager.tsx @@ -9,14 +9,11 @@ import type {DataTableColumn} from './DataTable'; import {Percentage} from '../../utils/widthUtils'; -import {MutableRefObject, Reducer} from 'react'; -import { - DataSource, - DataSourceView, - DataSourceVirtualizer, -} from '../../data-source/index'; +import {MutableRefObject, Reducer, RefObject} from 'react'; +import {DataSourceVirtualizer} from '../../data-source/index'; import produce, {castDraft, immerable, original} from 'immer'; import {theme} from '../theme'; +import {DataSource, getFlipperLib, _DataSourceView} from 'flipper-plugin-core'; export type OnColumnResize = (id: string, size: number | Percentage) => void; export type Sorting = { @@ -59,6 +56,21 @@ type PersistedState = { highlightSearchSetting: SearchHighlightSetting; }; +type AddColumnFilterOptions = { + /** + * The filter is strict by default + * All entries that are missing this value(undefined) will be filtered out. + * By disabling this option you make filter include rows that don't have this value ie undefined + */ + strict?: boolean; + /** + * By default, the filter is matched as a substring: rowValue.toLowerCase().includes(filter). + * With this flag the filter is going to be matched by exact equality: rowValue === filter + */ + exact?: boolean; + disableOthers?: boolean; +}; + type Action = {type: Name} & Args; type DataManagerActions = @@ -100,12 +112,21 @@ type DataManagerActions = /** Changing column filters */ | Action< 'addColumnFilter', - {column: keyof T; value: string; disableOthers?: boolean} + {column: keyof T; value: string; options: AddColumnFilterOptions} + > + | Action< + 'removeColumnFilter', + | {column: keyof T; index: number; label?: never} + | {column: keyof T; index?: never; label: string} + > + | Action< + 'toggleColumnFilter', + | {column: keyof T; index: number; label?: never} + | {column: keyof T; index?: never; label: string} > - | Action<'removeColumnFilter', {column: keyof T; index: number}> - | Action<'toggleColumnFilter', {column: keyof T; index: number}> | Action<'setColumnFilterInverse', {column: keyof T; inversed: boolean}> | Action<'setColumnFilterFromSelection', {column: keyof T}> + | Action<'setFilterExceptions', {exceptions: string[] | undefined}> | Action<'appliedInitialScroll'> | Action<'toggleUseRegex'> | Action<'toggleAutoScroll'> @@ -121,7 +142,7 @@ type DataManagerActions = type DataManagerConfig = { dataSource: DataSource; - dataView: DataSourceView; + dataView: _DataSourceView; defaultColumns: DataTableColumn[]; scope: string; onSelect: undefined | ((item: T | undefined, items: T[]) => void); @@ -144,6 +165,7 @@ export type DataManagerState = { showNumberedHistory: boolean; autoScroll: boolean; searchValue: string; + filterExceptions: string[] | undefined; /** Used to remember the record entry to lookup when user presses ctrl */ previousSearchValue: string; searchHistory: string[]; @@ -168,6 +190,7 @@ export const dataTableManagerReducer = produce< draft.sorting = undefined; draft.searchValue = ''; draft.selection = castDraft(emptySelection); + draft.filterExceptions = undefined; break; } case 'resetFilters': { @@ -175,6 +198,7 @@ export const dataTableManagerReducer = produce< c.filters?.forEach((f) => (f.enabled = false)), ); draft.searchValue = ''; + draft.filterExceptions = undefined; break; } case 'resizeColumn': { @@ -201,6 +225,7 @@ export const dataTableManagerReducer = produce< case 'setSearchValue': { draft.searchValue = action.value; draft.previousSearchValue = ''; + draft.filterExceptions = undefined; if ( action.addToHistory && action.value && @@ -215,6 +240,7 @@ export const dataTableManagerReducer = produce< break; } case 'toggleSearchValue': { + draft.filterExceptions = undefined; if (draft.searchValue) { draft.previousSearchValue = draft.searchValue; draft.searchValue = ''; @@ -271,35 +297,53 @@ export const dataTableManagerReducer = produce< break; } case 'addColumnFilter': { + draft.filterExceptions = undefined; addColumnFilter( draft.columns, action.column, action.value, - action.disableOthers, + action.options, ); break; } case 'removeColumnFilter': { - draft.columns - .find((c) => c.key === action.column)! - .filters?.splice(action.index, 1); + draft.filterExceptions = undefined; + const column = draft.columns.find((c) => c.key === action.column)!; + const index = + action.index ?? + column.filters?.findIndex((f) => f.label === action.label!); + + if (index === undefined || index < 0) { + break; + } + + column.filters?.splice(index, 1); break; } case 'toggleColumnFilter': { - const f = draft.columns.find((c) => c.key === action.column)!.filters![ - action.index - ]; + draft.filterExceptions = undefined; + const column = draft.columns.find((c) => c.key === action.column)!; + const index = + action.index ?? + column.filters?.findIndex((f) => f.label === action.label!); + + if (index === undefined || index < 0) { + break; + } + const f = column.filters![index]; f.enabled = !f.enabled; break; } case 'setColumnFilterInverse': { + draft.filterExceptions = undefined; draft.columns.find((c) => c.key === action.column)!.inversed = action.inversed; break; } case 'setColumnFilterFromSelection': { + draft.filterExceptions = undefined; const items = getSelectedItems( - config.dataView as DataSourceView, + config.dataView as _DataSourceView, draft.selection, ); items.forEach((item, index) => { @@ -307,7 +351,10 @@ export const dataTableManagerReducer = produce< draft.columns, action.column, getValueAtPath(item, String(action.column)), - index === 0, // remove existing filters before adding the first + { + disableOthers: index === 0, // remove existing filters before adding the first + exact: true, + }, ); }); break; @@ -347,6 +394,10 @@ export const dataTableManagerReducer = produce< draft.showNumberedHistory = action.showNumberedHistory; break; } + case 'setFilterExceptions': { + draft.filterExceptions = action.exceptions; + break; + } default: { throw new Error('Unknown action ' + (action as any).type); } @@ -376,17 +427,25 @@ export type DataTableManager = { toggleColumnVisibility(column: keyof T): void; sortColumn(column: keyof T, direction?: SortDirection): void; setSearchValue(value: string, addToHistory?: boolean): void; - dataView: DataSourceView; + dataView: _DataSourceView; + stateRef: RefObject>>; toggleSearchValue(): void; toggleHighlightSearch(): void; setSearchHighlightColor(color: string): void; toggleSideBySide(): void; showSearchDropdown(show: boolean): void; setShowNumberedHistory(showNumberedHistory: boolean): void; + addColumnFilter( + column: keyof T, + value: string, + options?: AddColumnFilterOptions, + ): void; + removeColumnFilter(column: keyof T, label: string): void; + setFilterExceptions(exceptions: string[] | undefined): void; }; export function createDataTableManager( - dataView: DataSourceView, + dataView: _DataSourceView, dispatch: DataTableDispatch, stateRef: MutableRefObject>, ): DataTableManager { @@ -427,6 +486,7 @@ export function createDataTableManager( dispatch({type: 'sortColumn', column, direction}); }, setSearchValue(value, addToHistory = false) { + getFlipperLib().logger.track('usage', 'data-table:filter:search'); dispatch({type: 'setSearchValue', value, addToHistory}); }, toggleSearchValue() { @@ -447,7 +507,19 @@ export function createDataTableManager( setShowNumberedHistory(showNumberedHistory) { dispatch({type: 'setShowNumberedHistory', showNumberedHistory}); }, + addColumnFilter(column, value, options = {}) { + getFlipperLib().logger.track('usage', 'data-table:filter:add-column'); + dispatch({type: 'addColumnFilter', column, value, options}); + }, + removeColumnFilter(column, label) { + getFlipperLib().logger.track('usage', 'data-table:filter:remove-column'); + dispatch({type: 'removeColumnFilter', column, label}); + }, + setFilterExceptions(exceptions: string[] | undefined) { + dispatch({type: 'setFilterExceptions', exceptions}); + }, dataView, + stateRef, }; } @@ -492,6 +564,7 @@ export function createInitialState( searchHistory: prefs?.searchHistory ?? [], useRegex: prefs?.useRegex ?? false, filterSearchHistory: prefs?.filterSearchHistory ?? true, + filterExceptions: undefined, autoScroll: prefs?.autoScroll ?? config.autoScroll ?? false, highlightSearchSetting: prefs?.highlightSearchSetting ?? { highlightEnabled: false, @@ -511,10 +584,13 @@ function addColumnFilter( columns: DataTableColumn[], columnId: keyof T, value: string, - disableOthers: boolean = false, + options: AddColumnFilterOptions = {}, ): void { + options = Object.assign({disableOthers: false, strict: true}, options); const column = columns.find((c) => c.key === columnId)!; - const filterValue = String(value).toLowerCase(); + const filterValue = options.exact + ? String(value) + : String(value).toLowerCase(); const existing = column.filters!.find((c) => c.value === filterValue); if (existing) { existing.enabled = true; @@ -523,9 +599,11 @@ function addColumnFilter( label: String(value), value: filterValue, enabled: true, + strict: options.strict, + exact: options.exact, }); } - if (disableOthers) { + if (options.disableOthers) { column.filters!.forEach((c) => { if (c.value !== filterValue) { c.enabled = false; @@ -535,14 +613,14 @@ function addColumnFilter( } export function getSelectedItem( - dataView: DataSourceView, + dataView: _DataSourceView, selection: Selection, ): T | undefined { return selection.current < 0 ? undefined : dataView.get(selection.current); } export function getSelectedItems( - dataView: DataSourceView, + dataView: _DataSourceView, selection: Selection, ): T[] { return [...selection.items] @@ -667,13 +745,18 @@ export function computeDataTableFilter( return function dataTableFilter(item: any) { for (const column of filteringColumns) { - const rowMatchesFilter = column.filters!.some( - (f) => - f.enabled && - String(getValueAtPath(item, column.key)) - .toLowerCase() - .includes(f.value), - ); + const rowMatchesFilter = column.filters!.some((f) => { + if (!f.enabled) { + return false; + } + + const value = getValueAtPath(item, column.key); + const isMatching = f.exact + ? String(value) === f.value + : String(value).toLowerCase().includes(f.value); + + return f.strict ? isMatching : isMatching || value === undefined; + }); if (column.inversed && rowMatchesFilter) { return false; } diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx new file mode 100644 index 000000000..7c2e08d9d --- /dev/null +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearch.tsx @@ -0,0 +1,924 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React, { + useCallback, + useLayoutEffect, + useMemo, + useRef, + useState, + RefObject, + MutableRefObject, + CSSProperties, + useEffect, + useReducer, +} from 'react'; +import {TableRow, DEFAULT_ROW_HEIGHT} from './TableRow'; +import {Layout} from '../Layout'; +import {TableHead} from './TableHead'; +import {Percentage} from '../../utils/widthUtils'; +import { + DataSourceRendererVirtual, + DataSourceRendererStatic, + DataSourceVirtualizer, +} from '../../data-source/index'; +import { + computeDataTableFilter, + createDataTableManager, + createInitialState, + DataManagerState, + DataTableManager, + dataTableManagerReducer, + DataTableReducer, + getSelectedItem, + getSelectedItems, + savePreferences, +} from './DataTableWithPowerSearchManager'; +import styled from '@emotion/styled'; +import {theme} from '../theme'; +import {tableContextMenuFactory} from './PowerSearchTableContextMenu'; +import {Menu, Switch, InputRef, Typography, Dropdown, Button} from 'antd'; +import { + CoffeeOutlined, + SearchOutlined, + PushpinFilled, + MenuOutlined, +} from '@ant-design/icons'; +import {useAssertStableRef} from '../../utils/useAssertStableRef'; +import {Formatter} from '../DataFormatter'; +import {usePluginInstanceMaybe} from '../../plugin/PluginContext'; +import {debounce} from 'lodash'; +import {useInUnitTest} from '../../utils/useInUnitTest'; +import { + createDataSource, + DataSource, + _DataSourceView, +} from 'flipper-plugin-core'; +import {useLatestRef} from '../../utils/useLatestRef'; +import { + PowerSearch, + PowerSearchConfig, + FieldConfig, + OperatorConfig, + SearchExpressionTerm, +} from '../PowerSearch'; +import { + dataTablePowerSearchOperatorProcessorConfig, + dataTablePowerSearchOperators, +} from './DataTableDefaultPowerSearchOperators'; + +type DataTableBaseProps = { + columns: DataTableColumn[]; + enableSearchbar?: boolean; + enableAutoScroll?: boolean; + enableHorizontalScroll?: boolean; + enableColumnHeaders?: boolean; + enableMultiSelect?: boolean; + enableContextMenu?: boolean; + enablePersistSettings?: boolean; + enableMultiPanels?: boolean; + // if set (the default) will grow and become scrollable. Otherwise will use natural size + scrollable?: boolean; + actionsRight?: React.ReactElement; + actionsTop?: React.ReactElement; + /** @deprecated **/ + extraActions?: React.ReactElement; + onSelect?(record: T | undefined, records: T[]): void; + onRowStyle?(record: T): CSSProperties | undefined; + tableManagerRef?: RefObject | undefined>; // Actually we want a MutableRefObject, but that is not what React.createRef() returns, and we don't want to put the burden on the plugin dev to cast it... + virtualizerRef?: RefObject; + onCopyRows?(records: T[]): string; + onContextMenu?: (selection: undefined | T) => React.ReactElement; + onRenderEmpty?: + | null + | ((dataView?: _DataSourceView) => React.ReactElement); + powerSearchInitialState?: SearchExpressionTerm[]; + /** + * Adds a special power search entry to search through the entire row (mathching a substring in it after stringifying it as a JSON) + * @default true + */ + enablePowerSearchWholeRowSearch?: boolean; +}; + +const powerSearchConfigEntireRow: FieldConfig = { + label: 'Row', + key: 'entireRow', + operators: { + searializable_object_contains: + dataTablePowerSearchOperators.searializable_object_contains(), + searializable_object_not_contains: + dataTablePowerSearchOperators.searializable_object_not_contains(), + }, + useWholeRow: true, +}; + +export type ItemRenderer = ( + item: T, + selected: boolean, + index: number, +) => React.ReactNode; + +type DataTableInput = + | { + dataSource: DataSource; + viewId?: string; + records?: undefined; + recordsKey?: undefined; + } + | { + records: readonly T[]; + recordsKey?: keyof T; + viewId?: string; + dataSource?: undefined; + }; + +export type DataTableColumn = { + //this can be a dotted path into a nest objects. e.g foo.bar + key: keyof T & string; + // possible future extension: getValue(row) (and free-form key) to support computed columns + onRender?: (row: T, selected: boolean, index: number) => React.ReactNode; + formatters?: Formatter[] | Formatter; + title?: string; + width?: number | Percentage | undefined; // undefined: use all remaining width + wrap?: boolean; + align?: 'left' | 'right' | 'center'; + visible?: boolean; + inversed?: boolean; + sortable?: boolean; + powerSearchConfig?: + | OperatorConfig[] + | false + | {operators: OperatorConfig[]; useWholeRow?: boolean}; +}; + +export interface TableRowRenderContext { + columns: DataTableColumn[]; + onMouseEnter( + e: React.MouseEvent, + item: T, + itemId: number, + ): void; + onMouseDown( + e: React.MouseEvent, + item: T, + itemId: number, + ): void; + onRowStyle?(item: T): React.CSSProperties | undefined; + onContextMenu?(): React.ReactElement; +} + +const Searchbar = styled(Layout.Horizontal)({ + backgroundColor: theme.backgroundWash, + padding: theme.space.small, +}); + +export type DataTableProps = DataTableInput & DataTableBaseProps; + +export function DataTable( + props: DataTableProps, +): React.ReactElement { + const {onRowStyle, onSelect, onCopyRows, onContextMenu} = props; + const dataSource = normalizeDataSourceInput(props); + const dataView = props?.viewId + ? dataSource.getAdditionalView(props.viewId) + : dataSource.view; + useAssertStableRef(dataSource, 'dataSource'); + useAssertStableRef(onRowStyle, 'onRowStyle'); + useAssertStableRef(props.onSelect, 'onRowSelect'); + useAssertStableRef(props.columns, 'columns'); + useAssertStableRef(onCopyRows, 'onCopyRows'); + useAssertStableRef(onContextMenu, 'onContextMenu'); + + const isUnitTest = useInUnitTest(); + + // eslint-disable-next-line + const scope = isUnitTest ? '' : usePluginInstanceMaybe()?.definition.id ?? ''; + let virtualizerRef = useRef(); + if (props.virtualizerRef) { + virtualizerRef = props.virtualizerRef as React.MutableRefObject< + DataSourceVirtualizer | undefined + >; + } + const [tableState, dispatch] = useReducer( + dataTableManagerReducer as DataTableReducer, + undefined, + () => + createInitialState({ + dataSource, + dataView, + defaultColumns: props.columns, + onSelect, + scope, + virtualizerRef, + autoScroll: props.enableAutoScroll, + enablePersistSettings: props.enablePersistSettings, + initialSearchExpression: props.powerSearchInitialState, + }), + ); + + const stateRef = useRef(tableState); + stateRef.current = tableState; + const searchInputRef = useRef(null) as MutableRefObject; + const lastOffset = useRef(0); + const dragging = useRef(false); + + const [tableManager] = useState(() => + createDataTableManager(dataView, dispatch, stateRef), + ); + // Make sure this is the main table + if (props.tableManagerRef && !props.viewId) { + (props.tableManagerRef as MutableRefObject).current = tableManager; + } + + const {columns, selection, searchExpression, sorting} = tableState; + + const latestSelectionRef = useLatestRef(selection); + const latestOnSelectRef = useLatestRef(onSelect); + useEffect(() => { + if (dataView) { + const unsubscribe = dataView.addListener((change) => { + if ( + change.type === 'update' && + latestSelectionRef.current.items.has(change.index) + ) { + latestOnSelectRef.current?.( + getSelectedItem(dataView, latestSelectionRef.current), + getSelectedItems(dataView, latestSelectionRef.current), + ); + } + }); + + return unsubscribe; + } + }, [dataView, latestSelectionRef, latestOnSelectRef]); + + const visibleColumns = useMemo( + () => columns.filter((column) => column.visible), + [columns], + ); + + const powerSearchConfig: PowerSearchConfig = useMemo(() => { + const res: PowerSearchConfig = {fields: {}}; + + if (props.enablePowerSearchWholeRowSearch) { + res.fields.entireRow = powerSearchConfigEntireRow; + } + + for (const column of columns) { + if (column.powerSearchConfig === false) { + continue; + } + + let useWholeRow = false; + let columnPowerSearchOperators: OperatorConfig[]; + // If no power search config provided we treat every input as a string + if (!column.powerSearchConfig) { + columnPowerSearchOperators = [ + dataTablePowerSearchOperators.string_contains(true), + dataTablePowerSearchOperators.string_not_contains(true), + dataTablePowerSearchOperators.string_matches_exactly(true), + dataTablePowerSearchOperators.string_not_matches_exactly(true), + ]; + } else if (Array.isArray(column.powerSearchConfig)) { + columnPowerSearchOperators = column.powerSearchConfig; + } else { + columnPowerSearchOperators = column.powerSearchConfig.operators; + useWholeRow = !!column.powerSearchConfig.useWholeRow; + } + + const columnFieldConfig: FieldConfig = { + label: column.title || column.key, + key: column.key, + operators: columnPowerSearchOperators.reduce((res, operatorConfig) => { + res[operatorConfig.key] = operatorConfig; + return res; + }, {} as Record), + useWholeRow, + }; + res.fields[column.key] = columnFieldConfig; + } + + return res; + }, [columns, props.enablePowerSearchWholeRowSearch]); + + const renderingConfig = useMemo>(() => { + let startIndex = 0; + + return { + columns: visibleColumns as DataTableColumn[], + onMouseEnter(e, _item, index) { + if (dragging.current && e.buttons === 1 && props.enableMultiSelect) { + // by computing range we make sure no intermediate items are missed when scrolling fast + tableManager.addRangeToSelection(startIndex, index); + } + }, + onMouseDown(e, _item, index) { + if (!props.enableMultiSelect && e.buttons > 1) { + tableManager.selectItem(index, false, true); + return; + } + if (!dragging.current) { + if (e.buttons > 1) { + // for right click we only want to add if needed, not deselect + tableManager.addRangeToSelection(index, index, false); + } else if (e.ctrlKey || e.metaKey) { + tableManager.addRangeToSelection(index, index, true); + } else if (e.shiftKey) { + tableManager.selectItem(index, true, true); + } else { + tableManager.selectItem(index, false, true); + } + + dragging.current = true; + startIndex = index; + + function onStopDragSelecting() { + dragging.current = false; + document.removeEventListener('mouseup', onStopDragSelecting); + } + + document.addEventListener('mouseup', onStopDragSelecting); + } + }, + onRowStyle, + onContextMenu: props.enableContextMenu + ? () => { + // using a ref keeps the config stable, so that a new context menu doesn't need + // all rows to be rerendered, but rather shows it conditionally + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return contextMenuRef.current?.()!; + } + : undefined, + }; + }, [ + visibleColumns, + tableManager, + onRowStyle, + props.enableContextMenu, + props.enableMultiSelect, + ]); + + const itemRenderer = useCallback( + function itemRenderer( + record: T, + index: number, + renderContext: TableRowRenderContext, + ) { + return ( + + ); + }, + [selection, onRowStyle], + ); + + /** + * Keyboard / selection handling + */ + const onKeyDown = useCallback( + (e: React.KeyboardEvent) => { + let handled = true; + const shiftPressed = e.shiftKey; + const outputSize = dataView.size; + const controlPressed = e.ctrlKey; + const windowSize = props.scrollable + ? virtualizerRef.current?.virtualItems.length ?? 0 + : dataView.size; + if (!windowSize) { + return; + } + switch (e.key) { + case 'ArrowUp': + tableManager.selectItem( + (idx) => (idx > 0 ? idx - 1 : 0), + shiftPressed, + ); + break; + case 'ArrowDown': + tableManager.selectItem( + (idx) => (idx < outputSize - 1 ? idx + 1 : idx), + shiftPressed, + ); + break; + case 'Home': + tableManager.selectItem(0, shiftPressed); + break; + case 'End': + tableManager.selectItem(outputSize - 1, shiftPressed); + break; + case ' ': // yes, that is a space + case 'PageDown': + tableManager.selectItem( + (idx) => Math.min(outputSize - 1, idx + windowSize - 1), + shiftPressed, + ); + break; + case 'PageUp': + tableManager.selectItem( + (idx) => Math.max(0, idx - windowSize + 1), + shiftPressed, + ); + break; + case 'Escape': + tableManager.clearSelection(); + break; + case 'f': + if (controlPressed && searchInputRef?.current) { + searchInputRef?.current.focus(); + } + break; + default: + handled = false; + } + if (handled) { + e.stopPropagation(); + e.preventDefault(); + } + }, + [dataView, props.scrollable, tableManager], + ); + + const [setFilter] = useState(() => (tableState: DataManagerState) => { + const selectedEntry = + tableState.selection.current >= 0 + ? dataView.getEntry(tableState.selection.current) + : null; + dataView.setFilter( + computeDataTableFilter( + tableState.searchExpression, + dataTablePowerSearchOperatorProcessorConfig, + ), + ); + dataView.setFilterExpections( + tableState.filterExceptions as T[keyof T][] | undefined, + ); + + // TODO: in the future setFilter effects could be async, at the moment it isn't, + // so we can safely assume the internal state of the dataView is updated with the + // filter changes and try to find the same entry back again + if (selectedEntry) { + const selectionIndex = dataView.getViewIndexOfEntry(selectedEntry); + tableManager.selectItem(selectionIndex, false, false); + // we disable autoScroll as is it can accidentally be annoying if it was never turned off and + // filter causes items to not fill the available space + dispatch({type: 'setAutoScroll', autoScroll: false}); + virtualizerRef.current?.scrollToIndex(selectionIndex, {align: 'center'}); + setTimeout(() => { + virtualizerRef.current?.scrollToIndex(selectionIndex, { + align: 'center', + }); + }, 0); + } + // TODO: could do the same for multiselections, doesn't seem to be requested so far + }); + + const [debouncedSetFilter] = useState(() => { + // we don't want to trigger filter changes too quickly, as they can be pretty expensive + // and would block the user from entering text in the search bar for example + // (and in the future would really benefit from concurrent mode here :)) + // leading is set to true so that an initial filter is immediately applied and a flash of wrong content is prevented + // this also makes clear act faster + return isUnitTest ? setFilter : debounce(setFilter, 250); + }); + + useEffect( + function updateFilter() { + if (!dataView.isFiltered) { + setFilter(tableState); + } else { + debouncedSetFilter(tableState); + } + }, + // Important dep optimization: we don't want to recalc filters if just the width or visibility changes! + // We pass entire state.columns to computeDataTableFilter, but only changes in the filter are a valid cause to compute a new filter function + // eslint-disable-next-line + [ + tableState.searchExpression, + // eslint-disable-next-line react-hooks/exhaustive-deps + ...tableState.columns.map((c) => c.inversed), + tableState.filterExceptions, + ], + ); + + useEffect( + function updateSorting() { + if (tableState.sorting === undefined) { + dataView.setSortBy(undefined); + dataView.setReversed(false); + } else { + dataView.setSortBy(tableState.sorting.key); + dataView.setReversed(tableState.sorting.direction === 'desc'); + } + }, + [dataView, tableState.sorting], + ); + + const isMounted = useRef(false); + useEffect( + function triggerSelection() { + if (isMounted.current) { + onSelect?.( + getSelectedItem(dataView, tableState.selection), + getSelectedItems(dataView, tableState.selection), + ); + } + isMounted.current = true; + }, + [onSelect, dataView, tableState.selection], + ); + + // The initialScrollPosition is used to both capture the initial px we want to scroll to, + // and whether we performed that scrolling already (if so, it will be 0) + useLayoutEffect( + function scrollSelectionIntoView() { + if (tableState.initialOffset) { + virtualizerRef.current?.scrollToOffset(tableState.initialOffset); + dispatch({ + type: 'appliedInitialScroll', + }); + } else if (selection && selection.current >= 0) { + dispatch({type: 'setAutoScroll', autoScroll: false}); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + virtualizerRef.current?.scrollToIndex(selection!.current, { + align: 'auto', + }); + } + }, + // initialOffset is relevant for the first run, + // but should not trigger the efffect in general + // eslint-disable-next-line + [selection], + ); + + /** Range finder */ + const [range, setRange] = useState(''); + const hideRange = useRef(); + + const onRangeChange = useCallback( + (start: number, end: number, total: number, offset) => { + setRange(`${start} - ${end} / ${total}`); + lastOffset.current = offset; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + clearTimeout(hideRange.current!); + hideRange.current = setTimeout(() => { + setRange(''); + }, 1000); + }, + [], + ); + + const onUpdateAutoScroll = useCallback( + (autoScroll: boolean) => { + if (props.enableAutoScroll) { + dispatch({type: 'setAutoScroll', autoScroll}); + } + }, + [props.enableAutoScroll], + ); + + const sidePanelToggle = useMemo( + () => ( + + { + e.stopPropagation(); + e.preventDefault(); + }}> + Side By Side View + { + tableManager.toggleSideBySide(); + }} + /> + + + ), + [tableManager, tableState.sideBySide], + ); + + /** Context menu */ + const contexMenu = isUnitTest + ? undefined + : // eslint-disable-next-line + useCallback( + () => + tableContextMenuFactory( + dataView, + dispatch as any, + selection, + tableState.columns, + visibleColumns, + onCopyRows, + onContextMenu, + props.enableMultiPanels ? sidePanelToggle : undefined, + ), + [ + dataView, + selection, + tableState.columns, + visibleColumns, + onCopyRows, + onContextMenu, + props.enableMultiPanels, + sidePanelToggle, + ], + ); + + const contextMenuRef = useRef(contexMenu); + contextMenuRef.current = contexMenu; + + useEffect(function initialSetup() { + return function cleanup() { + // write current prefs to local storage + savePreferences(stateRef.current, lastOffset.current); + // if the component unmounts, we reset the SFRW pipeline to + // avoid wasting resources in the background + dataView.reset(); + if (props.viewId) { + // this is a side panel + dataSource.deleteView(props.viewId); + } + // clean ref && Make sure this is the main table + if (props.tableManagerRef && !props.viewId) { + (props.tableManagerRef as MutableRefObject).current = undefined; + } + }; + // one-time setup and cleanup effect, everything in here is asserted to be stable: + // dataSource, tableManager, tableManagerRef + // eslint-disable-next-line + }, []); + + const header = ( + + {props.actionsTop ? {props.actionsTop} : null} + {props.enableSearchbar && ( + + { + tableManager.setSearchExpression(newSearchExpression); + }} + onConfirmUnknownOption={ + props.enablePowerSearchWholeRowSearch + ? (searchValue) => ({ + field: powerSearchConfigEntireRow, + operator: + dataTablePowerSearchOperators.searializable_object_contains(), + searchValue, + }) + : undefined + } + /> + {contexMenu && ( + + + + )} + {props.actionsRight} + {props.extraActions} + + )} + + ); + const columnHeaders = ( + + {props.enableColumnHeaders && ( + + )} + + ); + + const emptyRenderer = + props.onRenderEmpty === undefined + ? createDefaultEmptyRenderer(tableManager) + : props.onRenderEmpty; + + let mainSection: JSX.Element; + if (props.scrollable) { + const dataSourceRenderer = ( + > + dataView={dataView} + autoScroll={tableState.autoScroll && !dragging.current} + useFixedRowHeight={!tableState.usesWrapping} + defaultRowHeight={DEFAULT_ROW_HEIGHT} + context={renderingConfig} + itemRenderer={itemRenderer} + onKeyDown={onKeyDown} + virtualizerRef={virtualizerRef} + onRangeChange={onRangeChange} + onUpdateAutoScroll={onUpdateAutoScroll} + emptyRenderer={emptyRenderer} + /> + ); + + mainSection = props.enableHorizontalScroll ? ( + + {header} + + + {columnHeaders} + {dataSourceRenderer} + + + + ) : ( + +
+ {header} + {columnHeaders} +
+ {dataSourceRenderer} +
+ ); + } else { + mainSection = ( + + {header} + {columnHeaders} + > + dataView={dataView} + useFixedRowHeight={!tableState.usesWrapping} + defaultRowHeight={DEFAULT_ROW_HEIGHT} + context={renderingConfig} + maxRecords={dataSource.limit} + itemRenderer={itemRenderer} + onKeyDown={onKeyDown} + emptyRenderer={emptyRenderer} + /> + + ); + } + const mainPanel = ( + + {mainSection} + {props.enableAutoScroll && ( + + { + dispatch({type: 'toggleAutoScroll'}); + }} + /> + + )} + {range && !isUnitTest && {range}} + + ); + return props.enableMultiPanels && tableState.sideBySide ? ( + //TODO: Make the panels resizable by having a dynamic maxWidth for Layout.Right/Left possibly? + + {mainPanel} + { viewId={'1'} {...props} enableMultiPanels={false} />} + + ) : ( + mainPanel + ); +} + +DataTable.defaultProps = { + scrollable: true, + enableSearchbar: true, + enableAutoScroll: false, + enableHorizontalScroll: true, + enableColumnHeaders: true, + enableMultiSelect: true, + enableContextMenu: true, + enablePersistSettings: true, + onRenderEmpty: undefined, + enablePowerSearchWholeRowSearch: true, +} as Partial>; + +/* eslint-disable react-hooks/rules-of-hooks */ +function normalizeDataSourceInput( + props: DataTableInput, +): DataSource { + if (props.dataSource) { + return props.dataSource; + } + if (props.records) { + const [dataSource] = useState(() => + createDataSource(props.records, {key: props.recordsKey}), + ); + useEffect(() => { + syncRecordsToDataSource(dataSource, props.records); + }, [dataSource, props.records]); + + return dataSource; + } + throw new Error( + `Either the 'dataSource' or 'records' prop should be provided to DataTable`, + ); +} +/* eslint-enable */ + +function syncRecordsToDataSource( + ds: DataSource, + records: readonly T[], +) { + const startTime = Date.now(); + ds.clear(); + // TODO: optimize in the case we're only dealing with appends or replacements + records.forEach((r) => ds.append(r)); + const duration = Math.abs(Date.now() - startTime); + if (duration > 50 || records.length > 500) { + console.warn( + "The 'records' props is only intended to be used on small datasets. Please use a 'dataSource' instead. See createDataSource for details: https://fbflipper.com/docs/extending/flipper-plugin#createdatasource", + ); + } +} + +function createDefaultEmptyRenderer(dataTableManager?: DataTableManager) { + return (dataView?: _DataSourceView) => ( + + ); +} + +function EmptyTable({ + dataView, + dataManager, +}: { + dataView?: _DataSourceView; + dataManager?: DataTableManager; +}) { + const resetFilters = useCallback(() => { + dataManager?.resetFilters(); + }, [dataManager]); + return ( + + {dataView?.size === 0 ? ( + <> + + No records yet + + ) : ( + <> + + + No records match the current search / filter criteria. + + + + Reset filters + + + + )} + + ); +} + +const RangeFinder = styled.div({ + backgroundColor: theme.backgroundWash, + position: 'absolute', + right: 64, + bottom: 20, + padding: '4px 8px', + color: theme.textColorSecondary, + fontSize: '0.8em', +}); + +const AutoScroller = styled.div({ + backgroundColor: theme.backgroundWash, + position: 'absolute', + right: 40, + bottom: 20, + width: 24, + padding: '4px 8px', + color: theme.textColorSecondary, + fontSize: '0.8em', +}); diff --git a/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx new file mode 100644 index 000000000..76421bf86 --- /dev/null +++ b/desktop/flipper-plugin/src/ui/data-table/DataTableWithPowerSearchManager.tsx @@ -0,0 +1,600 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import type {DataTableColumn} from './DataTableWithPowerSearch'; +import {Percentage} from '../../utils/widthUtils'; +import {MutableRefObject, Reducer, RefObject} from 'react'; +import {DataSourceVirtualizer} from '../../data-source/index'; +import produce, {castDraft, immerable, original} from 'immer'; +import {DataSource, getFlipperLib, _DataSourceView} from 'flipper-plugin-core'; +import {SearchExpressionTerm} from '../PowerSearch'; +import {PowerSearchOperatorProcessorConfig} from './DataTableDefaultPowerSearchOperators'; + +export type OnColumnResize = (id: string, size: number | Percentage) => void; +export type Sorting = { + key: keyof T; + direction: Exclude; +}; +export type SearchHighlightSetting = { + highlightEnabled: boolean; + color: string; +}; + +export type SortDirection = 'asc' | 'desc' | undefined; + +export type Selection = {items: ReadonlySet; current: number}; + +const emptySelection: Selection = { + items: new Set(), + current: -1, +}; + +type PersistedState = { + /** Active search value */ + searchExpression?: SearchExpressionTerm[]; + /** current selection, describes the index index in the datasources's current output (not window!) */ + selection: {current: number; items: number[]}; + /** The currently applicable sorting, if any */ + sorting: Sorting | undefined; + /** The default columns, but normalized */ + columns: Pick[]; + scrollOffset: number; + autoScroll: boolean; +}; + +type Action = {type: Name} & Args; + +type DataManagerActions = + /** Reset the current table preferences, including column widths an visibility, back to the default */ + | Action<'reset'> + /** Disable the current column filters */ + | Action<'resetFilters'> + /** Resizes the column with the given key to the given width */ + | Action<'resizeColumn', {column: keyof T; width: number | Percentage}> + /** Sort by the given column. This toggles statefully between ascending, descending, none (insertion order of the data source) */ + | Action<'sortColumn', {column: keyof T; direction: SortDirection}> + /** Show / hide the given column */ + | Action<'toggleColumnVisibility', {column: keyof T}> + | Action<'setSearchExpression', {searchExpression?: SearchExpressionTerm[]}> + | Action< + 'selectItem', + { + nextIndex: number | ((currentIndex: number) => number); + addToSelection?: boolean; + allowUnselect?: boolean; + } + > + | Action< + 'selectItemById', + { + id: string; + addToSelection?: boolean; + } + > + | Action< + 'addRangeToSelection', + { + start: number; + end: number; + allowUnselect?: boolean; + } + > + | Action<'clearSelection', {}> + | Action<'setFilterExceptions', {exceptions: string[] | undefined}> + | Action<'appliedInitialScroll'> + | Action<'toggleAutoScroll'> + | Action<'setAutoScroll', {autoScroll: boolean}> + | Action<'toggleSideBySide'> + | Action<'showSearchDropdown', {show: boolean}> + | Action<'setShowNumberedHistory', {showNumberedHistory: boolean}>; + +type DataManagerConfig = { + dataSource: DataSource; + dataView: _DataSourceView; + defaultColumns: DataTableColumn[]; + scope: string; + onSelect: undefined | ((item: T | undefined, items: T[]) => void); + virtualizerRef: MutableRefObject; + autoScroll?: boolean; + enablePersistSettings?: boolean; + initialSearchExpression?: SearchExpressionTerm[]; +}; + +export type DataManagerState = { + config: DataManagerConfig; + usesWrapping: boolean; + storageKey: string; + initialOffset: number; + columns: DataTableColumn[]; + sorting: Sorting | undefined; + selection: Selection; + autoScroll: boolean; + searchExpression?: SearchExpressionTerm[]; + filterExceptions: string[] | undefined; + sideBySide: boolean; +}; + +export type DataTableReducer = Reducer< + DataManagerState, + DataManagerActions +>; +export type DataTableDispatch = React.Dispatch>; + +export const dataTableManagerReducer = produce< + DataManagerState, + [DataManagerActions] +>(function (draft, action) { + const config = original(draft.config)!; + switch (action.type) { + case 'reset': { + draft.columns = computeInitialColumns(config.defaultColumns); + draft.sorting = undefined; + draft.searchExpression = undefined; + draft.selection = castDraft(emptySelection); + draft.filterExceptions = undefined; + break; + } + case 'resetFilters': { + draft.searchExpression = undefined; + draft.filterExceptions = undefined; + break; + } + case 'resizeColumn': { + const {column, width} = action; + const col = draft.columns.find((c) => c.key === column)!; + col.width = width; + break; + } + case 'sortColumn': { + const {column, direction} = action; + if (direction === undefined) { + draft.sorting = undefined; + } else { + draft.sorting = {key: column, direction}; + } + break; + } + case 'toggleColumnVisibility': { + const {column} = action; + const col = draft.columns.find((c) => c.key === column)!; + col.visible = !col.visible; + break; + } + case 'setSearchExpression': { + getFlipperLib().logger.track('usage', 'data-table:filter:power-search'); + draft.searchExpression = action.searchExpression; + draft.filterExceptions = undefined; + break; + } + case 'selectItem': { + const {nextIndex, addToSelection, allowUnselect} = action; + draft.selection = castDraft( + computeSetSelection( + draft.selection, + nextIndex, + addToSelection, + allowUnselect, + ), + ); + break; + } + case 'selectItemById': { + const {id, addToSelection} = action; + // TODO: fix that this doesn't jumpt selection if items are shifted! sorting is swapped etc + const idx = config.dataSource.getIndexOfKey(id); + if (idx !== -1) { + draft.selection = castDraft( + computeSetSelection(draft.selection, idx, addToSelection), + ); + } + break; + } + case 'addRangeToSelection': { + const {start, end, allowUnselect} = action; + draft.selection = castDraft( + computeAddRangeToSelection(draft.selection, start, end, allowUnselect), + ); + break; + } + case 'clearSelection': { + draft.selection = castDraft(emptySelection); + break; + } + case 'appliedInitialScroll': { + draft.initialOffset = 0; + break; + } + case 'toggleAutoScroll': { + draft.autoScroll = !draft.autoScroll; + break; + } + case 'setAutoScroll': { + draft.autoScroll = action.autoScroll; + break; + } + case 'toggleSideBySide': { + draft.sideBySide = !draft.sideBySide; + break; + } + case 'setFilterExceptions': { + draft.filterExceptions = action.exceptions; + break; + } + default: { + throw new Error('Unknown action ' + (action as any).type); + } + } +}); + +/** + * Public only imperative convienience API for DataTable + */ +export type DataTableManager = { + reset(): void; + resetFilters(): void; + selectItem( + index: number | ((currentSelection: number) => number), + addToSelection?: boolean, + allowUnselect?: boolean, + ): void; + addRangeToSelection( + start: number, + end: number, + allowUnselect?: boolean, + ): void; + selectItemById(id: string, addToSelection?: boolean): void; + clearSelection(): void; + getSelectedItem(): T | undefined; + getSelectedItems(): readonly T[]; + toggleColumnVisibility(column: keyof T): void; + sortColumn(column: keyof T, direction?: SortDirection): void; + setSearchExpression(searchExpression: SearchExpressionTerm[]): void; + dataView: _DataSourceView; + stateRef: RefObject>>; + toggleSideBySide(): void; + setFilterExceptions(exceptions: string[] | undefined): void; +}; + +export function createDataTableManager( + dataView: _DataSourceView, + dispatch: DataTableDispatch, + stateRef: MutableRefObject>, +): DataTableManager { + return { + reset() { + dispatch({type: 'reset'}); + }, + resetFilters() { + dispatch({type: 'resetFilters'}); + }, + selectItem(index: number, addToSelection = false, allowUnselect = false) { + dispatch({ + type: 'selectItem', + nextIndex: index, + addToSelection, + allowUnselect, + }); + }, + selectItemById(id, addToSelection = false) { + dispatch({type: 'selectItemById', id, addToSelection}); + }, + addRangeToSelection(start, end, allowUnselect = false) { + dispatch({type: 'addRangeToSelection', start, end, allowUnselect}); + }, + clearSelection() { + dispatch({type: 'clearSelection'}); + }, + getSelectedItem() { + return getSelectedItem(dataView, stateRef.current.selection); + }, + getSelectedItems() { + return getSelectedItems(dataView, stateRef.current.selection); + }, + toggleColumnVisibility(column) { + dispatch({type: 'toggleColumnVisibility', column}); + }, + sortColumn(column, direction) { + dispatch({type: 'sortColumn', column, direction}); + }, + setSearchExpression(searchExpression) { + getFlipperLib().logger.track('usage', 'data-table:power-search:search'); + dispatch({type: 'setSearchExpression', searchExpression}); + }, + toggleSideBySide() { + dispatch({type: 'toggleSideBySide'}); + }, + setFilterExceptions(exceptions: string[] | undefined) { + dispatch({type: 'setFilterExceptions', exceptions}); + }, + dataView, + stateRef, + }; +} + +export function createInitialState( + config: DataManagerConfig, +): DataManagerState { + // by default a table is considered to be identical if plugins, and default column names are the same + const storageKey = `${config.scope}:DataTable:${config.defaultColumns + .map((c) => c.key) + .join(',')}`; + const prefs = config.enablePersistSettings + ? loadStateFromStorage(storageKey) + : undefined; + let initialColumns = computeInitialColumns(config.defaultColumns); + if (prefs) { + // merge prefs with the default column config + initialColumns = produce(initialColumns, (draft) => { + prefs.columns.forEach((pref) => { + const existing = draft.find((c) => c.key === pref.key); + if (existing) { + Object.assign(existing, pref); + } + }); + }); + } + + let searchExpression = config.initialSearchExpression; + if (prefs?.searchExpression?.length) { + searchExpression = prefs.searchExpression; + } + + const res: DataManagerState = { + config, + storageKey, + initialOffset: prefs?.scrollOffset ?? 0, + usesWrapping: config.defaultColumns.some((col) => col.wrap), + columns: initialColumns, + sorting: prefs?.sorting, + selection: prefs?.selection + ? { + current: prefs!.selection.current, + items: new Set(prefs!.selection.items), + } + : emptySelection, + searchExpression, + filterExceptions: undefined, + autoScroll: prefs?.autoScroll ?? config.autoScroll ?? false, + sideBySide: false, + }; + // @ts-ignore + res.config[immerable] = false; // optimization: never proxy anything in config + Object.freeze(res.config); + return res; +} + +export function getSelectedItem( + dataView: _DataSourceView, + selection: Selection, +): T | undefined { + return selection.current < 0 ? undefined : dataView.get(selection.current); +} + +export function getSelectedItems( + dataView: _DataSourceView, + selection: Selection, +): T[] { + return [...selection.items] + .sort((a, b) => a - b) // https://stackoverflow.com/a/15765283/1983583 + .map((i) => dataView.get(i)) + .filter(Boolean) as any[]; +} + +export function savePreferences( + state: DataManagerState, + scrollOffset: number, +) { + if (!state.config.scope || !state.config.enablePersistSettings) { + return; + } + const prefs: PersistedState = { + searchExpression: state.searchExpression, + selection: { + current: state.selection.current, + items: Array.from(state.selection.items), + }, + sorting: state.sorting, + columns: state.columns.map((c) => ({ + key: c.key, + width: c.width, + visible: c.visible, + })), + scrollOffset, + autoScroll: state.autoScroll, + }; + localStorage.setItem(state.storageKey, JSON.stringify(prefs)); +} + +function loadStateFromStorage(storageKey: string): PersistedState | undefined { + if (!storageKey) { + return undefined; + } + const state = localStorage.getItem(storageKey); + if (!state) { + return undefined; + } + try { + return JSON.parse(state) as PersistedState; + } catch (e) { + // forget about this state + return undefined; + } +} + +function computeInitialColumns( + columns: DataTableColumn[], +): DataTableColumn[] { + const visibleColumnCount = columns.filter((c) => c.visible !== false).length; + const columnsWithoutWidth = columns.filter( + (c) => c.visible !== false && c.width === undefined, + ).length; + + return columns.map((c) => ({ + ...c, + width: + c.width ?? + // if the width is not set, and there are multiple columns with unset widths, + // there will be multiple columns ith the same flex weight (1), meaning that + // they will all resize a best fits in a specifc row. + // To address that we distribute space equally + // (this need further fine tuning in the future as with a subset of fixed columns width can become >100%) + (columnsWithoutWidth > 1 + ? `${Math.floor(100 / visibleColumnCount)}%` + : undefined), + visible: c.visible !== false, + })); +} + +/** + * A somewhat primitive and unsafe way to access nested fields an object. + * @param obj keys should only be strings + * @param keyPath dotted string path, e.g foo.bar + * @returns value at the key path + */ + +export function getValueAtPath(obj: Record, keyPath: string): any { + let res = obj; + for (const key of keyPath.split('.')) { + if (res == null) { + return null; + } else { + res = res[key]; + } + } + + return res; +} + +export function computeDataTableFilter( + searchExpression: SearchExpressionTerm[] | undefined, + powerSearchProcessors: PowerSearchOperatorProcessorConfig, +) { + return function dataTableFilter(item: any) { + if (!searchExpression || !searchExpression.length) { + return true; + } + return searchExpression.every((searchTerm) => { + const value = searchTerm.field.useWholeRow + ? item + : getValueAtPath(item, searchTerm.field.key); + if (!value) { + console.warn( + 'computeDataTableFilter -> value at searchTerm.field.key is not recognized', + searchTerm, + item, + ); + return true; + } + + const processor = + powerSearchProcessors[ + searchTerm.operator.key as keyof typeof powerSearchProcessors + ]; + if (!processor) { + console.warn( + 'computeDataTableFilter -> processor at searchTerm.operator.key is not recognized', + searchTerm, + powerSearchProcessors, + ); + return true; + } + + return processor(searchTerm.operator, searchTerm.searchValue, value); + }); + }; +} + +export function safeCreateRegExp(source: string): RegExp | undefined { + try { + return new RegExp(source); + } catch (_e) { + return undefined; + } +} + +export function computeSetSelection( + base: Selection, + nextIndex: number | ((currentIndex: number) => number), + addToSelection?: boolean, + allowUnselect?: boolean, +): Selection { + const newIndex = + typeof nextIndex === 'number' ? nextIndex : nextIndex(base.current); + // special case: toggle existing selection off + if ( + !addToSelection && + allowUnselect && + base.items.size === 1 && + base.current === newIndex + ) { + return emptySelection; + } + if (newIndex < 0) { + return emptySelection; + } + if (base.current < 0 || !addToSelection) { + return { + current: newIndex, + items: new Set([newIndex]), + }; + } else { + const lowest = Math.min(base.current, newIndex); + const highest = Math.max(base.current, newIndex); + return { + current: newIndex, + items: addIndicesToMultiSelection(base.items, lowest, highest), + }; + } +} + +export function computeAddRangeToSelection( + base: Selection, + start: number, + end: number, + allowUnselect?: boolean, +): Selection { + // special case: unselectiong a single item with the selection + if (start === end && allowUnselect) { + if (base?.items.has(start)) { + const copy = new Set(base.items); + copy.delete(start); + const current = [...copy]; + if (current.length === 0) { + return emptySelection; + } + return { + items: copy, + current: current[current.length - 1], // back to the last selected one + }; + } + // intentional fall-through + } + + // N.B. start and end can be reverted if selecting backwards + const lowest = Math.min(start, end); + const highest = Math.max(start, end); + const current = end; + + return { + items: addIndicesToMultiSelection(base.items, lowest, highest), + current, + }; +} + +function addIndicesToMultiSelection( + base: ReadonlySet, + lowest: number, + highest: number, +): ReadonlySet { + const copy = new Set(base); + for (let i = lowest; i <= highest; i++) { + copy.add(i); + } + return copy; +} diff --git a/desktop/flipper-plugin/src/ui/data-table/PowerSearchTableContextMenu.tsx b/desktop/flipper-plugin/src/ui/data-table/PowerSearchTableContextMenu.tsx new file mode 100644 index 000000000..066d2365e --- /dev/null +++ b/desktop/flipper-plugin/src/ui/data-table/PowerSearchTableContextMenu.tsx @@ -0,0 +1,212 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {CopyOutlined, FilterOutlined, TableOutlined} from '@ant-design/icons'; +import {Checkbox, Menu} from 'antd'; +import { + DataTableDispatch, + getSelectedItem, + getSelectedItems, + getValueAtPath, + Selection, +} from './DataTableManager'; +import React from 'react'; +import { + _tryGetFlipperLibImplementation, + _DataSourceView, +} from 'flipper-plugin-core'; +import {DataTableColumn} from './DataTableWithPowerSearch'; +import {toFirstUpper} from '../../utils/toFirstUpper'; +import {renderColumnValue} from './TableRow'; +import {textContent} from '../../utils/textContent'; + +const {Item, SubMenu} = Menu; + +export function tableContextMenuFactory( + dataView: _DataSourceView, + dispatch: DataTableDispatch, + selection: Selection, + columns: DataTableColumn[], + visibleColumns: DataTableColumn[], + onCopyRows: ( + rows: T[], + visibleColumns: DataTableColumn[], + ) => string = defaultOnCopyRows, + onContextMenu?: (selection: undefined | T) => React.ReactElement, + sideBySideOption?: React.ReactElement, +) { + const lib = _tryGetFlipperLibImplementation(); + if (!lib) { + return ( + + Menu not ready + + ); + } + const hasSelection = selection.items.size > 0 ?? false; + return ( + + {onContextMenu + ? onContextMenu(getSelectedItem(dataView, selection)) + : null} + } + disabled={!hasSelection}> + {visibleColumns.map((column, idx) => ( + { + dispatch({ + type: 'setColumnFilterFromSelection', + column: column.key, + }); + }}> + {friendlyColumnTitle(column)} + + ))} + + } + disabled={!hasSelection}> + { + const items = getSelectedItems(dataView, selection); + if (items.length) { + lib.writeTextToClipboard(onCopyRows(items, visibleColumns)); + } + }}> + Copy row(s) + + {lib.isFB && ( + { + const items = getSelectedItems(dataView, selection); + if (items.length) { + lib.createPaste(onCopyRows(items, visibleColumns)); + } + }}> + Create paste + + )} + { + const items = getSelectedItems(dataView, selection); + if (items.length) { + lib.writeTextToClipboard(rowsToJson(items)); + } + }}> + Copy row(s) (JSON) + + {lib.isFB && ( + { + const items = getSelectedItems(dataView, selection); + if (items.length) { + lib.createPaste(rowsToJson(items)); + } + }}> + Create paste (JSON) + + )} + + + } + disabled={!hasSelection}> + {visibleColumns.map((column, idx) => ( + { + const items = getSelectedItems(dataView, selection); + if (items.length) { + lib.writeTextToClipboard( + items + .map((item) => '' + getValueAtPath(item, column.key)) + .join('\n'), + ); + } + }}> + {friendlyColumnTitle(column)} + + ))} + + + + {columns.map((column, idx) => ( + + { + e.stopPropagation(); + e.preventDefault(); + dispatch({type: 'toggleColumnVisibility', column: column.key}); + }}> + {friendlyColumnTitle(column)} + + + ))} + + { + dispatch({type: 'resetFilters'}); + }}> + Reset filters + + { + dispatch({type: 'reset'}); + }}> + Reset view + + {sideBySideOption} + + ); +} + +function friendlyColumnTitle(column: DataTableColumn): string { + const name = column.title || column.key; + return toFirstUpper(name); +} + +function defaultOnCopyRows( + items: T[], + visibleColumns: DataTableColumn[], +) { + return ( + visibleColumns.map(friendlyColumnTitle).join('\t') + + '\n' + + items + .map((row, idx) => + visibleColumns + .map((col) => textContent(renderColumnValue(col, row, true, idx))) + .join('\t'), + ) + .join('\n') + ); +} + +function rowsToJson(items: T[]) { + return JSON.stringify(items.length > 1 ? items : items[0], null, 2); +} diff --git a/desktop/flipper-plugin/src/ui/data-table/TableContextMenu.tsx b/desktop/flipper-plugin/src/ui/data-table/TableContextMenu.tsx index ee8535846..53e183fcd 100644 --- a/desktop/flipper-plugin/src/ui/data-table/TableContextMenu.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/TableContextMenu.tsx @@ -19,10 +19,12 @@ import { Selection, } from './DataTableManager'; import React from 'react'; -import {tryGetFlipperLibImplementation} from '../../plugin/FlipperLib'; +import { + _tryGetFlipperLibImplementation, + _DataSourceView, +} from 'flipper-plugin-core'; import {DataTableColumn} from './DataTable'; import {toFirstUpper} from '../../utils/toFirstUpper'; -import {DataSourceView} from '../../data-source/index'; import {renderColumnValue} from './TableRow'; import {textContent} from '../../utils/textContent'; import {theme} from '../theme'; @@ -30,8 +32,8 @@ import {theme} from '../theme'; const {Item, SubMenu} = Menu; const {Option} = Select; -export function tableContextMenuFactory( - dataView: DataSourceView, +export function tableContextMenuFactory( + dataView: _DataSourceView, dispatch: DataTableDispatch, selection: Selection, highlightSearchSetting: SearchHighlightSetting, @@ -45,7 +47,7 @@ export function tableContextMenuFactory( onContextMenu?: (selection: undefined | T) => React.ReactElement, sideBySideOption?: React.ReactElement, ) { - const lib = tryGetFlipperLibImplementation(); + const lib = _tryGetFlipperLibImplementation(); if (!lib) { return ( @@ -280,7 +282,7 @@ function friendlyColumnTitle(column: DataTableColumn): string { return toFirstUpper(name); } -function defaultOnCopyRows( +function defaultOnCopyRows( items: T[], visibleColumns: DataTableColumn[], ) { diff --git a/desktop/flipper-plugin/src/ui/data-table/TableHead.tsx b/desktop/flipper-plugin/src/ui/data-table/TableHead.tsx index 27347a8b7..6b4cebd16 100644 --- a/desktop/flipper-plugin/src/ui/data-table/TableHead.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/TableHead.tsx @@ -13,7 +13,7 @@ import { Percentage, Width, } from '../../utils/widthUtils'; -import {memo, useRef} from 'react'; +import {HTMLAttributes, memo, useRef} from 'react'; import {Interactive, InteractiveProps} from '../Interactive'; import styled from '@emotion/styled'; import React from 'react'; @@ -135,11 +135,13 @@ function TableHeadColumn({ isResizable, sorted, dispatch, + isFilterable, }: { column: DataTableColumn; sorted: SortDirection; isResizable: boolean; dispatch: DataTableDispatch; + isFilterable: boolean; }) { const ref = useRef(null); @@ -174,26 +176,33 @@ function TableHeadColumn({ }); }; + let divProps: HTMLAttributes = {}; + if (column.sortable !== false) { + divProps = { + onClick: (e) => { + e.stopPropagation(); + const newDirection = + sorted === undefined ? 'asc' : sorted === 'asc' ? 'desc' : undefined; + dispatch({ + type: 'sortColumn', + column: column.key, + direction: newDirection, + }); + }, + role: 'button', + tabIndex: 0, + }; + } let children = ( -
{ - e.stopPropagation(); - const newDirection = - sorted === undefined - ? 'asc' - : sorted === 'asc' - ? 'desc' - : undefined; - dispatch({ - type: 'sortColumn', - column: column.key, - direction: newDirection, - }); - }} - role="button" - tabIndex={0}> - +
+ enabled).length + ? {color: theme.primaryColor, fontWeight: 'bold'} + : {} + }> {column.title === undefined ? ( toFirstUpper(column.key) ) : column.title === '' ? ( @@ -201,15 +210,21 @@ function TableHeadColumn({ ) : ( column.title )} - - dispatch({type: 'sortColumn', column: column.key, direction: dir}) - } - /> + {column.sortable !== false ? ( + + dispatch({ + type: 'sortColumn', + column: column.key, + direction: dir, + }) + } + /> + ) : null}
- + {isFilterable ? : null} ); @@ -237,11 +252,13 @@ export const TableHead = memo(function TableHead({ dispatch, sorting, scrollbarSize, + isFilterable = true, }: { dispatch: DataTableDispatch; visibleColumns: DataTableColumn[]; sorting: Sorting | undefined; scrollbarSize: number; + isFilterable?: boolean; }) { return ( @@ -252,6 +269,7 @@ export const TableHead = memo(function TableHead({ isResizable={i < visibleColumns.length - 1} dispatch={dispatch} sorted={sorting?.key === column.key ? sorting!.direction : undefined} + isFilterable={isFilterable} /> ))} diff --git a/desktop/flipper-plugin/src/ui/data-table/TableRow.tsx b/desktop/flipper-plugin/src/ui/data-table/TableRow.tsx index 92cd581a3..81211b105 100644 --- a/desktop/flipper-plugin/src/ui/data-table/TableRow.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/TableRow.tsx @@ -104,7 +104,7 @@ type TableRowProps = { style?: CSSProperties; }; -export const TableRow = memo(function TableRow({ +export const TableRow = memo(function TableRow({ record, itemIndex, highlighted, @@ -155,7 +155,7 @@ export const TableRow = memo(function TableRow({ } }); -export function renderColumnValue( +export function renderColumnValue( col: DataTableColumn, record: T, highlighted: boolean, diff --git a/desktop/flipper-plugin/src/ui/data-table/__tests__/DataTable.node.tsx b/desktop/flipper-plugin/src/ui/data-table/__tests__/DataTable.node.tsx index 3992cfb05..9b7012cba 100644 --- a/desktop/flipper-plugin/src/ui/data-table/__tests__/DataTable.node.tsx +++ b/desktop/flipper-plugin/src/ui/data-table/__tests__/DataTable.node.tsx @@ -10,16 +10,19 @@ import React, {createRef} from 'react'; import {DataTable, DataTableColumn} from '../DataTable'; import {render, act} from '@testing-library/react'; -import {createDataSource} from '../../../state/createDataSource'; +import {createDataSource} from 'flipper-plugin-core'; import {computeDataTableFilter, DataTableManager} from '../DataTableManager'; import {Button} from 'antd'; import {sleep} from 'flipper-common'; +import {TestUtils, _setFlipperLibImplementation} from 'flipper-plugin-core'; type Todo = { title: string; done: boolean; }; +_setFlipperLibImplementation(TestUtils.createMockFlipperLib()); + function createTestDataSource() { return createDataSource([ { diff --git a/desktop/flipper-plugin/src/ui/elements-inspector/elements.tsx b/desktop/flipper-plugin/src/ui/elements-inspector/elements.tsx index 881d00646..8824c88ab 100644 --- a/desktop/flipper-plugin/src/ui/elements-inspector/elements.tsx +++ b/desktop/flipper-plugin/src/ui/elements-inspector/elements.tsx @@ -16,8 +16,8 @@ import {theme} from '../theme'; import {Layout} from '../Layout'; import { getFlipperLib, - tryGetFlipperLibImplementation, -} from '../../plugin/FlipperLib'; + _tryGetFlipperLibImplementation, +} from 'flipper-plugin-core'; import {DownOutlined, RightOutlined} from '@ant-design/icons'; const {Text} = Typography; @@ -533,7 +533,7 @@ export class Elements extends PureComponent { }; isDarwin = - tryGetFlipperLibImplementation()?.environmentInfo.os.platform === 'darwin'; + _tryGetFlipperLibImplementation()?.environmentInfo.os.platform === 'darwin'; onKeyDown = (e: KeyboardEvent) => { const {selected} = this.props; diff --git a/desktop/flipper-plugin/src/ui/theme.tsx b/desktop/flipper-plugin/src/ui/theme.tsx index 2bde12e6d..061d9b4d0 100644 --- a/desktop/flipper-plugin/src/ui/theme.tsx +++ b/desktop/flipper-plugin/src/ui/theme.tsx @@ -34,6 +34,7 @@ export const theme = { buttonDefaultBackground: 'var(--flipper-button-default-background)', backgroundTransparentHover: 'var(--flipper-background-transparent-hover)', dividerColor: 'var(--flipper-divider-color)', + borderColor: 'var(--flipper-border-color)', borderRadius: 'var(--flipper-border-radius)', containerBorderRadius: 8, inlinePaddingV: 6, // vertical padding on inline elements like buttons @@ -50,6 +51,7 @@ export const theme = { large: '16px', default: '14px', small: '12px', + smaller: '10px', } as const, monospace: { fontFamily: 'SF Mono,Monaco,Andale Mono,monospace', @@ -69,7 +71,7 @@ export const theme = { } as const; export type Spacing = - | keyof typeof theme['space'] + | keyof (typeof theme)['space'] | number | undefined | boolean; diff --git a/desktop/flipper-plugin/src/utils/__tests__/createTablePlugin.node.tsx b/desktop/flipper-plugin/src/utils/__tests__/createTablePlugin.node.tsx index 4da693b96..96d83f7ae 100644 --- a/desktop/flipper-plugin/src/utils/__tests__/createTablePlugin.node.tsx +++ b/desktop/flipper-plugin/src/utils/__tests__/createTablePlugin.node.tsx @@ -26,7 +26,7 @@ test('createTablePlugin returns FlipperPlugin', () => { const tablePlugin = createTablePlugin(PROPS); const p = startPlugin(tablePlugin); expect(Object.keys(p.instance)).toMatchInlineSnapshot(` - Array [ + [ "selection", "rows", "isPaused", diff --git a/desktop/flipper-plugin/src/utils/createTablePlugin.tsx b/desktop/flipper-plugin/src/utils/createTablePlugin.tsx index 21b5c8678..4299e1097 100644 --- a/desktop/flipper-plugin/src/utils/createTablePlugin.tsx +++ b/desktop/flipper-plugin/src/utils/createTablePlugin.tsx @@ -8,14 +8,14 @@ */ import {notification, Typography} from 'antd'; -import {DataSource} from '../data-source/index'; +import {DataSource} from 'flipper-plugin-core'; import React from 'react'; -import {PluginClient} from '../plugin/Plugin'; +import {PluginClient} from 'flipper-plugin-core'; import {usePlugin} from '../plugin/PluginContext'; -import {createState} from '../state/atom'; +import {createState} from 'flipper-plugin-core'; import {DataTableColumn} from '../ui/data-table/DataTable'; import {MasterDetail} from '../ui/MasterDetail'; -import {createDataSource} from '../state/createDataSource'; +import {createDataSource} from 'flipper-plugin-core'; type PluginResult = { plugin(client: PluginClient>): { diff --git a/desktop/flipper-plugin/src/utils/renderReactRoot.tsx b/desktop/flipper-plugin/src/utils/renderReactRoot.tsx index 31e3d73c5..4db3ec749 100644 --- a/desktop/flipper-plugin/src/utils/renderReactRoot.tsx +++ b/desktop/flipper-plugin/src/utils/renderReactRoot.tsx @@ -7,7 +7,8 @@ * @format */ -import {createState, useValue} from '../state/atom'; +import {createState} from 'flipper-plugin-core'; +import {useValue} from '../state/atom'; import React, {ReactPortal} from 'react'; import {createPortal, unmountComponentAtNode} from 'react-dom'; diff --git a/desktop/flipper-plugin/src/utils/useAssertStableRef.tsx b/desktop/flipper-plugin/src/utils/useAssertStableRef.tsx index c52e2088c..7f42ee023 100644 --- a/desktop/flipper-plugin/src/utils/useAssertStableRef.tsx +++ b/desktop/flipper-plugin/src/utils/useAssertStableRef.tsx @@ -20,7 +20,7 @@ export const useAssertStableRef = !isProduction() const ref = useRef(value); if (ref.current !== value) { throw new Error( - `[useAssertStableRef] An unstable reference was passed to this component as property '${prop}'. For optimization purposes we expect that this prop doesn't change over time. You might want to create the value passed to this prop outside the render closure, store it in useCallback / useMemo / useState, or set a key on the parent component`, + `[useAssertStableRef] An unstable reference was passed to this component as property '${prop}'. For optimization purposes we expect that this prop doesn't change over time. You might want to create the value passed to this prop outside the render closure, store it in useCallback / useMemo / useState, or set a key on the parent component. Prev value: ${ref.current}. New value: ${value}`, ); } } diff --git a/desktop/flipper-plugin/src/utils/useLogger.tsx b/desktop/flipper-plugin/src/utils/useLogger.tsx index bd5e94b89..0791193c9 100644 --- a/desktop/flipper-plugin/src/utils/useLogger.tsx +++ b/desktop/flipper-plugin/src/utils/useLogger.tsx @@ -9,9 +9,9 @@ import {Logger} from 'flipper-common'; import {createContext, useContext} from 'react'; -import {stubLogger} from './Logger'; +import {_stubLogger} from 'flipper-plugin-core'; -export const _LoggerContext = createContext(stubLogger); +export const _LoggerContext = createContext(_stubLogger); /** * Provides the default logger that can be used for console logging, diff --git a/desktop/flipper-plugin/tsconfig.json b/desktop/flipper-plugin/tsconfig.json index f9a26ecc1..64886750f 100644 --- a/desktop/flipper-plugin/tsconfig.json +++ b/desktop/flipper-plugin/tsconfig.json @@ -3,12 +3,15 @@ "compilerOptions": { "outDir": "lib", "rootDir": "src", - "lib": ["dom", "ES2019"], + "lib": ["dom", "ES2021"], "types": ["jest", "../types/jest-extensions", "react/next", "react-dom/next"] }, "references": [ { "path": "../flipper-common" + }, + { + "path": "../flipper-plugin-core" } ] } diff --git a/desktop/flipper-server-client/README.md b/desktop/flipper-server-client/README.md new file mode 100644 index 000000000..33fc17518 --- /dev/null +++ b/desktop/flipper-server-client/README.md @@ -0,0 +1,3 @@ +# flipper-server-client (TBD) + +Lightweight abstraction to connect to Flipper Server and provide an API to interact with it diff --git a/desktop/flipper-server-client/package.json b/desktop/flipper-server-client/package.json new file mode 100644 index 000000000..3ddab4a2f --- /dev/null +++ b/desktop/flipper-server-client/package.json @@ -0,0 +1,30 @@ +{ + "name": "flipper-server-client", + "version": "0.0.0", + "description": "Lightweight abstraction to connect to Flipper Server and provide an API to interact with it", + "repository": "facebook/flipper", + "main": "lib/index.js", + "flipperBundlerEntry": "src", + "types": "lib/index.d.ts", + "license": "MIT", + "bugs": "https://github.com/facebook/flipper/issues", + "dependencies": { + "eventemitter3": "^4.0.7", + "flipper-common": "0.0.0", + "reconnecting-websocket": "^4.4.0" + }, + "peerDependencies": {}, + "scripts": { + "reset": "rimraf lib *.tsbuildinfo", + "build": "tsc -b", + "prepack": "yarn reset && yarn build" + }, + "files": [ + "lib/**/*" + ], + "homepage": "https://github.com/facebook/flipper", + "keywords": [ + "Flipper" + ], + "author": "Facebook, Inc" +} diff --git a/desktop/flipper-server-client/src/FlipperServerClient.tsx b/desktop/flipper-server-client/src/FlipperServerClient.tsx new file mode 100644 index 000000000..698245198 --- /dev/null +++ b/desktop/flipper-server-client/src/FlipperServerClient.tsx @@ -0,0 +1,206 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import EventEmitter from 'eventemitter3'; +import { + ExecWebSocketMessage, + FlipperServer, + FlipperServerCommands, + FlipperServerExecOptions, + ServerWebSocketMessage, +} from 'flipper-common'; +import ReconnectingWebSocket from 'reconnecting-websocket'; + +const CONNECTION_TIMEOUT = 30 * 1000; +const EXEC_TIMEOUT = 45 * 1000; + +export enum FlipperServerState { + CONNECTING, + CONNECTED, + DISCONNECTED, +} +export type {FlipperServer, FlipperServerCommands, FlipperServerExecOptions}; + +export function createFlipperServer( + host: string, + port: number, + tokenProvider: () => Promise, + onStateChange: (state: FlipperServerState) => void, +): Promise { + const URLProvider = async () => { + const token = await tokenProvider(); + return `ws://${host}:${port}?token=${token}`; + }; + + const socket = new ReconnectingWebSocket(URLProvider); + return createFlipperServerWithSocket(socket as WebSocket, onStateChange); +} + +export function createFlipperServerWithSocket( + socket: WebSocket, + onStateChange: (state: FlipperServerState) => void, +): Promise { + onStateChange(FlipperServerState.CONNECTING); + + return new Promise((resolve, reject) => { + let initialConnectionTimeout: ReturnType | undefined = + setTimeout(() => { + reject( + new Error( + `Failed to connect to the server in a timely manner. + It may be unresponsive. Run the following from the terminal + 'sudo kill -9 $(lsof -t -i :52342)' as to kill any existing running instance, if any.`, + ), + ); + }, CONNECTION_TIMEOUT); + + const eventEmitter = new EventEmitter(); + + const pendingRequests: Map< + number, + { + resolve: (data: any) => void; + reject: (data: any) => void; + timeout: ReturnType; + } + > = new Map(); + let requestId = 0; + let connected = false; + + socket.addEventListener('open', () => { + connected = true; + onStateChange(FlipperServerState.CONNECTED); + + if (initialConnectionTimeout) { + clearTimeout(initialConnectionTimeout); + initialConnectionTimeout = undefined; + + resolve(flipperServer); + } + }); + + socket.addEventListener('close', () => { + connected = false; + onStateChange(FlipperServerState.DISCONNECTED); + + pendingRequests.forEach((r) => + r.reject(new Error('flipper-server disconnected')), + ); + pendingRequests.clear(); + }); + + socket.addEventListener('message', ({data}) => { + try { + const {event, payload} = JSON.parse( + data.toString(), + ) as ServerWebSocketMessage; + + switch (event) { + case 'exec-response': { + const entry = pendingRequests.get(payload.id); + if (!entry) { + console.warn(`Unknown request id `, payload.id); + } else { + pendingRequests.delete(payload.id); + clearTimeout(entry.timeout); + entry.resolve(payload.data); + } + break; + } + case 'exec-response-error': { + const entry = pendingRequests.get(payload.id); + if (!entry) { + console.warn(`flipper-server: Unknown request id `, payload.id); + } else { + pendingRequests.delete(payload.id); + clearTimeout(entry.timeout); + entry.reject(payload.data); + } + break; + } + case 'server-event': { + eventEmitter.emit(payload.event, payload.data); + break; + } + default: { + console.warn( + 'flipper-server: received unknown message type', + data.toString(), + ); + } + } + } catch (e) { + console.warn( + 'flipper-server: failed to process message', + data.toString(), + ); + } + }); + + const commandOrOptionsIsOptions = ( + commandOrOptions: FlipperServerExecOptions | string, + ): commandOrOptions is FlipperServerExecOptions => + typeof commandOrOptions === 'object'; + + const flipperServer: FlipperServer = { + async connect() {}, + close() {}, + exec(commandOrOptions, ...argsAmbiguous): any { + let timeout: number; + let command: string; + let args: Parameters< + FlipperServerCommands[keyof FlipperServerCommands] + >; + if (commandOrOptionsIsOptions(commandOrOptions)) { + timeout = commandOrOptions.timeout; + command = argsAmbiguous[0] as string; + args = argsAmbiguous.slice(1) as typeof args; + } else { + timeout = EXEC_TIMEOUT; + command = commandOrOptions; + args = argsAmbiguous as typeof args; + } + + if (connected) { + const id = ++requestId; + return new Promise((resolve, reject) => { + pendingRequests.set(id, { + resolve, + reject, + timeout: setInterval(() => { + pendingRequests.delete(id); + reject( + new Error(`flipper-server: timeout for command '${command}'`), + ); + }, timeout), + }); + + const execMessage = { + event: 'exec', + payload: { + id, + command, + args, + }, + } as ExecWebSocketMessage; + socket.send(JSON.stringify(execMessage)); + }); + } else { + throw new Error('Not connected to Flipper server'); + } + }, + on(event, callback) { + eventEmitter.on(event, callback); + }, + off(event, callback) { + eventEmitter.off(event, callback); + }, + }; + }); +} diff --git a/desktop/flipper-server-client/src/index.tsx b/desktop/flipper-server-client/src/index.tsx new file mode 100644 index 000000000..d98a2dd80 --- /dev/null +++ b/desktop/flipper-server-client/src/index.tsx @@ -0,0 +1,10 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +export * from './FlipperServerClient'; diff --git a/desktop/flipper-server-client/tsconfig.json b/desktop/flipper-server-client/tsconfig.json new file mode 100644 index 000000000..352a4bb46 --- /dev/null +++ b/desktop/flipper-server-client/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src", + "lib": ["dom", "ES2021"] + }, + "references": [ + { + "path": "../flipper-common" + } + ] +} diff --git a/desktop/flipper-server-companion/package.json b/desktop/flipper-server-companion/package.json index 347f4a003..f0c4be040 100644 --- a/desktop/flipper-server-companion/package.json +++ b/desktop/flipper-server-companion/package.json @@ -1,6 +1,5 @@ { "name": "flipper-server-companion", - "private": true, "version": "0.0.0", "description": "Runs Flipper plugins on Flipper Server", "repository": "facebook/flipper", @@ -12,8 +11,8 @@ "dependencies": { "flipper-common": "0.0.0", "flipper-frontend-core": "0.0.0", - "flipper-plugin": "0.0.0", - "immer": "^9.0.12" + "flipper-plugin-core": "0.0.0", + "immer": "^9.0.18" }, "devDependencies": { "@types/node": "^17.0.29" diff --git a/desktop/flipper-server-companion/src/HeadlessClient.tsx b/desktop/flipper-server-companion/src/HeadlessClient.tsx index b233ce575..cffe42b1d 100644 --- a/desktop/flipper-server-companion/src/HeadlessClient.tsx +++ b/desktop/flipper-server-companion/src/HeadlessClient.tsx @@ -13,7 +13,7 @@ import { ClientConnection, BaseDevice, } from 'flipper-frontend-core'; -import {_SandyPluginDefinition} from 'flipper-plugin'; +import {_SandyPluginDefinition} from 'flipper-plugin-core'; export class HeadlessClient extends AbstractClient { constructor( diff --git a/desktop/flipper-server-companion/src/HeadlessPluginInitializer.tsx b/desktop/flipper-server-companion/src/HeadlessPluginInitializer.tsx index 5aaa9d300..170ac6a69 100644 --- a/desktop/flipper-server-companion/src/HeadlessPluginInitializer.tsx +++ b/desktop/flipper-server-companion/src/HeadlessPluginInitializer.tsx @@ -7,17 +7,13 @@ * @format */ -import { - ActivatablePluginDetails, - BundledPluginDetails, - InstalledPluginDetails, -} from 'flipper-common'; +import {ActivatablePluginDetails, InstalledPluginDetails} from 'flipper-common'; import { AbstractPluginInitializer, getRenderHostInstance, isSandyPlugin, } from 'flipper-frontend-core'; -import {_SandyPluginDefinition} from 'flipper-plugin'; +import {_SandyPluginDefinition} from 'flipper-plugin-core'; export class HeadlessPluginInitializer extends AbstractPluginInitializer { protected async getFlipperVersion() { @@ -30,19 +26,19 @@ export class HeadlessPluginInitializer extends AbstractPluginInitializer { protected async requirePluginImpl( pluginDetails: ActivatablePluginDetails, ): Promise<_SandyPluginDefinition> { - const plugin = pluginDetails.isBundled - ? this.defaultPluginsIndex[pluginDetails.name] - : await getRenderHostInstance().requirePlugin(pluginDetails.entry); - if (!plugin) { + const requiredPlugin = await getRenderHostInstance().requirePlugin( + pluginDetails.entry, + ); + if (!requiredPlugin || !requiredPlugin.plugin) { throw new Error( `Failed to obtain plugin source for: ${pluginDetails.name}`, ); } - return new _SandyPluginDefinition(pluginDetails, plugin); + return new _SandyPluginDefinition(pluginDetails, requiredPlugin.plugin); } protected async filterAllLocalVersions( - allLocalVersions: (BundledPluginDetails | InstalledPluginDetails)[], + allLocalVersions: InstalledPluginDetails[], ): Promise { const pluginsToLoad = await super.filterAllLocalVersions(allLocalVersions); return pluginsToLoad diff --git a/desktop/flipper-server-companion/src/companion.tsx b/desktop/flipper-server-companion/src/companion.tsx index d8be20b4a..1e2b29f74 100644 --- a/desktop/flipper-server-companion/src/companion.tsx +++ b/desktop/flipper-server-companion/src/companion.tsx @@ -18,8 +18,8 @@ import { FlipperCompanionAvailablePlugin, } from 'flipper-common'; import {BaseDevice} from 'flipper-frontend-core'; -import {_SandyPluginDefinition} from 'flipper-plugin'; -import {isAtom} from 'flipper-plugin'; +import {_SandyPluginDefinition} from 'flipper-plugin-core'; +import {isAtom} from 'flipper-plugin-core'; import {HeadlessClient} from './HeadlessClient'; import {FlipperServerCompanionEnv} from './init'; diff --git a/desktop/flipper-ui-browser/src/defaultPlugins/__mocks__/index.tsx b/desktop/flipper-server-companion/src/globalsReplacements/fakeEmotionCss.tsx similarity index 84% rename from desktop/flipper-ui-browser/src/defaultPlugins/__mocks__/index.tsx rename to desktop/flipper-server-companion/src/globalsReplacements/fakeEmotionCss.tsx index aa55b6add..7fe4ab506 100644 --- a/desktop/flipper-ui-browser/src/defaultPlugins/__mocks__/index.tsx +++ b/desktop/flipper-server-companion/src/globalsReplacements/fakeEmotionCss.tsx @@ -7,4 +7,4 @@ * @format */ -export default {} as any; +export const css = () => () => ({}); diff --git a/desktop/flipper-server-companion/src/init.tsx b/desktop/flipper-server-companion/src/init.tsx index 48a2e19cf..de8e3d156 100644 --- a/desktop/flipper-server-companion/src/init.tsx +++ b/desktop/flipper-server-companion/src/init.tsx @@ -7,9 +7,9 @@ * @format */ -import {FlipperServer, getLogger} from 'flipper-common'; +import {FlipperServer, getLogger, wrapRequire} from 'flipper-common'; import {getRenderHostInstance, setGlobalObject} from 'flipper-frontend-core'; -import * as FlipperPluginSDK from 'flipper-plugin'; +import * as FlipperPluginSDK from 'flipper-plugin-core'; import * as Immer from 'immer'; import {HeadlessPluginInitializer} from './HeadlessPluginInitializer'; import {initializeFlipperLibImplementation} from './initializeFlipperLibImplementation'; @@ -17,7 +17,9 @@ import {initializeRenderHost} from './initializeRenderHost'; import * as React from './globalsReplacements/fakeReact'; import * as ReactDOM from './globalsReplacements/fakeReactDOM'; import {styled} from './globalsReplacements/fakeEmotionStyled'; +import * as emotionCss from './globalsReplacements/fakeEmotionCss'; import * as legacyExports from './globalsReplacements/fakeLegacyExports'; +import Module from 'module'; export interface FlipperServerCompanionEnv { pluginInitializer: HeadlessPluginInitializer; @@ -37,8 +39,11 @@ export const initCompanionEnv = async ( Immer, antd: {}, emotion_styled: {default: styled}, + emotion_css: emotionCss, antdesign_icons: {}, + ReactJsxRuntime: {}, }); + Module.prototype.require = wrapRequire(Module.prototype.require); const flipperServerConfig = await flipperServer.exec('get-config'); initializeRenderHost(flipperServer, flipperServerConfig); diff --git a/desktop/flipper-server-companion/src/initializeFlipperLibImplementation.tsx b/desktop/flipper-server-companion/src/initializeFlipperLibImplementation.tsx index 4cefab179..0c71e1f9c 100644 --- a/desktop/flipper-server-companion/src/initializeFlipperLibImplementation.tsx +++ b/desktop/flipper-server-companion/src/initializeFlipperLibImplementation.tsx @@ -7,15 +7,27 @@ * @format */ -import {Logger, _setFlipperLibImplementation} from 'flipper-plugin'; +import { + createState, + Logger, + _setFlipperLibImplementation, +} from 'flipper-plugin-core'; import {baseFlipperLibImplementation, RenderHost} from 'flipper-frontend-core'; export function initializeFlipperLibImplementation( renderHost: RenderHost, logger: Logger, ) { + const base = baseFlipperLibImplementation(renderHost, logger); _setFlipperLibImplementation({ - ...baseFlipperLibImplementation(renderHost, logger), + ...base, + intern: { + ...base.intern, + // TODO: Implement me + currentUser: () => createState(null), + // TODO: Implement me + isConnected: () => createState(true), + }, enableMenuEntries() {}, createPaste() { // TODO: Implement me @@ -26,5 +38,8 @@ export function initializeFlipperLibImplementation( // TODO: Write to stdout/stderr? throw new Error('Not supported in headless context'); }, + settings() { + return {isDarkMode: false}; + }, }); } diff --git a/desktop/flipper-server-companion/src/initializeRenderHost.tsx b/desktop/flipper-server-companion/src/initializeRenderHost.tsx index a33caefa7..a35192397 100644 --- a/desktop/flipper-server-companion/src/initializeRenderHost.tsx +++ b/desktop/flipper-server-companion/src/initializeRenderHost.tsx @@ -14,21 +14,21 @@ export function initializeRenderHost( flipperServer: FlipperServer, flipperServerConfig: FlipperServerConfig, ) { - FlipperRenderHostInstance = { + globalThis.FlipperRenderHostInstance = { readTextFromClipboard() { - // TODO: return undefined; }, - writeTextToClipboard(_text: string) { - // TODO: - }, + writeTextToClipboard(_text: string) {}, async importFile() { throw new Error('Not implemented'); }, async exportFile() { throw new Error('Not implemented'); }, - openLink(url: string) { + async exportFileBinary() { + throw new Error('Not implemented'); + }, + openLink(_url: string) { throw new Error('Not implemented'); }, hasFocus() { @@ -43,31 +43,30 @@ export function initializeRenderHost( shouldUseDarkColors() { return false; }, - restartFlipper() { - // TODO: - }, - loadDefaultPlugins: getDefaultPluginsIndex, + restartFlipper() {}, serverConfig: flipperServerConfig, GK(gatekeeper) { return flipperServerConfig.gatekeepers[gatekeeper] ?? false; }, flipperServer, - async requirePlugin(path) { - let source = await flipperServer.exec('plugin-source', path); + async requirePlugin(path): Promise<{plugin: any; css?: string}> { + const source = await flipperServer.exec('plugin-source', path); + + let js = source.js; // append source url (to make sure a file entry shows up in the debugger) - source += `\n//# sourceURL=file://${path}`; + js += `\n//# sourceURL=file://${path}`; // and source map url (to get source code if available) - source += `\n//# sourceMappingURL=file://${path.replace(/.js$/, '.map')}`; + js += `\n//# sourceMappingURL=file://${path.replace(/.js$/, '.map')}`; // Plugins are compiled as typical CJS modules, referring to the global // 'module', which we'll make available by loading the source into a closure that captures 'module'. // Note that we use 'eval', and not 'new Function', because the latter will cause the source maps // to be off by two lines (as the function declaration uses two lines in the generated source) // eslint-disable-next-line no-eval - const cjsLoader = eval('(module) => {' + source + '\n}'); + const cjsLoader = eval('(module) => {' + js + '\n}'); const theModule = {exports: {}}; cjsLoader(theModule); - return theModule.exports; + return {plugin: theModule.exports}; }, getStaticResourceUrl(path): string { // the 'static' folder is mounted as static middleware in Express at the root @@ -81,10 +80,3 @@ export function initializeRenderHost( }, } as RenderHost; } - -function getDefaultPluginsIndex() { - // @ts-ignore - // eslint-disable-next-line import/no-unresolved - const index = require('./defaultPlugins'); - return index.default || index; -} diff --git a/desktop/flipper-server-companion/tsconfig.json b/desktop/flipper-server-companion/tsconfig.json index e0c7516ab..02b5716e1 100644 --- a/desktop/flipper-server-companion/tsconfig.json +++ b/desktop/flipper-server-companion/tsconfig.json @@ -13,7 +13,7 @@ "path": "../flipper-frontend-core" }, { - "path": "../flipper-plugin" + "path": "../flipper-plugin-core" } ] } diff --git a/desktop/flipper-server-core/package.json b/desktop/flipper-server-core/package.json index ba170222a..60d6d5a71 100644 --- a/desktop/flipper-server-core/package.json +++ b/desktop/flipper-server-core/package.json @@ -1,6 +1,5 @@ { "name": "flipper-server-core", - "private": true, "version": "0.0.0", "description": "Flipper server connection SDK", "repository": "facebook/flipper", @@ -17,21 +16,25 @@ "archiver": "^5.3.1", "async-mutex": "^0.3.2", "axios": "^0.26.0", + "exit-hook": "^2.1.1", + "express": "^4.17.3", "flipper-common": "0.0.0", "flipper-doctor": "0.0.0", - "flipper-plugin": "0.0.0", + "flipper-plugin-core": "0.0.0", "flipper-plugin-lib": "0.0.0", "flipper-server-companion": "0.0.0", "form-data": "^4.0.0", - "fs-extra": "^10.1.0", + "fs-extra": "^11.1.1", "invariant": "^2.2.4", - "js-base64": "^3.7.2", + "js-base64": "^3.7.5", + "jsonwebtoken": "^9.0.2", "lodash.memoize": "^4.1.2", - "node-fetch": "^3.2.4", + "memorystream": "^0.3.1", + "node-fetch": "^2.6.0", "node-forge": "^0.10.0", "open": "^8.3.0", "openssl-wrapper": "^0.3.4", - "promisify-child-process": "^4.1.1", + "promisify-child-process": "^4.1.2", "rimraf": "^3.0.2", "rsocket-core": "^0.0.27", "rsocket-flowable": "^0.0.27", @@ -46,24 +49,23 @@ "xdg-basedir": "^4.0.0" }, "devDependencies": { - "@types/express": "^4.17.13", - "@types/http-proxy": "^1.17.8", "@types/archiver": "^5.3.1", + "@types/express": "^4.17.13", "@types/invariant": "^2.2.35", + "@types/jsonwebtoken": "^9.0.1", "@types/memorystream": "^0.3.0", + "@types/mock-fs": "^4.13.1", "@types/node": "^17.0.31", - "@types/node-forge": "^0.10", + "@types/node-fetch": "2.6.4", + "@types/node-forge": "^0.10.0", "@types/rimraf": "^3.0.2", "@types/rsocket-core": "^0.0.7", "@types/rsocket-tcp-server": "^0.0.2", - "@types/split2": "^3.2.1", + "@types/split2": "^4.2.0", "@types/tmp": "^0.2.3", "@types/which": "^2.0.1", "@types/ws": "^8.5.3", - "memorystream": "^0.3.1", - "exit-hook": "^2.1.1", - "http-proxy": "^1.18.1", - "express": "^4.17.3" + "mock-fs": "^5.2.0" }, "peerDependencies": {}, "scripts": { diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index 63acd6422..5f0e55f2a 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -10,8 +10,7 @@ import './utils/macCa'; import './utils/fetch-polyfill'; import EventEmitter from 'events'; -import {ServerController} from './comms/ServerController'; -import {CertificateExchangeMedium} from './utils/CertificateProvider'; +import {ServerController} from './app-connectivity/ServerController'; import {AndroidDeviceManager} from './devices/android/androidDeviceManager'; import {IOSDeviceManager} from './devices/ios/iOSDeviceManager'; import metroDevice from './devices/metro/metroDeviceManager'; @@ -24,6 +23,10 @@ import { UninitializedClient, FlipperServerConfig, Logger, + FlipperServerExecOptions, + DeviceDebugData, + CertificateExchangeMedium, + Settings, } from 'flipper-common'; import {ServerDevice} from './devices/ServerDevice'; import {Base64} from 'js-base64'; @@ -49,9 +52,12 @@ import {promises} from 'fs'; import rm from 'rimraf'; import assert from 'assert'; import {initializeAdbClient} from './devices/android/adbClient'; -import {assertNotNull} from './comms/Utilities'; +import {assertNotNull} from './app-connectivity/Utilities'; import {mkdirp} from 'fs-extra'; import {flipperDataFolder, flipperSettingsFolder} from './utils/paths'; +import {DebuggableDevice} from './devices/DebuggableDevice'; +import {jfUpload} from './fb-stubs/jf'; +import path from 'path'; const {access, copyFile, mkdir, unlink, stat, readlink, readFile, writeFile} = promises; @@ -64,6 +70,25 @@ function isHandledStartupError(e: Error) { return false; } +function setProcessState(settings: Settings) { + const androidHome = settings.androidHome; + const idbPath = settings.idbPath; + + if (!process.env.ANDROID_HOME && !process.env.ANDROID_SDK_ROOT) { + process.env.ANDROID_HOME = androidHome; + process.env.ANDROID_SDK_ROOT = androidHome; + } + + // emulator/emulator is more reliable than tools/emulator, so prefer it if + // it exists + process.env.PATH = + ['emulator', 'tools', 'platform-tools'] + .map((directory) => path.resolve(androidHome, directory)) + .join(':') + + `:${idbPath}` + + `:${process.env.PATH}`; +} + /** * FlipperServer takes care of all incoming device & client connections. * It will set up managers per device type, and create the incoming @@ -95,6 +120,8 @@ export class FlipperServerImpl implements FlipperServer { console.log( 'Loaded flipper config, paths: ' + JSON.stringify(config.paths, null, 2), ); + + setProcessState(config.settings); const server = (this.server = new ServerController(this)); this.keytarManager = new KeytarManager(keytarModule); // given flipper-dump, it might make more sense to have the plugin command @@ -113,7 +140,7 @@ export class FlipperServerImpl implements FlipperServer { server.addListener( 'client-setup-error', ({client, error}: {client: UninitializedClient; error: Error}) => { - this.emit('notification', { + this.emit('connectivity-troubleshoot-notification', { title: `Connection to '${client.appName}' on '${client.deviceName}' failed`, description: `Failed to start client connection: ${error}`, type: 'error', @@ -134,7 +161,7 @@ export class FlipperServerImpl implements FlipperServer { const clientIdentifier = `${client.deviceName}#${client.appName}`; if (!this.unresponsiveClients.has(clientIdentifier)) { this.unresponsiveClients.add(clientIdentifier); - this.emit('notification', { + this.emit('connectivity-troubleshoot-notification', { type: 'error', title: `Timed out establishing connection with "${client.appName}" on "${client.deviceName}".`, description: @@ -172,19 +199,25 @@ export class FlipperServerImpl implements FlipperServer { await this.createFolders(); await this.server.init(); await this.pluginManager.start(); - await this.startDeviceListeners(); + + this.startDeviceListeners(); + this.setServerState('started'); } catch (e) { if (!isHandledStartupError(e)) { console.error('Failed to start FlipperServer', e); } this.setServerState('error', e); + + throw e; } } private async createFolders() { - await mkdirp(flipperDataFolder); - await mkdirp(flipperSettingsFolder); + await Promise.all([ + mkdirp(flipperDataFolder), + mkdirp(flipperSettingsFolder), + ]); } async startDeviceListeners() { @@ -197,7 +230,7 @@ export class FlipperServerImpl implements FlipperServer { return; } this.android = new AndroidDeviceManager(this, adbClient); - return this.android.watchAndroidDevices(); + return this.android.watchAndroidDevices(true); }) .catch((e) => { console.error( @@ -268,14 +301,54 @@ export class FlipperServerImpl implements FlipperServer { this.events.emit('*', event, payload); } + private isExecWithOptions( + argsAmbiguous: + | [ + FlipperServerExecOptions, + Event, + ...Parameters, + ] + | [Event, ...Parameters], + ): argsAmbiguous is [ + FlipperServerExecOptions, + Event, + ...Parameters, + ] { + return typeof argsAmbiguous[0] === 'object'; + } + + exec( + options: FlipperServerExecOptions, + event: Event, + ...args: Parameters + ): ReturnType; exec( event: Event, ...args: Parameters ): ReturnType; async exec( - event: Event, - ...args: any[] + ...argsAmbiguous: + | [ + FlipperServerExecOptions, + Event, + ...Parameters, + ] + | [Event, ...Parameters] ): Promise { + let _timeout: number; + let event: Event; + let args: Parameters; + if (this.isExecWithOptions(argsAmbiguous)) { + _timeout = argsAmbiguous[0].timeout; + event = argsAmbiguous[1]; + args = argsAmbiguous.slice(2) as typeof args; + } else { + // _timeout is currently not used, so we are setting it to a random value. Update it to a meaningful timeout before using it! + _timeout = 42; + event = argsAmbiguous[0]; + args = argsAmbiguous.slice(1) as typeof args; + } + try { const handler: (...args: any[]) => Promise = this.commandHandler[event]; @@ -378,6 +451,7 @@ export class FlipperServerImpl implements FlipperServer { 'device-clear-logs': async (serial) => this.getDevice(serial).clearLogs(), 'device-navigate': async (serial, loc) => this.getDevice(serial).navigateToLocation(loc), + 'fetch-debug-data': () => this.fetchDebugLogs(), 'metro-command': async (serial: string, command: string) => { const device = this.getDevice(serial); if (!(device instanceof MetroDevice)) { @@ -416,13 +490,21 @@ export class FlipperServerImpl implements FlipperServer { }, 'android-launch-emulator': async (name, coldBoot) => launchEmulator(this.config.settings.androidHome, name, coldBoot), + 'android-adb-kill': async () => { + assertNotNull(this.android); + return this.android.adbKill(); + }, 'ios-get-simulators': async (bootedOnly) => { assertNotNull(this.ios); return this.ios.getSimulators(bootedOnly); }, 'ios-launch-simulator': async (udid) => { assertNotNull(this.ios); - return this.ios.simctlBridge.launchSimulator(udid); + return this.ios.launchSimulator(udid); + }, + 'ios-idb-kill': async () => { + assertNotNull(this.ios); + return this.ios.idbKill(); }, 'persist-settings': async (settings) => saveSettings(settings), 'persist-launcher-settings': async (settings) => @@ -435,7 +517,6 @@ export class FlipperServerImpl implements FlipperServer { this.pluginManager.loadDynamicPlugins(), 'plugins-load-marketplace-plugins': () => this.pluginManager.loadMarketplacePlugins(), - 'plugins-get-bundled-plugins': () => this.pluginManager.getBundledPlugins(), 'plugins-get-installed-plugins': () => this.pluginManager.getInstalledPlugins(), 'plugins-remove-plugins': (plugins) => @@ -444,8 +525,11 @@ export class FlipperServerImpl implements FlipperServer { this.pluginManager.downloadPlugin(details), 'plugins-get-updatable-plugins': (query) => this.pluginManager.getUpdatablePlugins(query), - 'plugins-install-from-file': (path) => - this.pluginManager.installPluginFromFile(path), + 'plugins-install-from-content': (contents) => { + const bytes = Base64.toUint8Array(contents); + const buffer = Buffer.from(bytes); + return this.pluginManager.installPluginFromFileOrBuffer(buffer); + }, 'plugins-install-from-marketplace': (name: string) => this.pluginManager.installPluginForMarketplace(name), 'plugins-install-from-npm': (name) => @@ -492,6 +576,13 @@ export class FlipperServerImpl implements FlipperServer { return internGraphGETAPIRequest(endpoint, params, options, token); }, 'intern-upload-scribe-logs': sendScribeLogs, + 'intern-cloud-upload': async (path) => { + const uploadRes = await jfUpload(path); + if (!uploadRes) { + throw new Error('Upload failed'); + } + return uploadRes; + }, shutdown: async () => { process.exit(0); }, @@ -548,6 +639,10 @@ export class FlipperServerImpl implements FlipperServer { return device; } + hasDevice(serial: string): boolean { + return !!this.devices.get(serial); + } + getDeviceSerials(): string[] { return Array.from(this.devices.keys()); } @@ -556,6 +651,32 @@ export class FlipperServerImpl implements FlipperServer { return Array.from(this.devices.values()); } + private async fetchDebugLogs() { + const debugDataForEachDevice = await Promise.all( + [...this.devices.values()] + .filter( + (device) => + device.connected && + (device.info.os === 'Android' || device.info.os === 'iOS'), + ) + .map((device) => + (device as unknown as DebuggableDevice) + .readFlipperFolderForAllApps() + + .catch((e) => { + console.warn( + 'fetchDebugLogs -> could not fetch debug data', + device.info.serial, + e, + ); + }), + ), + ); + return debugDataForEachDevice + .filter((item): item is DeviceDebugData[] => !!item) + .flat(); + } + public async close() { this.server.close(); for (const device of this.devices.values()) { diff --git a/desktop/flipper-server-core/src/comms/BrowserClientConnection.tsx b/desktop/flipper-server-core/src/app-connectivity/BrowserClientConnection.tsx similarity index 100% rename from desktop/flipper-server-core/src/comms/BrowserClientConnection.tsx rename to desktop/flipper-server-core/src/app-connectivity/BrowserClientConnection.tsx diff --git a/desktop/flipper-server-core/src/comms/BrowserServerWebSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx similarity index 97% rename from desktop/flipper-server-core/src/comms/BrowserServerWebSocket.tsx rename to desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx index 60a6d2fcc..72512b872 100644 --- a/desktop/flipper-server-core/src/comms/BrowserServerWebSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx @@ -16,8 +16,7 @@ import {assertNotNull, parseClientQuery} from './Utilities'; import SecureServerWebSocket, { SecureConnectionCtx, } from './SecureServerWebSocket'; -import {SecureClientQuery} from './ServerAdapter'; -import {ClientDescription, DeviceOS} from 'flipper-common'; +import {ClientDescription, DeviceOS, SecureClientQuery} from 'flipper-common'; import {URL} from 'url'; import {isFBBuild} from '../fb-stubs/constants'; @@ -144,7 +143,7 @@ class BrowserServerWebSocket extends SecureServerWebSocket { if (!parsedBaseQuery) { return; } - return {...parsedBaseQuery, medium: 3}; + return {...parsedBaseQuery, medium: 'NONE'}; } protected verifyClient(): ws.VerifyClientCallbackSync { diff --git a/desktop/flipper-server-core/src/comms/ClientConnection.tsx b/desktop/flipper-server-core/src/app-connectivity/ClientConnection.tsx similarity index 100% rename from desktop/flipper-server-core/src/comms/ClientConnection.tsx rename to desktop/flipper-server-core/src/app-connectivity/ClientConnection.tsx diff --git a/desktop/flipper-server-core/src/comms/SecureServerWebSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/SecureServerWebSocket.tsx similarity index 91% rename from desktop/flipper-server-core/src/comms/SecureServerWebSocket.tsx rename to desktop/flipper-server-core/src/app-connectivity/SecureServerWebSocket.tsx index fb10863fe..7bcb381bf 100644 --- a/desktop/flipper-server-core/src/comms/SecureServerWebSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/SecureServerWebSocket.tsx @@ -8,9 +8,8 @@ */ import ServerWebSocket, {ConnectionCtx} from './ServerWebSocket'; -import {SecureClientQuery} from './ServerAdapter'; import {ParsedUrlQuery} from 'querystring'; -import {ClientDescription} from 'flipper-common'; +import {ClientDescription, SecureClientQuery} from 'flipper-common'; import { isWsResponseMessage, parseSecureClientQuery, @@ -19,6 +18,7 @@ import { import WebSocketClientConnection from './WebSocketClientConnection'; import {serializeError} from 'serialize-error'; import {WSCloseCode} from '../utils/WSCloseCode'; +import {recorder} from '../recorder'; export interface SecureConnectionCtx extends ConnectionCtx { clientQuery?: SecureClientQuery; @@ -40,15 +40,15 @@ class SecureServerWebSocket extends ServerWebSocket { const {clientQuery, ws} = ctx; assertNotNull(clientQuery); - console.info( - `[conn] Secure websocket connection attempt: ${clientQuery.app} on ${clientQuery.device_id}. Medium ${clientQuery.medium}. CSR: ${clientQuery.csr_path}`, + recorder.log( + clientQuery, + `Secure websocket connection attempt: ${clientQuery.app} on ${clientQuery.device}.`, ); + this.listener.onSecureConnectionAttempt(clientQuery); const clientConnection = new WebSocketClientConnection(ws); - // TODO: Could we just await it here? How much time could it be, potentially? - // DRI: @aigoncharov const clientPromise: Promise = this.listener .onConnectionCreated(clientQuery, clientConnection) .then((client) => { @@ -97,7 +97,6 @@ class SecureServerWebSocket extends ServerWebSocket { } // Received an "execute" message - if (client) { this.listener.onClientMessage(client.id, rawMessage); } else { diff --git a/desktop/flipper-server-core/src/comms/ServerController.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerController.tsx similarity index 69% rename from desktop/flipper-server-core/src/comms/ServerController.tsx rename to desktop/flipper-server-core/src/app-connectivity/ServerController.tsx index 8aa0db5f7..aeadb3765 100644 --- a/desktop/flipper-server-core/src/comms/ServerController.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerController.tsx @@ -7,10 +7,10 @@ * @format */ -import {CertificateExchangeMedium} from '../utils/CertificateProvider'; import { ClientDescription, ClientQuery, + SecureClientQuery, isTest, buildClientId, Logger, @@ -18,21 +18,13 @@ import { reportPlatformFailures, FlipperServerEvents, } from 'flipper-common'; -import CertificateProvider from '../utils/CertificateProvider'; +import CertificateProvider from './certificate-exchange/CertificateProvider'; import {ClientConnection, ConnectionStatus} from './ClientConnection'; import {EventEmitter} from 'events'; import invariant from 'invariant'; import DummyDevice from '../devices/DummyDevice'; -import { - appNameWithUpdateHint, - assertNotNull, - cloneClientQuerySafeForLogging, - transformCertificateExchangeMediumToType, -} from './Utilities'; -import ServerAdapter, { - SecureClientQuery, - ServerEventsListener, -} from './ServerAdapter'; +import {appNameWithUpdateHint, assertNotNull} from './Utilities'; +import ServerWebSocketBase, {ServerEventsListener} from './ServerWebSocketBase'; import { createBrowserServer, createServer, @@ -44,11 +36,13 @@ import { getFlipperServerConfig, } from '../FlipperServerConfig'; import { - extractAppNameFromCSR, + extractBundleIdFromCSR, loadSecureServerConfig, -} from '../utils/certificateUtils'; +} from './certificate-exchange/certificate-utils'; import DesktopCertificateProvider from '../devices/desktop/DesktopCertificateProvider'; import WWWCertificateProvider from '../fb-stubs/WWWCertificateProvider'; +import {tracker} from '../tracker'; +import {recorder} from '../recorder'; type ClientTimestampTracker = { insecureStart?: number; @@ -60,11 +54,6 @@ type ClientInfo = { client: ClientDescription; }; -type ClientCsrQuery = { - csr?: string | undefined; - csr_path?: string | undefined; -}; - /** * Responsible of creating and managing the actual underlying servers: * - Insecure (used for certificate exchange) @@ -80,11 +69,11 @@ export class ServerController connections: Map = new Map(); timestamps: Map = new Map(); - secureServer: ServerAdapter | null = null; - insecureServer: ServerAdapter | null = null; - altSecureServer: ServerAdapter | null = null; - altInsecureServer: ServerAdapter | null = null; - browserServer: ServerAdapter | null = null; + secureServer: ServerWebSocketBase | null = null; + insecureServer: ServerWebSocketBase | null = null; + altSecureServer: ServerWebSocketBase | null = null; + altInsecureServer: ServerWebSocketBase | null = null; + browserServer: ServerWebSocketBase | null = null; connectionTracker: ConnectionTracker; @@ -96,6 +85,8 @@ export class ServerController super(); this.flipperServer = flipperServer; this.connectionTracker = new ConnectionTracker(this.logger); + + recorder.enable(flipperServer); } onClientMessage(clientId: string, payload: string): void { @@ -122,10 +113,10 @@ export class ServerController const options = await loadSecureServerConfig(); - console.info('[conn] secure server listening at port: ', secure); + console.info('[ws] secure server listening at port: ', secure); this.secureServer = await createServer(secure, this, options); const {secure: altSecure} = getServerPortsConfig().altServerPorts; - console.info('[conn] secure server (ws) listening at port: ', altSecure); + console.info('[ws] secure server listening at port: ', altSecure); this.altSecureServer = await createServer( altSecure, this, @@ -133,13 +124,10 @@ export class ServerController TransportType.WebSocket, ); - console.info('[conn] insecure server listening at port: ', insecure); + console.info('[ws] insecure server listening at port: ', insecure); this.insecureServer = await createServer(insecure, this); const {insecure: altInsecure} = getServerPortsConfig().altServerPorts; - console.info( - '[conn] insecure server (ws) listening at port: ', - altInsecure, - ); + console.info('[ws] insecure server listening at port: ', altInsecure); this.altInsecureServer = await createServer( altInsecure, this, @@ -148,7 +136,7 @@ export class ServerController ); const browserPort = getServerPortsConfig().browserPort; - console.info('[conn] Browser server (ws) listening at port: ', browserPort); + console.info('[ws] Browser server listening at port: ', browserPort); this.browserServer = await createBrowserServer(browserPort, this); } @@ -181,11 +169,16 @@ export class ServerController medium, rsocket, } = clientQuery; - const transformedMedium = transformCertificateExchangeMediumToType(medium); - console.info( - `[conn] Connection established: ${app} on ${device_id}. Medium ${medium}. CSR: ${csr_path}`, - cloneClientQuerySafeForLogging(clientQuery), - ); + + recorder.log(clientQuery, 'Connection established'); + tracker.track('app-connection-created', { + app, + os, + device, + device_id, + medium, + }); + return this.addConnection( clientConnection, { @@ -194,10 +187,11 @@ export class ServerController device, device_id, sdk_version, - medium: transformedMedium, + medium, rsocket, + csr, + csr_path, }, - {csr, csr_path}, downgrade, ); } @@ -213,66 +207,86 @@ export class ServerController onSecureConnectionAttempt(clientQuery: SecureClientQuery): void { const strippedClientQuery = (({device_id, ...o}) => o)(clientQuery); let id = buildClientId({device_id: 'unknown', ...strippedClientQuery}); - const tracker = this.timestamps.get(id); - if (tracker) { + const timestamp = this.timestamps.get(id); + if (timestamp) { this.timestamps.delete(id); } id = buildClientId(clientQuery); this.timestamps.set(id, { secureStart: Date.now(), - ...tracker, + ...timestamp, }); - this.logger.track( - 'usage', - 'trusted-request-handler-called', - (({csr, ...o}) => o)(clientQuery), - ); + tracker.track('app-connection-secure-attempt', { + app: clientQuery.app, + os: clientQuery.os, + device: clientQuery.device, + device_id: clientQuery.device_id, + medium: clientQuery.medium, + }); - const {os, app, device_id} = clientQuery; - // without these checks, the user might see a connection timeout error instead, which would be much harder to track down - if (os === 'iOS' && !getFlipperServerConfig().settings.enableIOS) { - console.error( - `Refusing connection from ${app} on ${device_id}, since iOS support is disabled in settings`, + // Without these checks, the user might see a connection timeout error instead, + // which would be much harder to track down + if ( + clientQuery.os === 'iOS' && + !getFlipperServerConfig().settings.enableIOS + ) { + recorder.error( + clientQuery, + `Refusing connection since iOS support is disabled in settings`, ); return; } - if (os === 'Android' && !getFlipperServerConfig().settings.enableAndroid) { - console.error( - `Refusing connection from ${app} on ${device_id}, since Android support is disabled in settings`, + if ( + clientQuery.os === 'Android' && + !getFlipperServerConfig().settings.enableAndroid + ) { + recorder.error( + clientQuery, + `Refusing connection since Android support is disabled in settings`, ); return; } this.connectionTracker.logConnectionAttempt(clientQuery); - if (this.timeHandlers.get(clientQueryToKey(clientQuery))) { - clearTimeout(this.timeHandlers.get(clientQueryToKey(clientQuery))!); + const timeout = this.timeHandlers.get(clientQueryToKey(clientQuery)); + if (timeout) { + clearTimeout(timeout); } - const transformedMedium = transformCertificateExchangeMediumToType( - clientQuery.medium, - ); - if (transformedMedium === 'WWW' || transformedMedium === 'NONE') { + if (clientQuery.medium === 'WWW' || clientQuery.medium === 'NONE') { this.flipperServer.registerDevice( new DummyDevice( this.flipperServer, clientQuery.device_id, clientQuery.app + - (transformedMedium === 'WWW' ? ' Server Exchanged' : ''), + (clientQuery.medium === 'WWW' ? ' Server Exchanged' : ''), clientQuery.os, ), ); } } + /** + * A connection has been established between a running app and Flipper Desktop. + * The connection sole purpose is to perform the certificate exchange. + * @param clientQuery Client query defines the arguments passed down from the app + * to Flipper Desktop. + */ onConnectionAttempt(clientQuery: ClientQuery): void { + // Remove the device id from the query, if found. + // Instead, set the device id as 'unknown'. const strippedClientQuery = (({device_id, ...o}) => o)(clientQuery); const id = buildClientId({device_id: 'unknown', ...strippedClientQuery}); + this.timestamps.set(id, { insecureStart: Date.now(), }); - this.logger.track('usage', 'untrusted-request-handler-called', clientQuery); + + tracker.track('app-connection-insecure-attempt', clientQuery); + recorder.log(clientQuery, 'Insecure connection attempt'); + this.connectionTracker.logConnectionAttempt(clientQuery); const client: UninitializedClient = { @@ -287,7 +301,6 @@ export class ServerController unsanitizedCSR: string, clientQuery: ClientQuery, appDirectory: string, - medium: CertificateExchangeMedium, ): Promise<{deviceId: string}> { let certificateProvider: CertificateProvider; switch (clientQuery.os) { @@ -306,7 +319,7 @@ export class ServerController ); certificateProvider = this.flipperServer.ios.certificateProvider; - if (medium === 'WWW') { + if (clientQuery.medium === 'WWW') { certificateProvider = new WWWCertificateProvider( this.flipperServer.keytarManager, ); @@ -322,23 +335,30 @@ export class ServerController } default: { throw new Error( - `ServerController.onProcessCSR -> os ${clientQuery.os} does not support certificate exchange.`, + `OS '${clientQuery.os}' does not support certificate exchange.`, ); } } - certificateProvider.verifyMedium(medium); + certificateProvider.verifyMedium(clientQuery.medium); + + recorder.log(clientQuery, 'Certificate Signing Request being processed'); return new Promise((resolve, reject) => { reportPlatformFailures( certificateProvider.processCertificateSigningRequest( + clientQuery, unsanitizedCSR, - clientQuery.os, appDirectory, ), 'processCertificateSigningRequest', ) .then((response) => { + recorder.log( + clientQuery, + 'Certificate Signing Request successfully processed', + ); + const client: UninitializedClient = { os: clientQuery.os, deviceName: clientQuery.device, @@ -355,15 +375,26 @@ export class ServerController setTimeout(() => { this.emit('client-unresponsive-error', { client, - medium, + medium: clientQuery.medium, deviceID: response.deviceId, }); }, 30 * 1000), ); + tracker.track('app-connection-certificate-exchange', { + ...clientQuery, + successful: true, + device_id: response.deviceId, + }); + resolve(response); }) - .catch((error) => { + .catch((error: Error) => { + tracker.track('app-connection-certificate-exchange', { + ...clientQuery, + successful: false, + error: error.message, + }); reject(error); }); }); @@ -378,12 +409,7 @@ export class ServerController } onClientSetupError(clientQuery: ClientQuery, e: any) { - console.warn( - `[conn] Failed to exchange certificate with ${clientQuery.app} on ${ - clientQuery.device || clientQuery.device_id - }`, - e, - ); + recorder.error(clientQuery, 'Failed to exchange certificate', e); const client: UninitializedClient = { os: clientQuery.os, deviceName: clientQuery.device, @@ -391,59 +417,57 @@ export class ServerController }; this.emit('client-setup-error', { client, - error: `[conn] Failed to exchange certificate with ${ - clientQuery.app - } on ${clientQuery.device || clientQuery.device_id}: ${e}`, + error: `Failed to exchange certificate with ${clientQuery.app} on ${ + clientQuery.device || clientQuery.device_id + }: ${e}`, }); } /** * Creates a Client and sets the underlying connection. * @param connection A client connection to communicate between server and client. - * @param query The client query created from the initial handshake. + * @param clientQuery The client query created from the initial handshake. * @param csrQuery The CSR query which contains CSR related information. */ async addConnection( connection: ClientConnection, - query: ClientQuery & {medium: CertificateExchangeMedium}, - csrQuery: ClientCsrQuery, + clientQuery: SecureClientQuery, silentReplace?: boolean, ): Promise { - invariant(query, 'expected query'); + invariant(clientQuery, 'expected query'); // try to get id by comparing giving `csr` to file from `csr_path` - // otherwise, use given device_id - const {csr_path, csr} = csrQuery; + // otherwise, use given device_id. + const {csr_path, csr} = clientQuery; // For Android, device id might change - if (csr_path && csr && query.os === 'Android') { - const app_name = await extractAppNameFromCSR(csr); + if (csr_path && csr && clientQuery.os === 'Android') { + const bundleId = await extractBundleIdFromCSR(csr); assertNotNull(this.flipperServer.android); - // TODO: allocate new object, kept now as is to keep changes minimal - (query as any).device_id = + (clientQuery as any).device_id = await this.flipperServer.android.certificateProvider.getTargetDeviceId( - app_name, + clientQuery, + bundleId, csr_path, csr, ); - console.info( - `[conn] Detected ${app_name} on ${query.device_id} in certificate`, - query, + recorder.log( + clientQuery, + `Detected ${bundleId} on ${clientQuery.device_id} in certificate`, ); } - // TODO: allocate new object, kept now as is to keep changes minimal - (query as any).app = appNameWithUpdateHint(query); + (clientQuery as any).app = appNameWithUpdateHint(clientQuery); - const id = buildClientId(query); - console.info( - `[conn] Matching device for ${query.app} on ${query.device_id}...`, - query, + const id = buildClientId(clientQuery); + recorder.log( + clientQuery, + `Matching device for ${clientQuery.app} on ${clientQuery.device_id}`, ); const client: ClientDescription = { id, - query, + query: clientQuery, }; const info = { @@ -451,9 +475,9 @@ export class ServerController connection: connection, }; - console.info( - `[conn] Initializing client ${query.app} on ${query.device_id}...`, - query, + recorder.log( + clientQuery, + `Initializing client ${clientQuery.app} on ${clientQuery.device_id}`, ); connection.subscribeToEvents((status: ConnectionStatus) => { @@ -465,7 +489,7 @@ export class ServerController } }); - console.debug(`[conn] Device client initialized: ${id}.`, 'server', query); + recorder.log(clientQuery, `Device client initialized: ${id}`); /* If a device gets disconnected without being cleaned up properly, * Flipper won't be aware until it attempts to reconnect. @@ -495,12 +519,15 @@ export class ServerController const start = tracker.insecureStart ? tracker.insecureStart : tracker.secureStart; - const elapsed = Math.round(end - start!); - this.logger.track('performance', 'client-connection-tracker', { - 'time-to-connection': elapsed, - ...query, - }); - this.timestamps.delete(id); + + if (start) { + const elapsed = Math.round(end - start); + this.logger.track('performance', 'client-connection-tracker', { + 'time-to-connection': elapsed, + ...clientQuery, + }); + this.timestamps.delete(id); + } } return client; @@ -521,9 +548,9 @@ export class ServerController removeConnection(id: string) { const info = this.connections.get(id); if (info) { - console.info( - `[conn] Disconnected: ${info.client.query.app} on ${info.client.query.device_id}.`, + recorder.log( info.client.query, + `Disconnected: ${info.client.query.app} on ${info.client.query.device_id}.`, ); this.flipperServer.emit('client-disconnected', {id}); this.connections.delete(id); @@ -553,21 +580,21 @@ class ConnectionTracker { this.logger = logger; } - logConnectionAttempt(client: ClientQuery) { - const key = `${client.os}-${client.device}-${client.app}`; + logConnectionAttempt(clientQuery: ClientQuery) { + const key = `${clientQuery.os}-${clientQuery.device}-${clientQuery.app}`; const time = Date.now(); + let entry = this.connectionAttempts.get(key) || []; entry.push(time); entry = entry.filter((t) => t >= time - this.timeWindowMillis); this.connectionAttempts.set(key, entry); if (entry.length >= this.connectionProblemThreshold) { - console.warn( - `[conn] Connection loop detected with ${key}. Connected ${ + recorder.error( + clientQuery, + `Connection loop detected with ${key}. Connected ${ this.connectionProblemThreshold } times within ${this.timeWindowMillis / 1000}s.`, - 'server', - client, ); } } diff --git a/desktop/flipper-server-core/src/comms/ServerFactory.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerFactory.tsx similarity index 88% rename from desktop/flipper-server-core/src/comms/ServerFactory.tsx rename to desktop/flipper-server-core/src/app-connectivity/ServerFactory.tsx index 70db544ee..b3eb340e0 100644 --- a/desktop/flipper-server-core/src/comms/ServerFactory.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerFactory.tsx @@ -7,8 +7,8 @@ * @format */ -import {SecureServerConfig} from '../utils/certificateUtils'; -import ServerAdapter, {ServerEventsListener} from './ServerAdapter'; +import {SecureServerConfig} from './certificate-exchange/certificate-utils'; +import ServerWebSocketBase, {ServerEventsListener} from './ServerWebSocketBase'; import ServerRSocket from './ServerRSocket'; import SecureServerWebSocket from './SecureServerWebSocket'; import BrowserServerWebSocket from './BrowserServerWebSocket'; @@ -31,8 +31,8 @@ export async function createServer( listener: ServerEventsListener, sslConfig?: SecureServerConfig, transportType: TransportType = TransportType.RSocket, -): Promise { - let server: ServerAdapter; +): Promise { + let server: ServerWebSocketBase; if (transportType === TransportType.RSocket) { server = new ServerRSocket(listener); } else if (sslConfig) { @@ -56,7 +56,7 @@ export async function createServer( export async function createBrowserServer( port: number, listener: ServerEventsListener, -): Promise { +): Promise { const server = new BrowserServerWebSocket(listener); await server.start(port); return server; diff --git a/desktop/flipper-server-core/src/comms/ServerRSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx similarity index 92% rename from desktop/flipper-server-core/src/comms/ServerRSocket.tsx rename to desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx index 221f062bc..59db23943 100644 --- a/desktop/flipper-server-core/src/comms/ServerRSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx @@ -7,11 +7,7 @@ * @format */ -import {SecureServerConfig} from '../utils/certificateUtils'; -import ServerAdapter, { - SecureClientQuery, - ServerEventsListener, -} from './ServerAdapter'; +import ServerWebSocketBase, {ServerEventsListener} from './ServerWebSocketBase'; import tls from 'tls'; import net, {AddressInfo, Socket} from 'net'; import {RSocketServer} from 'rsocket-core'; @@ -27,13 +23,16 @@ import { ClientDescription, ClientQuery, ClientResponseType, + SecureClientQuery, } from 'flipper-common'; +import {transformCertificateExchangeMediumToType} from './Utilities'; +import {SecureServerConfig} from './certificate-exchange/certificate-utils'; /** * RSocket based server. RSocket uses its own protocol for communication between * client and server. */ -class ServerRSocket extends ServerAdapter { +class ServerRSocket extends ServerWebSocketBase { rawServer_: RSocketServer | null | undefined; constructor(listener: ServerEventsListener) { super(listener); @@ -104,9 +103,13 @@ class ServerRSocket extends ServerAdapter { return {}; } - const clientQuery: SecureClientQuery = JSON.parse(payload.data); - clientQuery.rsocket = true; - // TODO: Add a migration guide + const query = JSON.parse(payload.data); + const clientQuery: SecureClientQuery = { + ...query, + medium: transformCertificateExchangeMediumToType(query.medium), + rsocket: true, + }; + this.listener.onDeprecationNotice( `[conn] RSockets are being deprecated at Flipper. Please, use the latest Flipper client in your app to migrate to WebSockets. App: ${clientQuery.app}. Device: ${clientQuery.device}.`, ); @@ -214,7 +217,12 @@ class ServerRSocket extends ServerAdapter { return {}; } - const clientQuery: ClientQuery = JSON.parse(payload.data); + const query = JSON.parse(payload.data); + const clientQuery: ClientQuery = { + ...query, + medium: transformCertificateExchangeMediumToType(query.medium), + rsocket: true, + }; this.listener.onConnectionAttempt(clientQuery); return { diff --git a/desktop/flipper-server-core/src/comms/ServerWebSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx similarity index 79% rename from desktop/flipper-server-core/src/comms/ServerWebSocket.tsx rename to desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx index 6960191eb..b380ff97a 100644 --- a/desktop/flipper-server-core/src/comms/ServerWebSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx @@ -8,7 +8,7 @@ */ import {IncomingMessage} from 'http'; -import ServerAdapter from './ServerAdapter'; +import ServerWebSocketBase from './ServerWebSocketBase'; import WebSocket, { AddressInfo, Server as WSServer, @@ -24,10 +24,11 @@ import { parseMessageToJson, verifyClientQueryComesFromCertExchangeSupportedOS, } from './Utilities'; -import {SecureServerConfig} from '../utils/certificateUtils'; +import {SecureServerConfig} from './certificate-exchange/certificate-utils'; import {Server} from 'net'; import {serializeError} from 'serialize-error'; import {WSCloseCode} from '../utils/WSCloseCode'; +import {recorder} from '../recorder'; export interface ConnectionCtx { clientQuery?: ClientQuery; @@ -35,15 +36,14 @@ export interface ConnectionCtx { request: IncomingMessage; } -// based on https://github.com/websockets/ws/blob/master/lib/websocket-server.js#L40, -// exposed to share with socket.io defaults -export const WEBSOCKET_MAX_MESSAGE_SIZE = 100 * 1024 * 1024; +// This is the maximum size of a message that can be received in a single websocket message. +export const WEBSOCKET_MAX_MESSAGE_SIZE = Math.pow(2, 53) - 1; /** * It serves as a base class for WebSocket based servers. It delegates the 'connection' * event to subclasses as a customisation point. */ -class ServerWebSocket extends ServerAdapter { +class ServerWebSocket extends ServerWebSocketBase { protected wsServer?: WSServer; private httpServer?: Server; @@ -73,16 +73,20 @@ class ServerWebSocket extends ServerAdapter { wsServer.once('error', onConnectionError); server.listen(port, () => { console.debug( - `${sslConfig ? 'Secure' : 'Insecure'} server started on port ${port}`, + `[ws] ${ + sslConfig ? 'Secure' : 'Insecure' + } server started on port ${port}`, 'server', ); - // Unsubscribe connection error listener. We'll attach a permanent error listener later + // Unsubscribe connection error listener. + // We'll attach a permanent error listener later. wsServer.off('error', onConnectionError); this.listener.onListening(port); this.wsServer = wsServer; this.httpServer = server; + resolve((server.address() as AddressInfo).port); }); }); @@ -94,7 +98,7 @@ class ServerWebSocket extends ServerAdapter { 'connection', (ws: WebSocket, request: IncomingMessage) => { ws.on('error', (error) => { - console.error('[conn] WS connection error:', error); + console.error('[ws] Connection error:', error); this.listener.onError(error); }); @@ -121,7 +125,7 @@ class ServerWebSocket extends ServerAdapter { }, ); this.wsServer.on('error', (error) => { - console.error('[conn] WS server error:', error); + console.error('[ws] Server error:', error); this.listener.onError(error); }); @@ -134,7 +138,7 @@ class ServerWebSocket extends ServerAdapter { } await new Promise((resolve, reject) => { - console.info('[conn] Stopping WS server'); + console.info('[ws] Stopping server'); assertNotNull(this.wsServer); this.wsServer.close((err) => { if (err) { @@ -145,7 +149,7 @@ class ServerWebSocket extends ServerAdapter { }); }); await new Promise((resolve, reject) => { - console.info('[conn] Stopping HTTP server'); + console.info('[ws] Stopping HTTP server'); assertNotNull(this.httpServer); this.httpServer.close((err) => { if (err) { @@ -166,13 +170,17 @@ class ServerWebSocket extends ServerAdapter { */ onConnection(ws: WebSocket, request: IncomingMessage): void { const ctx: ConnectionCtx = {ws, request}; - this.handleClientQuery(ctx); + + this.extractClientQuery(ctx); this.handleConnectionAttempt(ctx); ws.on('message', async (message: WebSocket.RawData) => { const messageString = message.toString(); try { - const parsedMessage = this.handleMessageDeserialization(messageString); + const parsedMessage = this.handleMessageDeserialization( + ctx, + messageString, + ); // Successful deserialization is a proof that the message is a string this.handleMessage(ctx, parsedMessage, messageString); } catch (error) { @@ -180,24 +188,33 @@ class ServerWebSocket extends ServerAdapter { // all other plugins might still be working correctly. So let's just report it. // This avoids ping-ponging connections if an individual plugin sends garbage (e.g. T129428800) // or throws an error when handling messages - console.error('Failed to handle message', messageString, error); + console.error('[ws] Failed to handle message', messageString, error); } }); } - protected handleClientQuery(ctx: ConnectionCtx): void { + /** + * Extract and create a ClientQuery from the request URL. This method will throw if: + * @param ctx The connection context. + * @returns It doesn't return anything, if the client query + * is extracted, this one is set into the connection context. + */ + protected extractClientQuery(ctx: ConnectionCtx): void { const {request} = ctx; + if (!request.url) { + return; + } - const query = querystring.decode(request.url!.split('?')[1]); + const query = querystring.decode(request.url.split('?')[1]); const clientQuery = this.parseClientQuery(query); if (!clientQuery) { console.warn( - '[conn] Unable to extract the client query from the request URL.', + '[ws] Unable to extract the client query from the request URL.', request.url, ); throw new UnableToExtractClientQueryError( - '[conn] Unable to extract the client query from the request URL.', + 'Unable to extract the client query from the request URL.', ); } @@ -208,18 +225,24 @@ class ServerWebSocket extends ServerAdapter { const {clientQuery} = ctx; assertNotNull(clientQuery); - console.info( - `[conn] Insecure websocket connection attempt: ${clientQuery.app} on ${clientQuery.device_id}.`, + recorder.log( + clientQuery, + `Insecure websocket connection attempt: ${clientQuery.app} on ${clientQuery.device_id}.`, ); this.listener.onConnectionAttempt(clientQuery); } - protected handleMessageDeserialization(message: unknown): object { + protected handleMessageDeserialization( + ctx: ConnectionCtx, + message: unknown, + ): object { + const {clientQuery} = ctx; + assertNotNull(clientQuery); + const parsedMessage = parseMessageToJson(message); if (!parsedMessage) { - console.error('[conn] Failed to parse message', message); - // TODO: Create custom DeserializationError - throw new Error(`[conn] Failed to parse message`); + recorder.error(clientQuery, 'Failed to parse message', message); + throw new Error(`Failed to parse message`); } return parsedMessage; } @@ -227,7 +250,7 @@ class ServerWebSocket extends ServerAdapter { protected async handleMessage( ctx: ConnectionCtx, parsedMessage: object, - // Not used in this method, but left as a reference for overriding classes + // Not used in this method, but left as a reference for overriding classes. _rawMessage: string, ) { const {clientQuery, ws} = ctx; diff --git a/desktop/flipper-server-core/src/comms/ServerAdapter.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx similarity index 87% rename from desktop/flipper-server-core/src/comms/ServerAdapter.tsx rename to desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx index bcecdb99d..b7ae2824e 100644 --- a/desktop/flipper-server-core/src/comms/ServerAdapter.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx @@ -7,31 +7,14 @@ * @format */ -import {CertificateExchangeMedium} from '../utils/CertificateProvider'; import {ClientConnection} from './ClientConnection'; -import {transformCertificateExchangeMediumToType} from './Utilities'; import { ClientDescription, ClientQuery, + SecureClientQuery, SignCertificateMessage, } from 'flipper-common'; -import {SecureServerConfig} from '../utils/certificateUtils'; - -/** - * ClientCsrQuery defines a client query with CSR - * information. - */ -export type ClientCsrQuery = { - csr?: string | undefined; - csr_path?: string | undefined; -}; - -/** - * SecureClientQuery combines a ClientQuery with - * ClientCsrQuery. It also adds medium information. - */ -export type SecureClientQuery = ClientQuery & - ClientCsrQuery & {medium: 1 /*FS*/ | 2 /*WWW*/ | 3 /*NONE*/ | undefined}; +import {SecureServerConfig} from './certificate-exchange/certificate-utils'; /** * Defines an interface for events triggered by a running server interacting @@ -75,7 +58,6 @@ export interface ServerEventsListener { unsanitizedCSR: string, clientQuery: ClientQuery, appDirectory: string, - medium: CertificateExchangeMedium, ): Promise<{deviceId: string}>; /** * A secure connection has been established with a validated client. @@ -115,7 +97,7 @@ export interface ServerEventsListener { * Defines the base class to be used by any server implementation e.g. * RSocket, WebSocket, etc. */ -abstract class ServerAdapter { +abstract class ServerWebSocketBase { constructor(protected listener: ServerEventsListener) {} /** @@ -161,7 +143,7 @@ abstract class ServerAdapter { if (message.method === 'signCertificate') { console.debug('CSR received from device', 'server'); - const {csr, destination, medium} = message; + const {csr, destination} = message; console.info( `[conn] Starting certificate exchange: ${clientQuery.app} on ${clientQuery.device}`, @@ -171,7 +153,6 @@ abstract class ServerAdapter { csr, clientQuery, destination, - transformCertificateExchangeMediumToType(medium), ); console.info( @@ -190,4 +171,4 @@ abstract class ServerAdapter { } } -export default ServerAdapter; +export default ServerWebSocketBase; diff --git a/desktop/flipper-server-core/src/comms/Utilities.tsx b/desktop/flipper-server-core/src/app-connectivity/Utilities.tsx similarity index 82% rename from desktop/flipper-server-core/src/comms/Utilities.tsx rename to desktop/flipper-server-core/src/app-connectivity/Utilities.tsx index b83ce0e98..bae385dcf 100644 --- a/desktop/flipper-server-core/src/comms/Utilities.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/Utilities.tsx @@ -7,10 +7,14 @@ * @format */ -import {ClientQuery, DeviceOS, ResponseMessage} from 'flipper-common'; +import { + CertificateExchangeMedium, + ClientQuery, + SecureClientQuery, + DeviceOS, + ResponseMessage, +} from 'flipper-common'; import {ParsedUrlQuery} from 'querystring'; -import {CertificateExchangeMedium} from '../utils/CertificateProvider'; -import {SecureClientQuery} from './ServerAdapter'; /** * Transforms the certificate exchange medium type as number to the @@ -67,7 +71,7 @@ export function isWsResponseMessage( return typeof (message as ResponseMessage).id === 'number'; } -const certExchangeSupportedOSes = new Set([ +const supportedOSForCertificateExchange = new Set([ 'Android', 'iOS', 'MacOS', @@ -81,7 +85,7 @@ const certExchangeSupportedOSes = new Set([ export function verifyClientQueryComesFromCertExchangeSupportedOS( query: ClientQuery | undefined, ): ClientQuery | undefined { - if (!query || !certExchangeSupportedOSes.has(query.os)) { + if (!query || !supportedOSForCertificateExchange.has(query.os)) { return; } return query; @@ -126,21 +130,33 @@ export function parseClientQuery( return; } + let medium: number | undefined; + if (typeof query.medium === 'string') { + medium = parseInt(query.medium, 10); + } else if (typeof query.medium === 'number') { + medium = query.medium; + } + + if (medium !== undefined && (medium < 1 || medium > 3)) { + throw new Error('Unsupported exchange medium: ' + medium); + } + + let sdk_version: number | undefined; + if (typeof query.sdk_version === 'string') { + sdk_version = parseInt(query.sdk_version, 10); + } else if (typeof query.sdk_version === 'number') { + sdk_version = query.sdk_version; + } + const clientQuery: ClientQuery = { device_id, device, app, os, + medium: transformCertificateExchangeMediumToType(medium), + sdk_version, }; - if (typeof query.sdk_version === 'string') { - const sdk_version = parseInt(query.sdk_version, 10); - if (sdk_version) { - // TODO: allocate new object, kept now as is to keep changes minimal - (clientQuery as any).sdk_version = sdk_version; - } - } - return clientQuery; } @@ -178,18 +194,25 @@ export function parseSecureClientQuery( let medium: number | undefined; if (typeof query.medium === 'string') { medium = parseInt(query.medium, 10); + } else if (typeof query.medium === 'number') { + medium = query.medium; } + if (medium !== undefined && (medium < 1 || medium > 3)) { throw new Error('Unsupported exchange medium: ' + medium); } - return {...clientQuery, csr, csr_path, medium: medium as any}; + return { + ...clientQuery, + csr, + csr_path, + medium: transformCertificateExchangeMediumToType(medium), + }; } export function cloneClientQuerySafeForLogging(clientQuery: SecureClientQuery) { return {...clientQuery, csr: !clientQuery.csr ? clientQuery.csr : ''}; } -// TODO: Merge with the same fn in desktop/app/src/utils export function assertNotNull( value: T, message: string = 'Unexpected null/undefined value found', diff --git a/desktop/flipper-server-core/src/comms/WebSocketClientConnection.tsx b/desktop/flipper-server-core/src/app-connectivity/WebSocketClientConnection.tsx similarity index 87% rename from desktop/flipper-server-core/src/comms/WebSocketClientConnection.tsx rename to desktop/flipper-server-core/src/app-connectivity/WebSocketClientConnection.tsx index b31aeb04d..dd6ed69f1 100644 --- a/desktop/flipper-server-core/src/comms/WebSocketClientConnection.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/WebSocketClientConnection.tsx @@ -41,9 +41,11 @@ export default class WebSocketClientConnection implements ClientConnection { const callbacks = this.pendingRequests.get(id); if (!callbacks) { - console.debug(`[conn] Pending request ${id} is not found. Ignore.`); + console.debug(`[ws] Pending request ${id} is not found. Ignore.`); // It must be a response for a message from the older connection. Ignore. - // TODO: When we decide to bump sdk_version, make `id` a string equal to `connectionId:messageId`. Ignore messages only from other conections. Raise an error for missing mesages from this connection. + // TODO: When we decide to bump sdk_version, make `id` a string equal to `connectionId:messageId`. + // Ignore messages only from other conections. + // Raise an error for missing mesages from this connection. return; } diff --git a/desktop/flipper-server-core/src/comms/__tests__/BrowserServerWebSocket.node.tsx b/desktop/flipper-server-core/src/app-connectivity/__tests__/BrowserServerWebSocket.node.tsx similarity index 99% rename from desktop/flipper-server-core/src/comms/__tests__/BrowserServerWebSocket.node.tsx rename to desktop/flipper-server-core/src/app-connectivity/__tests__/BrowserServerWebSocket.node.tsx index 6dbc602e6..702c85e66 100644 --- a/desktop/flipper-server-core/src/comms/__tests__/BrowserServerWebSocket.node.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/__tests__/BrowserServerWebSocket.node.tsx @@ -12,13 +12,13 @@ import { ExecuteMessage, GetPluginsMessage, ResponseMessage, + SecureClientQuery, } from 'flipper-common'; import WebSocket from 'ws'; import {BrowserClientConnection} from '../BrowserClientConnection'; import {getFlipperServerConfig} from '../../FlipperServerConfig'; import BrowserServerWebSocket from '../BrowserServerWebSocket'; -import {SecureClientQuery} from '../ServerAdapter'; import {createMockSEListener, WSMessageAccumulator} from './utils'; jest.mock('../../FlipperServerConfig'); @@ -81,7 +81,7 @@ describe('BrowserServerWebSocket', () => { os, app, sdk_version: sdkVersion, - medium: 3, + medium: 'NONE', }; expect(mockSEListener.onConnectionAttempt).toBeCalledWith( expectedClientQuery, @@ -183,7 +183,7 @@ describe('BrowserServerWebSocket', () => { os: 'MacOS', app: device, sdk_version: 4, - medium: 3, + medium: 'NONE', }; expect(mockSEListener.onConnectionAttempt).toBeCalledWith( expectedClientQuery, diff --git a/desktop/flipper-server-core/src/comms/__tests__/SecureServerWebSocket.node.tsx b/desktop/flipper-server-core/src/app-connectivity/__tests__/SecureServerWebSocket.node.tsx similarity index 96% rename from desktop/flipper-server-core/src/comms/__tests__/SecureServerWebSocket.node.tsx rename to desktop/flipper-server-core/src/app-connectivity/__tests__/SecureServerWebSocket.node.tsx index 1e4c309c7..8b66bc296 100644 --- a/desktop/flipper-server-core/src/comms/__tests__/SecureServerWebSocket.node.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/__tests__/SecureServerWebSocket.node.tsx @@ -12,12 +12,13 @@ import { ExecuteMessage, GetPluginsMessage, ResponseMessage, + SecureClientQuery, } from 'flipper-common'; import {toBase64} from 'js-base64'; import WebSocket from 'ws'; import SecureServerWebSocket from '../SecureServerWebSocket'; -import {SecureClientQuery} from '../ServerAdapter'; +import {transformCertificateExchangeMediumToType} from '../Utilities'; import WebSocketClientConnection from '../WebSocketClientConnection'; import {createMockSEListener, WSMessageAccumulator} from './utils'; @@ -80,7 +81,7 @@ describe('SecureServerWebSocket', () => { sdk_version: sdkVersion, csr, csr_path: csrPath, - medium, + medium: transformCertificateExchangeMediumToType(medium), }; expect(mockSEListener.onSecureConnectionAttempt).toBeCalledWith( expectedClientQuery, diff --git a/desktop/flipper-server-core/src/comms/__tests__/ServerWebSocket.node.tsx b/desktop/flipper-server-core/src/app-connectivity/__tests__/ServerWebSocket.node.tsx similarity index 98% rename from desktop/flipper-server-core/src/comms/__tests__/ServerWebSocket.node.tsx rename to desktop/flipper-server-core/src/app-connectivity/__tests__/ServerWebSocket.node.tsx index 8b2c57a6e..2eaac0f74 100644 --- a/desktop/flipper-server-core/src/comms/__tests__/ServerWebSocket.node.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/__tests__/ServerWebSocket.node.tsx @@ -45,7 +45,7 @@ describe('ServerWebSocket', () => { expect(mockSEListener.onConnectionAttempt).toBeCalledTimes(0); wsClient = new WebSocket( - `ws://localhost:${port}?device_id=${deviceId}&device=${device}&app=${app}&os=${os}&sdk_version=${sdkVersion}`, + `ws://localhost:${port}?device_id=${deviceId}&device=${device}&app=${app}&os=${os}&sdk_version=${sdkVersion}&medium=2`, ); const receivedMessages = new WSMessageAccumulator(); await new Promise((resolve, reject) => { @@ -60,6 +60,7 @@ describe('ServerWebSocket', () => { os, app, sdk_version: sdkVersion, + medium: 'WWW', }; expect(mockSEListener.onConnectionAttempt).toBeCalledWith( expectedClientQuery, diff --git a/desktop/flipper-server-core/src/comms/__tests__/utils.tsx b/desktop/flipper-server-core/src/app-connectivity/__tests__/utils.tsx similarity index 96% rename from desktop/flipper-server-core/src/comms/__tests__/utils.tsx rename to desktop/flipper-server-core/src/app-connectivity/__tests__/utils.tsx index 709fdb7ba..c39e4b13b 100644 --- a/desktop/flipper-server-core/src/comms/__tests__/utils.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/__tests__/utils.tsx @@ -8,7 +8,7 @@ */ import {ClientQuery, ClientDescription} from 'flipper-common'; -import {ServerEventsListener} from '../ServerAdapter'; +import {ServerEventsListener} from '../ServerWebSocketBase'; export class WSMessageAccumulator { private messages: unknown[] = []; diff --git a/desktop/flipper-server-core/src/utils/CertificateProvider.tsx b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/CertificateProvider.tsx similarity index 54% rename from desktop/flipper-server-core/src/utils/CertificateProvider.tsx rename to desktop/flipper-server-core/src/app-connectivity/certificate-exchange/CertificateProvider.tsx index 43f41cbd4..a65523dc6 100644 --- a/desktop/flipper-server-core/src/utils/CertificateProvider.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/CertificateProvider.tsx @@ -7,16 +7,16 @@ * @format */ +import {CertificateExchangeMedium, ClientQuery} from 'flipper-common'; +import {recorder} from '../../recorder'; import { deviceCAcertFile, deviceClientCertFile, ensureOpenSSLIsAvailable, - extractAppNameFromCSR, + extractBundleIdFromCSR, generateClientCertificate, getCACertificate, -} from './certificateUtils'; - -export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW' | 'NONE'; +} from './certificate-utils'; export default abstract class CertificateProvider { abstract medium: CertificateExchangeMedium; @@ -29,43 +29,79 @@ export default abstract class CertificateProvider { } async processCertificateSigningRequest( + clientQuery: ClientQuery, unsanitizedCsr: string, - os: string, appDirectory: string, ): Promise<{deviceId: string}> { const csr = this.santitizeString(unsanitizedCsr); if (csr === '') { - return Promise.reject(new Error(`Received empty CSR from ${os} device`)); + const msg = `Received empty CSR from ${clientQuery.os} device`; + recorder.error(clientQuery, msg); + return Promise.reject(new Error(msg)); } + + recorder.log(clientQuery, 'Ensure OpenSSL is available'); await ensureOpenSSLIsAvailable(); + + recorder.log(clientQuery, 'Obtain CA certificate'); const caCert = await getCACertificate(); + + recorder.log(clientQuery, 'Deploy CA certificate to application sandbox'); await this.deployOrStageFileForDevice( + clientQuery, appDirectory, deviceCAcertFile, caCert, csr, ); + + recorder.log(clientQuery, 'Generate client certificate'); const clientCert = await generateClientCertificate(csr); + + recorder.log( + clientQuery, + 'Deploy client certificate to application sandbox', + ); await this.deployOrStageFileForDevice( + clientQuery, appDirectory, deviceClientCertFile, clientCert, csr, ); - const appName = await extractAppNameFromCSR(csr); - const deviceId = await this.getTargetDeviceId(appName, appDirectory, csr); + + recorder.log(clientQuery, 'Extract application name from CSR'); + const bundleId = await extractBundleIdFromCSR(csr); + + recorder.log( + clientQuery, + 'Get target device from CSR and application name', + ); + const deviceId = await this.getTargetDeviceId( + clientQuery, + bundleId, + appDirectory, + csr, + ); + + recorder.log( + clientQuery, + `Finished processing CSR, device identifier is '${deviceId}'`, + ); return { deviceId, }; } abstract getTargetDeviceId( - _appName: string, - _appDirectory: string, - _csr: string, + clientQuery: ClientQuery, + bundleId: string, + appDirectory: string, + csr: string, ): Promise; protected abstract deployOrStageFileForDevice( + clientQuery: ClientQuery, destination: string, filename: string, contents: string, diff --git a/desktop/flipper-server-core/src/utils/certificateUtils.tsx b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx similarity index 64% rename from desktop/flipper-server-core/src/utils/certificateUtils.tsx rename to desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx index 66b416080..e75f91240 100644 --- a/desktop/flipper-server-core/src/utils/certificateUtils.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/certificate-utils.tsx @@ -9,22 +9,26 @@ import {promisify} from 'util'; import fs from 'fs-extra'; +import os from 'os'; import { openssl, isInstalled as opensslInstalled, } from './openssl-wrapper-with-promises'; import path from 'path'; import tmp, {FileOptions} from 'tmp'; -import {reportPlatformFailures} from 'flipper-common'; +import {FlipperServerConfig, reportPlatformFailures} from 'flipper-common'; import {isTest} from 'flipper-common'; -import {flipperDataFolder} from './paths'; +import {flipperDataFolder} from '../../utils/paths'; +import * as jwt from 'jsonwebtoken'; +import {getFlipperServerConfig} from '../../FlipperServerConfig'; +import {Mutex} from 'async-mutex'; const tmpFile = promisify(tmp.file) as ( options?: FileOptions, ) => Promise; -const getFilePath = (fileName: string): string => { - return path.resolve(flipperDataFolder, 'certs', fileName); +const getFilePath = (filename: string): string => { + return path.resolve(flipperDataFolder, 'certs', filename); }; // Desktop file paths @@ -34,6 +38,7 @@ export const serverKey = getFilePath('server.key'); export const serverCsr = getFilePath('server.csr'); export const serverSrl = getFilePath('server.srl'); export const serverCert = getFilePath('server.crt'); +export const serverAuthToken = getFilePath('auth.token'); // Device file paths export const csrFileName = 'app.csr'; @@ -69,19 +74,32 @@ export const ensureOpenSSLIsAvailable = async (): Promise => { } }; +let serverConfig: SecureServerConfig | undefined; export const loadSecureServerConfig = async (): Promise => { + if (serverConfig) { + return serverConfig; + } + await ensureOpenSSLIsAvailable(); await certificateSetup(); - return { - key: await fs.readFile(serverKey), - cert: await fs.readFile(serverCert), - ca: await fs.readFile(caCert), + await generateAuthToken(); + + const [key, cert, ca] = await Promise.all([ + fs.readFile(serverKey), + fs.readFile(serverCert), + fs.readFile(caCert), + ]); + serverConfig = { + key, + cert, + ca, requestCert: true, rejectUnauthorized: true, // can be false if necessary as we don't strictly need to verify the client }; + return serverConfig; }; -export const extractAppNameFromCSR = async (csr: string): Promise => { +export const extractBundleIdFromCSR = async (csr: string): Promise => { const path = await writeToTempFile(csr); const subject = await openssl('req', { in: path, @@ -136,28 +154,36 @@ const certificateSetup = async () => { } }; +const mutex = new Mutex(); const ensureServerCertExists = async (): Promise => { - const allExist = await Promise.all([ - fs.pathExists(serverKey), - fs.pathExists(serverCert), - fs.pathExists(caCert), - ]).then((exist) => exist.every(Boolean)); - if (!allExist) { - return generateServerCertificate(); - } + return mutex.runExclusive(async () => { + const allExist = await Promise.all([ + fs.pathExists(serverKey), + fs.pathExists(serverCert), + fs.pathExists(caCert), + ]).then((exist) => exist.every(Boolean)); - try { - await checkCertIsValid(serverCert); - await verifyServerCertWasIssuedByCA(); - } catch (e) { - console.warn('Not all certs are valid, generating new ones', e); - await generateServerCertificate(); - } + if (!allExist) { + console.info('No certificates were found, generating new ones'); + await generateServerCertificate(); + } else { + try { + console.info('Checking for certificates validity'); + await checkCertIsValid(serverCert); + console.info('Checking certificate was issued by current CA'); + await verifyServerCertWasIssuedByCA(); + console.info('Current certificates are valid'); + } catch (e) { + console.warn('Not all certificates are valid, generating new ones', e); + await generateServerCertificate(); + } + } + }); }; const generateServerCertificate = async (): Promise => { await ensureCertificateAuthorityExists(); - console.warn('Creating new server cert', logTag); + console.warn('Creating new server certificate'); await openssl('genrsa', {out: serverKey, '2048': false}); await openssl('req', { new: true, @@ -187,7 +213,7 @@ const generateCertificateAuthority = async (): Promise => { if (!(await fs.pathExists(getFilePath('')))) { await fs.mkdir(getFilePath(''), {recursive: true}); } - console.log('Generating new CA', logTag); + console.log('Generating new CA'); await openssl('genrsa', {out: caKey, '2048': false}); await openssl('req', { new: true, @@ -254,3 +280,77 @@ const writeToTempFile = async (content: string): Promise => { await fs.writeFile(path, content); return path; }; + +const manifestFilename = 'manifest.json'; +const getManifestPath = (config: FlipperServerConfig): string => { + return path.resolve(config.paths.staticPath, manifestFilename); +}; + +const exportTokenToManifest = async ( + config: FlipperServerConfig, + token: string, +) => { + const manifestPath = getManifestPath(config); + try { + const manifestData = await fs.readFile(manifestPath, { + encoding: 'utf-8', + }); + const manifest = JSON.parse(manifestData); + manifest.token = token; + + const newManifestData = JSON.stringify(manifest, null, 4); + + await fs.writeFile(manifestPath, newManifestData); + } catch (e) { + console.error( + 'Unable to export authentication token to manifest, may be non existent.', + ); + } +}; + +export const generateAuthToken = async () => { + console.info('Generate client authentication token'); + + await ensureServerCertExists(); + + const config = getFlipperServerConfig(); + + const privateKey = await fs.readFile(serverKey); + const token = jwt.sign({unixname: os.userInfo().username}, privateKey, { + algorithm: 'RS256', + expiresIn: '21 days', + }); + + await fs.writeFile(serverAuthToken, token); + + console.info('Token generated and saved to disk'); + if (config.environmentInfo.isHeadlessBuild) { + console.info('Token exported to manifest'); + await exportTokenToManifest(config, token); + } + + return token; +}; + +export const getAuthToken = async (): Promise => { + if (!(await hasAuthToken())) { + return generateAuthToken(); + } + + const token = await fs.readFile(serverAuthToken); + return token.toString(); +}; + +export const hasAuthToken = async (): Promise => { + return fs.pathExists(serverAuthToken); +}; + +export const validateAuthToken = (token: string) => { + if (!serverConfig) { + throw new Error( + 'Unable to validate auth token as no server configuration is available', + ); + } + + jwt.verify(token, serverConfig.cert); +}; diff --git a/desktop/flipper-server-core/src/utils/openssl-wrapper-with-promises.tsx b/desktop/flipper-server-core/src/app-connectivity/certificate-exchange/openssl-wrapper-with-promises.tsx similarity index 100% rename from desktop/flipper-server-core/src/utils/openssl-wrapper-with-promises.tsx rename to desktop/flipper-server-core/src/app-connectivity/certificate-exchange/openssl-wrapper-with-promises.tsx diff --git a/desktop/flipper-server-core/src/commands/DownloadFile.tsx b/desktop/flipper-server-core/src/commands/DownloadFile.tsx index e6766a27b..c231f4ea4 100644 --- a/desktop/flipper-server-core/src/commands/DownloadFile.tsx +++ b/desktop/flipper-server-core/src/commands/DownloadFile.tsx @@ -11,6 +11,8 @@ import {FlipperServerCommands, FlipperServerEvents, uuid} from 'flipper-common'; import {pathExists} from 'fs-extra'; import {promises, createWriteStream, ReadStream} from 'fs'; import axios from 'axios'; +import http from 'http'; +import https from 'https'; const {unlink} = promises; @@ -43,6 +45,21 @@ export const commandDownloadFileStartFactory = await unlink(dest); } + console.debug('commandDownloadFileStartFactory -> start', { + http: { + usedSockets: Object.keys(http.globalAgent.sockets).length, + freeSocket: Object.keys(http.globalAgent.freeSockets).length, + maxSockets: http.globalAgent.maxSockets, + maxTotalSockets: http.globalAgent.maxTotalSockets, + }, + https: { + usedSockets: Object.keys(https.globalAgent.sockets).length, + freeSocket: Object.keys(https.globalAgent.freeSockets).length, + maxSockets: https.globalAgent.maxSockets, + maxTotalSockets: https.globalAgent.maxTotalSockets, + }, + }); + const downloadId = uuid(); const response = await axios.request({ @@ -84,6 +101,16 @@ export const commandDownloadFileStartFactory = totalSize, status: 'success', }); + console.debug('commandDownloadFileStartFactory -> finish', { + http: { + usedSockets: Object.keys(http.globalAgent.sockets).length, + freeSocket: Object.keys(http.globalAgent.freeSockets).length, + }, + https: { + usedSockets: Object.keys(https.globalAgent.sockets).length, + freeSocket: Object.keys(https.globalAgent.freeSockets).length, + }, + }); }); writeStream.on('error', (e: Error) => { @@ -96,6 +123,16 @@ export const commandDownloadFileStartFactory = message: e.message, stack: e.stack, }); + console.debug('commandDownloadFileStartFactory -> error', { + http: { + usedSockets: Object.keys(http.globalAgent.sockets).length, + freeSocket: Object.keys(http.globalAgent.freeSockets).length, + }, + https: { + usedSockets: Object.keys(https.globalAgent.sockets).length, + freeSocket: Object.keys(https.globalAgent.freeSockets).length, + }, + }); }); return { diff --git a/desktop/flipper-server-core/src/devices/DebuggableDevice.tsx b/desktop/flipper-server-core/src/devices/DebuggableDevice.tsx new file mode 100644 index 000000000..edd6db560 --- /dev/null +++ b/desktop/flipper-server-core/src/devices/DebuggableDevice.tsx @@ -0,0 +1,14 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {DeviceDebugData} from 'flipper-common'; + +export interface DebuggableDevice { + readFlipperFolderForAllApps(): Promise; +} diff --git a/desktop/flipper-server-core/src/devices/DummyDevice.tsx b/desktop/flipper-server-core/src/devices/DummyDevice.tsx index 69d6807f5..5673acc3f 100644 --- a/desktop/flipper-server-core/src/devices/DummyDevice.tsx +++ b/desktop/flipper-server-core/src/devices/DummyDevice.tsx @@ -12,7 +12,12 @@ import {FlipperServerImpl} from '../FlipperServerImpl'; import {ServerDevice} from './ServerDevice'; /** - * Use this device when you do not have the actual uuid of the device. For example, it is currently used in the case when, we do certificate exchange through WWW mode. In this mode we do not know the device id of the app and we generate a fake one. + * Use this device when you do not have the actual uuid of the device. + * For example, it is currently used in the case when, we do certificate + * exchange through WWW mode. + * + * In this mode we do not know the device id of the app and we + * generate a fake one. */ export default class DummyDevice extends ServerDevice { constructor( diff --git a/desktop/flipper-server-core/src/devices/ServerDevice.tsx b/desktop/flipper-server-core/src/devices/ServerDevice.tsx index 89dd1439e..5d65f8454 100644 --- a/desktop/flipper-server-core/src/devices/ServerDevice.tsx +++ b/desktop/flipper-server-core/src/devices/ServerDevice.tsx @@ -58,30 +58,28 @@ export abstract class ServerDevice { } async startScreenCapture(_destination: string): Promise { - throw new Error('startScreenCapture not implemented on BaseDevice '); + throw new Error('startScreenCapture not implemented'); } async stopScreenCapture(): Promise { - throw new Error('stopScreenCapture not implemented on BaseDevice '); + throw new Error('stopScreenCapture not implemented'); } async executeShell(_command: string): Promise { - throw new Error('executeShell not implemented on BaseDevice'); + throw new Error('executeShell not implemented'); } async forwardPort(_local: string, _remote: string): Promise { - throw new Error('forwardPort not implemented on BaseDevice'); + throw new Error('forwardPort not implemented'); } - async clearLogs(): Promise { - // no-op on most devices - } + async clearLogs(): Promise {} async navigateToLocation(_location: string) { - throw new Error('navigateLocation not implemented on BaseDevice'); + throw new Error('navigateLocation not implemented'); } async installApp(_appBundlePath: string): Promise { - throw new Error('Install not implemented'); + throw new Error('installApp not implemented'); } } diff --git a/desktop/flipper-server-core/src/devices/android/AndroidCertificateProvider.tsx b/desktop/flipper-server-core/src/devices/android/AndroidCertificateProvider.tsx index 5ff253eb4..6b8c293b2 100644 --- a/desktop/flipper-server-core/src/devices/android/AndroidCertificateProvider.tsx +++ b/desktop/flipper-server-core/src/devices/android/AndroidCertificateProvider.tsx @@ -7,12 +7,15 @@ * @format */ -import CertificateProvider from '../../utils/CertificateProvider'; +import CertificateProvider from '../../app-connectivity/certificate-exchange/CertificateProvider'; import {Client} from 'adbkit'; import * as androidUtil from './androidContainerUtility'; -import {csrFileName, extractAppNameFromCSR} from '../../utils/certificateUtils'; - -const logTag = 'AndroidCertificateProvider'; +import { + csrFileName, + extractBundleIdFromCSR, +} from '../../app-connectivity/certificate-exchange/certificate-utils'; +import {ClientQuery} from 'flipper-common'; +import {recorder} from '../../recorder'; export default class AndroidCertificateProvider extends CertificateProvider { name = 'AndroidCertificateProvider'; @@ -23,74 +26,92 @@ export default class AndroidCertificateProvider extends CertificateProvider { } async getTargetDeviceId( + clientQuery: ClientQuery, appName: string, appDirectory: string, csr: string, ): Promise { - const devicesInAdb = await this.adb.listDevices(); - if (devicesInAdb.length === 0) { + recorder.log(clientQuery, 'Query available devices via adb'); + const devices = await this.adb.listDevices(); + if (devices.length === 0) { + recorder.error(clientQuery, 'No devices found via adb'); throw new Error('No Android devices found'); } - const deviceMatchList = devicesInAdb.map(async (device) => { + + const deviceMatches = devices.map(async (device) => { try { const result = await this.androidDeviceHasMatchingCSR( appDirectory, device.id, appName, csr, + clientQuery, ); return {id: device.id, ...result, error: null}; } catch (e) { console.warn( - `Unable to check for matching CSR in ${device.id}:${appName}`, - logTag, + `[conn] Unable to check for matching CSR in ${device.id}:${appName}`, e, ); return {id: device.id, isMatch: false, foundCsr: null, error: e}; } }); - const devices = await Promise.all(deviceMatchList); - const matchingIds = devices.filter((m) => m.isMatch).map((m) => m.id); + const matches = await Promise.all(deviceMatches); + const matchingIds = matches.filter((m) => m.isMatch).map((m) => m.id); + if (matchingIds.length == 0) { - const erroredDevice = devices.find((d) => d.error); + recorder.error( + clientQuery, + 'Unable to find a matching device for the incoming request', + ); + + const erroredDevice = matches.find((d) => d.error); if (erroredDevice) { throw erroredDevice.error; } - const foundCsrs = devices + const foundCsrs = matches .filter((d) => d.foundCsr !== null) .map((d) => (d.foundCsr ? encodeURI(d.foundCsr) : 'null')); - console.warn(`Looking for CSR (url encoded): + console.warn( + `[conn] Looking for CSR (url encoded):${encodeURI( + this.santitizeString(csr), + )} Found these:${foundCsrs.join('\n\n')}`, + ); - ${encodeURI(this.santitizeString(csr))} - - Found these: - - ${foundCsrs.join('\n\n')}`); throw new Error(`No matching device found for app: ${appName}`); } if (matchingIds.length > 1) { - console.warn( - new Error('[conn] More than one matching device found for CSR'), - csr, - ); + console.warn(`[conn] Multiple devices found for app: ${appName}`); } return matchingIds[0]; } protected async deployOrStageFileForDevice( + clientQuery: ClientQuery, destination: string, filename: string, contents: string, csr: string, ) { - const appName = await extractAppNameFromCSR(csr); - const deviceId = await this.getTargetDeviceId(appName, destination, csr); + recorder.log( + clientQuery, + `Deploying file '${filename}' to device at '${destination}'`, + ); + + const appName = await extractBundleIdFromCSR(csr); + const deviceId = await this.getTargetDeviceId( + clientQuery, + appName, + destination, + csr, + ); await androidUtil.push( this.adb, deviceId, appName, destination + filename, contents, + clientQuery, ); } @@ -99,12 +120,14 @@ export default class AndroidCertificateProvider extends CertificateProvider { deviceId: string, processName: string, csr: string, + clientQuery: ClientQuery, ): Promise<{isMatch: boolean; foundCsr: string}> { const deviceCsr = await androidUtil.pull( this.adb, deviceId, processName, directory + csrFileName, + clientQuery, ); // Santitize both of the string before comparation // The csr string extraction on client side return string in both way @@ -112,6 +135,7 @@ export default class AndroidCertificateProvider extends CertificateProvider { deviceCsr.toString(), csr, ].map((s) => this.santitizeString(s)); + const isMatch = sanitizedDeviceCsr === sanitizedClientCsr; return {isMatch: isMatch, foundCsr: sanitizedDeviceCsr}; } diff --git a/desktop/flipper-server-core/src/devices/android/AndroidDevice.tsx b/desktop/flipper-server-core/src/devices/android/AndroidDevice.tsx index d3bc7431d..dee90d1e2 100644 --- a/desktop/flipper-server-core/src/devices/android/AndroidDevice.tsx +++ b/desktop/flipper-server-core/src/devices/android/AndroidDevice.tsx @@ -7,10 +7,10 @@ * @format */ -import adb, {Client as ADBClient, PullTransfer} from 'adbkit'; +import adb, {util, Client as ADBClient, PullTransfer} from 'adbkit'; import {Reader} from 'adbkit-logcat'; import {createWriteStream} from 'fs'; -import type {DeviceType} from 'flipper-common'; +import type {DeviceDebugData, DeviceType} from 'flipper-common'; import {spawn} from 'child_process'; import {dirname, join} from 'path'; import {DeviceSpec} from 'flipper-common'; @@ -18,10 +18,15 @@ import {ServerDevice} from '../ServerDevice'; import {FlipperServerImpl} from '../../FlipperServerImpl'; import {AndroidCrashWatcher} from './AndroidCrashUtils'; import {AndroidLogListener} from './AndroidLogListener'; +import {DebuggableDevice} from '../DebuggableDevice'; +import {executeCommandAsApp, pull} from './androidContainerUtility'; const DEVICE_RECORDING_DIR = '/sdcard/flipper_recorder'; -export default class AndroidDevice extends ServerDevice { +export default class AndroidDevice + extends ServerDevice + implements DebuggableDevice +{ adb: ADBClient; pidAppMapping: {[key: number]: string} = {}; private recordingProcess?: Promise; @@ -80,20 +85,20 @@ export default class AndroidDevice extends ServerDevice { ); } - reverse(ports: number[]): Promise { - return Promise.all( + async reverse(ports: number[]): Promise { + await Promise.all( ports.map((port) => this.adb.reverse(this.serial, `tcp:${port}`, `tcp:${port}`), ), - ).then(() => { - return; - }); + ); } - clearLogs(): Promise { - return this.executeShellOrDie(['logcat', '-c']).catch((e) => { + async clearLogs(): Promise { + try { + return await this.executeShellOrDie(['logcat', '-c']); + } catch (e) { console.warn('Failed to clear logs:', e); - }); + } } async navigateToLocation(location: string) { @@ -162,6 +167,17 @@ export default class AndroidDevice extends ServerDevice { } } + async screenShotAvailable(): Promise { + try { + await this.executeShellOrDie( + `[ ! -f /system/bin/screencap ] && echo "File does not exist"`, + ); + return true; + } catch (_e) { + return false; + } + } + async executeShell(command: string): Promise { return await this.adb .shell(this.serial, command) @@ -279,6 +295,118 @@ export default class AndroidDevice extends ServerDevice { console.log(`Installing app with adb ${apkPath}`); await this.adb.install(this.serial, apkPath); } + + async readFlipperFolderForAllApps(): Promise { + console.debug( + 'AndroidDevice.readFlipperFolderForAllApps', + this.info.serial, + ); + const output = await this.adb + .shell(this.info.serial, 'pm list packages -3 -e') + .then(util.readAll) + .then((buffer) => buffer.toString()); + + const appIds = output + .split('\n') + // Each appId has \n at the end. The last appId also has it. + // As a result, there is an "" (empty string) item at the end after the split. + .filter((appId) => appId !== '') + // Cut off the "package:" prefix + .map((appIdRaw) => appIdRaw.substring('package:'.length)); + console.debug( + 'AndroidDevice.readFlipperFolderForAllApps -> found apps', + this.info.serial, + appIds, + ); + + const appsCommandsResults = await Promise.all( + appIds.map(async (appId): Promise => { + const sonarDirFilePaths = await executeCommandAsApp( + this.adb, + this.info.serial, + appId, + `find /data/data/${appId}/files/sonar -type f`, + ) + .then((output) => { + if (output.includes('No such file or directory')) { + console.debug( + 'AndroidDevice.readFlipperFolderForAllApps -> skipping app because sonar dir does not exist', + this.info.serial, + appId, + ); + return; + } + + return ( + output + .split('\n') + // Each entry has \n at the end. The last one also has it. + // As a result, there is an "" (empty string) item at the end after the split. + .filter((appId) => appId !== '') + ); + }) + .catch((e) => { + console.debug( + 'AndroidDevice.readFlipperFolderForAllApps -> failed to fetch sonar dir', + this.info.serial, + appId, + e, + ); + }); + + if (!sonarDirFilePaths) { + return; + } + + const sonarDirContentPromises = sonarDirFilePaths.map( + async (filePath) => { + if (filePath.endsWith('pem')) { + return { + path: filePath, + data: '===SECURE_CONTENT===', + }; + } + return { + path: filePath, + data: await pull( + this.adb, + this.info.serial, + appId, + filePath, + ).catch((e) => `Couldn't pull the file: ${e}`), + }; + }, + ); + + const sonarDirContentWithStatsCommandPromise = executeCommandAsApp( + this.adb, + this.info.serial, + appId, + `ls -al /data/data/${appId}/files/sonar`, + ).then((output): DeviceDebugData['data'][0] => ({ + command: `ls -al /data/data/${appId}/files/sonar`, + result: output, + })); + + const singleAppCommandResults = await Promise.all([ + sonarDirContentWithStatsCommandPromise, + ...sonarDirContentPromises, + ]); + + return { + serial: this.info.serial, + appId, + data: singleAppCommandResults, + }; + }), + ); + + return ( + appsCommandsResults + // Filter out apps without Flipper integration + .filter((res): res is DeviceDebugData => !!res) + ); + } } export async function launchEmulator( diff --git a/desktop/flipper-server-core/src/devices/android/AndroidLogListener.tsx b/desktop/flipper-server-core/src/devices/android/AndroidLogListener.tsx index ee8ed3c6d..aa312059d 100644 --- a/desktop/flipper-server-core/src/devices/android/AndroidLogListener.tsx +++ b/desktop/flipper-server-core/src/devices/android/AndroidLogListener.tsx @@ -63,7 +63,7 @@ export class AndroidLogListener extends DeviceListener { .on('end', () => { if (!gracefulShutdown) { // logs didn't stop gracefully - console.error('Unexpected shutdown of adb logcat'); + console.warn('Unexpected shutdown of adb logcat'); this._state.set( 'fatal', lastKnownError ?? new Error('Unexpected shutdown of adb logcat'), diff --git a/desktop/flipper-server-core/src/devices/android/__tests__/AndroidCrashUtils.node.tsx b/desktop/flipper-server-core/src/devices/android/__tests__/AndroidCrashUtils.node.tsx index 802d28fe6..78333a87e 100644 --- a/desktop/flipper-server-core/src/devices/android/__tests__/AndroidCrashUtils.node.tsx +++ b/desktop/flipper-server-core/src/devices/android/__tests__/AndroidCrashUtils.node.tsx @@ -7,7 +7,7 @@ * @format */ -import {DeviceLogEntry} from 'flipper-plugin'; +import {DeviceLogEntry} from 'flipper-plugin-core'; import {parseAndroidCrash, shouldParseAndroidLog} from '../AndroidCrashUtils'; function getAndroidLog( diff --git a/desktop/flipper-server-core/src/devices/android/__tests__/adbConfig.node.tsx b/desktop/flipper-server-core/src/devices/android/__tests__/adbConfig.node.tsx index 808dedbbe..2f63972c9 100644 --- a/desktop/flipper-server-core/src/devices/android/__tests__/adbConfig.node.tsx +++ b/desktop/flipper-server-core/src/devices/android/__tests__/adbConfig.node.tsx @@ -43,7 +43,7 @@ test('have defaults', () => { process.env.ADB_SERVER_SOCKET = undefined; const {port, host} = adbConfig(); expect(port).toBe(5037); - expect(host).toBe('localhost'); + expect(host).toBe('127.0.0.1'); }); test('prefer settings parameters over ANDROID_ADB_SERVER_PORT', () => { @@ -83,5 +83,5 @@ test('prefer port settings parameters over ANDROID_ADB_SERVER_PORT', () => { process.env.ADB_SERVER_SOCKET = undefined; const {port, host} = adbConfig({port: 1338}); expect(port).toBe(1338); - expect(host).toBe('localhost'); + expect(host).toBe('127.0.0.1'); }); diff --git a/desktop/flipper-server-core/src/devices/android/adbClient.tsx b/desktop/flipper-server-core/src/devices/android/adbClient.tsx index b9c0b96ba..589c64e20 100644 --- a/desktop/flipper-server-core/src/devices/android/adbClient.tsx +++ b/desktop/flipper-server-core/src/devices/android/adbClient.tsx @@ -52,12 +52,20 @@ async function startAdbServer(androidHome: string) { const adbPath = path.resolve(androidHome, 'platform-tools', 'adb'); const args = ['start-server']; - return execFile(adbPath, args).catch((error) => { - if (error.code == 'ENOENT') { - console.info('falling back to the alternative adb path'); - return execFile(path.resolve(androidHome, 'adb'), args); - } + return execFile(adbPath, args) + .catch((error) => { + if (error.code == 'ENOENT') { + console.info('falling back to the alternative adb path'); + return execFile(path.resolve(androidHome, 'adb'), args); + } + throw error; + }) + .catch((error) => { + if (error.code == 'ENOENT') { + console.info('falling back to the adb path of last resort'); + return execFile(androidHome, args); + } - throw error; - }); + throw error; + }); } diff --git a/desktop/flipper-server-core/src/devices/android/adbConfig.tsx b/desktop/flipper-server-core/src/devices/android/adbConfig.tsx index c3dba0016..c52f241c0 100644 --- a/desktop/flipper-server-core/src/devices/android/adbConfig.tsx +++ b/desktop/flipper-server-core/src/devices/android/adbConfig.tsx @@ -15,7 +15,7 @@ export default (settings?: {host?: string; port?: number}) => { 5037, ) as number; - let host = 'localhost'; + let host = '127.0.0.1'; const socket = (process.env.ADB_SERVER_SOCKET || '').trim(); if (socket && socket.length > 0) { diff --git a/desktop/flipper-server-core/src/devices/android/androidContainerUtility.tsx b/desktop/flipper-server-core/src/devices/android/androidContainerUtility.tsx index de658d0c5..3ddac60d9 100644 --- a/desktop/flipper-server-core/src/devices/android/androidContainerUtility.tsx +++ b/desktop/flipper-server-core/src/devices/android/androidContainerUtility.tsx @@ -7,13 +7,15 @@ * @format */ -import {UnsupportedError} from 'flipper-common'; +import {ClientQuery, UnsupportedError} from 'flipper-common'; import adbkit, {Client} from 'adbkit'; +import {recorder} from '../../recorder'; const allowedAppNameRegex = /^[\w.-]+$/; const appNotApplicationRegex = /not an application/; const appNotDebuggableRegex = /debuggable/; const operationNotPermittedRegex = /not permitted/; +const permissionDeniedRegex = /permission denied/; const logTag = 'androidContainerUtility'; export type AppName = string; @@ -22,27 +24,29 @@ export type FilePath = string; export type FileContent = string; export async function push( - client: Client, + adbClient: Client, deviceId: string, app: string, filepath: string, contents: string, + clientQuery?: ClientQuery, ): Promise { validateAppName(app); validateFilePath(filepath); validateFileContent(contents); - return await _push(client, deviceId, app, filepath, contents); + return await _push(adbClient, deviceId, app, filepath, contents, clientQuery); } export async function pull( - client: Client, + adbClient: Client, deviceId: string, app: string, path: string, + clientQuery?: ClientQuery, ): Promise { validateAppName(app); validateFilePath(path); - return await _pull(client, deviceId, app, path); + return await _pull(adbClient, deviceId, app, path, clientQuery); } function validateAppName(app: string): void { @@ -66,6 +70,7 @@ function validateFileContent(content: string): void { enum RunAsErrorCode { NotAnApp = 1, NotDebuggable = 2, + PermissionDenied = 3, } class RunAsError extends Error { @@ -78,54 +83,129 @@ class RunAsError extends Error { } } -function _push( - client: Client, +async function _push( + adbClient: Client, deviceId: string, app: AppName, filename: FilePath, contents: FileContent, + clientQuery?: ClientQuery, ): Promise { console.debug(`Deploying ${filename} to ${deviceId}:${app}`, logTag); - // TODO: this is sensitive to escaping issues, can we leverage client.push instead? - // https://www.npmjs.com/package/adbkit#pushing-a-file-to-all-connected-devices - const command = `echo "${contents}" > '${filename}' && chmod 644 '${filename}'`; - return executeCommandAsApp(client, deviceId, app, command) - .then((_) => undefined) - .catch((error) => { - if (error instanceof RunAsError) { - // Fall back to running the command directly. This will work if adb is running as root. - executeCommandWithSu(client, deviceId, app, command, error); - return undefined; - } - throw error; + + const cmd = `echo "${contents}" > '${filename}' && chmod 644 '${filename}'`; + const description = 'Push file to device using adb shell (echo / chmod)'; + const troubleshoot = 'adb may be unresponsive, try `adb kill-server`'; + + const reportSuccess = () => { + recorder.event('cmd', { + cmd, + description, + troubleshoot, + success: true, + context: clientQuery, }); + }; + const reportFailure = (error: Error) => { + recorder.event('cmd', { + cmd, + description, + troubleshoot, + stdout: error.message, + success: false, + context: clientQuery, + }); + }; + + try { + await executeCommandAsApp(adbClient, deviceId, app, cmd); + reportSuccess(); + } catch (error) { + if (error instanceof RunAsError) { + // Fall back to running the command directly. + // This will work if adb is running as root. + try { + await executeCommandWithSu(adbClient, deviceId, app, cmd, error); + reportSuccess(); + return; + } catch (suError) { + reportFailure(suError); + throw suError; + } + } + reportFailure(error); + throw error; + } } -function _pull( - client: Client, +async function _pull( + adbClient: Client, deviceId: string, app: AppName, path: FilePath, + clientQuery?: ClientQuery, ): Promise { - const command = `cat '${path}'`; - return executeCommandAsApp(client, deviceId, app, command).catch((error) => { + const cmd = `cat '${path}'`; + const description = 'Pull file from device using adb shell (cat)'; + const troubleshoot = 'adb may be unresponsive, try `adb kill-server`'; + + const reportSuccess = () => { + recorder.event('cmd', { + cmd, + description, + troubleshoot, + success: true, + context: clientQuery, + }); + }; + const reportFailure = (error: Error) => { + recorder.event('cmd', { + cmd, + description, + troubleshoot, + stdout: error.message, + success: false, + context: clientQuery, + }); + }; + + try { + const content = await executeCommandAsApp(adbClient, deviceId, app, cmd); + reportSuccess(); + return content; + } catch (error) { if (error instanceof RunAsError) { - // Fall back to running the command directly. This will work if adb is running as root. - return executeCommandWithSu(client, deviceId, app, command, error); + // Fall back to running the command directly. + // This will work if adb is running as root. + try { + const content = await executeCommandWithSu( + adbClient, + deviceId, + app, + cmd, + error, + ); + reportSuccess(); + return content; + } catch (suError) { + reportFailure(suError); + throw suError; + } } + reportFailure(error); throw error; - }); + } } // Keep this method private since it relies on pre-validated arguments -function executeCommandAsApp( - client: Client, +export function executeCommandAsApp( + adbClient: Client, deviceId: string, app: string, command: string, ): Promise { return _executeCommandWithRunner( - client, + adbClient, deviceId, app, command, @@ -134,28 +214,27 @@ function executeCommandAsApp( } async function executeCommandWithSu( - client: Client, + adbClient: Client, deviceId: string, app: string, command: string, originalErrorToThrow: RunAsError, ): Promise { try { - return _executeCommandWithRunner(client, deviceId, app, command, 'su'); + return _executeCommandWithRunner(adbClient, deviceId, app, command, 'su'); } catch (e) { - console.debug(e); throw originalErrorToThrow; } } function _executeCommandWithRunner( - client: Client, + adbClient: Client, deviceId: string, app: string, command: string, runner: string, ): Promise { - return client + return adbClient .shell(deviceId, `echo '${command}' | ${runner}`) .then(adbkit.util.readAll) .then((buffer) => buffer.toString()) @@ -177,6 +256,12 @@ function _executeCommandWithRunner( `Your android device (${deviceId}) does not support the adb shell run-as command. We're tracking this at https://github.com/facebook/flipper/issues/92`, ); } + if (output.toLowerCase().match(permissionDeniedRegex)) { + throw new RunAsError( + RunAsErrorCode.PermissionDenied, + `No permission to run-as application. To use it with Flipper, either run adb as root or allow running as app`, + ); + } return output; }); } diff --git a/desktop/flipper-server-core/src/devices/android/androidDeviceManager.tsx b/desktop/flipper-server-core/src/devices/android/androidDeviceManager.tsx index b42d71c75..337f98672 100644 --- a/desktop/flipper-server-core/src/devices/android/androidDeviceManager.tsx +++ b/desktop/flipper-server-core/src/devices/android/androidDeviceManager.tsx @@ -100,12 +100,10 @@ export class AndroidDeviceManager { // The default way of capturing screenshots through adb does not seem to work // There is a way of getting a screenshot through KaiOS dev tools though if (androidLikeDevice instanceof AndroidDevice) { - const screenRecordAvailable = - await androidLikeDevice.screenRecordAvailable(); androidLikeDevice.info.features.screenCaptureAvailable = - screenRecordAvailable; + await androidLikeDevice.screenRecordAvailable(); androidLikeDevice.info.features.screenshotAvailable = - screenRecordAvailable; + await androidLikeDevice.screenShotAvailable(); } resolve(androidLikeDevice); @@ -122,7 +120,7 @@ export class AndroidDeviceManager { } else { const isAuthorizationError = message.includes('device unauthorized'); if (!isAuthorizationError) { - console.error('Failed to connect to android device', e); + console.warn('Failed to connect to android device', e); } this.flipperServer.emit('notification', { type: 'error', @@ -191,7 +189,24 @@ export class AndroidDeviceManager { }); } - async watchAndroidDevices() { + async watchAndroidDevices(initialRun = false) { + if (initialRun) { + try { + const devices = await this.adbClient.listDevices(); + for (const device of devices) { + if (device.type !== 'offline') { + this.registerDevice(this.adbClient, device); + } else { + this.handleOfflineDevice(device); + } + } + } catch (e) { + console.warn( + `Failed to populate the initial list of android devices: ${e.message}`, + ); + } + } + try { this.adbClient .trackDevices() @@ -214,12 +229,17 @@ export class AndroidDeviceManager { }); tracker.on('add', async (device) => { + // Check if we have already registered this device during the `initialRun` + if (this.flipperServer.hasDevice(device.id)) { + console.debug( + `[conn] Trying to add an existing Android device ${device.id}. Skipping.`, + ); + return; + } if (device.type !== 'offline') { this.registerDevice(this.adbClient, device); } else { - console.warn( - `[conn] Found device ${device.id}, but it has status offline. If this concerns an emulator and the problem persists, try these potential solutions: https://stackoverflow.com/a/21330228/1983583, https://stackoverflow.com/a/56053223/1983583`, - ); + this.handleOfflineDevice(device); } }); @@ -247,6 +267,16 @@ export class AndroidDeviceManager { } } + async adbKill() { + await this.adbClient.kill(); + } + + private handleOfflineDevice(device: Device): void { + console.warn( + `[conn] Found device ${device.id}, but it has status offline. If this concerns an emulator and the problem persists, try these potential solutions: https://stackoverflow.com/a/21330228/1983583, https://stackoverflow.com/a/56053223/1983583`, + ); + } + private async registerDevice(adbClient: ADBClient, deviceData: Device) { const androidDevice = await this.createDevice(adbClient, deviceData); if (!androidDevice) { diff --git a/desktop/flipper-server-core/src/devices/desktop/DesktopCertificateProvider.tsx b/desktop/flipper-server-core/src/devices/desktop/DesktopCertificateProvider.tsx index cbecfaa7d..f9b0782d4 100644 --- a/desktop/flipper-server-core/src/devices/desktop/DesktopCertificateProvider.tsx +++ b/desktop/flipper-server-core/src/devices/desktop/DesktopCertificateProvider.tsx @@ -7,20 +7,27 @@ * @format */ -import CertificateProvider from '../../utils/CertificateProvider'; +import CertificateProvider from '../../app-connectivity/certificate-exchange/CertificateProvider'; import fs from 'fs-extra'; +import {ClientQuery} from 'flipper-common'; export default class DesktopCertificateProvider extends CertificateProvider { name = 'DesktopCertificateProvider'; medium = 'FS_ACCESS' as const; + + /** + * For Desktop devices, we currently return an empty string as the device + * identifier. TODO: Is there an actual device serial we could use instead? + * - What if some app connects from a remote device? + * - What if two apps connect from several different remote devices? + * @returns An empty string. + */ async getTargetDeviceId(): Promise { - // TODO: Could we use some real device serial? Currently, '' corresponds to a local device. - // Whats if some app connects from a remote device? - // What if two apps connect from several different remote devices? return ''; } protected async deployOrStageFileForDevice( + _: ClientQuery, destination: string, filename: string, contents: string, diff --git a/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx b/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx index a8269247c..51f5230f7 100644 --- a/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx +++ b/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx @@ -8,14 +8,19 @@ */ import fs from 'fs-extra'; -import iosUtil from './iOSContainerUtility'; +import iosUtil, { + getDeviceSetPath, + isIdbAvailable, + queryTargetsWithXcode, +} from './iOSContainerUtility'; import child_process from 'child_process'; -import type {IOSDeviceParams} from 'flipper-common'; +import type {DeviceTarget} from 'flipper-common'; import {DeviceType, uuid} from 'flipper-common'; import path from 'path'; -import {exec, execFile} from 'promisify-child-process'; +import {ChildProcessPromise, exec, execFile} from 'promisify-child-process'; import {getFlipperServerConfig} from '../../FlipperServerConfig'; +import iOSContainerUtility from './iOSContainerUtility'; export const ERR_NO_IDB_OR_XCODE_AVAILABLE = 'Neither Xcode nor idb available. Cannot provide iOS device functionality.'; @@ -31,6 +36,16 @@ type iOSSimulatorDevice = { udid: string; }; +// https://fbidb.io/docs/commands#list-apps +interface IOSInstalledAppDescriptor { + bundleID: string; + name: string; + installType: 'user' | 'user_development' | 'system'; + architectures: string[]; + runningStatus: 'Unknown' | 'Running'; + debuggableStatus: boolean; +} + export interface IOSBridge { startLogListener: ( udid: string, @@ -42,12 +57,21 @@ export interface IOSBridge { serial: string, outputFile: string, ) => child_process.ChildProcess; - getActiveDevices: (bootedOnly: boolean) => Promise>; + getActiveDevices: (bootedOnly: boolean) => Promise>; installApp: ( serial: string, ipaPath: string, tempPath: string, ) => Promise; + getInstalledApps: (serial: string) => Promise; + ls: (serial: string, appBundleId: string, path: string) => Promise; + pull: ( + serial: string, + src: string, + bundleId: string, + dst: string, + ) => Promise; + launchSimulator(udid: string): Promise; } export class IDBBridge implements IOSBridge { @@ -55,17 +79,81 @@ export class IDBBridge implements IOSBridge { private idbPath: string, private enablePhysicalDevices: boolean, ) {} + async launchSimulator(udid: string): Promise { + await this._execIdb(`boot --udid ${udid}`); + await execFile('open', ['-a', 'simulator']); + } + + async getInstalledApps(serial: string): Promise { + const {stdout} = await this._execIdb(`list-apps --udid ${serial}`); + if (typeof stdout !== 'string') { + throw new Error( + `IDBBridge.getInstalledApps -> returned ${typeof stdout}, not a string`, + ); + } + // Skip last item, as the last line also has \n at the end + const appStrings = stdout.split('\n').slice(0, -1); + const appDescriptors = appStrings.map( + (appString): IOSInstalledAppDescriptor => { + const [ + bundleID, + name, + installType, + architecturesString, + runningStatus, + debuggableStatusString, + ] = appString.split(' | '); + return { + bundleID, + name, + installType: installType as IOSInstalledAppDescriptor['installType'], + architectures: architecturesString.split(', '), + runningStatus: + runningStatus as IOSInstalledAppDescriptor['runningStatus'], + debuggableStatus: debuggableStatusString !== 'Not Debuggable', + }; + }, + ); + return appDescriptors; + } + + async ls( + serial: string, + appBundleId: string, + path: string, + ): Promise { + const {stdout} = await this._execIdb( + `file ls --udid ${serial} --log ERROR --bundle-id ${appBundleId} '${path}'`, + ); + if (typeof stdout !== 'string') { + throw new Error( + `IDBBridge.ls -> returned ${typeof stdout}, not a string`, + ); + } + // Skip last item, as the last line also has \n at the end + const pathContent = stdout.split('\n').slice(0, -1); + return pathContent; + } + + pull( + serial: string, + src: string, + bundleId: string, + dst: string, + ): Promise { + return iOSContainerUtility.pull(serial, src, bundleId, dst, this.idbPath); + } async installApp(serial: string, ipaPath: string): Promise { console.log(`Installing app via IDB ${ipaPath} ${serial}`); await this._execIdb(`install ${ipaPath} --udid ${serial}`); } - async getActiveDevices(_bootedOnly: boolean): Promise { + async getActiveDevices(bootedOnly: boolean): Promise { return iosUtil - .targets(this.idbPath, this.enablePhysicalDevices) + .targets(this.idbPath, this.enablePhysicalDevices, bootedOnly) .catch((e) => { - console.error('Failed to get active iOS devices:', e.message); + console.warn('Failed to get active iOS devices:', e.message); return []; }); } @@ -100,12 +188,35 @@ export class IDBBridge implements IOSBridge { ); } - _execIdb(command: string): child_process.ChildProcess { + _execIdb(command: string): ChildProcessPromise { return exec(`${this.idbPath} ${command}`); } } export class SimctlBridge implements IOSBridge { + pull( + serial: string, + src: string, + bundleId: string, + dst: string, + ): Promise { + return iOSContainerUtility.pull(serial, src, bundleId, dst, ''); + } + + async getInstalledApps( + _serial: string, + ): Promise { + throw new Error( + 'SimctlBridge does not support getInstalledApps. Install idb (https://fbidb.io/).', + ); + } + + async ls(_serial: string, _appBundleId: string): Promise { + throw new Error( + 'SimctlBridge does not support ls. Install idb (https://fbidb.io/).', + ); + } + async installApp( serial: string, ipaPath: string, @@ -173,30 +284,11 @@ export class SimctlBridge implements IOSBridge { ); } - async getActiveDevices(bootedOnly: boolean): Promise> { - return execFile('xcrun', [ - 'simctl', - ...getDeviceSetPath(), - 'list', - 'devices', - '--json', - ]) - .then(({stdout}) => JSON.parse(stdout!.toString()).devices) - .then((simulatorDevices: Array) => { - const simulators = Object.values(simulatorDevices).flat(); - return simulators - .filter( - (simulator) => - (!bootedOnly || simulator.state === 'Booted') && - isSimulatorAvailable(simulator), - ) - .map((simulator) => { - return { - ...simulator, - type: 'emulator', - } as IOSDeviceParams; - }); - }); + async getActiveDevices(bootedOnly: boolean): Promise> { + const devices = await queryTargetsWithXcode(); + return devices.filter( + (target) => !bootedOnly || (bootedOnly && target.state === 'booted'), + ); } async launchSimulator(udid: string): Promise { @@ -205,27 +297,6 @@ export class SimctlBridge implements IOSBridge { } } -function isSimulatorAvailable(simulator: iOSSimulatorDevice): boolean { - // For some users "availability" is set, for others it's "isAvailable" - // It's not clear which key is set, so we are checking both. - // We've also seen isAvailable return "YES" and true, depending on version. - return ( - simulator.availability === '(available)' || - simulator.isAvailable === 'YES' || - simulator.isAvailable === true - ); -} - -async function isAvailable(idbPath: string): Promise { - if (!idbPath) { - return false; - } - return fs.promises - .access(idbPath, fs.constants.X_OK) - .then((_) => true) - .catch((_) => false); -} - function getLogExtraArgs(deviceType: DeviceType) { if (deviceType === 'physical') { return [ @@ -250,7 +321,6 @@ function makeTempScreenshotFilePath() { } async function unzip(filePath: string, destination: string): Promise { - //todo this probably shouldn't involve shelling out... await exec(`unzip -qq -o ${filePath} -d ${destination}`); if (!(await fs.pathExists(path.join(destination, 'Payload')))) { throw new Error( @@ -265,24 +335,17 @@ async function readScreenshotIntoBuffer(imagePath: string): Promise { return buffer; } -export function getDeviceSetPath() { - return process.env.DEVICE_SET_PATH - ? ['--set', process.env.DEVICE_SET_PATH] - : []; -} - export async function makeIOSBridge( idbPath: string, isXcodeDetected: boolean, enablePhysicalDevices: boolean, - isAvailableFn: (idbPath: string) => Promise = isAvailable, + isAvailable: (idbPath: string) => Promise = isIdbAvailable, ): Promise { - // prefer idb - if (await isAvailableFn(idbPath)) { + if (await isAvailable(idbPath)) { return new IDBBridge(idbPath, enablePhysicalDevices); } - // no idb, if it's a simulator and xcode is available, we can use xcrun + // If Xcode is available then xcrun instead of idb is used. if (isXcodeDetected) { return new SimctlBridge(); } diff --git a/desktop/flipper-server-core/src/devices/ios/IOSDevice.tsx b/desktop/flipper-server-core/src/devices/ios/IOSDevice.tsx index 0123a8dfc..3e88ad3b8 100644 --- a/desktop/flipper-server-core/src/devices/ios/IOSDevice.tsx +++ b/desktop/flipper-server-core/src/devices/ios/IOSDevice.tsx @@ -7,15 +7,25 @@ * @format */ -import {DeviceType, timeout} from 'flipper-common'; +import {DeviceDebugData, DeviceType, timeout} from 'flipper-common'; import {ChildProcess} from 'child_process'; import {IOSBridge} from './IOSBridge'; import {ServerDevice} from '../ServerDevice'; import {FlipperServerImpl} from '../../FlipperServerImpl'; import {iOSCrashWatcher} from './iOSCrashUtils'; import {iOSLogListener} from './iOSLogListener'; +import {DebuggableDevice} from '../DebuggableDevice'; +import tmp, {DirOptions} from 'tmp'; +import {promisify} from 'util'; +import path from 'path'; +import {readFile} from 'fs/promises'; -export default class IOSDevice extends ServerDevice { +const tmpDir = promisify(tmp.dir) as (options?: DirOptions) => Promise; + +export default class IOSDevice + extends ServerDevice + implements DebuggableDevice +{ private recording?: {process: ChildProcess; destination: string}; private iOSBridge: IOSBridge; readonly logListener: iOSLogListener; @@ -48,14 +58,16 @@ export default class IOSDevice extends ServerDevice { this.serial, this.info.deviceType, ); - // It is OK not to await the start of the log listener. We just spawn it and handle errors internally. + // It is OK not to await the start of the log listener. + // We just spawn it and handle errors internally. this.logListener .start() .catch((e) => console.error('IOSDevice.logListener.start -> unexpected error', e), ); this.crashWatcher = new iOSCrashWatcher(this); - // It is OK not to await the start of the crash watcher. We just spawn it and handle errors internally. + // It is OK not to await the start of the crash watcher. + // We just spawn it and handle errors internally. this.crashWatcher .start() .catch((e) => @@ -128,6 +140,119 @@ export default class IOSDevice extends ServerDevice { ); } + async readFlipperFolderForAllApps(): Promise { + console.debug('IOSDevice.readFlipperFolderForAllApps', this.info.serial); + const installedApps = await this.iOSBridge.getInstalledApps( + this.info.serial, + ); + const userApps = installedApps.filter( + ({installType}) => + installType === 'user' || installType === 'user_development', + ); + console.debug( + 'IOSDevice.readFlipperFolderForAllApps -> found apps', + this.info.serial, + userApps, + ); + + const appsCommandsResults = await Promise.all( + userApps.map(async (userApp): Promise => { + let sonarDirFileNames: string[]; + try { + sonarDirFileNames = await this.iOSBridge.ls( + this.info.serial, + userApp.bundleID, + '/Library/Application Support/sonar', + ); + } catch (e) { + console.debug( + 'IOSDevice.readFlipperFolderForAllApps -> ignoring app as it does not have sonar dir', + this.info.serial, + userApp.bundleID, + ); + return; + } + + const dir = await tmpDir({unsafeCleanup: true}); + + const sonarDirContent = await Promise.all( + sonarDirFileNames.map(async (fileName) => { + const filePath = `/Library/Application Support/sonar/${fileName}`; + + if (fileName.endsWith('pem')) { + return { + path: filePath, + data: '===SECURE_CONTENT===', + }; + } + try { + // See iOSCertificateProvider to learn why we need 2 pulls + try { + await this.iOSBridge.pull( + this.info.serial, + filePath, + userApp.bundleID, + dir, + ); + } catch (e) { + console.debug( + 'IOSDevice.readFlipperFolderForAllApps -> Original idb pull failed. Most likely it is a physical device that requires us to handle the dest path dirrently. Forcing a re-try with the updated dest path. See D32106952 for details. Original error:', + this.info.serial, + userApp.bundleID, + fileName, + filePath, + e, + ); + await this.iOSBridge.pull( + this.info.serial, + filePath, + userApp.bundleID, + path.join(dir, fileName), + ); + console.debug( + 'IOSDevice.readFlipperFolderForAllApps -> Subsequent idb pull succeeded. Nevermind previous warnings.', + this.info.serial, + userApp.bundleID, + fileName, + filePath, + ); + } + return { + path: filePath, + data: await readFile(path.join(dir, fileName), { + encoding: 'utf-8', + }), + }; + } catch (e) { + return { + path: filePath, + data: `Couldn't pull the file: ${e}`, + }; + } + }), + ); + + return { + serial: this.info.serial, + appId: userApp.bundleID, + data: [ + { + command: 'iOSBridge.ls /Library/Application Support/sonar', + result: sonarDirFileNames.join('\n'), + }, + ...sonarDirContent, + ], + }; + }), + ); + + return ( + appsCommandsResults + // Filter out apps without Flipper integration + .filter((res): res is DeviceDebugData => !!res) + ); + } + disconnect() { if (this.recording) { this.stopScreenCapture(); diff --git a/desktop/flipper-server-core/src/devices/ios/__tests__/iOSContainerUtility.node.tsx b/desktop/flipper-server-core/src/devices/ios/__tests__/iOSContainerUtility.node.tsx deleted file mode 100644 index bfe144838..000000000 --- a/desktop/flipper-server-core/src/devices/ios/__tests__/iOSContainerUtility.node.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import {queryTargetsWithoutXcodeDependency} from '../iOSContainerUtility'; - -test('uses idbcompanion command for queryTargetsWithoutXcodeDependency', async () => { - const mockedExec = jest.fn((_) => - Promise.resolve({ - stdout: '{"udid": "udid", "type": "physical", "name": "name"}', - stderr: '{ "msg": "mocked stderr"}', - }), - ); - await queryTargetsWithoutXcodeDependency( - 'idbCompanionPath', - true, - (_) => Promise.resolve(true), - mockedExec, - ); - - expect(mockedExec).toBeCalledWith('idbCompanionPath --list 1 --only device'); -}); - -test('do not call idbcompanion if the path does not exist', async () => { - const mockedExec = jest.fn((_) => - Promise.resolve({ - stdout: '{"udid": "udid", "type": "physical", "name": "name"}', - stderr: '{"msg": "mocked stderr"}', - }), - ); - await queryTargetsWithoutXcodeDependency( - 'idbCompanionPath', - true, - (_) => Promise.resolve(false), - mockedExec, - ); - expect(mockedExec).toHaveBeenCalledTimes(0); -}); diff --git a/desktop/flipper-server-core/src/devices/ios/__tests__/iOSDevice.node.tsx b/desktop/flipper-server-core/src/devices/ios/__tests__/iOSDevice.node.tsx index 307053f47..6db146a50 100644 --- a/desktop/flipper-server-core/src/devices/ios/__tests__/iOSDevice.node.tsx +++ b/desktop/flipper-server-core/src/devices/ios/__tests__/iOSDevice.node.tsx @@ -14,21 +14,23 @@ import { getFlipperServerConfig, setFlipperServerConfig, } from '../../../FlipperServerConfig'; -import {IOSDeviceParams} from 'flipper-common'; +import {DeviceTarget} from 'flipper-common'; let fakeSimctlBridge: any; let fakeIDBBridge: any; let fakeFlipperServer: any; -const fakeDevices: IOSDeviceParams[] = [ +const fakeDevices: DeviceTarget[] = [ { udid: 'luke', type: 'emulator', name: 'Luke', + osVersion: '16.4', }, { udid: 'yoda', type: 'emulator', name: 'Yoda', + osVersion: '16.4', }, ]; const fakeExistingDevices = [ @@ -120,7 +122,7 @@ test('test queryDevices when simctl used', async () => { fakeFlipperServer, getFlipperServerConfig().settings, ); - ios.simctlBridge = fakeSimctlBridge; + ios.ctlBridge = fakeSimctlBridge; await ios.queryDevices(fakeSimctlBridge); @@ -143,7 +145,7 @@ test('test queryDevices when idb used', async () => { fakeFlipperServer, getFlipperServerConfig().settings, ); - ios.simctlBridge = fakeSimctlBridge; + ios.ctlBridge = fakeSimctlBridge; await ios.queryDevices(fakeIDBBridge); diff --git a/desktop/flipper-server-core/src/devices/ios/iOSCertificateProvider.tsx b/desktop/flipper-server-core/src/devices/ios/iOSCertificateProvider.tsx index e3d8dc48f..5e8b01c4a 100644 --- a/desktop/flipper-server-core/src/devices/ios/iOSCertificateProvider.tsx +++ b/desktop/flipper-server-core/src/devices/ios/iOSCertificateProvider.tsx @@ -7,13 +7,18 @@ * @format */ -import CertificateProvider from '../../utils/CertificateProvider'; +import CertificateProvider from '../../app-connectivity/certificate-exchange/CertificateProvider'; import iosUtil, {IdbConfig} from './iOSContainerUtility'; import fs from 'fs-extra'; import {promisify} from 'util'; import tmp, {DirOptions} from 'tmp'; -import {csrFileName, extractAppNameFromCSR} from '../../utils/certificateUtils'; +import { + csrFileName, + extractBundleIdFromCSR, +} from '../../app-connectivity/certificate-exchange/certificate-utils'; import path from 'path'; +import {ClientQuery} from 'flipper-common'; +import {recorder} from '../../recorder'; const tmpDir = promisify(tmp.dir) as (options?: DirOptions) => Promise; @@ -28,25 +33,32 @@ export default class iOSCertificateProvider extends CertificateProvider { } async getTargetDeviceId( + clientQuery: ClientQuery, appName: string, appDirectory: string, csr: string, ): Promise { const matches = /\/Devices\/([^/]+)\//.exec(appDirectory); if (matches && matches.length == 2) { - // It's a simulator, the deviceId is in the filepath. + // It's a simulator, the device identifier is in the filepath. return matches[1]; } + + recorder.log(clientQuery, 'Query available devices'); const targets = await iosUtil.targets( this.idbConfig.idbPath, this.idbConfig.enablePhysicalIOS, + true, + clientQuery, ); if (targets.length === 0) { + recorder.error(clientQuery, 'No devices found'); throw new Error('No iOS devices found'); } const deviceMatchList = targets.map(async (target) => { try { const isMatch = await this.iOSDeviceHasMatchingCSR( + clientQuery, appDirectory, target.udid, appName, @@ -54,8 +66,12 @@ export default class iOSCertificateProvider extends CertificateProvider { ); return {id: target.udid, isMatch}; } catch (e) { - console.info( - `Unable to check for matching CSR in ${target.udid}:${appName}`, + recorder.error( + clientQuery, + 'Unable to find a matching device for the incoming request', + ); + console.warn( + `[conn] Unable to check for matching CSR in ${target.udid}:${appName}`, logTag, e, ); @@ -68,28 +84,40 @@ export default class iOSCertificateProvider extends CertificateProvider { throw new Error(`No matching device found for app: ${appName}`); } if (matchingIds.length > 1) { - console.warn(`Multiple devices found for app: ${appName}`); + console.warn(`[conn] Multiple devices found for app: ${appName}`); } return matchingIds[0]; } protected async deployOrStageFileForDevice( + clientQuery: ClientQuery, destination: string, filename: string, contents: string, csr: string, ) { - const appName = await extractAppNameFromCSR(csr); + recorder.log( + clientQuery, + `Deploying file '${filename}' to device at '${destination}'`, + ); + + const bundleId = await extractBundleIdFromCSR(csr); try { await fs.writeFile(destination + filename, contents); } catch (err) { - // Writing directly to FS failed. It's probably a physical device. const relativePathInsideApp = this.getRelativePathInAppContainer(destination); - const udid = await this.getTargetDeviceId(appName, destination, csr); + + const udid = await this.getTargetDeviceId( + clientQuery, + bundleId, + destination, + csr, + ); await this.pushFileToiOSDevice( + clientQuery, udid, - appName, + bundleId, relativePathInsideApp, filename, contents, @@ -106,6 +134,7 @@ export default class iOSCertificateProvider extends CertificateProvider { } private async pushFileToiOSDevice( + clientQuery: ClientQuery, udid: string, bundleId: string, destination: string, @@ -113,88 +142,78 @@ export default class iOSCertificateProvider extends CertificateProvider { contents: string, ): Promise { const dir = await tmpDir({unsafeCleanup: true}); - const filePath = path.resolve(dir, filename); - await fs.writeFile(filePath, contents); + const src = path.resolve(dir, filename); + await fs.writeFile(src, contents); await iosUtil.push( udid, - filePath, + src, bundleId, destination, this.idbConfig.idbPath, + clientQuery, ); } private async iOSDeviceHasMatchingCSR( + clientQuery: ClientQuery, directory: string, deviceId: string, bundleId: string, csr: string, ): Promise { - const originalFile = this.getRelativePathInAppContainer( + const src = this.getRelativePathInAppContainer( path.resolve(directory, csrFileName), ); - const dir = await tmpDir({unsafeCleanup: true}); + const dst = await tmpDir({unsafeCleanup: true}); - // Workaround for idb weirdness - // Originally started at D27590885 - // Re-appared at https://github.com/facebook/flipper/issues/3009 - // - // People reported various workarounds. None of them worked consistently for everyone. - // Usually, the workarounds included re-building idb from source or re-installing it. - // - // The only more or less reasonable explanation I was able to find is that the final behavior depends on whether the idb_companion is local or not. - // - // This is how idb_companion sets its locality - // https://github.com/facebook/idb/blob/main/idb_companion/Server/FBIDBServiceHandler.mm#L1507 - // idb sends a connection request and provides a file path to a temporary file. idb_companion checks if it can access that file. - // - // So when it is "local", the pulled filed is written directly to the destination path - // https://github.com/facebook/idb/blob/main/idb/grpc/client.py#L698 - // So it is expected that the destination path ends with a file to write to. - // However, if the companion is remote, then we seem to get here https://github.com/facebook/idb/blob/71791652efa2d5e6f692cb8985ff0d26b69bf08f/idb/common/tar.py#L232 - // Where we create a tree of directories and write the file stream there. - // - // So the only explanation I could come up with is that somehow, by re-installing idb and playing with the env, people could affect the locality of the idb_companion. - // - // The ultimate workaround is to try pulling the cert file without the cert name attached first, if it fails, try to append it. try { await iosUtil.pull( deviceId, - originalFile, + src, bundleId, - dir, + dst, this.idbConfig.idbPath, + clientQuery, ); } catch (e) { - console.warn( - 'Original idb pull failed. Most likely it is a physical device that requires us to handle the dest path dirrently. Forcing a re-try with the updated dest path. See D32106952 for details. Original error:', + recorder.log( + clientQuery, + `Original idb pull failed. Most likely it is a physical device + that requires us to handle the dest path dirrently. + Forcing a re-try with the updated dest path. See D32106952 for details.`, e, ); await iosUtil.pull( deviceId, - originalFile, + src, bundleId, - path.join(dir, csrFileName), + path.join(dst, csrFileName), this.idbConfig.idbPath, + clientQuery, ); - console.info( - 'Subsequent idb pull succeeded. Nevermind previous wranings.', + recorder.log( + clientQuery, + 'Subsequent idb pull succeeded. Nevermind previous warnings.', ); } - const items = await fs.readdir(dir); + const items = await fs.readdir(dst); if (items.length > 1) { - throw new Error('Conflict in temp dir'); + throw new Error('Conflict in temporary directory'); } if (items.length === 0) { - throw new Error('Failed to pull CSR from device'); + throw new Error('No CSR found on device'); } - const fileName = items[0]; - const copiedFile = path.resolve(dir, fileName); - console.debug('Trying to read CSR from', copiedFile); - const data = await fs.readFile(copiedFile); + + const filename = items[0]; + const filepath = path.resolve(dst, filename); + + recorder.log(clientQuery, `Read CSR from: '${filepath}'`); + + const data = await fs.readFile(filepath); const csrFromDevice = this.santitizeString(data.toString()); + return csrFromDevice === this.santitizeString(csr); } } diff --git a/desktop/flipper-server-core/src/devices/ios/iOSContainerUtility.tsx b/desktop/flipper-server-core/src/devices/ios/iOSContainerUtility.tsx index 1c58eb0d9..ebec6222d 100644 --- a/desktop/flipper-server-core/src/devices/ios/iOSContainerUtility.tsx +++ b/desktop/flipper-server-core/src/devices/ios/iOSContainerUtility.tsx @@ -8,15 +8,14 @@ */ import {Mutex} from 'async-mutex'; -import {exec as unsafeExec, Output} from 'promisify-child-process'; -import {reportPlatformFailures} from 'flipper-common'; +import {exec as unsafeExec, Output, execFile} from 'promisify-child-process'; +import {DeviceTarget, DeviceType, reportPlatformFailures} from 'flipper-common'; import {promises, constants} from 'fs'; import memoize from 'lodash.memoize'; -import {notNull} from '../../utils/typeUtils'; import {promisify} from 'util'; import child_process from 'child_process'; import fs from 'fs-extra'; -import path from 'path'; +import {recorder} from '../../recorder'; const exec = promisify(child_process.exec); export type IdbConfig = { @@ -24,88 +23,265 @@ export type IdbConfig = { enablePhysicalIOS: boolean; }; +export type IdbTarget = { + udid: string; + type: string; + name: string; + os_version: string; + architecture: string; + state?: string; + target_type?: string | DeviceType; +}; + +export type XcodeTarget = { + state: 'Booted' | 'Shutdown' | 'Shutting Down'; + availability?: string; + isAvailable?: 'YES' | 'NO' | true | false; + name: string; + osVersion?: string; + udid: string; +}; + // Use debug to get helpful logs when idb fails -const idbLogLevel = 'DEBUG'; -const operationPrefix = 'iosContainerUtility'; +const IDB_LOG_LEVEL = 'DEBUG'; +const LOG_TAG = 'iOSContainerUtility'; +const CMD_RECORD_THROTTLE_COUNT = 10; const mutex = new Mutex(); -type IdbTarget = { - name: string; - udid: string; - state: 'Booted' | 'Shutdown'; - type: string | DeviceType; - target_type?: string | DeviceType; - os_version: string; - architecture: string; -}; +let idbDeviceListing = 0; +let idbCompanionDeviceListing = 0; +let xcodeDeviceListing = 0; -export type DeviceType = 'physical' | 'emulator'; - -export type DeviceTarget = { - udid: string; - type: DeviceType; - name: string; -}; - -function isAvailable(idbPath: string): Promise { +export async function isIdbAvailable(idbPath: string): Promise { if (!idbPath) { - return Promise.resolve(false); + return false; } - return promises - .access(idbPath, constants.X_OK) - .then((_) => true) - .catch((_) => false); + try { + await promises.access(idbPath, constants.X_OK); + } catch (e) { + return false; + } + return true; } -function safeExec( +async function safeExec( command: string, ): Promise<{stdout: string; stderr: string} | Output> { - return mutex - .acquire() - .then((release) => unsafeExec(command).finally(release)); + const release = await mutex.acquire(); + return await unsafeExec(command).finally(release); } -export async function queryTargetsWithoutXcodeDependency( +export function getDeviceSetPath() { + return process.env.DEVICE_SET_PATH + ? ['--set', process.env.DEVICE_SET_PATH] + : []; +} + +export function isSimulatorAvailable(simulator: XcodeTarget): boolean { + // For some users "availability" is set, for others it's "isAvailable" + // It's not clear which key is set, so we are checking both. + // We've also seen isAvailable return "YES" and true, depending on version. + return ( + simulator.availability === '(available)' || + simulator.isAvailable === 'YES' || + simulator.isAvailable === true + ); +} + +function getOSVersionFromXCRunOutput(s: string): string | undefined { + // E.g. 'com.apple.CoreSimulator.SimRuntime.iOS-16-1' + const match = s.match( + /com\.apple\.CoreSimulator\.SimRuntime\.iOS-(\d+)-(\d+)/, + ); + if (match) { + return `${match[1]}.${match[2]}`; + } +} + +export async function queryTargetsWithXcode( + context?: any, +): Promise> { + const cmd = 'xcrun simctl list devices --json'; + const description = 'Query available devices with Xcode'; + const troubleshoot = `Xcode command line tools are not installed. + Run 'xcode-select --install' from terminal.`; + + try { + const {stdout} = await execFile('xcrun', [ + 'simctl', + ...getDeviceSetPath(), + 'list', + 'devices', + '--json', + ]); + + if (!stdout) { + recorder.event('cmd', { + cmd, + description, + success: false, + troubleshoot, + context, + }); + throw new Error('No output from command'); + } + + xcodeDeviceListing++; + if (xcodeDeviceListing % CMD_RECORD_THROTTLE_COUNT === 0) { + recorder.event('cmd', { + cmd, + description, + success: true, + context, + }); + } + + const devices = JSON.parse(stdout.toString()).devices as { + [key: string]: Array; + }; + + return Object.keys(devices).flatMap((key: string) => + devices[key] + .filter((simulator: XcodeTarget) => isSimulatorAvailable(simulator)) + .map((simulator: XcodeTarget) => { + return { + ...simulator, + type: 'emulator', + state: simulator.state.toLowerCase(), + osVersion: getOSVersionFromXCRunOutput(key), + } as DeviceTarget; + }), + ); + } catch (e) { + recorder.event('cmd', { + cmd, + description, + success: false, + troubleshoot, + stderr: e.toString(), + context, + }); + return []; + } +} + +async function queryTargetsWithIdb( + idbPath: string, + context: any, +): Promise> { + const cmd = `${idbPath} list-targets --json`; + const description = `Query available devices with idb. idb is aware of the companions that you have + manually connected, as well as other iOS targets that do not yet have companions.`; + const troubleshoot = `Either idb is not installed or needs to be reset. + Run 'idb kill' from terminal.`; + + try { + const {stdout} = await unsafeExec(cmd); + + if (!stdout) { + recorder.event('cmd', { + cmd, + description, + success: false, + troubleshoot, + context, + }); + throw new Error('No output from command'); + } + + idbDeviceListing++; + if (idbDeviceListing % CMD_RECORD_THROTTLE_COUNT === 0) { + recorder.event('cmd', { + cmd, + description, + success: true, + context, + }); + } + + return parseIdbTargets(stdout.toString()); + } catch (e) { + recorder.event('cmd', { + cmd, + description, + success: false, + troubleshoot, + stderr: e.toString(), + context, + }); + return []; + } +} + +async function _queryTargetsWithIdbCompanion( idbCompanionPath: string, isPhysicalDeviceEnabled: boolean, - isAvailableFunc: (idbPath: string) => Promise, - safeExecFunc: ( - command: string, - ) => Promise<{stdout: string; stderr: string} | Output>, + context: any, ): Promise> { - if (await isAvailableFunc(idbCompanionPath)) { - return safeExecFunc(`${idbCompanionPath} --list 1 --only device`) - .then(({stdout}) => parseIdbTargets(stdout!.toString())) - .then((devices) => { - if (devices.length > 0 && !isPhysicalDeviceEnabled) { - // TODO: Show a notification to enable the toggle or integrate Doctor to better suggest this advice. - console.warn( - 'You are trying to connect Physical Device. Please enable the toggle "Enable physical iOS device" from the setting screen.', - ); - } - return devices; - }) - .catch((e: Error) => { - console.warn( - 'Failed to query idb_companion --list 1 --only device for physical targets:', - e, + const cmd = `${idbCompanionPath} --list 1 --only device`; + const description = `Query available devices with idb companion. Lists all available devices and simulators + in the current context. If Xcode is not correctly installed, only devices will be listed.`; + const troubleshoot = `Unable to locate idb_companion in '${idbCompanionPath}'. + Try running sudo yum install -y fb-idb`; + + if (await isIdbAvailable(idbCompanionPath)) { + try { + const {stdout} = await safeExec(cmd); + if (!stdout) { + recorder.event('cmd', { + cmd, + description, + success: false, + troubleshoot, + context, + }); + throw new Error('No output from command'); + } + + idbCompanionDeviceListing++; + if (idbCompanionDeviceListing % CMD_RECORD_THROTTLE_COUNT === 0) { + recorder.event('cmd', { + cmd, + description, + success: true, + context, + }); + } + + const devices = parseIdbTargets(stdout.toString()); + if (devices.length > 0 && !isPhysicalDeviceEnabled) { + recorder.rawError( + `You are trying to connect Physical Device. + Please enable the toggle "Enable physical iOS device" from the setting screen.`, ); - return []; + } + return devices; + } catch (e) { + recorder.event('cmd', { + cmd, + description, + success: false, + troubleshoot, + stderr: e.toString(), + context, }); + return []; + } } else { - console.warn( - `Unable to locate idb_companion in ${idbCompanionPath}. Try running sudo yum install -y fb-idb`, - ); + recorder.event('cmd', { + cmd, + description, + success: false, + troubleshoot, + context, + }); return []; } } function parseIdbTarget(line: string): DeviceTarget | undefined { const parsed: IdbTarget = JSON.parse(line); - if (parsed.state.toLocaleLowerCase() !== 'booted') { - return; - } return { udid: parsed.udid, type: @@ -113,6 +289,8 @@ function parseIdbTarget(line: string): DeviceTarget | undefined { ? 'emulator' : ('physical' as DeviceType), name: parsed.name, + osVersion: parsed.os_version, + state: parsed.state?.toLocaleLowerCase(), }; } @@ -125,8 +303,6 @@ function parseIdbTargets(lines: string): Array { .map((line) => parseIdbTarget(line)) .filter((target): target is DeviceTarget => !!target); - // For some reason, idb can return duplicates - // TODO: Raise the issue with idb const dedupedIdbTargets: Record = {}; for (const idbTarget of parsedIdbTargets) { dedupedIdbTargets[idbTarget.udid] = @@ -135,100 +311,92 @@ function parseIdbTargets(lines: string): Array { return Object.values(dedupedIdbTargets); } -export async function idbListTargets( +async function idbDescribeTarget( idbPath: string, - safeExecFunc: ( - command: string, - ) => Promise<{stdout: string; stderr: string} | Output> = safeExec, -): Promise> { - return safeExecFunc(`${idbPath} list-targets --json`) - .then(({stdout}) => - // See above. - parseIdbTargets(stdout!.toString()), - ) - .catch((e: Error) => { - console.warn('Failed to query idb for targets:', e); - return []; - }); -} - -export async function idbDescribeTarget( - idbPath: string, - safeExecFunc: ( - command: string, - ) => Promise<{stdout: string; stderr: string} | Output> = safeExec, + context: any, ): Promise { - return safeExecFunc(`${idbPath} describe --json`) - .then(({stdout}) => - // See above. - parseIdbTarget(stdout!.toString()), - ) - .catch((e: Error) => { - console.warn('Failed to query idb to describe a target:', e); - return undefined; + const cmd = `${idbPath} describe --json`; + const description = `Returns metadata about the specified target, including: + UDID, + Name, + Screen dimensions and density, + State (booted/...), + Type (simulator/device), + iOS version, + Architecture, + Information about its companion, + `; + const troubleshoot = `Either idb is not installed or needs to be reset. + Run 'idb kill' from terminal.`; + + try { + const {stdout} = await safeExec(cmd); + if (!stdout) { + recorder.event('cmd', { + cmd, + description, + success: false, + troubleshoot, + context, + }); + throw new Error('No output from command'); + } + + recorder.event('cmd', { + cmd, + description, + success: true, + stdout: stdout.toString(), + context, }); + + return parseIdbTarget(stdout.toString()); + } catch (e) { + recorder.event('cmd', { + cmd, + description, + success: false, + troubleshoot, + stderr: e.toString(), + context, + }); + return undefined; + } } async function targets( idbPath: string, isPhysicalDeviceEnabled: boolean, + bootedOnly: boolean = false, + context?: any, ): Promise> { if (process.platform !== 'darwin') { return []; } - // If companion is started by some external process and its address provided to Flipper via IDB_COMPANION environment variable, - // use that companion and do not query other devices + const bootedFilter = (targets: DeviceTarget[] | undefined) => { + return targets + ? targets.filter( + (target) => !bootedOnly || (bootedOnly && target.state === 'booted'), + ) + : []; + }; + + // If companion is started by some external process and its path + // is provided to Flipper via IDB_COMPANION environment variable, + // use that instead and do not query other devices. // See stack of D36315576 for details if (process.env.IDB_COMPANION) { - const target = await idbDescribeTarget(idbPath); - return target ? [target] : []; + const target = await idbDescribeTarget(idbPath, context); + return bootedFilter(target ? [target] : []); } - const isXcodeInstalled = await isXcodeDetected(); - if (!isXcodeInstalled) { - if (!isPhysicalDeviceEnabled) { - // TODO: Show a notification to enable the toggle or integrate Doctor to better suggest this advice. - console.warn( - 'You are trying to connect Physical Device. Please enable the toggle "Enable physical iOS device" from the setting screen.', - ); - } - const idbCompanionPath = path.dirname(idbPath) + '/idb_companion'; - return queryTargetsWithoutXcodeDependency( - idbCompanionPath, - isPhysicalDeviceEnabled, - isAvailable, - safeExec, - ); - } - - // Not all users have idb installed because you can still use - // Flipper with Simulators without it. - // But idb is MUCH more CPU efficient than xcrun, so - // when installed, use it. This still holds true - // with the move from instruments to xcrun. - // TODO: Move idb availability check up. - if (await memoize(isAvailable)(idbPath)) { - return await idbListTargets(idbPath); + if (await memoize(isIdbAvailable)(idbPath)) { + const targets = await queryTargetsWithIdb(idbPath, context); + return bootedFilter(targets); } else { - return safeExec('xcrun xctrace list devices') - .then(({stdout}) => - stdout! - .toString() - .split('\n') - .map((line) => line.trim()) - .filter(Boolean) - .map((line) => /(.+) \([^(]+\) \[(.*)\]( \(Simulator\))?/.exec(line)) - .filter(notNull) - .filter(([_match, _name, _udid, isSim]) => !isSim) - .map(([_match, name, udid]) => { - return {udid, type: 'physical', name}; - }), - ) - .catch((e) => { - console.warn('Failed to query for devices using xctrace:', e); - return []; - }); + const targets = await queryTargetsWithXcode(context); + return bootedFilter(targets); } } @@ -238,19 +406,40 @@ async function push( bundleId: string, dst: string, idbPath: string, + context?: any, ): Promise { await memoize(checkIdbIsInstalled)(idbPath); - return reportPlatformFailures( - safeExec( - `${idbPath} file push --log ${idbLogLevel} --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`, - ) - .then(() => { - return; - }) - .catch((e) => handleMissingIdb(e, idbPath)), - `${operationPrefix}:push`, - ); + const push_ = async () => { + const cmd = `${idbPath} file push --log ${IDB_LOG_LEVEL} --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`; + const description = `idb push file to device`; + const troubleshoot = `Either idb is not installed or needs to be reset. + Run 'idb kill' from terminal.`; + + try { + await safeExec(cmd); + recorder.event('cmd', { + cmd, + description, + success: true, + troubleshoot, + context, + }); + } catch (e) { + recorder.event('cmd', { + cmd, + description, + success: false, + stdout: e.toString(), + troubleshoot, + context, + }); + handleMissingIdb(e, idbPath); + throw e; + } + }; + + return reportPlatformFailures(push_(), `${LOG_TAG}:push`); } async function pull( @@ -259,33 +448,55 @@ async function pull( bundleId: string, dst: string, idbPath: string, + context?: any, ): Promise { await memoize(checkIdbIsInstalled)(idbPath); - return reportPlatformFailures( - safeExec( - `${idbPath} file pull --log ${idbLogLevel} --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`, - ) - .then(() => { - return; - }) - .catch((e) => handleMissingIdb(e, idbPath)) - .catch((e) => handleMissingPermissions(e)), - `${operationPrefix}:pull`, - ); + const pull_ = async () => { + const cmd = `${idbPath} file pull --log ${IDB_LOG_LEVEL} --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`; + const description = `idb pull file from device`; + const troubleshoot = `Either idb is not installed or needs to be reset. + Run 'idb kill' from terminal.`; + + try { + await safeExec(cmd); + recorder.event('cmd', { + cmd, + description, + success: true, + troubleshoot, + context, + }); + } catch (e) { + recorder.event('cmd', { + cmd, + description, + success: false, + stdout: e.toString(), + troubleshoot, + context, + }); + handleMissingIdb(e, idbPath); + handleMissingPermissions(e); + throw e; + } + }; + + return reportPlatformFailures(pull_(), `${LOG_TAG}:pull`); } -export async function checkIdbIsInstalled(idbPath: string): Promise { - const isInstalled = await isAvailable(idbPath); +async function checkIdbIsInstalled(idbPath: string): Promise { + const isInstalled = await isIdbAvailable(idbPath); if (!isInstalled) { throw new Error( - `idb is required to use iOS devices. Install it with instructions from https://github.com/facebook/idb and set the installation path in Flipper settings.`, + `idb is required to use iOS devices. Install it with instructions + from https://github.com/facebook/idb and set the installation path in Flipper settings.`, ); } } -// The fb-internal idb binary is a shim that downloads the proper one on first run. It requires sudo to do so. -// If we detect this, Tell the user how to fix it. +// The fb-internal idb binary is a shim that downloads the proper one on first run. +// It requires sudo to do so. If we detect this, tell the user how to fix it. function handleMissingIdb(e: Error, idbPath: string): void { if ( e.message && @@ -296,7 +507,6 @@ function handleMissingIdb(e: Error, idbPath: string): void { `idb doesn't appear to be installed. Run "${idbPath} list-targets" to fix this.`, ); } - throw e; } function handleMissingPermissions(e: Error): void { @@ -309,22 +519,21 @@ function handleMissingPermissions(e: Error): void { console.warn(e); throw new Error( 'Cannot connect to iOS application. idb_certificate_pull_failed' + - 'Idb lacks permissions to exchange certificates. Did you install a source build ([FB] or enable certificate exchange)? See console logs for more details.', + 'idb lacks permissions to exchange certificates. Did you install a source build ([FB] or enable certificate exchange)? See console logs for more details.', ); } - throw e; } async function isXcodeDetected(): Promise { - return exec('xcode-select -p') - .then(({stdout}) => { - return fs.pathExists(stdout.trim()); - }) - .catch((_) => false); + try { + const {stdout} = await exec('xcode-select -p'); + return fs.pathExists(stdout.trim()); + } catch (e) { + return false; + } } export default { - isAvailable, targets, push, pull, diff --git a/desktop/flipper-server-core/src/devices/ios/iOSCrashUtils.tsx b/desktop/flipper-server-core/src/devices/ios/iOSCrashUtils.tsx index 49f37880f..96f16e466 100644 --- a/desktop/flipper-server-core/src/devices/ios/iOSCrashUtils.tsx +++ b/desktop/flipper-server-core/src/devices/ios/iOSCrashUtils.tsx @@ -72,7 +72,8 @@ export function shouldShowiOSCrashNotification( ): boolean { const appPath = legacy ? parsePathLegacy(content) : parsePathModern(content); if (!appPath || !appPath.includes(serial)) { - // Do not show notifications for the app which are not running on this device + // Do not show notifications for the app which + // are not running on this device. return false; } return true; diff --git a/desktop/flipper-server-core/src/devices/ios/iOSDeviceManager.tsx b/desktop/flipper-server-core/src/devices/ios/iOSDeviceManager.tsx index aceb87c1f..d9458649b 100644 --- a/desktop/flipper-server-core/src/devices/ios/iOSDeviceManager.tsx +++ b/desktop/flipper-server-core/src/devices/ios/iOSDeviceManager.tsx @@ -8,7 +8,7 @@ */ import {ChildProcess} from 'child_process'; -import type {IOSDeviceParams} from 'flipper-common'; +import type {DeviceTarget} from 'flipper-common'; import path from 'path'; import childProcess from 'child_process'; import {exec} from 'promisify-child-process'; @@ -18,11 +18,11 @@ import { ERR_NO_IDB_OR_XCODE_AVAILABLE, IOSBridge, makeIOSBridge, - SimctlBridge, } from './IOSBridge'; import {FlipperServerImpl} from '../../FlipperServerImpl'; import {getFlipperServerConfig} from '../../FlipperServerConfig'; import iOSCertificateProvider from './iOSCertificateProvider'; +import exitHook from 'exit-hook'; export class IOSDeviceManager { private portForwarders: Array = []; @@ -33,7 +33,7 @@ export class IOSDeviceManager { 'MacOS', 'PortForwardingMacApp', ); - simctlBridge: SimctlBridge = new SimctlBridge(); + ctlBridge: IOSBridge | undefined; readonly certificateProvider: iOSCertificateProvider; @@ -73,6 +73,11 @@ export class IOSDeviceManager { console.log(`[conn] Port forwarding app exited gracefully`); } }); + + exitHook(() => { + child.kill('SIGKILL'); + }); + return child; } @@ -91,31 +96,26 @@ export class IOSDeviceManager { ]; } - queryDevices(bridge: IOSBridge): Promise { - return bridge - .getActiveDevices(true) - .then((devices) => this.processDevices(bridge, devices)); + async queryDevices(bridge: IOSBridge): Promise { + const devices = await bridge.getActiveDevices(true); + return this.processDevices(bridge, devices); } - private processDevices(bridge: IOSBridge, activeDevices: IOSDeviceParams[]) { - console.debug('[conn] processDevices', activeDevices); + private processDevices(bridge: IOSBridge, activeDevices: DeviceTarget[]) { const currentDeviceIDs = new Set( this.flipperServer .getDevices() .filter((device) => device.info.os === 'iOS') + .filter((device) => device.info.deviceType !== 'dummy') .map((device) => device.serial), ); - console.debug( - '[conn] processDevices -> currentDeviceIDs', - currentDeviceIDs, - ); for (const activeDevice of activeDevices) { const {udid, type, name} = activeDevice; if (currentDeviceIDs.has(udid)) { currentDeviceIDs.delete(udid); } else { - console.info(`[conn] detected new iOS device ${udid}`, activeDevice); + console.info(`[conn] Detected new iOS device ${udid}`, activeDevice); const iOSDevice = new IOSDevice( this.flipperServer, bridge, @@ -133,9 +133,23 @@ export class IOSDeviceManager { }); } + async getBridge(): Promise { + if (this.ctlBridge !== undefined) { + return this.ctlBridge; + } + + const isDetected = await iosUtil.isXcodeDetected(); + this.ctlBridge = await makeIOSBridge( + this.idbConfig.idbPath, + isDetected, + this.idbConfig.enablePhysicalIOS, + ); + + return this.ctlBridge; + } + public async watchIOSDevices() { try { - const isDetected = await iosUtil.isXcodeDetected(); if (this.idbConfig.enablePhysicalIOS) { this.startDevicePortForwarders(); } @@ -143,12 +157,8 @@ export class IOSDeviceManager { // Check for version mismatch now for immediate error handling. await this.checkXcodeVersionMismatch(); // Awaiting the promise here to trigger immediate error handling. - const bridge = await makeIOSBridge( - this.idbConfig.idbPath, - isDetected, - this.idbConfig.enablePhysicalIOS, - ); - this.queryDevicesForever(bridge); + const bridge = await this.getBridge(); + await this.queryDevicesForever(bridge); } catch (err) { // This case is expected if both Xcode and idb are missing. if (err.message === ERR_NO_IDB_OR_XCODE_AVAILABLE) { @@ -165,8 +175,11 @@ export class IOSDeviceManager { } } - getSimulators(bootedOnly: boolean): Promise> { - return this.simctlBridge.getActiveDevices(bootedOnly).catch((e: Error) => { + async getSimulators(bootedOnly: boolean): Promise> { + try { + const bridge = await this.getBridge(); + return await bridge.getActiveDevices(bootedOnly); + } catch (e) { console.warn('Failed to query simulators:', e); if (e.message.includes('Xcode license agreements')) { this.flipperServer.emit('notification', { @@ -176,20 +189,28 @@ export class IOSDeviceManager { 'The Xcode license agreement has changed. You need to either open Xcode and agree to the terms or run `sudo xcodebuild -license` in a Terminal to allow simulators to work with Flipper.', }); } - return Promise.resolve([]); - }); + return []; + } } - private queryDevicesForever(bridge: IOSBridge) { - return this.queryDevices(bridge) - .then(() => { - // It's important to schedule the next check AFTER the current one has completed - // to avoid simultaneous queries which can cause multiple user input prompts. - setTimeout(() => this.queryDevicesForever(bridge), 3000); - }) - .catch((err) => { - console.warn('Failed to continuously query devices:', err); - }); + async launchSimulator(udid: string) { + try { + const bridge = await this.getBridge(); + await bridge.launchSimulator(udid); + } catch (e) { + console.warn('Failed to launch simulator:', e); + } + } + + private async queryDevicesForever(bridge: IOSBridge) { + try { + await this.queryDevices(bridge); + // It's important to schedule the next check AFTER the current one has completed + // to avoid simultaneous queries which can cause multiple user input prompts. + setTimeout(() => this.queryDevicesForever(bridge), 3000); + } catch (err) { + console.warn('Failed to continuously query devices:', err); + } } async checkXcodeVersionMismatch() { @@ -219,9 +240,18 @@ export class IOSDeviceManager { description: errorMessage, }); } catch (e) { - console.error('Failed to determine Xcode version:', e); + // This is not an error. It depends on the user's local setup that we cannot influence. + console.warn('Failed to determine Xcode version:', e); } } + + async idbKill() { + if (!this.idbConfig.idbPath || this.idbConfig.idbPath.length === 0) { + return; + } + const cmd = `${this.idbConfig.idbPath} kill`; + await exec(cmd); + } } function confirmSimulatorAppMatchesThatOfXcodeSelect( diff --git a/desktop/flipper-server-core/src/devices/metro/metroDeviceManager.tsx b/desktop/flipper-server-core/src/devices/metro/metroDeviceManager.tsx index ff95b4266..8677fbccf 100644 --- a/desktop/flipper-server-core/src/devices/metro/metroDeviceManager.tsx +++ b/desktop/flipper-server-core/src/devices/metro/metroDeviceManager.tsx @@ -38,7 +38,7 @@ async function isMetroRunning(): Promise { }) .on('error', (err: any) => { if (err.code !== 'ECONNREFUSED' && err.code !== 'ECONNRESET') { - console.error('Could not connect to METRO ' + err); + console.warn('Could not connect to METRO ' + err); } resolve(false); }); diff --git a/desktop/flipper-server-core/src/fb-stubs/GK.tsx b/desktop/flipper-server-core/src/fb-stubs/GK.tsx index 0ffb27828..f551582fe 100644 --- a/desktop/flipper-server-core/src/fb-stubs/GK.tsx +++ b/desktop/flipper-server-core/src/fb-stubs/GK.tsx @@ -13,7 +13,9 @@ export const TEST_PASSING_GK = 'TEST_PASSING_GK'; export const TEST_FAILING_GK = 'TEST_FAILING_GK'; export type GKMap = {[key: string]: boolean}; -const whitelistedGKs: Array = []; +// Allow OSS users start flipper-server + +const whitelistedGKs: Array = ['']; export function loadGKs(_username: string, _gks: Array): Promise { return Promise.reject( @@ -61,6 +63,6 @@ export default class GK { } static allGKs(): GKMap { - return {}; + return Object.fromEntries(whitelistedGKs.map((gk) => [gk, true])); } } diff --git a/desktop/flipper-server-core/src/fb-stubs/Logger.tsx b/desktop/flipper-server-core/src/fb-stubs/Logger.tsx new file mode 100644 index 000000000..09a50b396 --- /dev/null +++ b/desktop/flipper-server-core/src/fb-stubs/Logger.tsx @@ -0,0 +1,17 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {EnvironmentInfo, Logger, LoggerArgs, NoopLogger} from 'flipper-common'; + +export function initializeLogger( + _environmentInfo: EnvironmentInfo, + _args?: LoggerArgs, +): Logger { + return new NoopLogger(); +} diff --git a/desktop/flipper-server-core/src/fb-stubs/WWWCertificateProvider.tsx b/desktop/flipper-server-core/src/fb-stubs/WWWCertificateProvider.tsx index cf706ecab..fc3ddb6cc 100644 --- a/desktop/flipper-server-core/src/fb-stubs/WWWCertificateProvider.tsx +++ b/desktop/flipper-server-core/src/fb-stubs/WWWCertificateProvider.tsx @@ -8,11 +8,12 @@ */ import {KeytarManager} from '../utils/keytar'; -import CertificateProvider from '../utils/CertificateProvider'; +import CertificateProvider from '../app-connectivity/certificate-exchange/CertificateProvider'; export default class WWWCertificateProvider extends CertificateProvider { name = 'WWWCertificateProvider'; medium = 'WWW' as const; + constructor(private keytarManager: KeytarManager) { super(); } diff --git a/desktop/flipper-server-core/src/fb-stubs/constants.tsx b/desktop/flipper-server-core/src/fb-stubs/constants.tsx index b79f09fe9..5c9967f66 100644 --- a/desktop/flipper-server-core/src/fb-stubs/constants.tsx +++ b/desktop/flipper-server-core/src/fb-stubs/constants.tsx @@ -8,3 +8,4 @@ */ export const isFBBuild: boolean = false; +export const GRAPH_SECRET = ''; diff --git a/desktop/flipper-server-core/src/fb-stubs/internRequests.tsx b/desktop/flipper-server-core/src/fb-stubs/internRequests.tsx index 5501b350f..d811f4ba6 100644 --- a/desktop/flipper-server-core/src/fb-stubs/internRequests.tsx +++ b/desktop/flipper-server-core/src/fb-stubs/internRequests.tsx @@ -20,6 +20,8 @@ export async function internGraphPOSTAPIRequest( options: { timeout?: number; internGraphUrl?: string; + headers?: Record; + vpnMode?: 'vpn' | 'vpnless'; }, token: string, ): Promise { @@ -34,6 +36,8 @@ export async function internGraphGETAPIRequest( _options: { timeout?: number; internGraphUrl?: string; + headers?: Record; + vpnMode?: 'vpn' | 'vpnless'; }, token: string, ): Promise { diff --git a/desktop/flipper-server-core/src/fb-stubs/jf.tsx b/desktop/flipper-server-core/src/fb-stubs/jf.tsx new file mode 100644 index 000000000..173b6d6d6 --- /dev/null +++ b/desktop/flipper-server-core/src/fb-stubs/jf.tsx @@ -0,0 +1,17 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +/** + * Upload a file at path to a temporary cloud storage + * + * @returns Download URL + */ +export const jfUpload = async (_path: string): Promise => { + throw new Error('jf upload is not implemented'); +}; diff --git a/desktop/flipper-server-core/src/fb-stubs/sendScribeLogs.tsx b/desktop/flipper-server-core/src/fb-stubs/sendScribeLogs.tsx index d735a36bd..588d0a256 100644 --- a/desktop/flipper-server-core/src/fb-stubs/sendScribeLogs.tsx +++ b/desktop/flipper-server-core/src/fb-stubs/sendScribeLogs.tsx @@ -7,6 +7,8 @@ * @format */ +import {ScribeMessage} from 'flipper-common'; + export async function sendScribeLogs( - _messages: {category: string; message: string}[], + _messages: ScribeMessage[], ): Promise {} diff --git a/desktop/flipper-server-core/src/index.tsx b/desktop/flipper-server-core/src/index.tsx index e8dcc900e..7e8099f8b 100644 --- a/desktop/flipper-server-core/src/index.tsx +++ b/desktop/flipper-server-core/src/index.tsx @@ -9,6 +9,7 @@ export {FlipperServerImpl} from './FlipperServerImpl'; export {loadSettings} from './utils/settings'; +export * from './tracker'; export {loadLauncherSettings} from './utils/launcherSettings'; export {loadProcessConfig} from './utils/processConfig'; export {getEnvironmentInfo} from './utils/environmentInfo'; @@ -18,5 +19,20 @@ export * from './server/attachSocketServer'; export * from './server/startFlipperServer'; export * from './server/startServer'; export * from './server/utilities'; +export {isFBBuild} from './fb-stubs/constants'; +export {initializeLogger} from './fb-stubs/Logger'; -export {WEBSOCKET_MAX_MESSAGE_SIZE} from './comms/ServerWebSocket'; +export {WEBSOCKET_MAX_MESSAGE_SIZE} from './app-connectivity/ServerWebSocket'; + +export { + getAuthToken, + hasAuthToken, +} from './app-connectivity/certificate-exchange/certificate-utils'; + +export {sessionId} from './sessionId'; +import dns from 'dns'; + +// The default on node16 is to prefer ipv4 results which causes issues +// in some setups. +// @ts-ignore: Not in our node typings yet +dns.setDefaultResultOrder('verbatim'); diff --git a/desktop/flipper-server-core/src/plugins/PluginManager.tsx b/desktop/flipper-server-core/src/plugins/PluginManager.tsx index 178b6d326..7baa08dee 100644 --- a/desktop/flipper-server-core/src/plugins/PluginManager.tsx +++ b/desktop/flipper-server-core/src/plugins/PluginManager.tsx @@ -13,21 +13,22 @@ import tmp from 'tmp'; import {promisify} from 'util'; import {default as axios} from 'axios'; import { - BundledPluginDetails, DownloadablePluginDetails, ExecuteMessage, FlipperServerForServerAddOn, InstalledPluginDetails, + PluginSource, ServerAddOnStartDetails, } from 'flipper-common'; -import {getStaticPath} from '../utils/pathUtils'; + import {loadDynamicPlugins} from './loadDynamicPlugins'; import { cleanupOldInstalledPluginVersions, getInstalledPluginDetails, getInstalledPlugins, getPluginVersionInstallationDir, - installPluginFromFile, + getPluginDirNameFromPackageName, + installPluginFromFileOrBuffer, removePlugins, getUpdatablePlugins, getInstalledPlugin, @@ -70,33 +71,30 @@ export class PluginManager { removePlugins = removePlugins; getUpdatablePlugins = getUpdatablePlugins; getInstalledPlugin = getInstalledPlugin; - installPluginFromFile = installPluginFromFile; + installPluginFromFileOrBuffer = installPluginFromFileOrBuffer; installPluginFromNpm = installPluginFromNpm; - async loadSource(path: string) { - return await fs.readFile(path, 'utf8'); - } + async loadSource(path: string): Promise { + const js = await fs.readFile(path, 'utf8'); - async getBundledPlugins(): Promise> { - if ( - process.env.NODE_ENV === 'test' || - process.env.FLIPPER_NO_BUNDLED_PLUGINS === 'true' - ) { - return []; - } - // defaultPlugins that are included in the Flipper distributive. - // List of default bundled plugins is written at build time to defaultPlugins/bundled.json. - const pluginPath = getStaticPath( - path.join('defaultPlugins', 'bundled.json'), - {asarUnpacked: true}, - ); - let bundledPlugins: Array = []; + /** + * Check if the plugin includes a bundled css. If so, + * load its content too. + */ + let css = undefined; + const idx = path.lastIndexOf('.'); + const cssPath = path.substring(0, idx < 0 ? path.length : idx) + '.css'; try { - bundledPlugins = await fs.readJson(pluginPath); - } catch (e) { - console.error('Failed to load list of bundled plugins', e); - } - return bundledPlugins; + await fs.promises.access(cssPath); + + const buffer = await fs.promises.readFile(cssPath, {encoding: 'utf-8'}); + css = buffer.toString(); + } catch (e) {} + + return { + js, + css, + }; } async loadMarketplacePlugins() { @@ -132,7 +130,10 @@ export class PluginManager { `Downloading plugin "${title}" v${version} from "${downloadUrl}" to "${installationDir}".`, ); const tmpDir = await getTempDirName(); - const tmpFile = path.join(tmpDir, `${name}-${version}.tgz`); + const tmpFile = path.join( + tmpDir, + `${getPluginDirNameFromPackageName(name)}-${version}.tgz`, + ); try { const cancelationSource = axios.CancelToken.source(); if (await fs.pathExists(installationDir)) { @@ -185,10 +186,10 @@ export class PluginManager { await new Promise((resolve, reject) => writeStream.once('finish', resolve).once('error', reject), ); - return await installPluginFromFile(tmpFile); + return await installPluginFromFileOrBuffer(tmpFile); } } catch (error) { - console.error( + console.warn( `Failed to download plugin "${title}" v${version} from "${downloadUrl}" to "${installationDir}".`, error, ); diff --git a/desktop/flipper-server-core/src/plugins/ServerAddManager.tsx b/desktop/flipper-server-core/src/plugins/ServerAddManager.tsx index 507ff855c..55f1f9101 100644 --- a/desktop/flipper-server-core/src/plugins/ServerAddManager.tsx +++ b/desktop/flipper-server-core/src/plugins/ServerAddManager.tsx @@ -13,7 +13,7 @@ import { FlipperServerForServerAddOn, ServerAddOnStartDetails, } from 'flipper-common'; -import {assertNotNull} from '../comms/Utilities'; +import {assertNotNull} from '../app-connectivity/Utilities'; import {StateMachine} from '../utils/StateMachine'; import {ServerAddOn} from './ServerAddOn'; diff --git a/desktop/flipper-server-core/src/plugins/ServerAddOn.tsx b/desktop/flipper-server-core/src/plugins/ServerAddOn.tsx index bb03c8f30..285eb0bf7 100644 --- a/desktop/flipper-server-core/src/plugins/ServerAddOn.tsx +++ b/desktop/flipper-server-core/src/plugins/ServerAddOn.tsx @@ -8,7 +8,7 @@ */ import assert from 'assert'; -import {assertNotNull} from '../comms/Utilities'; +import {assertNotNull} from '../app-connectivity/Utilities'; import { FlipperServerForServerAddOn, ServerAddOnCleanup, diff --git a/desktop/flipper-server-core/src/plugins/__tests__/PluginManager.node.tsx b/desktop/flipper-server-core/src/plugins/__tests__/PluginManager.node.tsx index 99974c271..7b7b81c6f 100644 --- a/desktop/flipper-server-core/src/plugins/__tests__/PluginManager.node.tsx +++ b/desktop/flipper-server-core/src/plugins/__tests__/PluginManager.node.tsx @@ -12,12 +12,7 @@ import {loadServerAddOn} from '../loadServerAddOn'; import {PluginManager} from '../PluginManager'; import {ServerAddOnManager} from '../ServerAddManager'; import {ServerAddOnModuleToDesktopConnection} from '../ServerAddOnModuleToDesktopConnection'; -import { - detailsBundled, - detailsInstalled, - initialOwner, - pluginName, -} from './utils'; +import {detailsInstalled, initialOwner, pluginName} from './utils'; jest.mock('../loadServerAddOn'); const loadServerAddOnMock = loadServerAddOn as jest.Mock; @@ -70,10 +65,7 @@ describe('PluginManager', () => { }; }; - describe.each([ - ['bundled', detailsBundled], - ['installed', detailsInstalled], - ])('%s', (_name, details) => { + describe.each([['installed', detailsInstalled]])('%s', (_name, details) => { test('stops the add-on when the initial owner is removed', async () => { const {pluginManager, addOnCleanupMock} = await startServerAddOn( details, diff --git a/desktop/flipper-server-core/src/plugins/__tests__/ServerAddOn.node.tsx b/desktop/flipper-server-core/src/plugins/__tests__/ServerAddOn.node.tsx index 8dc6ff101..fb3444d44 100644 --- a/desktop/flipper-server-core/src/plugins/__tests__/ServerAddOn.node.tsx +++ b/desktop/flipper-server-core/src/plugins/__tests__/ServerAddOn.node.tsx @@ -11,12 +11,7 @@ import {ServerAddOnStartDetails, createControlledPromise} from 'flipper-common'; import {loadServerAddOn} from '../loadServerAddOn'; import {ServerAddOn} from '../ServerAddOn'; import {ServerAddOnModuleToDesktopConnection} from '../ServerAddOnModuleToDesktopConnection'; -import { - detailsBundled, - detailsInstalled, - initialOwner, - pluginName, -} from './utils'; +import {detailsInstalled, initialOwner, pluginName} from './utils'; jest.mock('../loadServerAddOn'); const loadServerAddOnMock = loadServerAddOn as jest.Mock; @@ -67,10 +62,7 @@ describe('ServerAddOn', () => { }; }; - describe.each([ - ['bundled', detailsBundled], - ['installed', detailsInstalled], - ])('%s', (_name, details) => { + describe.each([['installed', detailsInstalled]])('%s', (_name, details) => { test('stops the add-on when the initial owner is removed', async () => { const {serverAddOn, addOnCleanupMock} = await startServerAddOn(details); diff --git a/desktop/flipper-server-core/src/plugins/__tests__/utils.tsx b/desktop/flipper-server-core/src/plugins/__tests__/utils.tsx index cdd3dd649..4f28665ed 100644 --- a/desktop/flipper-server-core/src/plugins/__tests__/utils.tsx +++ b/desktop/flipper-server-core/src/plugins/__tests__/utils.tsx @@ -11,9 +11,6 @@ import {ServerAddOnStartDetails} from 'flipper-common'; export const pluginName = 'lightSaber'; export const initialOwner = 'yoda'; -export const detailsBundled: ServerAddOnStartDetails = { - isBundled: true, -}; export const detailsInstalled: ServerAddOnStartDetails = { path: '/dagobar/', }; diff --git a/desktop/flipper-server-core/src/plugins/fb-stubs/pluginMarketplaceAPI.tsx b/desktop/flipper-server-core/src/plugins/fb-stubs/pluginMarketplaceAPI.tsx index 12934bb05..eeeb38590 100644 --- a/desktop/flipper-server-core/src/plugins/fb-stubs/pluginMarketplaceAPI.tsx +++ b/desktop/flipper-server-core/src/plugins/fb-stubs/pluginMarketplaceAPI.tsx @@ -8,6 +8,7 @@ */ import {FlipperServer, MarketplacePluginDetails} from 'flipper-common'; +import fetch from 'node-fetch'; export async function loadAvailablePlugins( server: FlipperServer, diff --git a/desktop/flipper-server-core/src/plugins/isPluginVersionMoreRecent.tsx b/desktop/flipper-server-core/src/plugins/isPluginVersionMoreRecent.tsx index 2a710f5eb..b0cc69776 100644 --- a/desktop/flipper-server-core/src/plugins/isPluginVersionMoreRecent.tsx +++ b/desktop/flipper-server-core/src/plugins/isPluginVersionMoreRecent.tsx @@ -26,13 +26,6 @@ export function isPluginVersionMoreRecent( if (semver.gt(versionDetails.version, otherVersionDetails.version)) { return true; } - if ( - semver.eq(versionDetails.version, otherVersionDetails.version) && - versionDetails.isBundled - ) { - // prefer bundled versions - return true; - } if ( semver.eq(versionDetails.version, otherVersionDetails.version) && versionDetails.isActivatable && diff --git a/desktop/flipper-server-core/src/plugins/loadDynamicPlugins.tsx b/desktop/flipper-server-core/src/plugins/loadDynamicPlugins.tsx index 11f1c935d..d35945cd4 100644 --- a/desktop/flipper-server-core/src/plugins/loadDynamicPlugins.tsx +++ b/desktop/flipper-server-core/src/plugins/loadDynamicPlugins.tsx @@ -7,8 +7,6 @@ * @format */ -import path from 'path'; -import fs from 'fs-extra'; import { getSourcePlugins, moveInstalledPluginsFromLegacyDir, @@ -19,46 +17,24 @@ import {InstalledPluginDetails} from 'flipper-common'; import {getStaticPath} from '../utils/pathUtils'; // Load "dynamic" plugins, e.g. those which are either pre-installed (default), installed or loaded from sources (for development). -// This opposed to "bundled" plugins which are included into Flipper bundle. export async function loadDynamicPlugins(): Promise { if (process.env.NODE_ENV === 'test') { return []; } - if (process.env.FLIPPER_FAST_REFRESH) { - console.log( - '❌ Skipping loading of dynamic plugins because Fast Refresh is enabled. Fast Refresh only works with bundled plugins.', - ); - return []; - } await moveInstalledPluginsFromLegacyDir().catch((ex) => console.error( 'Eror while migrating installed plugins from legacy folder', ex, ), ); - const bundledPlugins = new Set( - ( - await fs.readJson( - getStaticPath(path.join('defaultPlugins', 'bundled.json'), { - asarUnpacked: true, - }), - ) - ).map((p: any) => p.name) as string[], - ); - console.log( - `✅ Detected ${bundledPlugins.size} bundled plugins: ${Array.from( - bundledPlugins, - ).join(', ')}.`, - ); - const [installedPlugins, unfilteredSourcePlugins] = await Promise.all([ + + const [installedPlugins, sourcePlugins] = await Promise.all([ process.env.FLIPPER_NO_PLUGIN_MARKETPLACE ? Promise.resolve([]) : getAllInstalledPluginVersions(), getSourcePlugins(), ]); - const sourcePlugins = unfilteredSourcePlugins.filter( - (p) => !bundledPlugins.has(p.name), - ); + const defaultPluginsDir = getStaticPath('defaultPlugins', { asarUnpacked: true, }); diff --git a/desktop/flipper-server-core/src/plugins/loadServerAddOn.tsx b/desktop/flipper-server-core/src/plugins/loadServerAddOn.tsx index d3fe04c63..c3ea11c6e 100644 --- a/desktop/flipper-server-core/src/plugins/loadServerAddOn.tsx +++ b/desktop/flipper-server-core/src/plugins/loadServerAddOn.tsx @@ -11,10 +11,8 @@ import { ServerAddOn as ServerAddOnFn, ServerAddOnStartDetails, } from 'flipper-common'; -import {assertNotNull} from '../comms/Utilities'; -// Special subset of flipper-plugin exports designed for server-side usage -// eslint-disable-next-line no-restricted-imports -import * as FlipperPluginSDK from 'flipper-plugin/src/server'; +import {assertNotNull} from '../app-connectivity/Utilities'; +import * as FlipperPluginSDK from 'flipper-plugin-core'; declare global { // eslint-disable-next-line no-var @@ -22,15 +20,6 @@ declare global { } global.FlipperPlugin = FlipperPluginSDK; -// defaultPlugins has to be required after we set FlipperPlugin. -// In server add-ons, developers might import utilities from 'flipper-plugin' -// In babel-transformer/plugin-flipper-requires flipper-plugin is replaces with global.FlipperPlugin. -// If defaultPlugins is required before we set global.FlipperPlugin, -// then flipper-plugin replaced with global.FlipperPlugin is evaluated in server add-ons before we set it - to undefined. -// -// The file is generated automatically by "prepareDefaultPlugins" in "scripts" -const defaultPlugins = require('../defaultPlugins').default; - interface ServerAddOnModule { default: ServerAddOnFn; } @@ -41,21 +30,11 @@ export const loadServerAddOn = ( ): ServerAddOnModule => { console.debug('loadPlugin', pluginName, details); - if (details.isBundled) { - const bundledPlugin = defaultPlugins[pluginName]; - assertNotNull( - bundledPlugin, - `loadPlugin (isBundled = true) -> plugin ${pluginName} not found.`, - ); - return bundledPlugin; - } - assertNotNull( details.path, - `loadPlugin (isBundled = false) -> server add-on path is empty plugin ${pluginName}.`, + `loadPlugin -> server add-on path is empty plugin ${pluginName}.`, ); - // eslint-disable-next-line no-eval - const serverAddOnModule = eval(`require("${details.path}")`); + const serverAddOnModule = electronRequire(details.path); return serverAddOnModule; }; diff --git a/desktop/flipper-server-core/src/recorder.tsx b/desktop/flipper-server-core/src/recorder.tsx new file mode 100644 index 000000000..9725f35e6 --- /dev/null +++ b/desktop/flipper-server-core/src/recorder.tsx @@ -0,0 +1,125 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import { + ClientQuery, + ConnectionRecordEntry, + CommandRecordEntry, + getLogger, +} from 'flipper-common'; +import {FlipperServerImpl} from './FlipperServerImpl'; + +type CommandEventPayload = { + cmd: string; + description: string; + success: boolean; + stdout?: string; + stderr?: string; + troubleshoot?: string; + context?: any; +}; + +type ConnectionRecorderEvents = { + cmd: CommandEventPayload; +}; + +class Recorder { + private flipperServer_: FlipperServerImpl | undefined; + private undefinedClientQuery_: ClientQuery = { + app: 'NONE', + device: 'NONE', + medium: 'NONE', + os: 'Browser', + device_id: '', + }; + + private handler_ = { + cmd: (payload: CommandEventPayload) => { + if (this.flipperServer_) { + const clientQuery = payload.context as ClientQuery | undefined; + + const device = clientQuery?.device ?? 'NONE'; + const app = clientQuery?.app ?? 'NONE'; + const medium = clientQuery?.medium ?? 'NONE'; + const os = + clientQuery?.os ?? (payload.cmd.includes('idb') ? 'iOS' : 'Android'); + + const entry: CommandRecordEntry = { + time: new Date(), + type: payload.success ? 'info' : 'error', + os, + device, + app, + message: payload.cmd, + medium, + cmd: payload.cmd, + description: payload.description, + success: payload.success, + stdout: payload.stdout, + stderr: payload.stderr, + troubleshoot: payload.troubleshoot, + }; + + this.flipperServer_.emit('connectivity-troubleshoot-cmd', entry); + } + }, + }; + + private log_ = ( + type: 'info' | 'warning' | 'error', + clientQuery: ClientQuery, + ...args: any[] + ) => { + console.log(`[conn][${type}]`, ...args); + if (this.flipperServer_) { + const entry: ConnectionRecordEntry = { + time: new Date(), + type, + os: clientQuery.os, + device: clientQuery.device, + app: clientQuery.app, + message: args.join(' '), + medium: clientQuery.medium, + }; + + this.flipperServer_.emit('connectivity-troubleshoot-log', entry); + getLogger().track('usage', 'connectivity-log', entry); + } + }; + + event( + event: Event, + payload: ConnectionRecorderEvents[Event], + ): void { + const handler: (...args: any[]) => void = this.handler_[event]; + if (!handler) { + return; + } + handler(payload); + } + + log(clientQuery: ClientQuery, ...args: any[]) { + this.log_('info', clientQuery, args); + } + + rawError(...args: any[]) { + this.log_('error', this.undefinedClientQuery_, args); + } + + error(clientQuery: ClientQuery, ...args: any[]) { + this.log_('error', clientQuery, args); + } + + enable(flipperServer: FlipperServerImpl) { + this.flipperServer_ = flipperServer; + } +} + +const recorder = new Recorder(); +export {recorder}; diff --git a/desktop/flipper-server-core/src/server/attachSocketServer.tsx b/desktop/flipper-server-core/src/server/attachSocketServer.tsx index 0b7e9b303..d89eb358a 100644 --- a/desktop/flipper-server-core/src/server/attachSocketServer.tsx +++ b/desktop/flipper-server-core/src/server/attachSocketServer.tsx @@ -17,6 +17,7 @@ import { SystemError, getLogger, CompanionEventWebSocketMessage, + isProduction, } from 'flipper-common'; import {FlipperServerImpl} from '../FlipperServerImpl'; import {RawData, WebSocketServer} from 'ws'; @@ -25,6 +26,9 @@ import { FlipperServerCompanionEnv, } from 'flipper-server-companion'; import {URLSearchParams} from 'url'; +import {getFlipperServerConfig} from '../FlipperServerConfig'; +import {tracker} from '../tracker'; +import {performance} from 'perf_hooks'; const safe = (f: () => void) => { try { @@ -36,9 +40,11 @@ const safe = (f: () => void) => { } }; +let numberOfConnectedClients = 0; + /** * Attach and handle incoming messages from clients. - * @param flipperServer A FlipperServer instance. + * @param server A FlipperServer instance. * @param socket A ws socket on which to listen for events. */ export function attachSocketServer( @@ -46,6 +52,14 @@ export function attachSocketServer( server: FlipperServerImpl, companionEnv: FlipperServerCompanionEnv, ) { + const t0 = performance.now(); + const browserConnectionTimeout = setTimeout(() => { + tracker.track('browser-connection-created', { + successful: false, + timeMS: performance.now() - t0, + }); + }, 20000); + socket.on('connection', (client, req) => { const clientAddress = (req.socket.remoteAddress && @@ -53,6 +67,14 @@ export function attachSocketServer( ''; console.log('Client connected', clientAddress); + numberOfConnectedClients++; + + clearTimeout(browserConnectionTimeout); + tracker.track('browser-connection-created', { + successful: true, + timeMS: performance.now() - t0, + }); + let connected = true; let flipperServerCompanion: FlipperServerCompanion | undefined; @@ -220,24 +242,50 @@ export function attachSocketServer( safe(() => onClientMessage(data)); }); - async function onClientClose(error: Error | undefined = undefined) { - if (error) { - console.error(`Client disconnected ${clientAddress} with error`, error); - } else { - console.log(`Client disconnected ${clientAddress}`); - } + async function onClientClose(closeOnIdle: boolean) { + console.log(`Client disconnected ${clientAddress}`); + + numberOfConnectedClients--; connected = false; server.offAny(onServerEvent); flipperServerCompanion?.destroyAll(); + + if ( + getFlipperServerConfig().environmentInfo.isHeadlessBuild && + closeOnIdle + ) { + if (numberOfConnectedClients === 0 && isProduction()) { + console.info('Shutdown as no clients are currently connected'); + process.exit(0); + } + } } - client.on('close', () => { - safe(() => onClientClose()); + client.on('close', (code, _reason) => { + console.info('[flipper-server] Client close with code', code); + /** + * The socket will close as the endpoint is terminating + * the connection. Status code 1000 and 1001 are used for normal + * closures. Either the connection is no longer needed or the + * endpoint is going away i.e. browser navigating away from the + * current page. + * WS RFC: https://www.rfc-editor.org/rfc/rfc6455 + */ + const closeOnIdle = code === 1000 || code === 1001; + safe(() => onClientClose(closeOnIdle)); }); client.on('error', (error) => { - safe(() => onClientClose(error)); + safe(() => { + /** + * The socket will close due to an error. In this case, + * do not close on idle as there's a high probability the + * client will attempt to connect again. + */ + onClientClose(false); + console.error('Client disconnected with error', error); + }); }); }); } diff --git a/desktop/flipper-server-core/src/server/startFlipperServer.tsx b/desktop/flipper-server-core/src/server/startFlipperServer.tsx index ce92bf0f2..243887fb0 100644 --- a/desktop/flipper-server-core/src/server/startFlipperServer.tsx +++ b/desktop/flipper-server-core/src/server/startFlipperServer.tsx @@ -12,38 +12,38 @@ import { parseEnvironmentVariables, getLogger, FlipperServerType, + EnvironmentInfo, } from 'flipper-common'; import path from 'path'; import fs from 'fs-extra'; import {KeytarModule} from '../utils/keytar'; import {FlipperServerImpl} from '../FlipperServerImpl'; -import {getEnvironmentInfo} from '../utils/environmentInfo'; import {getGatekeepers} from '../gk'; import {loadLauncherSettings} from '../utils/launcherSettings'; import {loadProcessConfig} from '../utils/processConfig'; import {loadSettings} from '../utils/settings'; +import {sessionId} from '../sessionId'; /** * Creates an instance of FlipperServer (FlipperServerImpl). This is the * server used by clients to connect to. - * @param rootDir Application path. + * @param rootPath Application path. * @param staticPath Static assets path. * @param settingsString Optional settings used to override defaults. * @param enableLauncherSettings Optional launcher settings used to override defaults. * @returns */ export async function startFlipperServer( - rootDir: string, + rootPath: string, staticPath: string, settingsString: string, enableLauncherSettings: boolean, - keytarModule: KeytarModule, + keytarModule: KeytarModule | undefined, type: FlipperServerType, + environmentInfo: EnvironmentInfo, ): Promise { const execPath = process.execPath; - const appPath = rootDir; - const isProduction = - process.env.NODE_ENV !== 'development' && process.env.NODE_ENV !== 'test'; + const appPath = rootPath; const env = process.env; let desktopPath = path.resolve(os.homedir(), 'Desktop'); @@ -53,13 +53,15 @@ export async function startFlipperServer( desktopPath = os.homedir(); } - const environmentInfo = await getEnvironmentInfo(appPath, isProduction, true); - + const [launcherSettings, settings] = await Promise.all([ + loadLauncherSettings(enableLauncherSettings), + loadSettings(settingsString), + ]); return new FlipperServerImpl( { + sessionId, environmentInfo, env: parseEnvironmentVariables(process.env), - // TODO: make username parameterizable gatekeepers: getGatekeepers(environmentInfo.os.unixname), paths: { appPath, @@ -69,9 +71,9 @@ export async function startFlipperServer( tempPath: os.tmpdir(), desktopPath: desktopPath, }, - launcherSettings: await loadLauncherSettings(enableLauncherSettings), + launcherSettings, processConfig: loadProcessConfig(env), - settings: await loadSettings(settingsString), + settings, validWebSocketOrigins: ['localhost:', 'http://localhost:'], type, }, diff --git a/desktop/flipper-server-core/src/server/startServer.tsx b/desktop/flipper-server-core/src/server/startServer.tsx index 832b5038a..4531de67a 100644 --- a/desktop/flipper-server-core/src/server/startServer.tsx +++ b/desktop/flipper-server-core/src/server/startServer.tsx @@ -7,28 +7,26 @@ * @format */ -import os from 'os'; - import express, {Express} from 'express'; -import http, {ServerResponse} from 'http'; +import http from 'http'; import path from 'path'; import fs from 'fs-extra'; import {ServerOptions, VerifyClientCallbackSync, WebSocketServer} from 'ws'; -import {WEBSOCKET_MAX_MESSAGE_SIZE} from '../comms/ServerWebSocket'; +import {WEBSOCKET_MAX_MESSAGE_SIZE} from '../app-connectivity/ServerWebSocket'; import {parse} from 'url'; -import {makeSocketPath, checkSocketInUse} from './utilities'; - -import proxy from 'http-proxy'; import exitHook from 'exit-hook'; import {attachSocketServer} from './attachSocketServer'; import {FlipperServerImpl} from '../FlipperServerImpl'; import {FlipperServerCompanionEnv} from 'flipper-server-companion'; +import {validateAuthToken} from '../app-connectivity/certificate-exchange/certificate-utils'; +import {tracker} from '../tracker'; +import {EnvironmentInfo, isProduction} from 'flipper-common'; +import {GRAPH_SECRET} from '../fb-stubs/constants'; type Config = { port: number; - staticDir: string; + staticPath: string; entry: string; - tcp: boolean; }; type ReadyForConnections = ( @@ -36,18 +34,89 @@ type ReadyForConnections = ( companionEnv: FlipperServerCompanionEnv, ) => Promise; +const verifyAuthToken = (req: http.IncomingMessage): boolean => { + let token: string | null = null; + if (req.url) { + const url = new URL(req.url, `http://${req.headers.host}`); + token = url.searchParams.get('token'); + } + + if (!token && req.headers['x-access-token']) { + token = req.headers['x-access-token'] as string; + } + + if (!token) { + console.warn('[conn] A token is required for authentication'); + tracker.track('server-auth-token-verification', { + successful: false, + present: false, + error: 'No token was supplied', + }); + return false; + } + + try { + validateAuthToken(token); + console.info('[conn] Token was successfully validated'); + tracker.track('server-auth-token-verification', { + successful: true, + present: true, + }); + } catch (err) { + console.warn('[conn] An invalid token was supplied for authentication'); + tracker.track('server-auth-token-verification', { + successful: false, + present: true, + error: err.toString(), + }); + return false; + } + return true; +}; + /** - * Orchestrates the creation of the HTTP server, proxy, and web socket. - * @param config Server configuration. - * @returns Returns a promise to the created server, proxy and web socket. + * The following two variables are used to control when + * the server is ready to accept incoming connections. + * - isReady, is used to synchronously check if the server is + * ready or not. + * - isReadyWaitable achieves the same but is used by + * asynchronous functions which may want to wait until the + * server is ready. */ -export async function startServer(config: Config): Promise<{ +let isReady = false; +let isReadyWaitable: Promise | undefined; + +/** + * Time to wait until server becomes ready to accept incoming connections. + * If within 30 seconds it is not ready, the server is considered unresponsive + * and must be terminated. + */ +const timeoutSeconds = 30; + +/** + * Orchestrates the creation of the HTTP server, proxy, and WS server. + * @param config Server configuration. + * @returns Returns a promise to the created server, proxy and WS server. + */ +export async function startServer( + config: Config, + environmentInfo: EnvironmentInfo, +): Promise<{ app: Express; server: http.Server; socket: WebSocketServer; readyForIncomingConnections: ReadyForConnections; }> { - return await startHTTPServer(config); + setTimeout(() => { + if (!isReady && isProduction()) { + console.error( + `[flipper-server] Unable to become ready within ${timeoutSeconds} seconds, exit`, + ); + process.exit(1); + } + }, timeoutSeconds * 1000); + + return await startHTTPServer(config, environmentInfo); } /** @@ -56,7 +125,10 @@ export async function startServer(config: Config): Promise<{ * @param config Server configuration. * @returns A promise to both app and HTTP server. */ -async function startHTTPServer(config: Config): Promise<{ +async function startHTTPServer( + config: Config, + environmentInfo: EnvironmentInfo, +): Promise<{ app: Express; server: http.Server; socket: WebSocketServer; @@ -72,111 +144,102 @@ async function startHTTPServer(config: Config): Promise<{ }); app.get('/', (_req, res) => { - fs.readFile(path.join(config.staticDir, config.entry), (_err, content) => { - res.end(content); + const resource = isReady + ? path.join(config.staticPath, config.entry) + : path.join(config.staticPath, 'loading.html'); + fs.readFile(resource, (_err, content) => { + const processedContent = content + .toString() + .replace('GRAPH_SECRET_REPLACE_ME', GRAPH_SECRET) + .replace('FLIPPER_APP_VERSION_REPLACE_ME', environmentInfo.appVersion); + res.end(processedContent); }); }); + app.get('/ready', (_req, res) => { + tracker.track('server-endpoint-hit', {name: 'ready'}); + res.json({isReady}); + }); + + app.get('/info', (_req, res) => { + tracker.track('server-endpoint-hit', {name: 'info'}); + res.json(environmentInfo); + }); + + app.get('/shutdown', (_req, res) => { + console.info( + '[flipper-server] Received shutdown request, process will terminate', + ); + tracker.track('server-endpoint-hit', {name: 'shutdown'}); + res.json({success: true}); + + // Just exit the process, this will trigger the shutdown hooks. + process.exit(0); + }); + app.get('/health', (_req, res) => { + tracker.track('server-endpoint-hit', {name: 'health'}); res.end('flipper-ok'); }); - app.use(express.static(config.staticDir)); + app.use(express.static(config.staticPath)); - return startProxyServer(config, app); -} - -/** - * Creates and starts the HTTP and proxy servers. - * @param config Server configuration. - * @param app Express app. - * @returns Returns both the app and server configured and - * listening. - */ -async function startProxyServer( - config: Config, - app: Express, -): Promise<{ - app: Express; - server: http.Server; - socket: WebSocketServer; - readyForIncomingConnections: ReadyForConnections; -}> { const server = http.createServer(app); - const socket = addWebsocket(server, config); - - // For now, we only support domain socket access on POSIX-like systems. - // On Windows, a proxy is not created and the server starts - // listening at the specified port. - if (os.platform() === 'win32') { - if (!config.tcp) { - console.error( - 'No port was supplied and domain socket access is not available for non-POSIX systems, unable to start server', - ); - process.exit(1); - } - return new Promise((resolve) => { - console.log(`Starting server on http://localhost:${config.port}`); - const readyForIncomingConnections = (): Promise => { - return new Promise((resolve) => { - server.listen(config.port, undefined, () => resolve()); - }); - }; - resolve({app, server, socket, readyForIncomingConnections}); - }); - } - - const socketPath = await makeSocketPath(); - if (await checkSocketInUse(socketPath)) { - console.warn( - `Cannot start flipper-server because socket ${socketPath} is in use.`, - ); - } else { - console.info(`Cleaning up stale socket ${socketPath}`); - await fs.rm(socketPath, {force: true}); - } - - const proxyServer: proxy | undefined = config.tcp - ? proxy.createProxyServer({ - target: {host: 'localhost', port: 0, socketPath}, - autoRewrite: true, - ws: true, - }) - : undefined; - - console.log('Starting socket server on ', socketPath); - if (proxyServer) { - console.log(`Starting proxy server on http://localhost:${config.port}`); - } + const socket = attachWS(server, config); exitHook(() => { - console.log('Shutdown server'); - proxyServer?.close(); + console.log('[flipper-server] Shutdown HTTP server'); server.close(); - - console.log('Cleaning up socket on exit:', socketPath); - // This *must* run synchronously and we're not blocking any UI loop by definition. - // eslint-disable-next-line node/no-sync - fs.rmSync(socketPath, {force: true}); }); - proxyServer?.on('error', (err, _req, res) => { - console.warn('Error in proxy server:', err); - if (res instanceof ServerResponse) { - res.writeHead(502, 'Failed to proxy request'); + server.on('error', (e: NodeJS.ErrnoException) => { + console.warn('[flipper-server] HTTP server error: ', e.code); + tracker.track('server-error', {code: e.code, message: e.message}); + + if (e.code === 'EADDRINUSE') { + console.warn( + `[flipper-server] Unable to listen at port: ${config.port}, is already in use`, + ); + tracker.track('server-socket-already-in-use', {}); + process.exit(1); } - res.end('Failed to proxy request: ' + err); + }); + + server.listen(config.port); + + /** + * Create the promise which can be waited on. In this case, + * a reference to resolve is kept outside of the body of the promise + * so that other asynchronous functions can resolve the promise + * on its behalf. + */ + let isReadyResolver: (value: void | PromiseLike) => void; + isReadyWaitable = new Promise((resolve, _reject) => { + isReadyResolver = resolve; }); return new Promise((resolve) => { + console.info( + `[flipper-server] Starting server on http://localhost:${config.port}`, + ); const readyForIncomingConnections = ( serverImpl: FlipperServerImpl, companionEnv: FlipperServerCompanionEnv, ): Promise => { attachSocketServer(socket, serverImpl, companionEnv); + /** + * At this point, the server is ready to accept incoming + * connections. Change the isReady state and resolve the + * promise so that other asychronous function become unblocked. + */ + isReady = true; + isReadyResolver(); return new Promise((resolve) => { - proxyServer?.listen(config.port); - server.listen(socketPath, undefined, () => resolve()); + tracker.track('server-started', { + port: config.port, + }); + + resolve(); }); }; resolve({app, server, socket, readyForIncomingConnections}); @@ -190,61 +253,26 @@ async function startProxyServer( * incoming connections origin. * @returns Returns the created WS. */ -function addWebsocket(server: http.Server, config: Config) { - const localhostIPV4 = `localhost:${config.port}`; - const localhostIPV6 = `[::1]:${config.port}`; - const localhostIPV6NoBrackets = `::1:${config.port}`; - const localhostIPV4Electron = 'localhost:3000'; - - const possibleHosts = [ - localhostIPV4, - localhostIPV6, - localhostIPV6NoBrackets, - localhostIPV4Electron, - ]; - const possibleOrigins = possibleHosts - .map((host) => `http://${host}`) - .concat(['file://']); - - const verifyClient: VerifyClientCallbackSync = ({origin, req}) => { - const noOriginHeader = origin === undefined; - if ( - (noOriginHeader || possibleOrigins.includes(origin)) && - req.headers.host && - possibleHosts.includes(req.headers.host) - ) { - // no origin header? The request is not originating from a browser, so should be OK to pass through - // If origin matches our own address, it means we are serving the page - return true; - } else { - // for now we don't allow cross origin request, so that an arbitrary website cannot try to - // connect a socket to localhost:serverport, and try to use the all powerful Flipper APIs to read - // for example files. - // Potentially in the future we do want to allow this, e.g. if we want to connect to a local flipper-server - // directly from intern. But before that, we should either authenticate the request somehow, - // and discuss security impact and for example scope the files that can be read by Flipper. - console.warn( - `Refused socket connection from cross domain request, origin: ${origin}, host: ${ - req.headers.host - }. Expected origins: ${possibleOrigins.join( - ' or ', - )}. Expected hosts: ${possibleHosts.join(' or ')}`, - ); - return false; - } +function attachWS(server: http.Server, _config: Config) { + const verifyClient: VerifyClientCallbackSync = ({req}) => { + return process.env.SKIP_TOKEN_VERIFICATION ? true : verifyAuthToken(req); }; const options: ServerOptions = { noServer: true, maxPayload: WEBSOCKET_MAX_MESSAGE_SIZE, + verifyClient, }; - if (config.tcp) { - options.verifyClient = verifyClient; - } const wss = new WebSocketServer(options); - server.on('upgrade', function upgrade(request, socket, head) { - const {pathname} = parse(request.url!); + server.on('upgrade', async function upgrade(request, socket, head) { + if (!request.url) { + console.log('[flipper-server] No request URL available'); + socket.destroy(); + return; + } + + const {pathname} = parse(request.url); // Handled by Metro if (pathname === '/hot') { @@ -252,13 +280,18 @@ function addWebsocket(server: http.Server, config: Config) { } if (pathname === '/') { + // Wait until the server is ready to accept incoming connections. + await isReadyWaitable; wss.handleUpgrade(request, socket, head, function done(ws) { wss.emit('connection', ws, request); }); return; } - console.error('addWebsocket.upgrade -> unknown pathname', pathname); + console.error( + '[flipper-server] Unable to upgrade, unknown pathname', + pathname, + ); socket.destroy(); }); diff --git a/desktop/flipper-server-core/src/server/utilities.tsx b/desktop/flipper-server-core/src/server/utilities.tsx index adfe3815d..5e4aaf213 100644 --- a/desktop/flipper-server-core/src/server/utilities.tsx +++ b/desktop/flipper-server-core/src/server/utilities.tsx @@ -11,63 +11,97 @@ import os from 'os'; import xdgBasedir from 'xdg-basedir'; import net from 'net'; import fs from 'fs-extra'; +import fetch from 'node-fetch'; +import {EnvironmentInfo} from 'flipper-common'; +import semver from 'semver'; /** - * Checks if a socket is in used for given path. - * If the path doesn't exist then is not in use. If it does, - * create a socket client and attempt to connect to it. - * If the connection is established, then there's a process - * already listening. Otherwise, it kind of indicates the - * contrary. - * @param path Path used instead of port number. - * @returns True or false dependning on whether the socket is in - * use or not. + * Checks if a port is in use. + * @param port The port to check. + * @returns True if the port is in use. Otherwise, false. */ -export async function checkSocketInUse(path: string): Promise { - if (!(await fs.pathExists(path))) { - return false; +export async function checkPortInUse(port: number): Promise { + interface HttpError extends Error { + code?: string; } - return new Promise((resolve, _reject) => { - const client = net - .createConnection(path, () => { + + return new Promise((resolve, reject) => { + const tester = net + .createServer() + .once('error', function (err: HttpError) { + if (err.code != 'EADDRINUSE') return reject(err); resolve(true); - client.destroy(); }) - .on('error', (e) => { - if (e.message.includes('ECONNREFUSED')) { - resolve(false); - } else { - console.warn( - `[conn] Socket ${path} is in use, but we don't know why.`, - e, - ); - resolve(false); - } - client.destroy(); - }); + .once('listening', function () { + tester + .once('close', function () { + resolve(false); + }) + .close(); + }) + .listen(port); }); } /** - * Creates a socket path. Used to open the socket at location. - * Format: flipper-server-${userInfo}.sock - * @returns The created socket path. + * Checks if a running Flipper server is available on the given port. + * @param port The port to check. + * @returns If successful, it will return the version of the running + * Flipper server. Otherwise, undefined. */ -export async function makeSocketPath(): Promise { - const runtimeDir = xdgBasedir.runtime || '/tmp'; - await fs.mkdirp(runtimeDir); - const filename = `flipper-server-${os.userInfo().uid}.sock`; - const path = `${runtimeDir}/${filename}`; - - // Depending on the OS this is between 104 and 108: - // https://unix.stackexchange.com/a/367012/10198 - if (path.length >= 104) { - console.warn( - 'Ignoring XDG_RUNTIME_DIR as it would exceed the total limit for domain socket paths. See man 7 unix.', +export async function checkServerRunning( + port: number, +): Promise { + try { + const response = await fetch(`http://localhost:${port}/info`); + if (response.status >= 200 && response.status < 300) { + const environmentInfo: EnvironmentInfo = await response.json(); + return environmentInfo.appVersion; + } else { + console.info( + '[flipper-server] Running instance found, failed with HTTP status code: ', + response.status, + ); + } + } catch (e) { + console.info( + `[flipper-server] No running instance found, error found: ${e}`, ); - // Even with the INT32_MAX userid, we should have plenty of room. - return `/tmp/${filename}`; + } +} + +/** + * Attempts to shutdown an existing Flipper server instance. + * @param port The port of the running Flipper server + * @returns Returns true if the shutdown was successful. Otherwise, false. + */ +export async function shutdownRunningInstance(port: number): Promise { + try { + const response = await fetch(`http://localhost:${port}/shutdown`); + if (response.status >= 200 && response.status < 300) { + const json = await response.json(); + console.info( + `[flipper-server] Shutdown request acknowledge: ${json?.success}`, + ); + return json?.success; + } else { + console.warn( + '[flipper-server] Shutdown request not handled, HTTP status code: ', + response.status, + ); + } + } catch (e) { + console.warn('[flipper-server] Shutdown request failed with error: ', e); } - return path; + return false; +} + +/** + * Compares two versions excluding build identifiers + * (the bit after + in the semantic version string). + * @return 0 if v1 == v2, 1 if v1 is greater, -1 if v2 is greater. + */ +export function compareServerVersion(v1: string, v2: string): number { + return semver.compare(v1, v2); } diff --git a/desktop/flipper-server-core/src/sessionId.tsx b/desktop/flipper-server-core/src/sessionId.tsx new file mode 100644 index 000000000..4df6f1020 --- /dev/null +++ b/desktop/flipper-server-core/src/sessionId.tsx @@ -0,0 +1,12 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {uuid} from 'flipper-common'; + +export const sessionId = uuid(); diff --git a/desktop/flipper-server-core/src/tracker.tsx b/desktop/flipper-server-core/src/tracker.tsx new file mode 100644 index 000000000..4f69560a7 --- /dev/null +++ b/desktop/flipper-server-core/src/tracker.tsx @@ -0,0 +1,69 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {getLogger, CertificateExchangeMedium} from 'flipper-common'; + +type AppConnectionPayload = { + app: string; + os: string; + device: string; + device_id: string; + medium?: CertificateExchangeMedium; +}; + +type AppConnectionCertificateExchangePayload = AppConnectionPayload & { + successful: boolean; + error?: string; +}; + +type ServerBootstrapPerformancePayload = { + loggerInitializedMS: number; + keytarLoadedMS: number; + runningInstanceShutdownMS: number; + httpServerStartedMS: number; + serverCreatedMS: number; + companionEnvironmentInitializedMS: number; + appServerStartedMS: number; + developmentServerAttachedMS: number; + serverStartedMS: number; + launchedMS: number; + startupMS: number; +}; + +type TrackerEvents = { + 'server-bootstrap-performance': ServerBootstrapPerformancePayload; + 'server-started': {port: number}; + 'server-error': {code: string | undefined; message: string}; + 'server-endpoint-hit': {name: string}; + 'server-auth-token-verification': { + successful: boolean; + present: boolean; + error?: string; + }; + 'server-socket-already-in-use': {}; + 'browser-connection-created': { + successful: boolean; + timeMS: number; + }; + 'app-connection-created': AppConnectionPayload; + 'app-connection-secure-attempt': AppConnectionPayload; + 'app-connection-insecure-attempt': AppConnectionPayload; + 'app-connection-certificate-exchange': AppConnectionCertificateExchangePayload; +}; + +class ServerCoreTracker { + track( + event: Event, + payload: TrackerEvents[Event], + ): void { + getLogger().track('usage', event, payload); + } +} + +export const tracker = new ServerCoreTracker(); diff --git a/desktop/flipper-server-core/src/utils/DeviceListener.tsx b/desktop/flipper-server-core/src/utils/DeviceListener.tsx index 45f2103c0..cf2807eb5 100644 --- a/desktop/flipper-server-core/src/utils/DeviceListener.tsx +++ b/desktop/flipper-server-core/src/utils/DeviceListener.tsx @@ -8,7 +8,7 @@ */ import {sleep} from 'flipper-common'; -import {assertNotNull} from '../comms/Utilities'; +import {assertNotNull} from '../app-connectivity/Utilities'; import {StateMachine} from './StateMachine'; export const RESTART_CNT = 3; @@ -106,14 +106,14 @@ export abstract class DeviceListener { } catch (e) { if (this.restartCnt <= 0) { this._state.set('fatal', e); - console.error( + console.warn( `${this.name}.start -> failure after ${RESTART_CNT} retries`, e, ); return; } - console.warn( + console.debug( `${this.name}.start -> error. Retrying. ${this.restartCnt} retries left.`, e, ); diff --git a/desktop/flipper-server-core/src/utils/__tests__/processConfig.node.tsx b/desktop/flipper-server-core/src/utils/__tests__/processConfig.node.tsx index 2a1d6af63..5e4a3d0f4 100644 --- a/desktop/flipper-server-core/src/utils/__tests__/processConfig.node.tsx +++ b/desktop/flipper-server-core/src/utils/__tests__/processConfig.node.tsx @@ -17,6 +17,8 @@ test('config is decoded from env', () => { launcherMsg: 'wubba lubba dub dub', screenCapturePath: '/my/screenshot/path', launcherEnabled: false, + suppressPluginUpdateNotifications: true, + updaterEnabled: true, }), }); @@ -26,6 +28,8 @@ test('config is decoded from env', () => { launcherMsg: 'wubba lubba dub dub', screenCapturePath: '/my/screenshot/path', launcherEnabled: false, + suppressPluginUpdateNotifications: true, + updaterEnabled: true, }); }); @@ -36,5 +40,7 @@ test('config is decoded from env with defaults', () => { launcherMsg: undefined, screenCapturePath: undefined, launcherEnabled: true, + suppressPluginUpdateNotifications: false, + updaterEnabled: true, }); }); diff --git a/desktop/flipper-server-core/src/utils/keytar.tsx b/desktop/flipper-server-core/src/utils/keytar.tsx index 4c81233ed..cc8b09858 100644 --- a/desktop/flipper-server-core/src/utils/keytar.tsx +++ b/desktop/flipper-server-core/src/utils/keytar.tsx @@ -13,17 +13,27 @@ import {UserNotSignedInError} from 'flipper-common'; export const SERVICE_FLIPPER = 'flipper.oAuthToken'; export type KeytarModule = { - getPassword(service: string, username: string): string; - deletePassword(service: string, username: string): void; - setPassword(service: string, username: string, password: string): void; + getPassword(service: string, username: string): Promise; + deletePassword(service: string, username: string): Promise; + setPassword( + service: string, + username: string, + password: string, + ): Promise; }; export class KeytarManager { + private memoryFallback = new Map(); + constructor(private keytar: KeytarModule | undefined) {} public async writeKeychain(service: string, password: string): Promise { if (this.keytar == null) { - throw new Error('Keytar is not available.'); + console.warn( + 'Keytar is not available, using session only memory storage as fallback', + ); + this.memoryFallback.set(service, password); + return; } await this.keytar.deletePassword(service, os.userInfo().username); @@ -31,17 +41,17 @@ export class KeytarManager { } public async unsetKeychain(service: string): Promise { - await this.keytar?.deletePassword(service, os.userInfo().username); + if (this.keytar) { + await this.keytar.deletePassword(service, os.userInfo().username); + } else { + this.memoryFallback.delete(service); + } } public async retrieveToken(service: string): Promise { - if (this.keytar == null) { - throw new Error('Keytar is not available.'); - } - const token = await this.keytar.getPassword( - service, - os.userInfo().username, - ); + const token = this.keytar + ? await this.keytar.getPassword(service, os.userInfo().username) + : this.memoryFallback.get(service); if (!token) { throw new UserNotSignedInError(); } diff --git a/desktop/flipper-server-core/src/utils/processConfig.tsx b/desktop/flipper-server-core/src/utils/processConfig.tsx index 71085f59a..a71904453 100644 --- a/desktop/flipper-server-core/src/utils/processConfig.tsx +++ b/desktop/flipper-server-core/src/utils/processConfig.tsx @@ -18,5 +18,11 @@ export function loadProcessConfig(env: NodeJS.ProcessEnv): ProcessConfig { screenCapturePath: json.screenCapturePath, launcherEnabled: typeof json.launcherEnabled === 'boolean' ? json.launcherEnabled : true, + updaterEnabled: + typeof json.updaterEnabled === 'boolean' ? json.updaterEnabled : true, + suppressPluginUpdateNotifications: + typeof json.suppressPluginUpdateNotifications === 'boolean' + ? json.suppressPluginUpdateNotifications + : false, }; } diff --git a/desktop/flipper-server-core/tsconfig.json b/desktop/flipper-server-core/tsconfig.json index 8f00d4714..ceed7bc5f 100644 --- a/desktop/flipper-server-core/tsconfig.json +++ b/desktop/flipper-server-core/tsconfig.json @@ -8,7 +8,8 @@ "../types/JSONStream", "../types/adbkit", "../types/openssl-wrapper", - "../types/adbkit-logcat" + "../types/adbkit-logcat", + "../types/flipperGlobals" ] }, "references": [ @@ -19,7 +20,7 @@ "path": "../flipper-common" }, { - "path": "../flipper-plugin" + "path": "../flipper-plugin-core" }, { "path": "../flipper-server-companion" diff --git a/desktop/flipper-server/package.json b/desktop/flipper-server/package.json index 344e0cc30..cb6775428 100644 --- a/desktop/flipper-server/package.json +++ b/desktop/flipper-server/package.json @@ -8,27 +8,30 @@ "bin": "server.js", "license": "MIT", "bugs": "https://github.com/facebook/flipper/issues", - "dependencies": {}, - "devDependencies": { - "@types/express": "^4.17.13", - "@types/http-proxy": "^1.17.8", - "@types/node": "^17.0.31", + "dependencies": { "chalk": "^4", "exit-hook": "^2.1.1", "express": "^4.17.3", "file-stream-rotator": "^0.6.1", "flipper-common": "0.0.0", "flipper-pkg-lib": "0.0.0", + "flipper-server-client": "0.0.0", "flipper-server-companion": "0.0.0", "flipper-server-core": "0.0.0", - "fs-extra": "^10.1.0", + "fs-extra": "^11.1.1", "http-proxy": "^1.18.1", "metro": "^0.70.2", "open": "^8.3.0", "p-filter": "^2.1.0", - "ws": "^8.5.0", + "reconnecting-websocket": "^4.4.0", + "ws": "8.13.0", "xdg-basedir": "^4", - "yargs": "^17.4.1" + "yargs": "^17.7.2" + }, + "devDependencies": { + "@types/express": "^4.17.13", + "@types/http-proxy": "^1.17.8", + "@types/node": "^17.0.31" }, "peerDependencies": {}, "scripts": { @@ -36,7 +39,7 @@ "build": "tsc -b" }, "files": [ - "dist/**/*", + "lib/**/*", "static/**/*", "README.md", "server.js" diff --git a/desktop/flipper-server/server.js b/desktop/flipper-server/server.js index 196636018..8b22e0933 100755 --- a/desktop/flipper-server/server.js +++ b/desktop/flipper-server/server.js @@ -8,9 +8,7 @@ * @format */ -/* eslint-disable */ +process.title = 'flipper-runtime'; -// flipper-server uses the same infra & babel transforms as Electron -// to make sure our own sources are bundled up, but node modules arent -global.electronRequire = require; -require('./dist/index.js'); +// eslint-disable-next-line import/no-unresolved +require('./lib/index.js'); diff --git a/desktop/flipper-server/src/attachDevServer.tsx b/desktop/flipper-server/src/attachDevServer.tsx index 2cac7a99f..40cd14ac9 100644 --- a/desktop/flipper-server/src/attachDevServer.tsx +++ b/desktop/flipper-server/src/attachDevServer.tsx @@ -15,6 +15,8 @@ import fs from 'fs-extra'; import {WebSocketServer} from 'ws'; import pFilter from 'p-filter'; import {homedir} from 'os'; +import {InstalledPluginDetails} from 'flipper-common'; +import {isFBBuild} from 'flipper-server-core'; // This file is heavily inspired by scripts/start-dev-server.ts! // part of that is done by start-flipper-server-dev (compiling "main"), @@ -23,19 +25,15 @@ import {homedir} from 'os'; const uiSourceDirs = [ 'flipper-ui-browser', 'flipper-ui-core', + 'flipper-plugin-core', 'flipper-plugin', + 'flipper-frontend-core', 'flipper-common', ]; // copied from plugin-lib/src/pluginPaths export async function getPluginSourceFolders(): Promise { const pluginFolders: string[] = []; - if (process.env.FLIPPER_NO_DEFAULT_PLUGINS) { - console.log( - '🥫 Skipping default plugins because "--no-default-plugins" flag provided', - ); - return pluginFolders; - } const flipperConfigPath = path.join(homedir(), '.flipper', 'config.json'); if (await fs.pathExists(flipperConfigPath)) { const config = await fs.readJson(flipperConfigPath); @@ -65,7 +63,8 @@ export async function attachDevServer( // prevent bundling! const Metro = electronRequire('metro'); const MetroResolver = electronRequire('metro-resolver'); - const {getWatchFolders} = electronRequire('flipper-pkg-lib'); + const {getWatchFolders, startWatchPlugins} = + electronRequire('flipper-pkg-lib'); const babelTransformationsDir = path.resolve( rootDir, @@ -90,7 +89,6 @@ export async function attachDevServer( uiSourceDirs.map((dir) => getWatchFolders(path.resolve(rootDir, dir))), ) ).flat(), - ...(await getPluginSourceFolders()), ]); const baseConfig = await Metro.loadConfig(); @@ -154,6 +152,21 @@ export async function attachDevServer( next(); }); + await startWatchPlugins( + process.env.FLIPPER_RELEASE_CHANNEL === 'insiders', + isFBBuild && !process.env.FLIPPER_FORCE_PUBLIC_BUILD, + (changedPlugins: InstalledPluginDetails[]) => { + socket.clients.forEach((client) => { + client.send( + JSON.stringify({ + event: 'plugins-source-updated', + payload: changedPlugins, + }), + ); + }); + }, + ); + console.log('DEV webserver started.'); } diff --git a/desktop/flipper-server/src/electronRequire.tsx b/desktop/flipper-server/src/electronRequire.tsx new file mode 100644 index 000000000..ec76d9644 --- /dev/null +++ b/desktop/flipper-server/src/electronRequire.tsx @@ -0,0 +1,11 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +// @ts-expect-error +global.electronRequire = require; diff --git a/desktop/flipper-server/src/fb-stubs.tsx b/desktop/flipper-server/src/fb-stubs.tsx new file mode 100644 index 000000000..784f22324 --- /dev/null +++ b/desktop/flipper-server/src/fb-stubs.tsx @@ -0,0 +1,34 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import Module from 'module'; + +Module.prototype.require = new Proxy(Module.prototype.require, { + apply(target, thisArg, argumentsList) { + const name = argumentsList[0]; + + if ( + process.env.FLIPPER_FORCE_PUBLIC_BUILD !== 'true' && + typeof name === 'string' && + name.includes('fb-stubs') + ) { + const replacement = name.replace('/fb-stubs/', '/fb/'); + try { + return Reflect.apply(target, thisArg, [ + replacement, + argumentsList.slice(1), + ]); + } catch { + return Reflect.apply(target, thisArg, argumentsList); + } + } + + return Reflect.apply(target, thisArg, argumentsList); + }, +}); diff --git a/desktop/flipper-server/src/findInstallation.tsx b/desktop/flipper-server/src/findInstallation.tsx new file mode 100644 index 000000000..63b4469be --- /dev/null +++ b/desktop/flipper-server/src/findInstallation.tsx @@ -0,0 +1,29 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import path from 'path'; +import fs from 'fs-extra'; +import os from 'os'; + +export async function findInstallation(): Promise { + if (os.platform() !== 'darwin') { + return; + } + + const appPath = path.join( + os.homedir(), + 'Applications', + 'Chrome Apps.localized', + 'Flipper.app', + ); + const appPlistPath = path.join(appPath, 'Contents', 'Info.plist'); + if (await fs.pathExists(appPlistPath)) { + return appPath; + } +} diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index 47664a2c4..2a4cf381e 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -7,6 +7,8 @@ * @format */ +import './fb-stubs'; +import './electronRequire'; import process from 'process'; import chalk from 'chalk'; import path from 'path'; @@ -15,10 +17,22 @@ import {initializeLogger} from './logger'; import fs from 'fs-extra'; import yargs from 'yargs'; import open from 'open'; +import os from 'os'; import {initCompanionEnv} from 'flipper-server-companion'; -import {startFlipperServer, startServer} from 'flipper-server-core'; -import {isTest} from 'flipper-common'; +import { + checkPortInUse, + checkServerRunning, + compareServerVersion, + getEnvironmentInfo, + shutdownRunningInstance, + startFlipperServer, + startServer, + tracker, +} from 'flipper-server-core'; +import {addLogTailer, isTest, LoggerFormat} from 'flipper-common'; import exitHook from 'exit-hook'; +import {getAuthToken} from 'flipper-server-core'; +import {findInstallation} from './findInstallation'; const argv = yargs .usage('yarn flipper-server [args]') @@ -56,36 +70,60 @@ const argv = yargs type: 'boolean', default: true, }, - tcp: { - describe: - 'Open a TCP port (--no-tcp can be specified as to use unix-domain-socket exclusively)', - type: 'boolean', - default: true, - }, }) .version('DEV') .help() .parse(process.argv.slice(1)); console.log( - `Starting flipper server with ${ + `[flipper-server] Starting flipper server with ${ argv.bundler ? 'UI bundle from source' : 'pre-bundled UI' }`, ); -const rootDir = argv.bundler +/** + * When running as a standlone app not run from the terminal, the process itself + * doesn't inherit the user's terminal PATH environment variable. + * The PATH, when NOT launched from terminal is `/usr/bin:/bin:/usr/sbin:/sbin` + * which is missing `/usr/local/bin`. + */ +if (os.platform() !== 'win32') { + process.env.PATH = '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'; +} + +const rootPath = argv.bundler ? path.resolve(__dirname, '..', '..') - : path.resolve(__dirname, '..'); // in pre packaged versions of the server, static is copied inside the package -const staticDir = path.join(rootDir, 'static'); + : path.resolve(__dirname, '..'); // In pre-packaged versions of the server, static is copied inside the package. +const staticPath = path.join(rootPath, 'static'); async function start() { - const enhanceLogger = await initializeLogger(staticDir); + const t0 = performance.now(); + + const isProduction = + process.env.NODE_ENV !== 'development' && process.env.NODE_ENV !== 'test'; + const environmentInfo = await getEnvironmentInfo( + rootPath, + isProduction, + true, + ); + + await initializeLogger(environmentInfo, staticPath); + + const t1 = performance.now(); + const loggerInitializedMS = t1 - t0; + console.info( + `[flipper-server][bootstrap] Logger initialised (${loggerInitializedMS} ms)`, + ); let keytar: any = undefined; try { - if (!isTest()) { + if (process.env.FLIPPER_DISABLE_KEYTAR) { + console.log( + '[flipper-server][bootstrap] Using keytar in-memory implementation as per FLIPPER_DISABLE_KEYTAR env var.', + ); + } else if (!isTest()) { const keytarPath = path.join( - staticDir, + staticPath, 'native-modules', `keytar-${process.platform}-${process.arch}.node`, ); @@ -97,55 +135,204 @@ async function start() { keytar = electronRequire(keytarPath); } } catch (e) { - console.error('Failed to load keytar:', e); + console.error('[flipper-server] Failed to load keytar:', e); } - const {app, server, socket, readyForIncomingConnections} = await startServer({ - staticDir, - entry: `index.web${argv.bundler ? '.dev' : ''}.html`, - port: argv.port, - tcp: argv.tcp, - }); + const t2 = performance.now(); + const keytarLoadedMS = t2 - t1; + console.info( + `[flipper-server][bootstrap] Keytar loaded (${keytarLoadedMS} ms)`, + ); + + let launchAndFinish = false; + + console.info('[flipper-server] Check for running instances'); + const existingRunningInstanceVersion = await checkServerRunning(argv.port); + if (existingRunningInstanceVersion) { + console.info( + `[flipper-server] Running instance found with version: ${existingRunningInstanceVersion}, current version: ${environmentInfo.appVersion}`, + ); + if ( + compareServerVersion( + environmentInfo.appVersion, + existingRunningInstanceVersion, + ) > 0 + ) { + console.info(`[flipper-server] Shutdown running instance`); + await shutdownRunningInstance(argv.port); + } else { + launchAndFinish = true; + } + } else { + console.info('[flipper-server] Checking if port is in use (TCP)'); + if (await checkPortInUse(argv.port)) { + console.info(`[flipper-server] Shutdown running instance`); + await shutdownRunningInstance(argv.port); + } + } + + const t3 = performance.now(); + const runningInstanceShutdownMS = t3 - t2; + console.info( + `[flipper-server][bootstrap] Check for running instances completed (${runningInstanceShutdownMS} ms)`, + ); + + if (launchAndFinish) { + return await launch(); + } + + const {app, server, socket, readyForIncomingConnections} = await startServer( + { + staticPath, + entry: `index.web${argv.bundler ? '.dev' : ''}.html`, + port: argv.port, + }, + environmentInfo, + ); + + const t4 = performance.now(); + const httpServerStartedMS = t4 - t3; + + console.info( + `[flipper-server][bootstrap] HTTP server started (${httpServerStartedMS} ms)`, + ); const flipperServer = await startFlipperServer( - rootDir, - staticDir, + rootPath, + staticPath, argv.settingsString, argv.launcherSettings, keytar, 'external', + environmentInfo, ); + const t5 = performance.now(); + const serverCreatedMS = t5 - t4; + console.info( + `[flipper-server][bootstrap] FlipperServer created (${serverCreatedMS} ms)`, + ); + + // At this point, the HTTP server is ready and configuration is set. + await launch(); + + if (!isProduction) { + addLogTailer((level, ...data) => { + flipperServer.emit('server-log', LoggerFormat(level, ...data)); + }); + } + + const t6 = performance.now(); + const launchedMS = t6 - t5; + exitHook(async () => { + console.log('[flipper-server] Shutdown Flipper server'); await flipperServer.close(); }); - enhanceLogger((logEntry) => { - flipperServer.emit('server-log', logEntry); - }); - const companionEnv = await initCompanionEnv(flipperServer); + + const t7 = performance.now(); + const companionEnvironmentInitializedMS = t7 - t6; + + console.info( + `[flipper-server][bootstrap] Companion environment initialised (${companionEnvironmentInitializedMS} ms)`, + ); + if (argv.failFast) { flipperServer.on('server-state', ({state}) => { if (state === 'error') { console.error( - '[flipper-server-process-exit] state changed to error, process will exit.', + '[flipper-server] state changed to error, process will exit.', ); process.exit(1); } }); } - await flipperServer.connect(); + await flipperServer + .connect() + .catch((e) => console.warn('Flipper Server failed to initialize', e)); + + const t8 = performance.now(); + const appServerStartedMS = t8 - t7; + console.info( + `[flipper-server][bootstrap] Ready for app connections (${appServerStartedMS} ms)`, + ); if (argv.bundler) { - await attachDevServer(app, server, socket, rootDir); + await attachDevServer(app, server, socket, rootPath); } - await readyForIncomingConnections(flipperServer, companionEnv); + + const t9 = performance.now(); + const developmentServerAttachedMS = t9 - t8; + console.info( + `[flipper-server][bootstrap] Development server attached (${developmentServerAttachedMS} ms)`, + ); + readyForIncomingConnections(flipperServer, companionEnv); + + const t10 = performance.now(); + const serverStartedMS = t10 - t9; + console.info( + `[flipper-server][bootstrap] Listening at port ${chalk.green( + argv.port, + )} (${serverStartedMS} ms)`, + ); + + const startupMS = t10 - t0; + + tracker.track('server-bootstrap-performance', { + loggerInitializedMS, + keytarLoadedMS, + runningInstanceShutdownMS, + httpServerStartedMS, + serverCreatedMS, + companionEnvironmentInitializedMS, + appServerStartedMS, + developmentServerAttachedMS, + serverStartedMS, + launchedMS, + startupMS, + }); +} + +async function launch() { + console.info('[flipper-server] Launch UI'); + + const token = await getAuthToken(); + console.info( + `[flipper-server] Get authentication token: ${token?.length != 0}`, + ); + + if (!argv.open) { + return; + } + + const openInBrowser = async () => { + console.info('[flipper-server] Open in browser'); + + const searchParams = new URLSearchParams({token: token ?? ''}); + const url = new URL(`http://localhost:${argv.port}?${searchParams}`); + + open(url.toString(), {app: {name: open.apps.chrome}}); + }; + + if (argv.bundler) { + await openInBrowser(); + } else { + const path = await findInstallation(); + if (path) { + open(path); + } else { + await openInBrowser(); + } + } + + console.info('[flipper-server] Launch UI completed'); } process.on('uncaughtException', (error) => { console.error( - '[flipper-server-process-exit] uncaught exception, process will exit.', + '[flipper-server] uncaught exception, process will exit.', error, ); process.exit(1); @@ -160,22 +347,7 @@ process.on('unhandledRejection', (reason, promise) => { ); }); -start() - .then(() => { - if (!argv.tcp) { - console.log('Flipper server started and listening'); - return; - } - console.log( - 'Flipper server started and listening at port ' + chalk.green(argv.port), - ); - const url = `http://localhost:${argv.port}`; - console.log('Go to: ' + chalk.green(chalk.bold(url))); - if (argv.open) { - open(url); - } - }) - .catch((e) => { - console.error(chalk.red('Server startup error: '), e); - process.exit(1); - }); +start().catch((e) => { + console.error(chalk.red('Server startup error: '), e); + process.exit(1); +}); diff --git a/desktop/flipper-server/src/logger.tsx b/desktop/flipper-server/src/logger.tsx index ed1d6469a..484e964a7 100644 --- a/desktop/flipper-server/src/logger.tsx +++ b/desktop/flipper-server/src/logger.tsx @@ -9,11 +9,11 @@ import path from 'path'; import { + addLogTailer, + EnvironmentInfo, LoggerExtractError, LoggerFormat, - LoggerInfo, LoggerTypes, - Logger, setLoggerInstance, } from 'flipper-common'; // @ts-expect-error @@ -21,81 +21,21 @@ import fsRotator from 'file-stream-rotator'; import {ensureFile} from 'fs-extra'; import {access} from 'fs/promises'; import {constants} from 'fs'; +import {initializeLogger as initLogger} from 'flipper-server-core'; export const loggerOutputFile = 'flipper-server-log.out'; -const logTypes: LoggerTypes[] = ['debug', 'info', 'warn', 'error']; - -function createLogger(): Logger { - return { - track(..._args: [any, any, any?, any?]) { - // TODO: only if verbose console.debug(...args); - // console.warn('(skipper track)', args); - }, - trackTimeSince(..._args: [any, any, any?]) { - // TODO: only if verbose console.debug(...args); - // console.warn('(skipped trackTimeSince)', args); - }, - debug(...args: any[]) { - console.debug(...args); - }, - error(...args: any[]) { - console.error(...args); - }, - warn(...args: any[]) { - console.warn(...args); - }, - info(...args: any[]) { - console.info(...args); - }, - }; -} - -type FlipperLogProxy = (entry: LoggerInfo) => void; - -const consoleProxy = (proxy: FlipperLogProxy) => { - function log(level: LoggerTypes, ...data: Array): void { - const logInfo = LoggerFormat(level, ...data); - proxy(logInfo); - - if (level === 'error') { - const { - message, - error: {stack, interaction, name}, - } = LoggerExtractError(data); - const logInfo = LoggerFormat(level, { - name, - stack, - interaction, - message, - }); - proxy(logInfo); - } - } - - for (const method of logTypes) { - const originalConsole: {[key: string]: any} = console; - const originalMethod = originalConsole[method]; - const overrideMethod = (...args: Array) => { - const result = originalMethod(...args); - log(method, ...args); - return result; - }; - originalConsole[method] = overrideMethod; - } -}; - -export async function initializeLogger(staticDir: string) { +export async function initializeLogger( + environmentInfo: EnvironmentInfo, + staticDir: string, +) { // Suppress stdout debug messages, but keep writing them to the file. console.debug = function () {}; - const logger = createLogger(); + const logger = initLogger(environmentInfo); setLoggerInstance(logger); - let onConsoleEntry: ((entry: LoggerInfo) => void) | undefined; - const logFilename = path.join(staticDir, loggerOutputFile); - let logStream: NodeJS.WriteStream | undefined = undefined; try { await ensureFile(logFilename); @@ -112,12 +52,16 @@ export async function initializeLogger(staticDir: string) { console.warn('initializeLogger -> cannot write logs to FS', e); } - consoleProxy((entry: LoggerInfo) => { - logStream?.write(`${JSON.stringify(entry)}\n`); - onConsoleEntry?.(entry); - }); + addLogTailer((level: LoggerTypes, ...data: Array) => { + const logInfo = LoggerFormat(level, ...data); + logStream?.write(`[${logInfo.time}][${logInfo.type}] ${logInfo.msg}\n`); - return (newOnConsoleEntry: (entry: LoggerInfo) => void) => { - onConsoleEntry = newOnConsoleEntry; - }; + if (level === 'error') { + const { + error: {stack, name}, + } = LoggerExtractError(data); + + logStream?.write(`${name}: \n${stack}\n`); + } + }); } diff --git a/desktop/flipper-server/tsconfig.json b/desktop/flipper-server/tsconfig.json index 215a4c1f9..4c526ac8b 100644 --- a/desktop/flipper-server/tsconfig.json +++ b/desktop/flipper-server/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "lib", "rootDir": "src", - "lib": ["ES2019"], + "lib": ["DOM", "ES2021"], "types": ["../types/flipperGlobals", "../types/metro-resolver", "../types/metro"] }, "include": ["./src/*"], @@ -17,6 +17,9 @@ { "path": "../flipper-server-companion" }, + { + "path": "../flipper-server-client" + }, { "path": "../pkg-lib" } diff --git a/desktop/flipper-ui-browser/package.json b/desktop/flipper-ui-browser/package.json index 586684606..6e25ac516 100644 --- a/desktop/flipper-ui-browser/package.json +++ b/desktop/flipper-ui-browser/package.json @@ -10,17 +10,21 @@ "license": "MIT", "bugs": "https://github.com/facebook/flipper/issues", "dependencies": { + "file-saver": "^2.0.5", + "js-base64": "^3.7.5", "reconnecting-websocket": "^4.4.0" }, "devDependencies": { + "@types/file-saver": "^2.0.5", "eventemitter3": "^4.0.7", "flipper-common": "0.0.0", "flipper-frontend-core": "0.0.0", + "flipper-server-client": "0.0.0", "flipper-ui-core": "0.0.0", "invariant": "^2.2.4", "metro-runtime": "^0.70.2", "pretty-format": "^27.5.0", - "react-refresh": "^0.10.0" + "react-refresh": "^0.14.0" }, "peerDependencies": {}, "scripts": { diff --git a/desktop/flipper-ui-browser/src/HMRClient.tsx b/desktop/flipper-ui-browser/src/HMRClient.tsx index f4de7d9b1..ad76b730e 100644 --- a/desktop/flipper-ui-browser/src/HMRClient.tsx +++ b/desktop/flipper-ui-browser/src/HMRClient.tsx @@ -270,7 +270,7 @@ function showCompileError() { // Symbolicating compile errors is wasted effort // because the stack trace is meaningless: (error as any).preventSymbolication = true; - window.flipperShowError?.(message); + window.flipperShowMessage?.(message); throw error; } diff --git a/desktop/flipper-ui-browser/src/fb-stubs/Logger.tsx b/desktop/flipper-ui-browser/src/fb-stubs/Logger.tsx new file mode 100644 index 000000000..085174fe0 --- /dev/null +++ b/desktop/flipper-ui-browser/src/fb-stubs/Logger.tsx @@ -0,0 +1,41 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Logger, LoggerArgs, NoopLogger} from 'flipper-common'; + +const naiveLogger: Logger = { + track(...args: [any, any, any?, any?]) { + console.warn('(skipper track)', args); + }, + trackTimeSince(...args: [any, any, any?]) { + console.warn('(skipped trackTimeSince)', args); + }, + debug(...args: any[]) { + console.debug(...args); + }, + error(...args: any[]) { + console.error(...args); + console.warn('(skipped error reporting)'); + }, + warn(...args: any[]) { + console.warn(...args); + console.warn('(skipped error reporting)'); + }, + info(...args: any[]) { + console.info(...args); + }, +}; + +export function init(args?: LoggerArgs): Logger { + if (args && args.isTest) { + return new NoopLogger(); + } else { + return naiveLogger; + } +} diff --git a/desktop/flipper-ui-browser/src/flipperServerConnection.tsx b/desktop/flipper-ui-browser/src/flipperServerConnection.tsx deleted file mode 100644 index 46f1e54bc..000000000 --- a/desktop/flipper-ui-browser/src/flipperServerConnection.tsx +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import EventEmitter from 'eventemitter3'; -import { - ExecWebSocketMessage, - FlipperServer, - ServerWebSocketMessage, -} from 'flipper-common'; -import ReconnectingWebSocket from 'reconnecting-websocket'; - -const CONNECTION_TIMEOUT = 30 * 1000; -const EXEC_TIMOUT = 30 * 1000; - -export function createFlipperServer(): Promise { - // TODO: polish this all! - window.flipperShowError?.('Connecting to server...'); - return new Promise((resolve, reject) => { - let initialConnectionTimeout: number | undefined = window.setTimeout(() => { - reject( - new Error('Failed to connect to Flipper server in a timely manner'), - ); - }, CONNECTION_TIMEOUT); - - const eventEmitter = new EventEmitter(); - // TODO: recycle the socket that is created in index.web.dev.html? - const socket = new ReconnectingWebSocket(`ws://${location.host}`); - const pendingRequests: Map< - number, - { - resolve: (data: any) => void; - reject: (data: any) => void; - timeout: ReturnType; - } - > = new Map(); - let requestId = 0; - let connected = false; - - socket.addEventListener('open', () => { - if (initialConnectionTimeout) { - // only relevant for the first connect - resolve(flipperServer); - clearTimeout(initialConnectionTimeout); - initialConnectionTimeout = undefined; - } - - window?.flipperHideError?.(); - console.log('Socket to Flipper server connected'); - connected = true; - }); - - socket.addEventListener('close', () => { - window?.flipperShowError?.('WebSocket connection lost'); - console.warn('Socket to Flipper server disconnected'); - connected = false; - pendingRequests.forEach((r) => - r.reject(new Error('FLIPPER_SERVER_SOCKET_CONNECT_LOST')), - ); - pendingRequests.clear(); - }); - - socket.addEventListener('message', ({data}) => { - const {event, payload} = JSON.parse( - data.toString(), - ) as ServerWebSocketMessage; - - switch (event) { - case 'exec-response': { - console.debug('exec <<<', payload); - const entry = pendingRequests.get(payload.id); - if (!entry) { - console.warn(`Unknown request id `, payload.id); - } else { - pendingRequests.delete(payload.id); - clearTimeout(entry.timeout); - entry.resolve(payload.data); - } - break; - } - case 'exec-response-error': { - // TODO: Deserialize error - console.debug('exec <<< [SERVER ERROR]', payload.id, payload.data); - const entry = pendingRequests.get(payload.id); - if (!entry) { - console.warn(`Unknown request id `, payload.id); - } else { - pendingRequests.delete(payload.id); - clearTimeout(entry.timeout); - entry.reject(payload.data); - } - break; - } - case 'server-event': { - eventEmitter.emit(payload.event, payload.data); - break; - } - default: { - console.warn( - 'createFlipperServer -> unknown message', - data.toString(), - ); - } - } - }); - - const flipperServer: FlipperServer = { - async connect() {}, - close() {}, - exec(command, ...args): any { - if (connected) { - const id = ++requestId; - return new Promise((resolve, reject) => { - console.debug('exec >>>', id, command, args); - - pendingRequests.set(id, { - resolve, - reject, - timeout: setInterval(() => { - pendingRequests.delete(id); - reject(new Error(`Timeout for command '${command}'`)); - }, EXEC_TIMOUT), - }); - - const execMessage = { - event: 'exec', - payload: { - id, - command, - args, - }, - } as ExecWebSocketMessage; - socket.send(JSON.stringify(execMessage)); - }); - // socket. - } else { - throw new Error('Not connected to Flipper Server'); - } - }, - on(event, callback) { - eventEmitter.on(event, callback); - }, - off(event, callback) { - eventEmitter.off(event, callback); - }, - }; - }); -} diff --git a/desktop/flipper-ui-browser/src/global.tsx b/desktop/flipper-ui-browser/src/global.tsx index f927c0c4b..849b40a32 100644 --- a/desktop/flipper-ui-browser/src/global.tsx +++ b/desktop/flipper-ui-browser/src/global.tsx @@ -16,8 +16,10 @@ declare global { entryPoint: string; debug: boolean; }; + GRAPH_SECRET: string; + FLIPPER_APP_VERSION: string; - flipperShowError?(error: string): void; - flipperHideError?(): void; + flipperShowMessage?(message: string): void; + flipperHideMessage?(): void; } } diff --git a/desktop/flipper-ui-browser/src/index.tsx b/desktop/flipper-ui-browser/src/index.tsx index 5c1d18352..2833d01cf 100644 --- a/desktop/flipper-ui-browser/src/index.tsx +++ b/desktop/flipper-ui-browser/src/index.tsx @@ -7,11 +7,24 @@ * @format */ -import {getLogger, Logger, setLoggerInstance} from 'flipper-common'; +import { + getLogger, + getStringFromErrorLike, + setLoggerInstance, +} from 'flipper-common'; +import {init as initLogger} from './fb-stubs/Logger'; import {initializeRenderHost} from './initializeRenderHost'; -import {createFlipperServer, FlipperServerState} from 'flipper-frontend-core'; +import {createFlipperServer, FlipperServerState} from 'flipper-server-client'; -document.getElementById('root')!.innerText = 'flipper-ui-browser started'; +const loadingContainer = document.getElementById('loading'); +if (loadingContainer) { + loadingContainer.innerText = 'Loading...'; +} + +let cachedFile: {name: string; data: string} | undefined; +let cachedDeepLinkURL: string | undefined; + +const logger = initLogger(); async function start() { // @ts-ignore @@ -24,105 +37,202 @@ async function start() { }; }; - const logger = createDelegatedLogger(); setLoggerInstance(logger); + const params = new URL(location.href).searchParams; + + const tokenProvider = async () => { + const providerParams = new URL(location.href).searchParams; + let token = providerParams.get('token'); + if (!token) { + console.info( + '[flipper-client][ui-browser] Get token from manifest instead', + ); + try { + const manifestResponse = await fetch('manifest.json'); + const manifest = await manifestResponse.json(); + token = manifest.token; + } catch (e) { + console.warn( + '[flipper-client][ui-browser] Failed to get token from manifest. Error:', + e.message, + ); + } + } + + getLogger().info( + '[flipper-client][ui-browser] Token is available: ', + token?.length != 0, + ); + + return token; + }; + + const openPlugin = params.get('open-plugin'); + if (openPlugin) { + function removePrefix(input: string, prefix: string): string { + const regex = new RegExp(`^${prefix}+`); + return input.replace(regex, ''); + } + + const url = new URL(openPlugin); + const maybeParams = removePrefix(url.pathname, '/'); + const params = new URLSearchParams(maybeParams); + + const deeplinkURL = new URL('flipper://open-plugin'); + deeplinkURL.search = params.toString(); + + cachedDeepLinkURL = deeplinkURL.toString(); + } + + getLogger().info('[flipper-client][ui-browser] Create WS client'); + const flipperServer = await createFlipperServer( location.hostname, parseInt(location.port, 10), + tokenProvider, (state: FlipperServerState) => { switch (state) { case FlipperServerState.CONNECTING: - window.flipperShowError?.('Connecting to flipper-server...'); + getLogger().info('[flipper-client] Connecting to server'); + window.flipperShowMessage?.('Connecting to server...'); break; case FlipperServerState.CONNECTED: - window?.flipperHideError?.(); + getLogger().info( + '[flipper-client] Connection established with server', + ); + window.flipperHideMessage?.(); break; case FlipperServerState.DISCONNECTED: - window?.flipperShowError?.('Lost connection to flipper-server'); + getLogger().info('[flipper-client] Disconnected from server'); + window.flipperShowMessage?.('Waiting for server...'); break; } }, ); + getLogger().info('[flipper-client][ui-browser] WS client connected'); + flipperServer.on('server-log', (logEntry) => { - console[logEntry.type]( + getLogger()[logEntry.type]( `[${logEntry.namespace}] (${new Date( logEntry.time, ).toLocaleTimeString()}): ${logEntry.msg}`, ); }); + getLogger().info( + '[flipper-client][ui-browser] Waiting for server connection', + ); await flipperServer.connect(); + getLogger().info( + '[flipper-client][ui-browser] Connected to server, get configuration', + ); const flipperServerConfig = await flipperServer.exec('get-config'); - initializeRenderHost(flipperServer, flipperServerConfig); + getLogger().info( + '[flipper-client][ui-browser] Configuration obtained, initialise render host', + ); + + initializeRenderHost(flipperServer, flipperServerConfig); + initializePWA(); - // By turning this in a require, we force the JS that the body of this module (init) has completed (initializeElectron), - // before starting the rest of the Flipper process. - // This prevent issues where the render host is referred at module initialisation level, - // but not set yet, which might happen when using normal imports. - // TODO: remove - window.flipperShowError?.('Connected to Flipper Server successfully'); // @ts-ignore // eslint-disable-next-line import/no-commonjs require('flipper-ui-core').startFlipperDesktop(flipperServer); - window.flipperHideError?.(); + window.flipperHideMessage?.(); + + getLogger().info('[flipper-client][ui-browser] UI initialised'); + logger.track('success-rate', 'flipper-ui-browser-started', {value: 1}); } start().catch((e) => { - console.error('Failed to start flipper-ui-browser', e); - window.flipperShowError?.('Failed to start flipper-ui-browser: ' + e); + getLogger().error('Failed to start flipper-ui-browser', e); + logger.track('success-rate', 'flipper-ui-browser-started', { + value: 0, + error: getStringFromErrorLike(e), + }); + window.flipperShowMessage?.('Failed to start UI with error: ' + e); }); -// getLogger() is not yet created when the electron app starts. -// we can't create it here yet, as the real logger is wired up to -// the redux store and the rest of the world. So we create a delegating logger -// that uses a simple implementation until the real one comes available -function createDelegatedLogger(): Logger { - const naiveLogger: Logger = { - track(...args: [any, any, any?, any?]) { - console.warn('(skipper track)', args); - }, - trackTimeSince(...args: [any, any, any?]) { - console.warn('(skipped trackTimeSince)', args); - }, - debug(...args: any[]) { - console.debug(...args); - }, - error(...args: any[]) { - console.error(...args); - console.warn('(skipped error reporting)'); - }, - warn(...args: any[]) { - console.warn(...args); - console.warn('(skipped error reporting)'); - }, - info(...args: any[]) { - console.info(...args); - }, - }; - // will be overwrittingen later - setLoggerInstance(naiveLogger); +async function initializePWA() { + getLogger().info('[PWA] Initialization'); - return { - track() { - // noop - }, - trackTimeSince() { - // noop - }, - debug(...args: any[]) { - getLogger().debug(...args); - }, - error(...args: any[]) { - getLogger().error(...args); - }, - warn(...args: any[]) { - getLogger().warn(...args); - }, - info(...args: any[]) { - getLogger().info(...args); - }, + let rehydrated = false; + const openFileIfAny = () => { + if (!cachedFile || !rehydrated) { + return; + } + window.dispatchEvent( + new CustomEvent('open-flipper-file', { + detail: [cachedFile.name, cachedFile.data], + }), + ); + cachedFile = undefined; }; + + const openURLIfAny = () => { + if (!cachedDeepLinkURL || !rehydrated) { + return; + } + window.dispatchEvent( + new CustomEvent('flipper-protocol-handler', { + detail: [cachedDeepLinkURL], + }), + ); + cachedDeepLinkURL = undefined; + }; + + if ('serviceWorker' in navigator) { + navigator.serviceWorker + .register('/service-worker.js') + .then(() => { + getLogger().info('[PWA] Service Worker has been registered'); + }) + .catch((e) => { + getLogger().error('[PWA] failed to register Service Worker', e); + }); + } + + if ('launchQueue' in window) { + getLogger().debug('[PWA] File Handling API is supported'); + + // @ts-ignore + window.launchQueue.setConsumer(async (launchParams) => { + if (!launchParams || !launchParams.files) { + return; + } + getLogger().debug('[PWA] Attempt to to open a file'); + for (const file of launchParams.files) { + const blob = await file.getFile(); + blob.handle = file; + + const data = await blob.text(); + const name = file.name; + + cachedFile = {name, data}; + + openFileIfAny(); + } + }); + } else { + console.warn('[PWA] File Handling API is not supported'); + } + + getLogger().debug('[PWA] Add before install prompt listener'); + window.addEventListener('beforeinstallprompt', (e) => { + // Prevent Chrome 67 and earlier from automatically showing the prompt. + e.preventDefault(); + // Stash the event so it can be triggered later. + // @ts-ignore + global.PWAppInstallationEvent = e; + getLogger().info('[PWA] Installation event has been captured'); + }); + + window.addEventListener('storeRehydrated', () => { + getLogger().info('[PWA] Store is rehydrated'); + rehydrated = true; + openFileIfAny(); + openURLIfAny(); + }); } diff --git a/desktop/flipper-ui-browser/src/initializeRenderHost.tsx b/desktop/flipper-ui-browser/src/initializeRenderHost.tsx index a9b6c990e..00ab059e6 100644 --- a/desktop/flipper-ui-browser/src/initializeRenderHost.tsx +++ b/desktop/flipper-ui-browser/src/initializeRenderHost.tsx @@ -7,26 +7,145 @@ * @format */ -import {FlipperServer, FlipperServerConfig, isProduction} from 'flipper-common'; +import { + FlipperServer, + FlipperServerConfig, + isProduction, + uuid, + wrapRequire, +} from 'flipper-common'; import type {RenderHost} from 'flipper-ui-core'; +import FileSaver from 'file-saver'; + +import {Base64} from 'js-base64'; + +declare module globalThis { + let require: any; +} + +// Whenever we bundle plugins, we assume that they are going to share some modules - React, React-DOM, ant design and etc. +// It allows us to decrease the bundle size and not to create separate React roots for every plugin +// To tell a plugin that a module is going to be provided externally, we add the module to the list of externals (see https://esbuild.github.io/api/#external). +// As a result, esbuild does not bundle hte contents of the module. Instead, it wraps the module name with `require(...)`. +// `require` does not exist ion the browser environment, so we substitute it here to feed the plugin our global module. +globalThis.require = wrapRequire((module: string) => { + throw new Error( + `Dynamic require is not supported in browser envs. Tried to require: ${module}`, + ); +}); + +type FileEncoding = 'utf-8' | 'base64' | 'binary'; +interface FileDescriptor { + data: string | Uint8Array | undefined; + name: string; + encoding: FileEncoding; +} export function initializeRenderHost( flipperServer: FlipperServer, flipperServerConfig: FlipperServerConfig, ) { FlipperRenderHostInstance = { - readTextFromClipboard() { - // TODO: - return undefined; + async readTextFromClipboard() { + return await navigator.clipboard.readText(); }, - writeTextToClipboard(_text: string) { - // TODO: + writeTextToClipboard(text: string) { + return navigator.clipboard.writeText(text); }, - async importFile() { - throw new Error('Not implemented'); + async importFile(options?: { + defaultPath?: string; + extensions?: string[]; + title?: string; + encoding?: FileEncoding; + multi?: false; + }) { + return new Promise( + (resolve, reject) => { + try { + let selectionMade = false; + + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.id = uuid(); + if (options?.extensions) { + fileInput.accept = options?.extensions.join(', '); + } + fileInput.multiple = options?.multi ?? false; + + fileInput.addEventListener('change', async (event) => { + selectionMade = true; + const target = event.target as HTMLInputElement | undefined; + if (!target || !target.files) { + resolve(undefined); + return; + } + + const files: File[] = Array.from(target.files); + const descriptors: FileDescriptor[] = await Promise.all( + files.map(async (file) => { + switch (options?.encoding) { + case 'base64': { + const bytes = new Uint8Array(await file.arrayBuffer()); + const base64Content = Base64.fromUint8Array(bytes); + return { + data: base64Content, + name: file.name, + encoding: 'base64', + }; + } + case 'binary': + return { + data: new Uint8Array(await file.arrayBuffer()), + name: file.name, + encoding: 'binary', + }; + default: + return { + data: await file.text(), + name: file.name, + encoding: 'utf-8', + }; + } + }), + ); + resolve(options?.multi ? descriptors : descriptors[0]); + }); + + window.addEventListener( + 'focus', + () => { + setTimeout(() => { + if (!selectionMade) { + resolve(undefined); + } + }, 300); + }, + {once: true}, + ); + + fileInput.click(); + } catch (error) { + reject(error); + } + }, + ); }, - async exportFile() { - throw new Error('Not implemented'); + async exportFile(data: string, {defaultPath}: {defaultPath?: string}) { + const file = new File([data], defaultPath ?? 'unknown', { + type: 'text/plain;charset=utf-8', + }); + FileSaver.saveAs(file); + return defaultPath; + }, + async exportFileBinary( + data: Uint8Array, + {defaultPath}: {defaultPath?: string}, + ) { + const file = new File([data], defaultPath ?? 'unknown', { + type: 'application/octet-stream', + }); + FileSaver.saveAs(file); + return defaultPath; }, openLink(url: string) { window.open(url, '_blank'); @@ -34,11 +153,13 @@ export function initializeRenderHost( hasFocus() { return document.hasFocus(); }, - onIpcEvent(_event) { - // no-op + onIpcEvent(event, cb) { + window.addEventListener(event as string, (ev) => { + cb(...((ev as CustomEvent).detail as any)); + }); }, - sendIpcEvent(_event, ..._args: any[]) { - // no-op + sendIpcEvent(event, ...args: any[]) { + window.dispatchEvent(new CustomEvent(event, {detail: args})); }, shouldUseDarkColors() { return !!( @@ -48,32 +169,33 @@ export function initializeRenderHost( ); }, restartFlipper() { - window.flipperShowError!( - 'Flipper settings have changed, please restart flipper server for the changes to take effect', - ); + flipperServer.exec('shutdown'); }, - loadDefaultPlugins: getDefaultPluginsIndex, serverConfig: flipperServerConfig, GK(gatekeeper) { return flipperServerConfig.gatekeepers[gatekeeper] ?? false; }, flipperServer, - async requirePlugin(path) { - let source = await flipperServer.exec('plugin-source', path); + async requirePlugin(path): Promise<{plugin: any; css?: string}> { + const source = await flipperServer.exec('plugin-source', path); + + let js = source.js; // append source url (to make sure a file entry shows up in the debugger) - source += `\n//# sourceURL=file://${path}`; - // and source map url (to get source code if available) - source += `\n//# sourceMappingURL=file://${path.replace(/.js$/, '.map')}`; + js += `\n//# sourceURL=file://${path}`; + if (isProduction()) { + // and source map url (to get source code if available) + js += `\n//# sourceMappingURL=file://${path}.map`; + } // Plugins are compiled as typical CJS modules, referring to the global // 'module', which we'll make available by loading the source into a closure that captures 'module'. // Note that we use 'eval', and not 'new Function', because the latter will cause the source maps // to be off by two lines (as the function declaration uses two lines in the generated source) // eslint-disable-next-line no-eval - const cjsLoader = eval('(module) => {' + source + '\n}'); + const cjsLoader = eval('(module) => {' + js + '\n}'); const theModule = {exports: {}}; cjsLoader(theModule); - return theModule.exports; + return {plugin: theModule.exports, css: source.css}; }, getStaticResourceUrl(path): string { // the 'static' folder is mounted as static middleware in Express at the root @@ -87,10 +209,3 @@ export function initializeRenderHost( }, } as RenderHost; } - -function getDefaultPluginsIndex() { - // @ts-ignore - // eslint-disable-next-line import/no-unresolved - const index = require('./defaultPlugins'); - return index.default || index; -} diff --git a/desktop/flipper-ui-browser/tsconfig.json b/desktop/flipper-ui-browser/tsconfig.json index c0f928036..666b09f03 100644 --- a/desktop/flipper-ui-browser/tsconfig.json +++ b/desktop/flipper-ui-browser/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "lib", "rootDir": "src", - "lib": ["dom", "ES2019"], + "lib": ["dom", "ES2021"], "types": ["../types/flipperGlobals", "react/next", "react-dom/next"] }, "references": [ @@ -13,6 +13,9 @@ { "path": "../flipper-frontend-core" }, + { + "path": "../flipper-server-client" + }, { "path": "../flipper-ui-core" } diff --git a/desktop/flipper-ui-core/package.json b/desktop/flipper-ui-core/package.json index e88ecf8d7..541fbcebb 100644 --- a/desktop/flipper-ui-core/package.json +++ b/desktop/flipper-ui-core/package.json @@ -13,21 +13,22 @@ "@ant-design/icons": "^4.7.0", "@emotion/css": "^11.7.1", "@emotion/react": "^11.8.2", - "@emotion/styled": "^11.8.1", + "@emotion/styled": "^11.10.4", + "@nicksrandall/console-feed": "^3.5.0", "@tanishiking/aho-corasick": "^0.0.1", - "antd": "4.19.2", + "antd": "^4.24", "cbuffer": "^2.2.0", - "console-feed": "^3.3.0", - "deep-equal": "^2.0.5", + "deep-equal": "^2.2.2", "eventemitter3": "^4.0.7", "flipper-client-sdk": "^0.0.3", "flipper-common": "0.0.0", "flipper-frontend-core": "0.0.0", "flipper-plugin": "0.0.0", "flipper-ui-core": "0.0.0", - "hotkeys-js": "^3.9.3", - "immer": "^9.0.12", - "js-base64": "^3.7.2", + "hotkeys-js": "^3.12.0", + "immer": "^9.0.18", + "js-base64": "^3.7.5", + "jszip": "^3.10.1", "lodash": "^4.17.21", "lodash.memoize": "^4.1.2", "p-map": "^4.0.0", @@ -38,24 +39,25 @@ "react-element-to-jsx-string": "^14.3.4", "react-is": "^18.2.0", "react-markdown": "^6.0.3", - "react-player": "^2.10.0", + "react-player": "^2.11.0", "react-redux": "^7.2.6", "react-test-renderer": "^0.0.0-experimental-2bf7c02f0-20220314", - "react-virtualized-auto-sizer": "^1.0.6", - "react-window": "^1.8.6", - "redux": "^4.1.2", + "react-virtualized-auto-sizer": "1.0.7", + "react-window": "^1.8.9", + "redux": "^4.2.1", "redux-persist": "^6.0.0", - "reselect": "^4.1.5", - "semver": "^7.3.7", + "reselect": "^4.1.8", + "semver": "^7.5.4", "uuid": "^8.3.2" }, "devDependencies": { - "@testing-library/dom": "^8.11.3", + "@testing-library/dom": "^8.20.0", "@types/deep-equal": "^1.0.1", + "@types/jest": "^29.5.3", "@types/lodash.memoize": "^4.1.7", "@types/react": "^17.0.39", "@types/react-dom": "^17.0.12", - "@types/react-is": "^17.0.3", + "@types/react-is": "^18.2.1", "@types/react-test-renderer": "^17.0.1", "@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-window": "^1.8.5", diff --git a/desktop/flipper-ui-core/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap b/desktop/flipper-ui-core/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap index 51dde958c..6557297c7 100644 --- a/desktop/flipper-ui-core/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap +++ b/desktop/flipper-ui-core/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap @@ -1,22 +1,23 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`can create a Fake flipper with legacy wrapper 1`] = ` -Object { +{ "clients": Map { - "TestApp#Android#MockAndroidDevice#serial" => Object { + "TestApp#Android#MockAndroidDevice#serial" => { "id": "TestApp#Android#MockAndroidDevice#serial", - "query": Object { + "query": { "app": "TestApp", "device": "MockAndroidDevice", "device_id": "serial", + "medium": "NONE", "os": "Android", "sdk_version": 4, }, }, }, "deepLinkPayload": null, - "devices": Array [ - Object { + "devices": [ + { "deviceType": "physical", "os": "Android", "serial": "serial", @@ -30,15 +31,15 @@ Object { "Hermesdebuggerrn", "React", }, - "enabledPlugins": Object { - "TestApp": Array [ + "enabledPlugins": { + "TestApp": [ "TestPlugin", ], }, - "pluginMenuEntries": Array [], + "pluginMenuEntries": [], "selectedAppId": "TestApp#Android#MockAndroidDevice#serial", "selectedAppPluginListRevision": 0, - "selectedDevice": Object { + "selectedDevice": { "deviceType": "physical", "os": "Android", "serial": "serial", @@ -46,7 +47,7 @@ Object { }, "selectedPlugin": "TestPlugin", "staticView": null, - "uninitializedClients": Array [], + "uninitializedClients": [], "userPreferredApp": "TestApp", "userPreferredDevice": "MockAndroidDevice", "userPreferredPlugin": "TestPlugin", @@ -54,16 +55,15 @@ Object { `; exports[`can create a Fake flipper with legacy wrapper 2`] = ` -Object { - "bundledPlugins": Map {}, +{ "clientPlugins": Map { "TestPlugin" => SandyPluginDefinition { - "details": Object { + "css": undefined, + "details": { "dir": "/Users/mock/.flipper/thirdparty/flipper-plugin-sample1", "entry": "./test/index.js", "id": "TestPlugin", "isActivatable": true, - "isBundled": false, "main": "dist/bundle.js", "name": "flipper-plugin-hello", "pluginType": "client", @@ -74,21 +74,21 @@ Object { }, "id": "TestPlugin", "isDevicePlugin": false, - "module": Object { + "module": { "Component": [Function], "plugin": [Function], }, }, }, "devicePlugins": Map {}, - "disabledPlugins": Array [], - "failedPlugins": Array [], - "gatekeepedPlugins": Array [], + "disabledPlugins": [], + "failedPlugins": [], + "gatekeepedPlugins": [], "initialized": true, "installedPlugins": Map {}, "loadedPlugins": Map {}, - "marketplacePlugins": Array [], - "selectedPlugins": Array [], + "marketplacePlugins": [], + "selectedPlugins": [], "uninstalledPluginNames": Set {}, } `; diff --git a/desktop/flipper-ui-core/src/__tests__/createMockFlipperWithPlugin.node.tsx b/desktop/flipper-ui-core/src/__tests__/createMockFlipperWithPlugin.node.tsx index 9818bc1d5..7d3bab7f4 100644 --- a/desktop/flipper-ui-core/src/__tests__/createMockFlipperWithPlugin.node.tsx +++ b/desktop/flipper-ui-core/src/__tests__/createMockFlipperWithPlugin.node.tsx @@ -64,5 +64,5 @@ test('can create a Fake flipper with legacy wrapper', async () => { await getAllClients(state.connections)[0] .sandyPluginStates.get(TestPlugin.id)! .exportState(testIdler, testOnStatusMessage), - ).toMatchInlineSnapshot(`"{\\"count\\":1}"`); + ).toMatchInlineSnapshot(`"{"count":1}"`); }); diff --git a/desktop/flipper-ui-core/src/__tests__/test-utils/MockFlipper.tsx b/desktop/flipper-ui-core/src/__tests__/test-utils/MockFlipper.tsx index decc96ce7..ebd2cc340 100644 --- a/desktop/flipper-ui-core/src/__tests__/test-utils/MockFlipper.tsx +++ b/desktop/flipper-ui-core/src/__tests__/test-utils/MockFlipper.tsx @@ -180,6 +180,7 @@ export default class MockFlipper { device: device.title, device_id: device.serial, sdk_version: 4, + medium: 'NONE', }; const id = buildClientId({ app: query.app, diff --git a/desktop/flipper-ui-core/src/chrome/ChangelogSheet.tsx b/desktop/flipper-ui-core/src/chrome/ChangelogSheet.tsx index 8ca2322c4..dfb023657 100644 --- a/desktop/flipper-ui-core/src/chrome/ChangelogSheet.tsx +++ b/desktop/flipper-ui-core/src/chrome/ChangelogSheet.tsx @@ -57,11 +57,7 @@ class ChangelogSheet extends Component { render() { return this.props.changelog ? ( - + ([], { + limit: 200000, + persist: 'connectivity-logs', +}); + +export function enableConnectivityHook(flipperServer: FlipperServer) { + flipperServer.on( + 'connectivity-troubleshoot-log', + (entry: ConnectionRecordEntry) => { + rows.append(entry); + }, + ); + flipperServer.on( + 'connectivity-troubleshoot-cmd', + (entry: CommandRecordEntry) => { + rows.append(entry); + }, + ); +} + +const iconStyle = { + fontSize: '16px', +}; + +const baseRowStyle = { + ...theme.monospace, +}; + +const logTypes: { + [level: string]: { + label: string; + icon?: React.ReactNode; + style?: CSSProperties; + enabled: boolean; + }; +} = { + log: { + label: 'Log', + enabled: true, + }, + cmd: { + label: 'Shell', + enabled: true, + style: { + ...baseRowStyle, + color: theme.primaryColor, + }, + icon: , + }, + error: { + label: 'Error', + style: { + ...baseRowStyle, + color: theme.errorColor, + }, + icon: , + enabled: true, + }, +}; + +function createColumnConfig(): DataTableColumn[] { + return [ + { + key: 'time', + title: 'Time', + width: 160, + }, + { + key: 'device', + title: 'Device', + width: 160, + }, + { + key: 'app', + title: 'App', + width: 160, + visible: true, + }, + { + key: 'medium', + title: 'Medium', + width: 80, + visible: true, + }, + { + key: 'message', + title: 'Message', + wrap: true, + formatters: [ + DataFormatter.truncate(400), + DataFormatter.prettyPrintJson, + DataFormatter.linkify, + ], + }, + ]; +} + +const columns = createColumnConfig(); + +function getRowStyle(entry: ConnectionRecordEntry): CSSProperties | undefined { + return (logTypes[entry.type]?.style as any) ?? baseRowStyle; +} + +const Placeholder = styled(Layout.Container)({ + center: true, + color: theme.textColorPlaceholder, + fontSize: 18, +}); + +const PanelContainer = styled.div({ + width: SIDEBAR_WIDTH - 2 * 32, + whiteSpace: 'pre-wrap', + overflow: 'scroll', +}); + +function isShellCommand( + entry: ConnectionRecordEntry, +): entry is CommandRecordEntry { + return 'cmd' in entry; +} + +function KillDebuggingBridge({os}: {os: DeviceOS}) { + const { + title, + cmd, + }: { + title: string; + cmd: keyof FlipperServerCommands; + } = + os === 'iOS' + ? {title: 'Kill idb', cmd: 'ios-idb-kill'} + : {title: 'Kill adb server', cmd: 'android-adb-kill'}; + + return ( + <> +

+ Sometimes the error can be resolved by killing the {os} debugging + bridge. +

+ + + ); +} + +function Sidebar({selection}: {selection: undefined | ConnectionRecordEntry}) { + const content: JSX.Element[] = []; + + if (selection) { + if (isShellCommand(selection)) { + content.push( + + {selection.cmd} + , + + {selection.description} + , + + {selection.stdout} + , + + {selection.stderr} + , + +

{selection.troubleshoot}

+ {!selection.success && } +
, + ); + } + content.push( + + + , + ); + } else { + content.push( + + Select an entry to visualize details + , + ); + } + + return {content}; +} + +function clearMessages() { + rows.clear(); +} + +export const ConnectivityLogs = () => { + const [selection, setSelection] = useState< + ConnectionRecordEntry | undefined + >(); + + const clearButton = ( + + ); + + const onSelection = useCallback( + (entry: ConnectionRecordEntry | undefined) => { + if (entry !== undefined && isShellCommand(entry)) { + setSelection(entry); + } else { + setSelection(undefined); + } + }, + [setSelection], + ); + + return ( + + + dataSource={rows} + columns={columns} + enableAutoScroll + onRowStyle={getRowStyle} + onSelect={onSelection} + extraActions={clearButton} + /> + {selection && } + + ); +}; diff --git a/desktop/flipper-ui-core/src/chrome/ConsoleLogs.tsx b/desktop/flipper-ui-core/src/chrome/ConsoleLogs.tsx index dafb6efc0..ee7911ca7 100644 --- a/desktop/flipper-ui-core/src/chrome/ConsoleLogs.tsx +++ b/desktop/flipper-ui-core/src/chrome/ConsoleLogs.tsx @@ -9,9 +9,9 @@ import {useMemo} from 'react'; import React from 'react'; -import {Console, Hook} from 'console-feed'; -import type {Methods} from 'console-feed/lib/definitions/Methods'; -import type {Styles} from 'console-feed/lib/definitions/Styles'; +import {Console} from '@nicksrandall/console-feed'; +import type {Methods} from '@nicksrandall/console-feed/lib/definitions/Methods'; +import type {Styles} from '@nicksrandall/console-feed/lib/definitions/Styles'; import {createState, useValue} from 'flipper-plugin'; import {useLocalStorageState} from 'flipper-plugin'; import {theme, Toolbar, Layout} from 'flipper-plugin'; @@ -19,32 +19,46 @@ import {useIsDarkMode} from '../utils/useIsDarkMode'; import {Button, Dropdown, Menu, Checkbox} from 'antd'; import {DownOutlined} from '@ant-design/icons'; import {DeleteOutlined} from '@ant-design/icons'; +import CBuffer from 'cbuffer'; +import {addLogTailer} from 'flipper-common'; +import {v4} from 'uuid'; -const MAX_LOG_ITEMS = 1000; +const MAX_DISPLAY_LOG_ITEMS = 1000; +const MAX_EXPORT_LOG_ITEMS = 5000; -export const logsAtom = createState([]); +// A list5 of log items meant to be used for exporting (and subsequent debugging) only +export const exportLogs = new CBuffer( + MAX_EXPORT_LOG_ITEMS, +); +export const displayLogsAtom = createState([]); export const errorCounterAtom = createState(0); +type ConsoleFeedLogMessage = { + id: string; + method: Methods; + data: any[]; +}; + export function enableConsoleHook() { - Hook( - window.console, - (log) => { - if (log.method === 'debug') { - return; // See below, skip debug messages which are generated very aggressively by Flipper - } - const newLogs = logsAtom.get().slice(-MAX_LOG_ITEMS); - newLogs.push(log); - logsAtom.set(newLogs); - if (log.method === 'error' || log.method === 'assert') { - errorCounterAtom.set(errorCounterAtom.get() + 1); - } - }, - false, - ); + addLogTailer((level, ...data) => { + const logMessage = {method: level, data: data, id: v4()}; + exportLogs.push(logMessage); + + if (level === 'debug') { + return; // See below, skip debug messages which are generated very aggressively by Flipper + } + const newLogs = displayLogsAtom.get().slice(-MAX_DISPLAY_LOG_ITEMS); + newLogs.push(logMessage); + displayLogsAtom.set(newLogs); + if (level === 'error') { + errorCounterAtom.set(errorCounterAtom.get() + 1); + } + }); } function clearLogs() { - logsAtom.set([]); + exportLogs.empty(); + displayLogsAtom.set([]); errorCounterAtom.set(0); } @@ -67,7 +81,7 @@ const defaultLogLevels: Methods[] = ['warn', 'error', 'table', 'assert']; export function ConsoleLogs() { const isDarkMode = useIsDarkMode(); - const logs = useValue(logsAtom); + const logs = useValue(displayLogsAtom); const [logLevels, setLogLevels] = useLocalStorageState( 'console-logs-loglevels', defaultLogLevels, @@ -77,7 +91,7 @@ export function ConsoleLogs() { return ( - + @@ -127,18 +141,15 @@ function buildTheme(): Styles { LOG_INFO_BACKGROUND: 'transparent', LOG_COMMAND_BACKGROUND: 'transparent', LOG_RESULT_BACKGROUND: 'transparent', - LOG_WARN_BACKGROUND: theme.warningColor, - LOG_ERROR_BACKGROUND: theme.errorColor, LOG_INFO_COLOR: theme.textColorPrimary, LOG_COMMAND_COLOR: theme.textColorSecondary, LOG_RESULT_COLOR: theme.textColorSecondary, - LOG_WARN_COLOR: 'white', - LOG_ERROR_COLOR: 'white', + LOG_ERROR_COLOR: theme.textColorPrimary, LOG_INFO_BORDER: theme.dividerColor, LOG_COMMAND_BORDER: theme.dividerColor, LOG_RESULT_BORDER: theme.dividerColor, - LOG_WARN_BORDER: theme.dividerColor, - LOG_ERROR_BORDER: theme.dividerColor, + LOG_WARN_BORDER: theme.warningColor, + LOG_ERROR_BORDER: theme.errorColor, LOG_BORDER: theme.dividerColor, }; } diff --git a/desktop/flipper-ui-core/src/chrome/PWAppInstallationWizard.tsx b/desktop/flipper-ui-core/src/chrome/PWAppInstallationWizard.tsx new file mode 100644 index 000000000..34cb02002 --- /dev/null +++ b/desktop/flipper-ui-core/src/chrome/PWAppInstallationWizard.tsx @@ -0,0 +1,236 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {Image, Modal, Button} from 'antd'; +import {getFlipperLib, Layout, _NuxManagerContext} from 'flipper-plugin'; +import {getRenderHostInstance} from 'flipper-frontend-core'; +import isProduction from '../utils/isProduction'; + +type Props = { + onHide: () => void; +}; + +type TrackerEvents = { + 'pwa-installation-wizard-should-show': {show: boolean; reason: string}; + 'pwa-installation-wizard-should-never-show': {}; + 'pwa-installation-wizard-shown': {}; + 'pwa-install-outcome': { + installed: boolean; + }; + 'pwa-install-error': { + error: Error; + }; +}; + +class PWAWizardTracker { + track( + event: Event, + payload: TrackerEvents[Event], + ): void { + getFlipperLib().logger.track('usage', event, payload); + } +} + +const tracker = new PWAWizardTracker(); + +function isElectron() { + return !getRenderHostInstance().serverConfig.environmentInfo.isHeadlessBuild; +} + +const lastShownTimestampKey = 'flipper-pwa-wizard-last-shown-timestamp'; +const neverAskAgainKey = 'flipper-pwa-wizard-never-ask-again'; +export function shouldShowPWAInstallationWizard(): boolean { + if (isElectron()) { + return false; + } + + if (!isProduction()) { + return false; + } + + if (window.matchMedia('(display-mode: standalone)').matches) { + tracker.track('pwa-installation-wizard-should-show', { + show: false, + reason: 'Display mode is standalone, seems is already running as PWA', + }); + return false; + } + + let neverAskAgain = undefined; + try { + neverAskAgain = window.localStorage.getItem(neverAskAgainKey); + } catch (e) {} + if (neverAskAgain !== undefined && neverAskAgain !== null) { + return false; + } + + let lastShownTimestampFromStorage = undefined; + try { + lastShownTimestampFromStorage = window.localStorage.getItem( + lastShownTimestampKey, + ); + } catch (e) {} + if (lastShownTimestampFromStorage) { + const withinOneDay = (timestamp: number) => { + const Day = 1 * 24 * 60 * 60 * 1000; + const DayAgo = Date.now() - Day; + + return timestamp > DayAgo; + }; + const lastShownTimestamp = Number(lastShownTimestampFromStorage); + + const notShownWithinOneDay = !withinOneDay(lastShownTimestamp); + + tracker.track('pwa-installation-wizard-should-show', { + show: notShownWithinOneDay, + reason: 'Last shown timestamp from storage is available', + }); + return notShownWithinOneDay; + } + + const lastShownTimestamp = Date.now(); + try { + window.localStorage.setItem( + lastShownTimestampKey, + String(lastShownTimestamp), + ); + } catch (e) {} + + tracker.track('pwa-installation-wizard-should-show', { + show: true, + reason: 'Last shown timestamp from storage is not available', + }); + + return true; +} + +function neverShowPWAInstallationWizard() { + try { + // Only interested in setting any value. However, + // in this case, the time in which this option was selected is + // stored as it may be relevant in the future. + const neverShowTimestamp = Date.now(); + + window.localStorage.setItem(neverAskAgainKey, String(neverShowTimestamp)); + } catch (e) {} + + tracker.track('pwa-installation-wizard-should-never-show', {}); +} + +async function install(event: any) { + event.prompt(); + + (event.userChoice as any) + .then((choiceResult: any) => { + if (choiceResult.outcome === 'accepted') { + tracker.track('pwa-install-outcome', {installed: true}); + console.log('PWA installation, user accepted the prompt.'); + } else { + tracker.track('pwa-install-outcome', {installed: false}); + console.log('PWA installation, user dismissed the prompt.'); + } + (globalThis as any).PWAppInstallationEvent = null; + }) + .catch((error: Error) => { + tracker.track('pwa-install-error', {error}); + console.error('PWA failed to install with error', error); + }); +} + +export default function PWAInstallationWizard(props: Props) { + tracker.track('pwa-installation-wizard-shown', {}); + const contents = ( + + +

+ Please install Flipper as a PWA. Installed Progressive Web Apps run in + a standalone window instead of a browser tab. They're launchable from + your home screen, dock, taskbar, or shelf. It's possible to search for + and jump between them with the app switcher, making them feel like + part of the device they're installed on. New capabilities open up + after a web app is installed. Keyboard shortcuts, usually reserved + when running in the browser, become available too. +

+

+ Install it by clicking the 'Install' button below. +

+

+ Alternatively, click on{' '} + which can + be found at the right-side of the search bar next to the bookmarks + icon.{' '} +

+ {getFlipperLib().isFB && ( +

+ Installation instructions can also be found{' '} + + here + + . +

+ )} +
+
+ ); + + const footer = ( + <> + + + + + ); + + return ( + { + props.onHide(); + }} + width={570} + title="Install Flipper" + footer={footer}> + {contents} + + ); +} diff --git a/desktop/flipper-ui-core/src/chrome/PlatformSelectWizard.tsx b/desktop/flipper-ui-core/src/chrome/PlatformSelectWizard.tsx index f6910e30d..6caa0b942 100644 --- a/desktop/flipper-ui-core/src/chrome/PlatformSelectWizard.tsx +++ b/desktop/flipper-ui-core/src/chrome/PlatformSelectWizard.tsx @@ -122,7 +122,7 @@ class PlatformSelectWizard extends Component { return ( { this.props.onHide(); diff --git a/desktop/flipper-ui-core/src/chrome/PluginActions.tsx b/desktop/flipper-ui-core/src/chrome/PluginActions.tsx index 4e71e1ec0..9ec4d225a 100644 --- a/desktop/flipper-ui-core/src/chrome/PluginActions.tsx +++ b/desktop/flipper-ui-core/src/chrome/PluginActions.tsx @@ -13,7 +13,7 @@ import { PlusOutlined, } from '@ant-design/icons'; import {Alert, Button} from 'antd'; -import {BundledPluginDetails, DownloadablePluginDetails} from 'flipper-common'; +import {DownloadablePluginDetails} from 'flipper-common'; import React, {useMemo} from 'react'; import {useCallback} from 'react'; import {useDispatch, useSelector} from 'react-redux'; @@ -86,16 +86,12 @@ function InstallButton({ plugin, type = 'primary', }: { - plugin: DownloadablePluginDetails | BundledPluginDetails; + plugin: DownloadablePluginDetails; type: 'link' | 'primary'; }) { const dispatch = useDispatch(); const installPlugin = useCallback(() => { - if (plugin.isBundled) { - dispatch(loadPlugin({plugin, enable: true, notifyIfFailed: true})); - } else { - dispatch(startPluginDownload({plugin, startedByUser: true})); - } + dispatch(startPluginDownload({plugin, startedByUser: true})); }, [plugin, dispatch]); const downloads = useSelector(getPluginDownloadStatusMap); const downloadStatus = useMemo( diff --git a/desktop/flipper-ui-core/src/chrome/RatingButton.tsx b/desktop/flipper-ui-core/src/chrome/RatingButton.tsx deleted file mode 100644 index 007b529d3..000000000 --- a/desktop/flipper-ui-core/src/chrome/RatingButton.tsx +++ /dev/null @@ -1,359 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import React, { - Component, - ReactElement, - useCallback, - useEffect, - useState, -} from 'react'; -import { - FlexColumn, - FlexRow, - Button, - Checkbox, - styled, - Input, - Link, -} from '../ui'; -import {LeftRailButton} from '../sandy-chrome/LeftRail'; -import * as UserFeedback from '../fb-stubs/UserFeedback'; -import {FeedbackPrompt} from '../fb-stubs/UserFeedback'; -import {StarOutlined} from '@ant-design/icons'; -import {Popover, Rate} from 'antd'; -import {useStore} from '../utils/useStore'; -import {isLoggedIn} from '../fb-stubs/user'; -import {useValue} from 'flipper-plugin'; -import {reportPlatformFailures} from 'flipper-common'; -import {getRenderHostInstance} from 'flipper-frontend-core'; - -type NextAction = 'select-rating' | 'leave-comment' | 'finished'; - -class PredefinedComment extends Component<{ - comment: string; - selected: boolean; - onClick: (_: unknown) => unknown; -}> { - static Container = styled.div<{selected: boolean}>((props) => { - return { - border: '1px solid #f2f3f5', - cursor: 'pointer', - borderRadius: 24, - backgroundColor: props.selected ? '#ecf3ff' : '#f2f3f5', - marginBottom: 4, - marginRight: 4, - padding: '4px 8px', - color: props.selected ? 'rgb(56, 88, 152)' : undefined, - borderColor: props.selected ? '#3578e5' : undefined, - ':hover': { - borderColor: '#3578e5', - }, - }; - }); - render() { - return ( - - {this.props.comment} - - ); - } -} - -const Row = styled(FlexRow)({ - marginTop: 5, - marginBottom: 5, - justifyContent: 'center', - textAlign: 'center', - color: '#9a9a9a', - flexWrap: 'wrap', -}); - -const DismissRow = styled(Row)({ - marginBottom: 0, - marginTop: 10, -}); - -const DismissButton = styled.span({ - '&:hover': { - textDecoration: 'underline', - cursor: 'pointer', - }, -}); - -const Spacer = styled(FlexColumn)({ - flexGrow: 1, -}); - -function dismissRow(dismiss: () => void) { - return ( - - - Dismiss - - - ); -} - -type FeedbackComponentState = { - rating: number | null; - hoveredRating: number; - allowUserInfoSharing: boolean; - nextAction: NextAction; - predefinedComments: {[key: string]: boolean}; - comment: string; -}; - -class FeedbackComponent extends Component< - { - submitRating: (rating: number) => void; - submitComment: ( - rating: number, - comment: string, - selectedPredefinedComments: Array, - allowUserInfoSharing: boolean, - ) => void; - close: () => void; - dismiss: () => void; - promptData: FeedbackPrompt; - }, - FeedbackComponentState -> { - state: FeedbackComponentState = { - rating: null, - hoveredRating: 0, - allowUserInfoSharing: true, - nextAction: 'select-rating' as NextAction, - predefinedComments: this.props.promptData.predefinedComments.reduce( - (acc, cv) => ({...acc, [cv]: false}), - {}, - ), - comment: '', - }; - onSubmitRating(newRating: number) { - const nextAction = newRating <= 2 ? 'leave-comment' : 'finished'; - this.setState({rating: newRating, nextAction: nextAction}); - this.props.submitRating(newRating); - if (nextAction === 'finished') { - setTimeout(this.props.close, 5000); - } - } - onCommentSubmitted(comment: string) { - this.setState({nextAction: 'finished'}); - const selectedPredefinedComments: Array = Object.entries( - this.state.predefinedComments, - ) - .map((x) => ({comment: x[0], enabled: x[1]})) - .filter((x) => x.enabled) - .map((x) => x.comment); - const currentRating = this.state.rating; - if (currentRating) { - this.props.submitComment( - currentRating, - comment, - selectedPredefinedComments, - this.state.allowUserInfoSharing, - ); - } else { - console.error('Illegal state: Submitting comment with no rating set.'); - } - setTimeout(this.props.close, 1000); - } - onAllowUserSharingChanged(allowed: boolean) { - this.setState({allowUserInfoSharing: allowed}); - } - render() { - let body: Array; - switch (this.state.nextAction) { - case 'select-rating': - body = [ - {this.props.promptData.bodyText}, - - this.onSubmitRating(newRating)} /> - , - dismissRow(this.props.dismiss), - ]; - break; - case 'leave-comment': - const predefinedComments = Object.entries( - this.state.predefinedComments, - ).map((c: [string, unknown], idx: number) => ( - - this.setState({ - predefinedComments: { - ...this.state.predefinedComments, - [c[0]]: !c[1], - }, - }) - } - /> - )); - body = [ - {predefinedComments}, - - this.setState({comment: e.target.value})} - onKeyDown={(e) => - e.key == 'Enter' && this.onCommentSubmitted(this.state.comment) - } - autoFocus - /> - , - - - {'Tool owner can contact me '} - , - - - , - dismissRow(this.props.dismiss), - ]; - break; - case 'finished': - body = [ - - Thanks for the feedback! You can now help - - prioritize bugs and features for Flipper in Papercuts - - , - dismissRow(this.props.dismiss), - ]; - break; - default: { - console.error('Illegal state: nextAction: ' + this.state.nextAction); - return null; - } - } - return ( - - - {this.state.nextAction === 'finished' - ? this.props.promptData.postSubmitHeading - : this.props.promptData.preSubmitHeading} - - {body} - - ); - } -} - -export function SandyRatingButton() { - const [promptData, setPromptData] = - useState(null); - const [isShown, setIsShown] = useState(false); - const [hasTriggered, setHasTriggered] = useState(false); - const sessionId = useStore((store) => store.application.sessionId); - const loggedIn = useValue(isLoggedIn()); - - const triggerPopover = useCallback(() => { - if (!hasTriggered) { - setIsShown(true); - setHasTriggered(true); - } - }, [hasTriggered]); - - useEffect(() => { - if ( - getRenderHostInstance().GK('flipper_enable_star_ratiings') && - !hasTriggered && - loggedIn - ) { - reportPlatformFailures( - UserFeedback.getPrompt().then((prompt) => { - setPromptData(prompt); - setTimeout(triggerPopover, 30000); - }), - 'RatingButton:getPrompt', - ).catch((e) => { - console.warn('Failed to load ratings prompt:', e); - }); - } - }, [triggerPopover, hasTriggered, loggedIn]); - - const onClick = () => { - const willBeShown = !isShown; - setIsShown(willBeShown); - setHasTriggered(true); - if (!willBeShown) { - UserFeedback.dismiss(sessionId); - } - }; - - const submitRating = (rating: number) => { - UserFeedback.submitRating(rating, sessionId); - }; - - const submitComment = ( - rating: number, - comment: string, - selectedPredefinedComments: Array, - allowUserInfoSharing: boolean, - ) => { - UserFeedback.submitComment( - rating, - comment, - selectedPredefinedComments, - allowUserInfoSharing, - sessionId, - ); - }; - - if (!promptData) { - return null; - } - if (!promptData.shouldPopup || (hasTriggered && !isShown)) { - return null; - } - return ( - { - setIsShown(false); - }} - dismiss={onClick} - promptData={promptData} - /> - } - placement="right" - trigger="click"> - } - title="Rate Flipper" - onClick={onClick} - small - /> - - ); -} diff --git a/desktop/flipper-ui-core/src/chrome/ScreenCaptureButtons.tsx b/desktop/flipper-ui-core/src/chrome/ScreenCaptureButtons.tsx index e42dc6732..40f06fdf7 100644 --- a/desktop/flipper-ui-core/src/chrome/ScreenCaptureButtons.tsx +++ b/desktop/flipper-ui-core/src/chrome/ScreenCaptureButtons.tsx @@ -7,22 +7,26 @@ * @format */ -import {Button as AntButton, message} from 'antd'; -import React, {useState, useCallback} from 'react'; +import {message} from 'antd'; +import React, {useState, useCallback, useEffect} from 'react'; import {capture, getCaptureLocation, getFileName} from '../utils/screenshot'; -import {CameraOutlined, VideoCameraOutlined} from '@ant-design/icons'; +import { + CameraOutlined, + VideoCameraFilled, + VideoCameraOutlined, +} from '@ant-design/icons'; import {useStore} from '../utils/useStore'; import {getRenderHostInstance} from 'flipper-frontend-core'; -import {path} from 'flipper-plugin'; +import {path, theme} from 'flipper-plugin'; +import {NavbarButton} from '../sandy-chrome/Navbar'; async function openFile(path: string) { getRenderHostInstance().flipperServer.exec('open-file', path); } -export default function ScreenCaptureButtons() { +export function NavbarScreenshotButton() { const selectedDevice = useStore((state) => state.connections.selectedDevice); const [isTakingScreenshot, setIsTakingScreenshot] = useState(false); - const [isRecording, setIsRecording] = useState(false); const handleScreenshot = useCallback(() => { setIsTakingScreenshot(true); @@ -37,6 +41,24 @@ export default function ScreenCaptureButtons() { }); }, [selectedDevice]); + return ( + + ); +} + +export function NavbarScreenRecordButton() { + const selectedDevice = useStore((state) => state.connections.selectedDevice); + const [isRecording, setIsRecording] = useState(false); + const handleRecording = useCallback(() => { if (!selectedDevice) { return; @@ -45,7 +67,7 @@ export default function ScreenCaptureButtons() { setIsRecording(true); const videoPath = path.join(getCaptureLocation(), getFileName('mp4')); return selectedDevice.startScreenCapture(videoPath).catch((e) => { - console.error('Failed to start recording', e); + console.warn('Failed to start recording', e); message.error('Failed to start recording' + e); setIsRecording(false); }); @@ -58,8 +80,8 @@ export default function ScreenCaptureButtons() { } }) .catch((e) => { - console.error('Failed to start recording', e); - message.error('Failed to start recording' + e); + console.warn('Failed to stop recording', e); + message.error('Failed to stop recording' + e); }) .finally(() => { setIsRecording(false); @@ -67,30 +89,33 @@ export default function ScreenCaptureButtons() { } }, [selectedDevice, isRecording]); + const [red, setRed] = useState(false); + useEffect(() => { + if (isRecording) { + setRed(true); + const handle = setInterval(() => { + setRed((red) => !red); + }, FlashInterval); + + return () => { + clearInterval(handle); + }; + } + }, [isRecording]); + return ( - <> - } - title="Take Screenshot" - type="ghost" - onClick={handleScreenshot} - disabled={ - !selectedDevice || - !selectedDevice.description.features.screenshotAvailable - } - loading={isTakingScreenshot} - /> - } - title="Make Screen Recording" - type={isRecording ? 'primary' : 'ghost'} - onClick={handleRecording} - disabled={ - !selectedDevice || - !selectedDevice.description.features.screenCaptureAvailable - } - danger={isRecording} - /> - + ); } + +const FlashInterval = 600; diff --git a/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx b/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx index 5ab70a300..96cf0a942 100644 --- a/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx +++ b/desktop/flipper-ui-core/src/chrome/SettingsSheet.tsx @@ -101,7 +101,7 @@ class SettingsSheet extends Component { ) { return ( { enablePluginMarketplace, enablePluginMarketplaceAutoUpdate, marketplaceURL, - server, } = this.state.updatedSettings; - - const serverUsageEnabled = getRenderHostInstance().GK( - 'flipper_desktop_use_server', - ); - const serverType = getRenderHostInstance().serverConfig.type; - const settingsPristine = isEqual(this.props.settings, this.state.updatedSettings) && isEqual(this.props.launcherSettings, this.state.updatedLauncherSettings); @@ -372,7 +365,7 @@ class SettingsSheet extends Component { }); }}> { /> - { - this.setState((prevState) => ({ - updatedSettings: { - ...prevState.updatedSettings, - server: {enabled: v}, - }, - })); - }}> - {serverUsageEnabled ? ( - <> - - {serverType ? ( - <> - - {serverType === 'external' ? ( - <> -
- - To stop the server, it may be necessary to kill the - process listening at port 52342. See below: - -
- - sudo lsof -i -P | grep LISTEN | grep 52342 -
- sudo kill <PID> -
- - ) : ( - <> - )} - - ) : ( - <> - )} - - ) : ( - - )} -
Reset all new user tooltips diff --git a/desktop/flipper-ui-core/src/chrome/ShareSheetExportFile.tsx b/desktop/flipper-ui-core/src/chrome/ShareSheetExportFile.tsx index ded73c4ac..a91cac408 100644 --- a/desktop/flipper-ui-core/src/chrome/ShareSheetExportFile.tsx +++ b/desktop/flipper-ui-core/src/chrome/ShareSheetExportFile.tsx @@ -13,15 +13,16 @@ import {reportPlatformFailures} from 'flipper-common'; import {Logger} from 'flipper-common'; import {IdlerImpl} from '../utils/Idler'; import { - exportStoreToFile, EXPORT_FLIPPER_TRACE_EVENT, displayFetchMetadataErrors, + exportStore, } from '../utils/exportData'; import ShareSheetErrorList from './ShareSheetErrorList'; import ShareSheetPendingDialog from './ShareSheetPendingDialog'; import {ReactReduxContext, ReactReduxContextValue} from 'react-redux'; import {MiddlewareAPI} from '../reducers/index'; import {Modal} from 'antd'; +import {getRenderHostInstance} from 'flipper-frontend-core'; const Container = styled(FlexColumn)({ padding: 20, @@ -47,7 +48,6 @@ const InfoText = styled(Text)({ type Props = { onHide: () => void; - file: string; logger: Logger; }; @@ -88,27 +88,25 @@ export default class ShareSheetExportFile extends Component { const mark = 'shareSheetExportFile'; performance.mark(mark); try { - if (!this.props.file) { - return; - } - const {fetchMetaDataErrors} = await reportPlatformFailures( - exportStoreToFile( - this.props.file, - this.store, - false, - this.idler, - (msg: string) => { + const {serializedString, fetchMetaDataErrors} = + await reportPlatformFailures( + exportStore(this.store, false, this.idler, (msg: string) => { this.setState({statusUpdate: msg}); - }, - ), - `${EXPORT_FLIPPER_TRACE_EVENT}:UI_FILE`, - ); + }), + `${EXPORT_FLIPPER_TRACE_EVENT}:UI_FILE`, + ); this.setState({ fetchMetaDataErrors, result: fetchMetaDataErrors ? {error: JSON.stringify(fetchMetaDataErrors) as any, kind: 'error'} : {kind: 'success'}, }); + + await getRenderHostInstance().exportFile(serializedString, { + defaultPath: 'export.flipper', + encoding: 'utf-8', + }); + this.props.logger.trackTimeSince(mark, 'export:file-success'); } catch (err) { const result: { @@ -202,7 +200,7 @@ export default class ShareSheetExportFile extends Component { render() { return ( - + {this.renderStatus()} ); diff --git a/desktop/flipper-ui-core/src/chrome/ShareSheetExportUrl.tsx b/desktop/flipper-ui-core/src/chrome/ShareSheetExportUrl.tsx index 4c7c0d06e..a2a60ccfb 100644 --- a/desktop/flipper-ui-core/src/chrome/ShareSheetExportUrl.tsx +++ b/desktop/flipper-ui-core/src/chrome/ShareSheetExportUrl.tsx @@ -152,7 +152,7 @@ export default class ShareSheetExportUrl extends Component { renderPending(statusUpdate: string | null) { return ( - + { const {title, errorArray} = displayFetchMetadataErrors(fetchMetaDataErrors); return ( - + <> diff --git a/desktop/flipper-ui-core/src/chrome/TroubleshootingHub.tsx b/desktop/flipper-ui-core/src/chrome/TroubleshootingHub.tsx new file mode 100644 index 000000000..4334fc3b8 --- /dev/null +++ b/desktop/flipper-ui-core/src/chrome/TroubleshootingHub.tsx @@ -0,0 +1,51 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Layout} from '../ui'; +import React from 'react'; +import {Tabs} from 'flipper-plugin'; +import SetupDoctorScreen from '../sandy-chrome/SetupDoctorScreen'; +import {ConsoleLogs} from './ConsoleLogs'; +import {FlipperMessages} from './FlipperMessages'; +import {ConnectivityLogs} from './ConnectivityLogs'; + +export function TroubleshootingHub() { + const items = React.useMemo( + () => [ + { + key: 'environment-check', + label: 'Environment Check', + children: ( + {}} /> + ), + }, + { + key: 'connectivity-logs', + label: 'Connectivity Logs', + children: , + }, + { + key: 'console-logs', + label: 'Console Logs', + children: , + }, + { + key: 'messages', + label: 'Messages', + children: , + }, + ], + [], + ); + return ( + + + + ); +} diff --git a/desktop/flipper-ui-core/src/chrome/UpdateIndicator.tsx b/desktop/flipper-ui-core/src/chrome/UpdateIndicator.tsx index fee3675cb..fdaaa3851 100644 --- a/desktop/flipper-ui-core/src/chrome/UpdateIndicator.tsx +++ b/desktop/flipper-ui-core/src/chrome/UpdateIndicator.tsx @@ -15,6 +15,8 @@ import fbConfig from '../fb-stubs/config'; import {useStore} from '../utils/useStore'; import {getAppVersion} from '../utils/info'; import {checkForUpdate} from '../fb-stubs/checkForUpdate'; +import {getRenderHostInstance} from 'flipper-frontend-core'; +import {NotificationBody} from '../ui/components/NotificationBody'; export type VersionCheckResult = | { @@ -63,13 +65,14 @@ export default function UpdateIndicator() { // trigger the update check, unless there is a launcher message already useEffect(() => { const version = getAppVersion(); + const config = getRenderHostInstance().serverConfig.processConfig; if (launcherMsg && launcherMsg.message) { if (launcherMsg.severity === 'error') { notification.error({ placement: 'bottomLeft', key: 'launchermsg', message: 'Launch problem', - description: launcherMsg.message, + description: , duration: null, }); } else { @@ -77,11 +80,16 @@ export default function UpdateIndicator() { placement: 'bottomLeft', key: 'launchermsg', message: 'Flipper version warning', - description: launcherMsg.message, + description: , duration: null, }); } - } else if (version && isProduction()) { + } else if ( + version && + config.updaterEnabled && + !config.suppressPluginUpdateNotifications && + isProduction() + ) { reportPlatformFailures( checkForUpdate(version).then((res) => { if (res.kind === 'error') { diff --git a/desktop/flipper-ui-core/src/chrome/__tests__/ChangelogSheet.node.tsx b/desktop/flipper-ui-core/src/chrome/__tests__/ChangelogSheet.node.tsx index 7ab1f53a1..971ef3021 100644 --- a/desktop/flipper-ui-core/src/chrome/__tests__/ChangelogSheet.node.tsx +++ b/desktop/flipper-ui-core/src/chrome/__tests__/ChangelogSheet.node.tsx @@ -54,8 +54,8 @@ describe('ChangelogSheet', () => { test('with last header, should not show changes', () => { markChangelogRead(storage, changelog); expect(storage.data).toMatchInlineSnapshot(` - Object { - "FlipperChangelogStatus": "{\\"lastHeader\\":\\"# Version 2.0\\"}", + { + "FlipperChangelogStatus": "{"lastHeader":"# Version 2.0"}", } `); expect(hasNewChangesToShow(storage, changelog)).toBe(false); @@ -74,18 +74,18 @@ ${changelog} expect(hasNewChangesToShow(storage, newChangelog)).toBe(true); expect(getRecentChangelog(storage, newChangelog)).toMatchInlineSnapshot(` -"# Version 3.0 + "# Version 3.0 -* Cool! + * Cool! -# Version 2.5 + # Version 2.5 -* This is visible as well" -`); + * This is visible as well" + `); markChangelogRead(storage, newChangelog); expect(storage.data).toMatchInlineSnapshot(` - Object { - "FlipperChangelogStatus": "{\\"lastHeader\\":\\"# Version 3.0\\"}", + { + "FlipperChangelogStatus": "{"lastHeader":"# Version 3.0"}", } `); expect(hasNewChangesToShow(storage, newChangelog)).toBe(false); diff --git a/desktop/flipper-ui-core/src/chrome/__tests__/__snapshots__/ShareSheetPendingDialog.node.tsx.snap b/desktop/flipper-ui-core/src/chrome/__tests__/__snapshots__/ShareSheetPendingDialog.node.tsx.snap index 73a1f069f..1aff655f7 100644 --- a/desktop/flipper-ui-core/src/chrome/__tests__/__snapshots__/ShareSheetPendingDialog.node.tsx.snap +++ b/desktop/flipper-ui-core/src/chrome/__tests__/__snapshots__/ShareSheetPendingDialog.node.tsx.snap @@ -4,13 +4,15 @@ exports[`ShareSheetPendingDialog is rendered with status update 1`] = `
@@ -255,10 +255,10 @@ exports[`load PluginInstaller list 1`] = ` href="https://yarnpkg.com/en/package/flipper-plugin-world" >
@@ -439,7 +439,7 @@ exports[`load PluginInstaller list with one plugin installed 1`] = `
@@ -641,10 +641,10 @@ exports[`load PluginInstaller list with one plugin installed 1`] = ` href="https://yarnpkg.com/en/package/flipper-plugin-world" >
diff --git a/desktop/flipper-ui-core/src/deprecated-exports.tsx b/desktop/flipper-ui-core/src/deprecated-exports.tsx index 23fff3ae7..6352f0760 100644 --- a/desktop/flipper-ui-core/src/deprecated-exports.tsx +++ b/desktop/flipper-ui-core/src/deprecated-exports.tsx @@ -19,8 +19,10 @@ export { internGraphGETAPIRequest, internGraphPOSTAPIRequest, graphQLQuery, - isLoggedIn, + currentUser as isLoggedIn, getUser, + fetchUser, + getCachedUser, } from './fb-stubs/user'; export {FlipperPlugin, FlipperDevicePlugin, BaseAction} from './plugin'; export {PluginClient, Props, KeyboardActions} from './plugin'; diff --git a/desktop/flipper-ui-core/src/dispatcher/__tests__/pluginManager.node.tsx b/desktop/flipper-ui-core/src/dispatcher/__tests__/pluginManager.node.tsx index 19e6c223a..931cd4128 100644 --- a/desktop/flipper-ui-core/src/dispatcher/__tests__/pluginManager.node.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/__tests__/pluginManager.node.tsx @@ -14,7 +14,6 @@ import { uninstallPlugin, } from '../../reducers/pluginManager'; import {requirePlugin} from '../plugins'; -import {mocked} from 'ts-jest/utils'; import {TestUtils} from 'flipper-plugin'; import * as TestPlugin from '../../__tests__/test-utils/TestPlugin'; import {_SandyPluginDefinition as SandyPluginDefinition} from 'flipper-plugin'; @@ -63,7 +62,7 @@ const devicePluginDefinition = new SandyPluginDefinition(devicePluginDetails, { }, }); -const mockedRequirePlugin = mocked(requirePlugin); +const mockedRequirePlugin = jest.mocked(requirePlugin); let mockFlipper: MockFlipper; let mockClient: Client; @@ -168,34 +167,6 @@ test('uninstall plugin', async () => { expect(mockClient.sandyPluginStates.has('plugin1')).toBeFalsy(); }); -test('uninstall bundled plugin', async () => { - const pluginDetails = TestUtils.createMockBundledPluginDetails({ - id: 'bundled-plugin', - name: 'flipper-bundled-plugin', - version: '0.43.0', - }); - const pluginDefinition = new SandyPluginDefinition(pluginDetails, TestPlugin); - mockedRequirePlugin.mockReturnValue(Promise.resolve(pluginDefinition)); - mockFlipper.dispatch( - loadPlugin({plugin: pluginDetails, enable: true, notifyIfFailed: false}), - ); - mockFlipper.dispatch(uninstallPlugin({plugin: pluginDefinition})); - - await awaitPluginCommandQueueEmpty(mockFlipper.store); - expect( - mockFlipper.getState().plugins.clientPlugins.has('bundled-plugin'), - ).toBeFalsy(); - expect( - mockFlipper.getState().plugins.loadedPlugins.has('bundled-plugin'), - ).toBeFalsy(); - expect( - mockFlipper - .getState() - .plugins.uninstalledPluginNames.has('flipper-bundled-plugin'), - ).toBeTruthy(); - expect(mockClient.sandyPluginStates.has('bundled-plugin')).toBeFalsy(); -}); - test('star plugin', async () => { mockFlipper.dispatch( loadPlugin({plugin: pluginDetails1, enable: false, notifyIfFailed: false}), diff --git a/desktop/flipper-ui-core/src/dispatcher/__tests__/plugins.node.tsx b/desktop/flipper-ui-core/src/dispatcher/__tests__/plugins.node.tsx index 7ecb808f5..2884ad1da 100644 --- a/desktop/flipper-ui-core/src/dispatcher/__tests__/plugins.node.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/__tests__/plugins.node.tsx @@ -38,13 +38,9 @@ const sampleInstalledPluginDetails: InstalledPluginDetails = { title: 'Sample', dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-sample', entry: 'this/path/does not/exist', - isBundled: false, isActivatable: true, }; -// bind to empty default plugin index so we try fetching from flipper-server every time -const requirePlugin = requirePluginInternal.bind({}, {}); - beforeEach(() => { loadDynamicPluginsMock = getRenderHostInstance().flipperServer.exec = jest.fn(); @@ -58,7 +54,7 @@ test('dispatcher dispatches REGISTER_PLUGINS', async () => { }); test('requirePluginInternal returns null for invalid requires', async () => { - const requireFn = createRequirePluginFunction(requirePlugin)([]); + const requireFn = createRequirePluginFunction(requirePluginInternal)([]); const plugin = await requireFn({ ...sampleInstalledPluginDetails, name: 'pluginID', @@ -72,7 +68,7 @@ test('requirePluginInternal returns null for invalid requires', async () => { test('requirePluginInternal loads plugin', async () => { const name = 'pluginID'; - const requireFn = createRequirePluginFunction(requirePlugin)([]); + const requireFn = createRequirePluginFunction(requirePluginInternal)([]); const plugin = await requireFn({ ...sampleInstalledPluginDetails, name, @@ -83,6 +79,7 @@ test('requirePluginInternal loads plugin', async () => { expect(plugin).not.toBeNull(); expect(Object.keys(plugin as any)).toEqual([ 'id', + 'css', 'details', 'isDevicePlugin', 'module', @@ -94,7 +91,7 @@ test('requirePluginInternal loads plugin', async () => { test('requirePluginInternal loads valid Sandy plugin', async () => { const name = 'pluginID'; - const requireFn = createRequirePluginFunction(requirePlugin)([]); + const requireFn = createRequirePluginFunction(requirePluginInternal)([]); const plugin = (await requireFn({ ...sampleInstalledPluginDetails, name, @@ -115,7 +112,6 @@ test('requirePluginInternal loads valid Sandy plugin', async () => { expect(plugin.details).toMatchObject({ flipperSDKVersion: '0.0.0', id: 'Sample', - isBundled: false, main: 'dist/bundle.js', name: 'pluginID', source: 'src/index.js', @@ -132,7 +128,9 @@ test('requirePluginInternal loads valid Sandy plugin', async () => { test('requirePluginInternal errors on invalid Sandy plugin', async () => { const name = 'pluginID'; const failedPlugins: any[] = []; - const requireFn = createRequirePluginFunction(requirePlugin)(failedPlugins); + const requireFn = createRequirePluginFunction(requirePluginInternal)( + failedPlugins, + ); await requireFn({ ...sampleInstalledPluginDetails, name, @@ -149,7 +147,7 @@ test('requirePluginInternal errors on invalid Sandy plugin', async () => { test('requirePluginInternal loads valid Sandy Device plugin', async () => { const name = 'pluginID'; - const requireFn = createRequirePluginFunction(requirePlugin)([]); + const requireFn = createRequirePluginFunction(requirePluginInternal)([]); const plugin = (await requireFn({ ...sampleInstalledPluginDetails, pluginType: 'device', @@ -171,7 +169,6 @@ test('requirePluginInternal loads valid Sandy Device plugin', async () => { expect(plugin.details).toMatchObject({ flipperSDKVersion: '0.0.0', id: 'Sample', - isBundled: false, main: 'dist/bundle.js', name: 'pluginID', source: 'src/index.js', diff --git a/desktop/flipper-ui-core/src/dispatcher/application.tsx b/desktop/flipper-ui-core/src/dispatcher/application.tsx index 903443ca2..936b23e61 100644 --- a/desktop/flipper-ui-core/src/dispatcher/application.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/application.tsx @@ -10,7 +10,7 @@ import {Store} from '../reducers/index'; import {Logger} from 'flipper-common'; import { - importFileToStore, + importDataToStore, IMPORT_FLIPPER_TRACE_EVENT, } from '../utils/exportData'; import {tryCatchReportPlatformFailures} from 'flipper-common'; @@ -67,9 +67,9 @@ export default (store: Store, logger: Logger) => { }); }); - renderHost.onIpcEvent('open-flipper-file', (url: string) => { + renderHost.onIpcEvent('open-flipper-file', (name: string, data: string) => { tryCatchReportPlatformFailures(() => { - return importFileToStore(url, store); + return importDataToStore(name, data, store); }, `${IMPORT_FLIPPER_TRACE_EVENT}:Deeplink`); }); }; diff --git a/desktop/flipper-ui-core/src/dispatcher/flipperServer.tsx b/desktop/flipper-ui-core/src/dispatcher/flipperServer.tsx index 48b581acb..da29ac660 100644 --- a/desktop/flipper-ui-core/src/dispatcher/flipperServer.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/flipperServer.tsx @@ -18,12 +18,15 @@ import { FlipperServerState, } from 'flipper-common'; import Client from '../Client'; -import {notification} from 'antd'; +import {Button, notification} from 'antd'; import {BaseDevice} from 'flipper-frontend-core'; import {ClientDescription, timeout} from 'flipper-common'; import {reportPlatformFailures} from 'flipper-common'; import {sideEffect} from '../utils/sideEffect'; import {waitFor} from '../utils/waitFor'; +import {NotificationBody} from '../ui/components/NotificationBody'; +import {Layout} from '../ui'; +import {toggleConnectivityModal} from '../reducers/application'; export function connectFlipperServerToStore( server: FlipperServer, @@ -31,41 +34,32 @@ export function connectFlipperServerToStore( logger: Logger, ) { server.on('notification', ({type, title, description}) => { - const text = `[${type}] ${title}: ${description}`; - console.warn(text); - notification.open({ - message: title, - description: description, - type: type, - duration: 0, - key: text, - }); + const key = `[${type}] ${title}: ${description}`; + showNotification(key, type, title, description); }); + server.on( + 'connectivity-troubleshoot-notification', + ({type, title, description}) => { + const key = `[${type}] ${title}: ${description}`; + showConnectivityTroubleshootNotification(store, key, title, description); + }, + ); + server.on('server-state', handleServerStateChange); server.on('server-error', (err) => { - notification.error({ - message: 'Connection error', - description: - err.code === 'EADDRINUSE' ? ( - <> - Couldn't start connection server. Looks like you have multiple - copies of Flipper running or another process is using the same - port(s). As a result devices will not be able to connect to Flipper. -
-
- Please try to kill the offending process by running{' '} - kill $(lsof -ti:PORTNUMBER) and restart flipper. -
-
- {'' + err} - - ) : ( - <>{err.message ?? err} - ), - duration: null, - }); + if (err.code === 'EADDRINUSE') { + handeEADDRINUSE('' + err); + } else { + const text = err.message ?? err; + notification.error({ + key: text, + message: 'Connection error', + description: , + duration: null, + }); + } }); server.on('device-connected', (deviceInfo) => { @@ -188,17 +182,44 @@ function handleServerStateChange({ error?: string; }) { if (state === 'error') { - console.warn(`[conn] Flipper server state -> ${state}`, error); - notification.error({ - message: 'Failed to start flipper-server', - description: '' + error, - duration: null, - }); + console.warn(`Flipper server state -> ${state}`, error); + if (error?.includes('EADDRINUSE')) { + handeEADDRINUSE(error); + } else { + notification.error({ + key: `server-${state}-error`, + message: 'Failed to start flipper-server', + description: '' + error, + duration: null, + }); + } } else { - console.info(`[conn] Flipper server state -> ${state}`); + console.info(`Flipper server state -> ${state}`); } } +function handeEADDRINUSE(errorMessage: string) { + notification.error({ + key: 'eaddrinuse-error', + message: 'Connection error', + description: ( + <> + Couldn't start connection server. Looks like you have multiple copies of + Flipper running or another process is using the same port(s). As a + result devices will not be able to connect to Flipper. +
+
+ Please try to kill the offending process by running{' '} + sudo kill -9 $(lsof -ti:PORTNUMBER) and restart flipper. +
+
+ {errorMessage} + + ), + duration: null, + }); +} + export function handleDeviceConnected( server: FlipperServer, store: Store, @@ -259,6 +280,50 @@ export function handleDeviceDisconnected( existing?.connected.set(false); } +function showNotification( + key: string, + type: 'success' | 'info' | 'error' | 'warning', + message: string, + description: string, +) { + notification.open({ + message, + description: , + type, + duration: 0, + key, + }); +} + +function showConnectivityTroubleshootNotification( + store: Store, + key: string, + message: string, + description: string, +) { + notification.error({ + key, + message, + description: ( + +

{description}

+
+ +
+
+ ), + duration: 0, + }); +} + export async function handleClientConnected( server: FlipperServer, store: Store, @@ -283,21 +348,24 @@ export async function handleClientConnected( } console.log( - `[conn] Searching matching device ${query.device_id} for client ${query.app}...`, + `Searching matching device ${query.device_id} for client ${query.app}...`, ); const device = getDeviceBySerial(store.getState(), query.device_id) ?? (await findDeviceForConnection(store, query.app, query.device_id).catch( (e) => { console.warn( - `[conn] Failed to find device '${query.device_id}' while connection app '${query.app}'`, + `Failed to find device '${query.device_id}' while connection app '${query.app}'`, e, ); - notification.error({ - message: 'Connection failed', - description: `Failed to find device '${query.device_id}' while trying to connect app '${query.app}'`, - duration: 0, - }); + const key = `device-find-failure-${query.device_id}`; + showConnectivityTroubleshootNotification( + store, + key, + 'Connection failed', + `Failed to find device '${query.device_id}' while trying to + connect app '${query.app}'`, + ); }, )); @@ -310,7 +378,9 @@ export async function handleClientConnected( query, { send(data: any) { - server.exec('client-request', id, data); + server.exec('client-request', id, data).catch((e) => { + console.warn(e); + }); }, async sendExpectResponse(data: any) { return await server.exec('client-request-response', id, data); @@ -338,19 +408,17 @@ export async function handleClientConnected( await timeout( 30 * 1000, client.init(), - `[conn] Failed to initialize client ${query.app} on ${query.device_id} in a timely manner`, - ); - console.log( - `[conn] ${query.app} on ${query.device_id} connected and ready.`, + `Failed to initialize client ${query.app} on ${query.device_id} in a timely manner`, ); + console.log(`${query.app} on ${query.device_id} connected and ready.`); } catch (e) { if (e instanceof NoLongerConnectedToClientError) { console.warn( - `[conn] Client ${query.app} on ${query.device_id} disconnected while initialising`, + `Client ${query.app} on ${query.device_id} disconnected while initialising`, ); return; } - throw e; + console.warn(`Failed to handle client connected: ${e}`); } } @@ -395,7 +463,7 @@ async function findDeviceForConnection( (device) => device.serial === serial, ); if (matchingDevice) { - console.log(`[conn] Found device for: ${clientId} on ${serial}.`); + console.log(`Found device for: ${clientId} on ${serial}.`); clearTimeout(timeout); resolve(matchingDevice); unsubscribe(); diff --git a/desktop/flipper-ui-core/src/dispatcher/handleOpenPluginDeeplink.tsx b/desktop/flipper-ui-core/src/dispatcher/handleOpenPluginDeeplink.tsx index d5869fa41..48ec33258 100644 --- a/desktop/flipper-ui-core/src/dispatcher/handleOpenPluginDeeplink.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/handleOpenPluginDeeplink.tsx @@ -9,14 +9,18 @@ import React from 'react'; import {Dialog, getFlipperLib} from 'flipper-plugin'; -import {isTest} from 'flipper-common'; -import {getUser} from '../fb-stubs/user'; -import {Store} from '../reducers/index'; +import { + isTest, + UserNotSignedInError, + UserUnauthorizedError, +} from 'flipper-common'; +import {fetchUser} from '../fb-stubs/user'; +import {State, Store} from '../reducers/index'; import {checkForUpdate} from '../fb-stubs/checkForUpdate'; import {getAppVersion} from '../utils/info'; -import {UserNotSignedInError} from 'flipper-common'; import { canBeDefaultDevice, + getAllClients, selectPlugin, setPluginEnabled, } from '../reducers/connections'; @@ -24,21 +28,19 @@ import {getUpdateAvailableMessage} from '../chrome/UpdateIndicator'; import {Typography} from 'antd'; import {getPluginStatus, PluginStatus} from '../utils/pluginUtils'; import {loadPluginsFromMarketplace} from './pluginMarketplace'; -import {loadPlugin, switchPlugin} from '../reducers/pluginManager'; +import {switchPlugin} from '../reducers/pluginManager'; import {startPluginDownload} from '../reducers/pluginDownloads'; import isProduction from '../utils/isProduction'; -import {BaseDevice} from 'flipper-frontend-core'; +import {BaseDevice, getRenderHostInstance} from 'flipper-frontend-core'; import Client from '../Client'; import {RocketOutlined} from '@ant-design/icons'; import {showEmulatorLauncher} from '../sandy-chrome/appinspect/LaunchEmulator'; -import {getAllClients} from '../reducers/connections'; import {showLoginDialog} from '../chrome/fb-stubs/SignInSheet'; import { DeeplinkInteraction, DeeplinkInteractionState, OpenPluginParams, } from '../deeplinkTracking'; -import {getRenderHostInstance} from 'flipper-frontend-core'; import {waitFor} from '../utils/waitFor'; export function parseOpenPluginParams(query: string): OpenPluginParams { @@ -143,6 +145,17 @@ export async function handleOpenPluginDeeplink( return; } console.debug('[deeplink] Cleared device plugin support check.'); + + console.debug( + '[deeplink] Waiting for client initialization', + client?.id, + client?.initializationPromise, + ); + + await client?.initializationPromise; + + console.debug('[deeplink] Client initialized ', client?.id); + if (!isDevicePlugin && !client!.plugins.has(params.pluginId)) { await Dialog.alert({ title, @@ -217,7 +230,7 @@ async function verifyLighthouseAndUserLoggedIn( }); try { - const user = await getUser(); + const user = await fetchUser(); spinnerDialog.close(); // User is logged in if (user) { @@ -228,7 +241,10 @@ async function verifyLighthouseAndUserLoggedIn( } } catch (e) { spinnerDialog.close(); - if (e instanceof UserNotSignedInError) { + if ( + e instanceof UserNotSignedInError || + e instanceof UserUnauthorizedError + ) { // connection, but user is not logged in return await showPleaseLoginDialog(store, title); } @@ -274,7 +290,18 @@ async function waitForLogin(store: Store) { } async function verifyFlipperIsUpToDate(title: string) { - if (!isProduction() || isTest()) { + const serverConfig = getRenderHostInstance().serverConfig; + // // If this is not a headless build, do not check for updates. + // if (!serverConfig.environmentInfo.isHeadlessBuild) { + // return; + // } + const config = serverConfig.processConfig; + if ( + !isProduction() || + isTest() || + !config.updaterEnabled || + config.suppressPluginUpdateNotifications + ) { return; } const currentVersion = getAppVersion(); @@ -316,6 +343,7 @@ async function verifyPluginStatus( pluginId: string, title: string, ): Promise<[boolean, PluginStatus]> { + await waitFor(store, (state) => state.plugins.initialized); // make sure we have marketplace plugin data present if (!isTest() && !store.getState().plugins.marketplacePlugins.length) { // plugins not yet fetched @@ -367,11 +395,6 @@ async function verifyPluginStatus( return [false, status]; } break; - case 'bundle_installable': { - // For convenience, don't ask user to install bundled plugins, handle it directly - await installBundledPlugin(store, pluginId, title); - break; - } case 'marketplace_installable': { if (!(await installMarketPlacePlugin(store, pluginId, title))) { return [false, status]; @@ -384,30 +407,6 @@ async function verifyPluginStatus( } } -async function installBundledPlugin( - store: Store, - pluginId: string, - title: string, -) { - const plugin = store.getState().plugins.bundledPlugins.get(pluginId); - if (!plugin || !plugin.isBundled) { - throw new Error(`Failed to find bundled plugin '${pluginId}'`); - } - const loadingDialog = Dialog.loading({ - title, - message: `Loading plugin '${pluginId}'...`, - }); - store.dispatch(loadPlugin({plugin, enable: true, notifyIfFailed: true})); - try { - await waitFor( - store, - () => getPluginStatus(store, pluginId)[0] !== 'bundle_installable', - ); - } finally { - loadingDialog.close(); - } -} - async function installMarketPlacePlugin( store: Store, pluginId: string, @@ -494,66 +493,97 @@ async function selectDevicesAndClient( return selectedDevice; } - console.debug('[deeplink] Not a device plugin. Waiting for valid client.'); - // wait for valid client - while (true) { - const origClients = store.getState().connections.clients; - const validClients = getAllClients(store.getState().connections) - .filter( - // correct app name, or, if not set, an app that at least supports this plugin - (c) => - params.client - ? c.query.app === params.client - : c.plugins.has(params.pluginId), - ) - .filter((c) => c.connected.get()) - .filter((c) => availableDevices.includes(c.device)); + console.debug( + '[deeplink] Not a device plugin. Waiting for valid client. current clients', + store.getState().connections.clients, + ); + return await waitForClient(store, availableDevices, params, title); +} - if (validClients.length === 1) { - return validClients[0]; - } - if (validClients.length > 1) { - const selectedClient = await selectClientDialog(validClients, title); - if (!selectedClient) { - return {errorState: 'PLUGIN_CLIENT_SELECTION_BAIL'}; - } - return selectedClient; - } +async function waitForClient( + store: Store, + availableDevices: BaseDevice[], + params: OpenPluginParams, + title: string, +): Promise { + const response = await tryGetClient( + store.getState(), + availableDevices, + params, + title, + ); - // no valid client yet - const result = await new Promise((resolve) => { - const dialog = Dialog.alert({ - title, - type: 'warning', - message: params.client - ? `Application '${params.client}' doesn't seem to be connected yet. Please start a debug version of the app to continue.` - : `No application that supports plugin '${params.pluginId}' seems to be running. Please start a debug application that supports the plugin to continue.`, - okText: 'Cancel', - }); - // eslint-disable-next-line promise/catch-or-return - dialog.then(() => resolve(false)); - - // eslint-disable-next-line promise/catch-or-return - waitFor(store, (state) => state.connections.clients !== origClients).then( - () => { - dialog.close(); - resolve(true); - }, - ); - - // We also want to react to changes in the available plugins and refresh. - origClients.forEach((c) => - c.on('plugins-change', () => { - dialog.close(); - resolve(true); - }), - ); - }); - - if (!result) { - return {errorState: 'PLUGIN_CLIENT_BAIL'}; // User cancelled - } + if (response != null) { + return response; } + + const dialog = Dialog.alert({ + title, + type: 'warning', + message: params.client + ? `Application '${params.client}' doesn't seem to be connected yet. Please start a debug version of the app to continue.` + : `No application that supports plugin '${params.pluginId}' seems to be running. Please start a debug application that supports the plugin to continue.`, + okText: 'Cancel', + }); + + const userCancelled: Promise = dialog.then(() => ({ + errorState: 'PLUGIN_CLIENT_BAIL', + })); + + let unsubStore: () => void; + const clientFound = new Promise(async (resolve) => { + unsubStore = store.subscribe(async () => { + const state = store.getState(); + const client = await tryGetClient(state, availableDevices, params, title); + if (client != null) { + resolve(client); + } + }); + }); + + return await Promise.race([userCancelled, clientFound]).finally(() => { + console.log('[deeplink] finally cleanup', {clientFound, dialog}); + dialog.close(); + unsubStore(); + }); +} + +async function tryGetClient( + state: State, + availableDevices: BaseDevice[], + params: OpenPluginParams, + title: string, +): Promise { + const validClients = getAllClients(state.connections) + .filter( + // correct app name, or, if not set, an app that at least supports this plugin + (c) => + params.client + ? c.query.app === params.client + : c.plugins.has(params.pluginId), + ) + .filter((c) => c.connected.get()) + .filter((c) => + availableDevices.map((d) => d.serial).includes(c.device.serial), + ); + + if (validClients.length === 1) { + return validClients[0]; + } + if (validClients.length > 1) { + const selectedClient = await selectClientDialog(validClients, title); + if (!selectedClient) { + return {errorState: 'PLUGIN_CLIENT_SELECTION_BAIL'}; + } + return selectedClient; + } + + console.debug(`[deeplink] Did not find client`, { + clients: getAllClients(state.connections), + params, + availableDevices, + }); + return null; } /** diff --git a/desktop/flipper-ui-core/src/dispatcher/index.tsx b/desktop/flipper-ui-core/src/dispatcher/index.tsx index fe5c21d3f..ebd445f2f 100644 --- a/desktop/flipper-ui-core/src/dispatcher/index.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/index.tsx @@ -19,13 +19,17 @@ import pluginMarketplace from './pluginMarketplace'; import pluginDownloads from './pluginDownloads'; import info from '../utils/info'; import pluginChangeListener from './pluginsChangeListener'; +import pluginsSourceUpdateListener from './pluginsSourceUpdateListener'; import {Logger} from 'flipper-common'; import {Store} from '../reducers/index'; import {Dispatcher} from './types'; import {notNull} from '../utils/typeUtils'; -export default function (store: Store, logger: Logger): () => Promise { +export default async function ( + store: Store, + logger: Logger, +): Promise<() => Promise> { // This only runs in development as when the reload // kicks in it doesn't unregister the shortcuts const dispatchers: Array = [ @@ -40,11 +44,13 @@ export default function (store: Store, logger: Logger): () => Promise { pluginDownloads, info, pluginChangeListener, + pluginsSourceUpdateListener, ].filter(notNull); - const globalCleanup = dispatchers - .map((dispatcher) => dispatcher(store, logger)) - .filter(Boolean); - return () => { - return Promise.all(globalCleanup).then(() => {}); + const globalCleanup = await Promise.all( + dispatchers.map((dispatcher) => dispatcher(store, logger)).filter(Boolean), + ); + + return async () => { + await Promise.all(globalCleanup); }; } diff --git a/desktop/flipper-ui-core/src/dispatcher/pluginDownloads.tsx b/desktop/flipper-ui-core/src/dispatcher/pluginDownloads.tsx index 8c71f71f7..584a022cf 100644 --- a/desktop/flipper-ui-core/src/dispatcher/pluginDownloads.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/pluginDownloads.tsx @@ -83,14 +83,14 @@ async function handlePluginDownload( `Successfully downloaded and installed plugin "${title}" v${version} from "${downloadUrl}".`, ); } catch (error) { - console.error( + console.warn( `Failed to download plugin "${title}" v${version} from "${downloadUrl}".`, error, ); if (startedByUser) { showErrorNotification( `Failed to download plugin "${title}" v${version}.`, - 'Please check that you are on VPN/Lighthouse.', + 'Please check that you are on VPN/Lighthouse and that you are logged into Flipper.', ); } throw error; diff --git a/desktop/flipper-ui-core/src/dispatcher/pluginManager.tsx b/desktop/flipper-ui-core/src/dispatcher/pluginManager.tsx index 0cdeec03f..1b1ea2ed3 100644 --- a/desktop/flipper-ui-core/src/dispatcher/pluginManager.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/pluginManager.tsx @@ -140,9 +140,6 @@ async function processPluginCommandsQueue( case 'UNINSTALL_PLUGIN': uninstallPlugin(store, command.payload); break; - case 'UPDATE_PLUGIN': - updatePlugin(store, command.payload); - break; case 'SWITCH_PLUGIN': switchPlugin(store, command.payload); break; @@ -160,6 +157,7 @@ async function processPluginCommandsQueue( async function loadPlugin(store: Store, payload: LoadPluginActionPayload) { try { + unloadPluginModule(payload.plugin); const plugin = await requirePlugin(payload.plugin); const enablePlugin = payload.enable; updatePlugin(store, {plugin, enablePlugin}); @@ -183,9 +181,7 @@ function uninstallPlugin(store: Store, {plugin}: UninstallPluginActionPayload) { clients.forEach((client) => { stopPlugin(client, plugin.id); }); - if (!plugin.details.isBundled) { - unloadPluginModule(plugin.details); - } + unloadPluginModule(plugin.details); store.dispatch(pluginUninstalled(plugin.details)); } catch (err) { console.error( @@ -294,17 +290,23 @@ function updateClientPlugin( .connections.enabledPlugins[c.query.app]?.includes(plugin.id) ); }); - const previousVersion = store.getState().plugins.clientPlugins.get(plugin.id); clientsWithEnabledPlugin.forEach((client) => { stopPlugin(client, plugin.id); }); clientsWithEnabledPlugin.forEach((client) => { startPlugin(client, plugin, true); }); - store.dispatch(pluginLoaded(plugin)); - if (previousVersion) { - // unload previous version from Electron cache - unloadPluginModule(previousVersion.details); + if ( + !store + .getState() + .plugins.disabledPlugins.find( + (disabledPlugin) => disabledPlugin.id === plugin.id, + ) && + !store + .getState() + .plugins.gatekeepedPlugins.find((gkPlugin) => gkPlugin.id === plugin.id) + ) { + store.dispatch(pluginLoaded(plugin)); } } @@ -323,15 +325,21 @@ function updateDevicePlugin( devicesWithEnabledPlugin.forEach((d) => { d.unloadDevicePlugin(plugin.id); }); - const previousVersion = store.getState().plugins.devicePlugins.get(plugin.id); - if (previousVersion) { - // unload previous version from Electron cache - unloadPluginModule(previousVersion.details); - } - store.dispatch(pluginLoaded(plugin)); devicesWithEnabledPlugin.forEach((d) => { d.loadDevicePlugin(plugin); }); + if ( + !store + .getState() + .plugins.disabledPlugins.find( + (disabledPlugin) => disabledPlugin.id === plugin.id, + ) && + !store + .getState() + .plugins.gatekeepedPlugins.find((gkPlugin) => gkPlugin.id === plugin.id) + ) { + store.dispatch(pluginLoaded(plugin)); + } } function startPlugin( @@ -368,9 +376,5 @@ function stopPlugin( } function unloadPluginModule(plugin: ActivatablePluginDetails) { - if (plugin.isBundled) { - // We cannot unload bundled plugin. - return; - } getRenderHostInstance().unloadModule?.(plugin.entry); } diff --git a/desktop/flipper-ui-core/src/dispatcher/pluginMarketplace.tsx b/desktop/flipper-ui-core/src/dispatcher/pluginMarketplace.tsx index 0265f0892..dc57115ba 100644 --- a/desktop/flipper-ui-core/src/dispatcher/pluginMarketplace.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/pluginMarketplace.tsx @@ -24,7 +24,7 @@ import isPluginCompatible from '../utils/isPluginCompatible'; import {selectCompatibleMarketplaceVersions} from './plugins'; import isPluginVersionMoreRecent from '../utils/isPluginVersionMoreRecent'; import {isConnectivityOrAuthError} from 'flipper-common'; -import {isLoggedIn} from '../fb-stubs/user'; +import {currentUser} from '../fb-stubs/user'; import {getRenderHostInstance} from 'flipper-frontend-core'; // TODO: provide this value from settings @@ -43,7 +43,7 @@ function isAutoUpdateDisabled(store: Store) { (!getFlipperLib().isFB && !store.getState().settingsState.enablePluginMarketplaceAutoUpdate) || // for internal build we disable auto-updates in case user is not logged - (getFlipperLib().isFB && !isLoggedIn().get()) || + (getFlipperLib().isFB && !currentUser().get()) || getRenderHostInstance().GK('flipper_disable_plugin_auto_update') || getRenderHostInstance().serverConfig.env.FLIPPER_NO_PLUGIN_AUTO_UPDATE !== undefined @@ -113,7 +113,7 @@ export async function loadPluginsFromMarketplace( } async function refreshMarketplacePlugins(store: Store): Promise { - if (getFlipperLib().isFB && !isLoggedIn().get()) { + if (getFlipperLib().isFB && !currentUser().get()) { // inside FB we cannot refresh when user is not logged return; } diff --git a/desktop/flipper-ui-core/src/dispatcher/plugins.tsx b/desktop/flipper-ui-core/src/dispatcher/plugins.tsx index ffda1f1c5..49364b099 100644 --- a/desktop/flipper-ui-core/src/dispatcher/plugins.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/plugins.tsx @@ -8,9 +8,14 @@ */ import type {Store} from '../reducers/index'; -import {Logger, MarketplacePluginDetails} from 'flipper-common'; +import { + InstalledPluginDetails, + Logger, + MarketplacePluginDetails, +} from 'flipper-common'; import {PluginDefinition} from '../plugin'; import React from 'react'; +import * as ReactJsxRuntime from 'react/jsx-runtime'; import ReactDOM from 'react-dom'; import ReactDOMClient from 'react-dom/client'; import ReactIs from 'react-is'; @@ -20,7 +25,6 @@ import { addDisabledPlugins, addFailedPlugins, registerLoadedPlugins, - registerBundledPlugins, registerMarketplacePlugins, pluginsInitialized, } from '../reducers/plugins'; @@ -31,6 +35,7 @@ import {_SandyPluginDefinition} from 'flipper-plugin'; import * as Immer from 'immer'; import * as antd from 'antd'; import * as emotion_styled from '@emotion/styled'; +import * as emotion_css from '@emotion/css'; import * as antdesign_icons from '@ant-design/icons'; import isPluginCompatible from '../utils/isPluginCompatible'; import {createSandyPluginWrapper} from '../utils/createSandyPluginWrapper'; @@ -69,7 +74,6 @@ class UIPluginInitializer extends AbstractPluginInitializer { ); } - this.store.dispatch(registerBundledPlugins(this.bundledPlugins)); this.store.dispatch(registerLoadedPlugins(this.loadedPlugins)); this.store.dispatch(addGatekeepedPlugins(this.gatekeepedPlugins)); this.store.dispatch(addDisabledPlugins(this.disabledPlugins)); @@ -83,7 +87,7 @@ class UIPluginInitializer extends AbstractPluginInitializer { } public requirePluginImpl(pluginDetails: ActivatablePluginDetails) { - return requirePluginInternal(this.defaultPluginsIndex, pluginDetails); + return requirePluginInternal(pluginDetails); } protected loadMarketplacePlugins() { @@ -100,17 +104,28 @@ class UIPluginInitializer extends AbstractPluginInitializer { let uiPluginInitializer: UIPluginInitializer; export default async (store: Store, _logger: Logger) => { + let FlipperPlugin = FlipperPluginSDK; + if (getRenderHostInstance().GK('flipper_power_search')) { + FlipperPlugin = { + ...FlipperPlugin, + MasterDetail: FlipperPlugin._MasterDetailWithPowerSearch as any, + DataTable: FlipperPlugin._DataTableWithPowerSearch as any, + }; + } + setGlobalObject({ React, ReactDOM, ReactDOMClient, ReactIs, Flipper: deprecatedExports, - FlipperPlugin: FlipperPluginSDK, + FlipperPlugin, Immer, antd, emotion_styled, + emotion_css, antdesign_icons, + ReactJsxRuntime, }); uiPluginInitializer = new UIPluginInitializer(store); @@ -123,22 +138,26 @@ export const requirePlugin = (pluginDetails: ActivatablePluginDetails) => )(pluginDetails); export const requirePluginInternal = async ( - defaultPluginsIndex: any, pluginDetails: ActivatablePluginDetails, ): Promise => { - let plugin = pluginDetails.isBundled - ? defaultPluginsIndex[pluginDetails.name] - : await getRenderHostInstance().requirePlugin(pluginDetails.entry); - if (!plugin) { + const requiredPlugin = await getRenderHostInstance().requirePlugin( + (pluginDetails as InstalledPluginDetails).entry, + ); + if (!requiredPlugin || !requiredPlugin.plugin) { throw new Error( `Failed to obtain plugin source for: ${pluginDetails.name}`, ); } if (isSandyPlugin(pluginDetails)) { // Sandy plugin - return new _SandyPluginDefinition(pluginDetails, plugin); + return new _SandyPluginDefinition( + pluginDetails, + requiredPlugin.plugin, + requiredPlugin.css, + ); } else { - // classic plugin + // Classic plugin + let plugin = requiredPlugin.plugin; if (plugin.default) { plugin = plugin.default; } diff --git a/desktop/flipper-ui-core/src/dispatcher/pluginsSourceUpdateListener.tsx b/desktop/flipper-ui-core/src/dispatcher/pluginsSourceUpdateListener.tsx new file mode 100644 index 000000000..77c7ddbeb --- /dev/null +++ b/desktop/flipper-ui-core/src/dispatcher/pluginsSourceUpdateListener.tsx @@ -0,0 +1,45 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {InstalledPluginDetails, Logger} from 'flipper-common'; +import {Store} from '../reducers'; +import {loadPlugin} from '../reducers/pluginManager'; +import {sideEffect} from '../utils/sideEffect'; + +export default (store: Store, _logger: Logger) => { + sideEffect( + store, + { + name: 'pluginsSourceUpdateListener', + throttleMs: 10, + fireImmediately: true, + }, + () => undefined, + (_state, _store) => { + window.addEventListener('message', (event) => { + if ( + typeof event.data === 'object' && + event.data.type === 'plugins-source-updated' && + Array.isArray(event.data.data) + ) { + const changedPlugins = event.data.data as InstalledPluginDetails[]; + for (const plugin of changedPlugins) { + store.dispatch( + loadPlugin({ + plugin, + enable: false, + notifyIfFailed: true, + }), + ); + } + } + }); + }, + ); +}; diff --git a/desktop/flipper-ui-core/src/dispatcher/tracking.tsx b/desktop/flipper-ui-core/src/dispatcher/tracking.tsx index 601cd23de..e78f5a34b 100644 --- a/desktop/flipper-ui-core/src/dispatcher/tracking.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/tracking.tsx @@ -71,7 +71,6 @@ export function emitBytesReceived(plugin: string, bytes: number) { bytesReceivedEmitter.emit('bytesReceived', plugin, bytes); } } - export default (store: Store, logger: Logger) => { const renderHost = getRenderHostInstance(); sideEffect( @@ -98,7 +97,7 @@ export default (store: Store, logger: Logger) => { renderHost.serverConfig.environmentInfo.processId === oldExitData.pid; const timeSinceLastStartup = Date.now() - parseInt(oldExitData.lastSeen, 10); - // console.log(isReload ? 'reload' : 'restart', oldExitData); + logger.track('usage', isReload ? 'reload' : 'restart', { ...oldExitData, pid: undefined, @@ -142,12 +141,14 @@ export default (store: Store, logger: Logger) => { ); } - renderHost.onIpcEvent('trackUsage', (...args: any[]) => { + const trackUsage = (...args: any[]) => { let state: State; try { state = store.getState(); } catch (e) { - // if trackUsage is called (indirectly) through a reducer, this will utterly die Flipper. Let's prevent that and log an error instead + // If trackUsage is called (indirectly) through a reducer, + // this will utterly kill Flipper. + // Let's prevent that and log an error instead. console.error( 'trackUsage triggered indirectly as side effect of a reducer', e, @@ -179,11 +180,6 @@ export default (store: Store, logger: Logger) => { Object.entries(state.connections.enabledPlugins).forEach( ([app, plugins]) => { - // TODO: remove "starred-plugns" event in favor of "enabled-plugins" after some transition period - logger.track('usage', 'starred-plugins', { - app, - starredPlugins: plugins, - }); logger.track('usage', 'enabled-plugins', { app, enabledPugins: plugins, @@ -246,7 +242,11 @@ export default (store: Store, logger: Logger) => { largeFrameDrops = 0; logger.track('usage', 'ping', info); - }); + }; + + renderHost.onIpcEvent('trackUsage', trackUsage); + + setInterval(trackUsage, 60 * 1000); }; export function computeUsageSummary( diff --git a/desktop/flipper-ui-core/src/fb-stubs/ErrorReporter.tsx b/desktop/flipper-ui-core/src/fb-stubs/ErrorReporter.tsx index 77fbc283a..299bd99f7 100644 --- a/desktop/flipper-ui-core/src/fb-stubs/ErrorReporter.tsx +++ b/desktop/flipper-ui-core/src/fb-stubs/ErrorReporter.tsx @@ -13,7 +13,7 @@ * so that all logged errors get reported to this class. */ export function cleanStack(_stack: string, _loc?: string) {} -import ScribeLogger from './ScribeLogger'; +import {ScribeLogger} from 'flipper-common'; export default class ErrorReporter { constructor(_scribeLogger: ScribeLogger) {} diff --git a/desktop/flipper-ui-core/src/fb-stubs/checkForUpdate.tsx b/desktop/flipper-ui-core/src/fb-stubs/checkForUpdate.tsx index 9d3381bdc..48f2b7007 100644 --- a/desktop/flipper-ui-core/src/fb-stubs/checkForUpdate.tsx +++ b/desktop/flipper-ui-core/src/fb-stubs/checkForUpdate.tsx @@ -9,8 +9,7 @@ import {VersionCheckResult} from '../chrome/UpdateIndicator'; import {getRenderHostInstance} from 'flipper-frontend-core'; - -const updateServer = 'https://www.facebook.com/fbflipper/public/latest.json'; +import config from './config'; const getPlatformSpecifier = (): string => { switch (getRenderHostInstance().serverConfig.environmentInfo.os.platform) { @@ -52,7 +51,7 @@ const parseResponse = (resp: any): VersionCheckResult => { export async function checkForUpdate( currentVersion: string, ): Promise { - return fetch(`${updateServer}?version=${currentVersion}`).then( + return fetch(`${config.updateServer}?version=${currentVersion}`).then( (res: Response) => { switch (res.status) { case 204: diff --git a/desktop/flipper-ui-core/src/fb-stubs/openSupportRequestForm.tsx b/desktop/flipper-ui-core/src/fb-stubs/openSupportRequestForm.tsx index c999c263a..ce0d54e16 100644 --- a/desktop/flipper-ui-core/src/fb-stubs/openSupportRequestForm.tsx +++ b/desktop/flipper-ui-core/src/fb-stubs/openSupportRequestForm.tsx @@ -12,11 +12,12 @@ import {State} from '../reducers'; export type SupportRequestDetails = { title?: string; whatAlreadyTried?: string; + everythingEverywhereAllAtOnceExportDownloadURL?: string; }; export default function openSupportRequestForm( _state: State, _details?: SupportRequestDetails, -): void { +): Promise { throw new Error('Not implemented!'); } diff --git a/desktop/flipper-ui-core/src/fb-stubs/user.tsx b/desktop/flipper-ui-core/src/fb-stubs/user.tsx index 451175d9a..b7493da9a 100644 --- a/desktop/flipper-ui-core/src/fb-stubs/user.tsx +++ b/desktop/flipper-ui-core/src/fb-stubs/user.tsx @@ -7,13 +7,21 @@ * @format */ -import {GraphFileUpload, User} from 'flipper-common'; +import {GraphFileUpload, GraphResponse, User} from 'flipper-common'; import {Atom, createState} from 'flipper-plugin'; -export async function getUser(): Promise { +export async function fetchUser(): Promise { throw new Error('Feature not implemented'); } +export function getCachedUser(): User | null { + return null; +} + +export function getUser(): Promise { + return fetchUser(); +} + export async function internGraphPOSTAPIRequest( _endpoint: string, _formFields: { @@ -28,6 +36,20 @@ export async function internGraphPOSTAPIRequest( throw new Error('Feature not implemented'); } +export async function internGraphPOSTAPIRequestRaw( + _endpoint: string, + _formFields: { + [key: string]: any; + } = {}, + _fileFields: Record = {}, + _options: { + timeout?: number; + internGraphUrl?: string; + } = {}, +): Promise { + throw new Error('Feature not implemented'); +} + export async function internGraphGETAPIRequest( _endpoint: string, _params: { @@ -41,6 +63,19 @@ export async function internGraphGETAPIRequest( throw new Error('Feature not implemented'); } +export async function internGraphGETAPIRequestRaw( + _endpoint: string, + _params: { + [key: string]: any; + } = {}, + _options: { + timeout?: number; + internGraphUrl?: string; + } = {}, +): Promise { + throw new Error('Feature not implemented'); +} + export async function graphQLQuery(_query: string): Promise { throw new Error('Feature not implemented'); } @@ -92,15 +127,19 @@ export async function getPreferredEditorUriScheme(): Promise { return 'vscode'; } +export async function initialize(): Promise { + return true; +} + export async function appendAccessTokenToUrl(_url: URL): Promise { throw new Error('Implement appendAccessTokenToUrl'); } -const isLoggedInAtom = createState(false); +const currentUserAtom = createState(null); const isConnectedAtom = createState(true); -export function isLoggedIn(): Atom { - return isLoggedInAtom; +export function currentUser(): Atom { + return currentUserAtom; } export function isConnected(): Atom { diff --git a/desktop/flipper-ui-core/src/index.tsx b/desktop/flipper-ui-core/src/index.tsx index 4ebb44613..83369ac66 100644 --- a/desktop/flipper-ui-core/src/index.tsx +++ b/desktop/flipper-ui-core/src/index.tsx @@ -7,9 +7,6 @@ * @format */ -// TODO: should not be exported anymore, but still needed for 'import from 'flipper'' stuff -export * from './deprecated-exports'; - export {RenderHost, getRenderHostInstance} from 'flipper-frontend-core'; export {startFlipperDesktop} from './startFlipperDesktop'; diff --git a/desktop/flipper-ui-core/src/plugin.tsx b/desktop/flipper-ui-core/src/plugin.tsx index c13d00e1a..0611858f7 100644 --- a/desktop/flipper-ui-core/src/plugin.tsx +++ b/desktop/flipper-ui-core/src/plugin.tsx @@ -102,7 +102,7 @@ export abstract class FlipperBasePlugin< Actions extends BaseAction, PersistedState, > extends Component, State> { - abstract ['constructor']: any; + ['constructor']: any; static title: string | null = null; static category: string | null = null; static id: string = ''; @@ -110,7 +110,6 @@ export abstract class FlipperBasePlugin< static version: string = ''; static icon: string | null = null; static gatekeeper: string | null = null; - static isBundled: boolean; static details: ActivatablePluginDetails; static keyboardActions: KeyboardActions | null; static screenshot: string | null; @@ -220,7 +219,6 @@ export class FlipperDevicePlugin< A extends BaseAction, P, > extends FlipperBasePlugin { - ['constructor']: typeof FlipperPlugin; device: BaseDevice; constructor(props: Props

) { @@ -253,10 +251,8 @@ export class FlipperPlugin< A extends BaseAction, P, > extends FlipperBasePlugin { - ['constructor']: typeof FlipperPlugin; constructor(props: Props

) { super(props); - // @ts-ignore constructor should be assigned already const {id} = this.constructor; this.subscriptions = []; const realClient = (this.realClient = props.target as Client); diff --git a/desktop/flipper-ui-core/src/reducers/__tests__/__snapshots__/healthchecks.node.tsx.snap b/desktop/flipper-ui-core/src/reducers/__tests__/__snapshots__/healthchecks.node.tsx.snap index 29510daff..bbbf36b2c 100644 --- a/desktop/flipper-ui-core/src/reducers/__tests__/__snapshots__/healthchecks.node.tsx.snap +++ b/desktop/flipper-ui-core/src/reducers/__tests__/__snapshots__/healthchecks.node.tsx.snap @@ -1,19 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`acknowledgeProblems 1`] = ` -Object { - "acknowledgedProblems": Array [ +{ + "acknowledgedProblems": [ "ios.sdk", "common.openssl", ], - "healthcheckReport": Object { - "categories": Object { - "android": Object { - "checks": Object { - "android.sdk": Object { + "healthcheckReport": { + "categories": { + "android": { + "checks": { + "android.sdk": { "key": "android.sdk", "label": "SDK Installed", - "result": Object { + "result": { "isAcknowledged": true, "status": "SUCCESS", }, @@ -21,17 +21,17 @@ Object { }, "key": "android", "label": "Android", - "result": Object { + "result": { "isAcknowledged": true, "status": "SUCCESS", }, }, - "common": Object { - "checks": Object { - "common.openssl": Object { + "common": { + "checks": { + "common.openssl": { "key": "common.openssl", "label": "OpenSSL Istalled", - "result": Object { + "result": { "isAcknowledged": true, "status": "FAILED", }, @@ -39,17 +39,17 @@ Object { }, "key": "common", "label": "Common", - "result": Object { + "result": { "isAcknowledged": true, "status": "FAILED", }, }, - "ios": Object { - "checks": Object { - "ios.sdk": Object { + "ios": { + "checks": { + "ios.sdk": { "key": "ios.sdk", "label": "SDK Installed", - "result": Object { + "result": { "isAcknowledged": true, "status": "FAILED", }, @@ -57,13 +57,13 @@ Object { }, "key": "ios", "label": "iOS", - "result": Object { + "result": { "isAcknowledged": true, "status": "FAILED", }, }, }, - "result": Object { + "result": { "isAcknowledged": true, "status": "FAILED", }, @@ -72,16 +72,16 @@ Object { `; exports[`finish 1`] = ` -Object { - "acknowledgedProblems": Array [], - "healthcheckReport": Object { - "categories": Object { - "android": Object { - "checks": Object { - "android.sdk": Object { +{ + "acknowledgedProblems": [], + "healthcheckReport": { + "categories": { + "android": { + "checks": { + "android.sdk": { "key": "android.sdk", "label": "SDK Installed", - "result": Object { + "result": { "isAcknowledged": false, "message": "Updated Test Message", "status": "SUCCESS", @@ -90,17 +90,17 @@ Object { }, "key": "android", "label": "Android", - "result": Object { + "result": { "isAcknowledged": false, "status": "SUCCESS", }, }, - "common": Object { - "checks": Object { - "common.openssl": Object { + "common": { + "checks": { + "common.openssl": { "key": "common.openssl", "label": "OpenSSL Istalled", - "result": Object { + "result": { "isAcknowledged": false, "message": "Updated Test Message", "status": "SUCCESS", @@ -109,17 +109,17 @@ Object { }, "key": "common", "label": "Common", - "result": Object { + "result": { "isAcknowledged": false, "status": "SUCCESS", }, }, - "ios": Object { - "checks": Object { - "ios.sdk": Object { + "ios": { + "checks": { + "ios.sdk": { "key": "ios.sdk", "label": "SDK Installed", - "result": Object { + "result": { "isAcknowledged": false, "message": "Updated Test Message", "status": "SUCCESS", @@ -128,13 +128,13 @@ Object { }, "key": "ios", "label": "iOS", - "result": Object { + "result": { "isAcknowledged": false, "status": "SUCCESS", }, }, }, - "result": Object { + "result": { "status": "SUCCESS", }, }, @@ -142,60 +142,60 @@ Object { `; exports[`startHealthCheck 1`] = ` -Object { - "acknowledgedProblems": Array [], - "healthcheckReport": Object { - "categories": Object { - "android": Object { - "checks": Object { - "android.sdk": Object { +{ + "acknowledgedProblems": [], + "healthcheckReport": { + "categories": { + "android": { + "checks": { + "android.sdk": { "key": "android.sdk", "label": "SDK Installed", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, }, "key": "android", "label": "Android", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, - "common": Object { - "checks": Object { - "common.openssl": Object { + "common": { + "checks": { + "common.openssl": { "key": "common.openssl", "label": "OpenSSL Istalled", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, }, "key": "common", "label": "Common", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, - "ios": Object { - "checks": Object { - "ios.sdk": Object { + "ios": { + "checks": { + "ios.sdk": { "key": "ios.sdk", "label": "SDK Installed", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, }, "key": "ios", "label": "iOS", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, }, - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, @@ -203,16 +203,16 @@ Object { `; exports[`statuses updated after healthchecks finished 1`] = ` -Object { - "acknowledgedProblems": Array [], - "healthcheckReport": Object { - "categories": Object { - "android": Object { - "checks": Object { - "android.sdk": Object { +{ + "acknowledgedProblems": [], + "healthcheckReport": { + "categories": { + "android": { + "checks": { + "android.sdk": { "key": "android.sdk", "label": "SDK Installed", - "result": Object { + "result": { "isAcknowledged": false, "message": "Updated Test Message", "status": "FAILED", @@ -221,17 +221,17 @@ Object { }, "key": "android", "label": "Android", - "result": Object { + "result": { "isAcknowledged": false, "status": "FAILED", }, }, - "common": Object { - "checks": Object { - "common.openssl": Object { + "common": { + "checks": { + "common.openssl": { "key": "common.openssl", "label": "OpenSSL Istalled", - "result": Object { + "result": { "isAcknowledged": false, "message": "Updated Test Message", "status": "SUCCESS", @@ -240,16 +240,16 @@ Object { }, "key": "common", "label": "Common", - "result": Object { + "result": { "status": "SUCCESS", }, }, - "ios": Object { - "checks": Object { - "ios.sdk": Object { + "ios": { + "checks": { + "ios.sdk": { "key": "ios.sdk", "label": "SDK Installed", - "result": Object { + "result": { "isAcknowledged": false, "message": "Updated Test Message", "status": "SUCCESS", @@ -258,12 +258,12 @@ Object { }, "key": "ios", "label": "iOS", - "result": Object { + "result": { "status": "SUCCESS", }, }, }, - "result": Object { + "result": { "isAcknowledged": false, "status": "FAILED", }, @@ -272,16 +272,16 @@ Object { `; exports[`updateHealthcheckResult 1`] = ` -Object { - "acknowledgedProblems": Array [], - "healthcheckReport": Object { - "categories": Object { - "android": Object { - "checks": Object { - "android.sdk": Object { +{ + "acknowledgedProblems": [], + "healthcheckReport": { + "categories": { + "android": { + "checks": { + "android.sdk": { "key": "android.sdk", "label": "SDK Installed", - "result": Object { + "result": { "isAcknowledged": false, "message": "Updated Test Message", "status": "SUCCESS", @@ -290,44 +290,44 @@ Object { }, "key": "android", "label": "Android", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, - "common": Object { - "checks": Object { - "common.openssl": Object { + "common": { + "checks": { + "common.openssl": { "key": "common.openssl", "label": "OpenSSL Istalled", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, }, "key": "common", "label": "Common", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, - "ios": Object { - "checks": Object { - "ios.sdk": Object { + "ios": { + "checks": { + "ios.sdk": { "key": "ios.sdk", "label": "SDK Installed", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, }, "key": "ios", "label": "iOS", - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, }, - "result": Object { + "result": { "status": "IN_PROGRESS", }, }, diff --git a/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx b/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx index c8b14aff9..02ff3095a 100644 --- a/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx +++ b/desktop/flipper-ui-core/src/reducers/__tests__/connections.node.tsx @@ -134,7 +134,7 @@ test('can handle plugins that throw at start', async () => { expect(client.connected.get()).toBe(true); expect((console.error as any).mock.calls[0]).toMatchInlineSnapshot(` - Array [ + [ "Failed to start plugin 'TestPlugin': ", [Error: Broken plugin], ] @@ -144,7 +144,7 @@ test('can handle plugins that throw at start', async () => { const client2 = await createClient(device2, client.query.app); expect((console.error as any).mock.calls[1]).toMatchInlineSnapshot(` - Array [ + [ "Failed to start plugin 'TestPlugin': ", [Error: Broken plugin], ] @@ -172,7 +172,7 @@ test('can handle device plugins that throw at start', async () => { ); expect(mockedConsole.errorCalls[0]).toMatchInlineSnapshot(` - Array [ + [ "Failed to start device plugin 'TestPlugin': ", [Error: Broken device plugin], ] @@ -188,7 +188,7 @@ test('can handle device plugins that throw at start', async () => { expect(store.getState().connections.devices.length).toBe(2); expect(device2.connected.get()).toBe(true); expect(mockedConsole.errorCalls[1]).toMatchInlineSnapshot(` - Array [ + [ "Failed to start device plugin 'TestPlugin': ", [Error: Broken device plugin], ] diff --git a/desktop/flipper-ui-core/src/reducers/__tests__/notifications.node.tsx b/desktop/flipper-ui-core/src/reducers/__tests__/notifications.node.tsx index 7ee6f8c8d..1ce592252 100644 --- a/desktop/flipper-ui-core/src/reducers/__tests__/notifications.node.tsx +++ b/desktop/flipper-ui-core/src/reducers/__tests__/notifications.node.tsx @@ -142,11 +142,11 @@ test('addNotification removes duplicates', () => { }), ); expect(res).toMatchInlineSnapshot(` - Object { - "activeNotifications": Array [ - Object { + { + "activeNotifications": [ + { "client": null, - "notification": Object { + "notification": { "id": "otherId", "message": "message", "severity": "warning", @@ -155,10 +155,10 @@ test('addNotification removes duplicates', () => { "pluginId": "test", }, ], - "blocklistedCategories": Array [], - "blocklistedPlugins": Array [], + "blocklistedCategories": [], + "blocklistedPlugins": [], "clearedNotifications": Set {}, - "invalidatedNotifications": Array [], + "invalidatedNotifications": [], } `); }); @@ -195,11 +195,11 @@ test('reduce removeNotification', () => { }), ); expect(res).toMatchInlineSnapshot(` - Object { - "activeNotifications": Array [ - Object { + { + "activeNotifications": [ + { "client": null, - "notification": Object { + "notification": { "id": "otherId", "message": "message", "severity": "warning", @@ -207,9 +207,9 @@ test('reduce removeNotification', () => { }, "pluginId": "test", }, - Object { + { "client": null, - "notification": Object { + "notification": { "id": "id", "message": "slightly different message", "severity": "warning", @@ -218,10 +218,10 @@ test('reduce removeNotification', () => { "pluginId": "test", }, ], - "blocklistedCategories": Array [], - "blocklistedPlugins": Array [], + "blocklistedCategories": [], + "blocklistedPlugins": [], "clearedNotifications": Set {}, - "invalidatedNotifications": Array [], + "invalidatedNotifications": [], } `); }); @@ -248,11 +248,11 @@ test('notifications from plugins arrive in the notifications reducer', async () sendMessage('testMessage', {}); client.flushMessageBuffer(); expect(store.getState().notifications).toMatchInlineSnapshot(` - Object { - "activeNotifications": Array [ - Object { + { + "activeNotifications": [ + { "client": "TestApp#Android#MockAndroidDevice#serial", - "notification": Object { + "notification": { "action": "dosomething", "id": "test", "message": "test message", @@ -262,10 +262,10 @@ test('notifications from plugins arrive in the notifications reducer', async () "pluginId": "TestPlugin", }, ], - "blocklistedCategories": Array [], - "blocklistedPlugins": Array [], + "blocklistedCategories": [], + "blocklistedPlugins": [], "clearedNotifications": Set {}, - "invalidatedNotifications": Array [], + "invalidatedNotifications": [], } `); }); @@ -290,11 +290,11 @@ test('notifications from a device plugin arrive in the notifications reducer', a const {store} = await createMockFlipperWithPlugin(TestPlugin); trigger(); expect(store.getState().notifications).toMatchInlineSnapshot(` - Object { - "activeNotifications": Array [ - Object { + { + "activeNotifications": [ + { "client": "serial", - "notification": Object { + "notification": { "action": "dosomething", "id": "test", "message": "test message", @@ -304,10 +304,10 @@ test('notifications from a device plugin arrive in the notifications reducer', a "pluginId": "TestPlugin", }, ], - "blocklistedCategories": Array [], - "blocklistedPlugins": Array [], + "blocklistedCategories": [], + "blocklistedPlugins": [], "clearedNotifications": Set {}, - "invalidatedNotifications": Array [], + "invalidatedNotifications": [], } `); }); @@ -334,25 +334,25 @@ test('errors end up as notifications if crash reporter is active', async () => { sendError('gone wrong'); client.flushMessageBuffer(); expect(store.getState().notifications).toMatchInlineSnapshot(` - Object { - "activeNotifications": Array [ - Object { + { + "activeNotifications": [ + { "client": "serial", - "notification": Object { + "notification": { "action": "0", - "category": "\\"gone wrong\\"", + "category": ""gone wrong"", "id": "0", "message": "Callstack: No callstack available", "severity": "error", - "title": "CRASH: Plugin ErrorReason: \\"gone wrong\\"", + "title": "CRASH: Plugin ErrorReason: "gone wrong"", }, "pluginId": "CrashReporter", }, ], - "blocklistedCategories": Array [], - "blocklistedPlugins": Array [], + "blocklistedCategories": [], + "blocklistedPlugins": [], "clearedNotifications": Set {}, - "invalidatedNotifications": Array [], + "invalidatedNotifications": [], } `); }); @@ -386,12 +386,12 @@ test('errors end NOT up as notifications if crash reporter is active but suppres sendError('gone wrong'); client.flushMessageBuffer(); expect(store.getState().notifications).toMatchInlineSnapshot(` - Object { - "activeNotifications": Array [], - "blocklistedCategories": Array [], - "blocklistedPlugins": Array [], + { + "activeNotifications": [], + "blocklistedCategories": [], + "blocklistedPlugins": [], "clearedNotifications": Set {}, - "invalidatedNotifications": Array [], + "invalidatedNotifications": [], } `); }); diff --git a/desktop/flipper-ui-core/src/reducers/__tests__/plugins.node.tsx b/desktop/flipper-ui-core/src/reducers/__tests__/plugins.node.tsx index 96e65542c..24facd4a0 100644 --- a/desktop/flipper-ui-core/src/reducers/__tests__/plugins.node.tsx +++ b/desktop/flipper-ui-core/src/reducers/__tests__/plugins.node.tsx @@ -37,7 +37,6 @@ test('add clientPlugin', () => { devicePlugins: new Map(), clientPlugins: new Map(), loadedPlugins: new Map(), - bundledPlugins: new Map(), gatekeepedPlugins: [], failedPlugins: [], disabledPlugins: [], @@ -58,7 +57,6 @@ test('add devicePlugin', () => { devicePlugins: new Map(), clientPlugins: new Map(), loadedPlugins: new Map(), - bundledPlugins: new Map(), gatekeepedPlugins: [], failedPlugins: [], disabledPlugins: [], @@ -79,7 +77,6 @@ test('do not add plugin twice', () => { devicePlugins: new Map(), clientPlugins: new Map(), loadedPlugins: new Map(), - bundledPlugins: new Map(), gatekeepedPlugins: [], failedPlugins: [], disabledPlugins: [], @@ -103,7 +100,6 @@ test('add gatekeeped plugin', () => { specVersion: 2, pluginType: 'client', source: 'src/index.ts', - isBundled: false, isActivatable: true, main: 'lib/index.js', title: 'test', @@ -116,7 +112,6 @@ test('add gatekeeped plugin', () => { devicePlugins: new Map(), clientPlugins: new Map(), loadedPlugins: new Map(), - bundledPlugins: new Map(), gatekeepedPlugins: [], failedPlugins: [], disabledPlugins: [], @@ -143,7 +138,6 @@ const EXAMPLE_PLUGIN = { dir: '/plugins/test', specVersion: 2, source: 'src/index.ts', - isBundled: false, isActivatable: true, main: 'lib/index.js', title: 'test', diff --git a/desktop/flipper-ui-core/src/reducers/__tests__/sandyplugins.node.tsx b/desktop/flipper-ui-core/src/reducers/__tests__/sandyplugins.node.tsx index c70032757..3258d2abc 100644 --- a/desktop/flipper-ui-core/src/reducers/__tests__/sandyplugins.node.tsx +++ b/desktop/flipper-ui-core/src/reducers/__tests__/sandyplugins.node.tsx @@ -230,10 +230,10 @@ test('it can send messages from sandy clients', async () => { client.initPlugin(TestPlugin.id); await pluginInstance.send('test', {test: 3}); expect(testMethodCalledWith).toMatchInlineSnapshot(` - Object { + { "api": "TestPlugin", "method": "test", - "params": Object { + "params": { "test": 3, }, } diff --git a/desktop/flipper-ui-core/src/reducers/__tests__/user.node.tsx b/desktop/flipper-ui-core/src/reducers/__tests__/user.node.tsx index 3a43d6b91..51eb9de65 100644 --- a/desktop/flipper-ui-core/src/reducers/__tests__/user.node.tsx +++ b/desktop/flipper-ui-core/src/reducers/__tests__/user.node.tsx @@ -7,11 +7,11 @@ * @format */ -import {default as reducer, login, logout} from '../user'; +import {default as reducer, setUserProfile} from '../user'; test('login', () => { const userData = {name: 'Jane Doe'}; - const res = reducer({}, login(userData)); + const res = reducer({}, setUserProfile(userData)); expect(res).toEqual(userData); }); @@ -20,7 +20,7 @@ test('logout', () => { { name: 'Jane Doe', }, - logout(), + setUserProfile(undefined), ); expect(res).toEqual({}); }); diff --git a/desktop/flipper-ui-core/src/reducers/application.tsx b/desktop/flipper-ui-core/src/reducers/application.tsx index 762653f3a..8a5957e36 100644 --- a/desktop/flipper-ui-core/src/reducers/application.tsx +++ b/desktop/flipper-ui-core/src/reducers/application.tsx @@ -7,7 +7,6 @@ * @format */ -import {v1 as uuidv1} from 'uuid'; import {getRenderHostInstance} from 'flipper-frontend-core'; import {Actions} from './'; @@ -37,18 +36,21 @@ export type ShareType = { } & SubShareType; export type State = { + isTroubleshootingModalOpen: boolean; + isNotificationModalOpen: boolean; leftSidebarVisible: boolean; rightSidebarVisible: boolean; rightSidebarAvailable: boolean; windowIsFocused: boolean; share: ShareType | null; - sessionId: string | null; launcherMsg: LauncherMsg; statusMessages: Array; }; type BooleanActionType = + | 'hasLeftSidebar' | 'leftSidebarVisible' + | 'isNotificationModalOpen' | 'rightSidebarVisible' | 'rightSidebarAvailable'; @@ -75,16 +77,22 @@ export type Action = | { type: 'REMOVE_STATUS_MSG'; payload: {msg: string; sender: string}; + } + | { + type: 'TOGGLE_CONNECTIVITY_MODAL'; }; export const initialState: () => State = () => ({ + topLevelSelection: 'appinspect', + hasLeftSidebar: true, + isTroubleshootingModalOpen: false, + isNotificationModalOpen: false, leftSidebarVisible: true, rightSidebarVisible: true, rightSidebarAvailable: false, windowIsFocused: getRenderHostInstance().hasFocus(), activeSheet: null, share: null, - sessionId: uuidv1(), launcherMsg: { severity: 'warning', message: '', @@ -112,6 +120,7 @@ export default function reducer( if ( action.type === 'leftSidebarVisible' || action.type === 'rightSidebarVisible' || + action.type === 'isNotificationModalOpen' || action.type === 'rightSidebarAvailable' ) { const newValue = @@ -128,6 +137,11 @@ export default function reducer( [action.type]: newValue, }; } + } else if (action.type === 'TOGGLE_CONNECTIVITY_MODAL') { + return { + ...state, + isTroubleshootingModalOpen: !state.isTroubleshootingModalOpen, + }; } else if (action.type === 'windowIsFocused') { return { ...state, @@ -170,11 +184,20 @@ export const toggleAction = ( payload, }); +export const toggleConnectivityModal = (): Action => ({ + type: 'TOGGLE_CONNECTIVITY_MODAL', +}); + export const toggleLeftSidebarVisible = (payload?: boolean): Action => ({ type: 'leftSidebarVisible', payload, }); +export const toggleHasLeftSidebar = (payload?: boolean): Action => ({ + type: 'hasLeftSidebar', + payload, +}); + export const toggleRightSidebarVisible = (payload?: boolean): Action => ({ type: 'rightSidebarVisible', payload, diff --git a/desktop/flipper-ui-core/src/reducers/connections.tsx b/desktop/flipper-ui-core/src/reducers/connections.tsx index 1b608c945..872187243 100644 --- a/desktop/flipper-ui-core/src/reducers/connections.tsx +++ b/desktop/flipper-ui-core/src/reducers/connections.tsx @@ -161,13 +161,12 @@ export type Action = } | RegisterPluginAction; -const DEFAULT_PLUGIN = 'DeviceLogs'; const DEFAULT_DEVICE_BLACKLIST: DeviceOS[] = ['MacOS', 'Metro', 'Windows']; const INITAL_STATE: State = { devices: [], selectedDevice: null, selectedAppId: null, - selectedPlugin: DEFAULT_PLUGIN, + selectedPlugin: null, pluginMenuEntries: [], userPreferredDevice: null, userPreferredPlugin: null, diff --git a/desktop/flipper-ui-core/src/reducers/pluginManager.tsx b/desktop/flipper-ui-core/src/reducers/pluginManager.tsx index 1c98e55a4..f4c66a723 100644 --- a/desktop/flipper-ui-core/src/reducers/pluginManager.tsx +++ b/desktop/flipper-ui-core/src/reducers/pluginManager.tsx @@ -19,7 +19,6 @@ export type State = { export type PluginCommand = | LoadPluginAction | UninstallPluginAction - | UpdatePluginAction | SwitchPluginAction; export type LoadPluginActionPayload = { @@ -47,11 +46,6 @@ export type UpdatePluginActionPayload = { enablePlugin: boolean; }; -export type UpdatePluginAction = { - type: 'UPDATE_PLUGIN'; - payload: UpdatePluginActionPayload; -}; - export type SwitchPluginActionPayload = { plugin: PluginDefinition; selectedApp?: string; @@ -80,7 +74,6 @@ export default function reducer( switch (action.type) { case 'LOAD_PLUGIN': case 'UNINSTALL_PLUGIN': - case 'UPDATE_PLUGIN': case 'SWITCH_PLUGIN': return produce(state, (draft) => { draft.pluginCommandsQueue.push(action); @@ -111,13 +104,6 @@ export const pluginCommandsProcessed = (payload: number): Action => ({ payload, }); -export const registerPluginUpdate = ( - payload: UpdatePluginActionPayload, -): Action => ({ - type: 'UPDATE_PLUGIN', - payload, -}); - export const switchPlugin = (payload: SwitchPluginActionPayload): Action => ({ type: 'SWITCH_PLUGIN', payload, diff --git a/desktop/flipper-ui-core/src/reducers/pluginMessageQueue.tsx b/desktop/flipper-ui-core/src/reducers/pluginMessageQueue.tsx index 6ff32c428..f41ca77da 100644 --- a/desktop/flipper-ui-core/src/reducers/pluginMessageQueue.tsx +++ b/desktop/flipper-ui-core/src/reducers/pluginMessageQueue.tsx @@ -8,7 +8,7 @@ */ import produce from 'immer'; -import {deconstructPluginKey} from 'flipper-common'; +import {deconstructPluginKey, getLogger} from 'flipper-common'; export const DEFAULT_MAX_QUEUE_SIZE = 10000; @@ -63,6 +63,8 @@ export default function reducer( newMessages = newMessages.slice( newMessages.length - 1 - Math.ceil(maxQueueSize * 0.9), ); + console.debug(`queue overflow for plugin ${pluginKey}`); + getLogger().track('usage', 'queue overflow', undefined, pluginKey); } return { ...state, diff --git a/desktop/flipper-ui-core/src/reducers/plugins.tsx b/desktop/flipper-ui-core/src/reducers/plugins.tsx index a2939688e..81e043af0 100644 --- a/desktop/flipper-ui-core/src/reducers/plugins.tsx +++ b/desktop/flipper-ui-core/src/reducers/plugins.tsx @@ -15,7 +15,6 @@ import type { import type { DownloadablePluginDetails, ActivatablePluginDetails, - BundledPluginDetails, InstalledPluginDetails, MarketplacePluginDetails, } from 'flipper-common'; @@ -30,7 +29,6 @@ type StateV1 = { devicePlugins: DevicePluginMap; clientPlugins: ClientPluginMap; loadedPlugins: Map; - bundledPlugins: Map; gatekeepedPlugins: Array; disabledPlugins: Array; failedPlugins: Array<[ActivatablePluginDetails, string]>; @@ -88,10 +86,6 @@ export type Action = type: 'REGISTER_LOADED_PLUGINS'; payload: Array; } - | { - type: 'REGISTER_BUNDLED_PLUGINS'; - payload: Array; - } | { type: 'REGISTER_INSTALLED_PLUGINS'; payload: InstalledPluginDetails[]; @@ -116,7 +110,6 @@ const INITIAL_STATE: State = { devicePlugins: new Map(), clientPlugins: new Map(), loadedPlugins: new Map(), - bundledPlugins: new Map(), gatekeepedPlugins: [], disabledPlugins: [], failedPlugins: [], @@ -175,11 +168,6 @@ export default function reducer( ...state, loadedPlugins: new Map(action.payload.map((p) => [p.id, p])), }; - } else if (action.type === 'REGISTER_BUNDLED_PLUGINS') { - return { - ...state, - bundledPlugins: new Map(action.payload.map((p) => [p.id, p])), - }; } else if (action.type === 'REGISTER_INSTALLED_PLUGINS') { return produce(state, (draft) => { draft.installedPlugins.clear(); @@ -270,13 +258,6 @@ export const registerLoadedPlugins = ( payload, }); -export const registerBundledPlugins = ( - payload: Array, -): Action => ({ - type: 'REGISTER_BUNDLED_PLUGINS', - payload, -}); - export const registerInstalledPlugins = ( payload: InstalledPluginDetails[], ): Action => ({ diff --git a/desktop/flipper-ui-core/src/reducers/user.tsx b/desktop/flipper-ui-core/src/reducers/user.tsx index 270db7217..4bf8a9338 100644 --- a/desktop/flipper-ui-core/src/reducers/user.tsx +++ b/desktop/flipper-ui-core/src/reducers/user.tsx @@ -12,14 +12,10 @@ import {User} from 'flipper-common'; export type State = User; -export type Action = - | { - type: 'LOGIN'; - payload: User; - } - | { - type: 'LOGOUT'; - }; +export type Action = { + type: 'SET_USER_PROFILE'; + payload?: User; +}; const INITIAL_STATE: State = {}; @@ -27,23 +23,14 @@ export default function reducer( state: State = INITIAL_STATE, action: Actions, ): State { - if (action.type === 'LOGOUT') { - return {}; - } else if (action.type === 'LOGIN') { - return { - ...state, - ...action.payload, - }; + if (action.type === 'SET_USER_PROFILE') { + return action.payload ?? {}; } else { return state; } } -export const login = (payload: User): Action => ({ - type: 'LOGIN', +export const setUserProfile = (payload: User | undefined): Action => ({ + type: 'SET_USER_PROFILE', payload, }); - -export const logout = (): Action => ({ - type: 'LOGOUT', -}); diff --git a/desktop/flipper-ui-core/src/sandy-chrome/LeftRail.tsx b/desktop/flipper-ui-core/src/sandy-chrome/LeftRail.tsx deleted file mode 100644 index b2227209c..000000000 --- a/desktop/flipper-ui-core/src/sandy-chrome/LeftRail.tsx +++ /dev/null @@ -1,512 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import React, {cloneElement, useState, useCallback, useMemo} from 'react'; -import {Button, Divider, Badge, Tooltip, Avatar, Popover, Menu} from 'antd'; -import { - MobileFilled, - AppstoreOutlined, - BellOutlined, - FileExclamationOutlined, - LoginOutlined, - SettingOutlined, - MedicineBoxOutlined, - RocketOutlined, -} from '@ant-design/icons'; -import {SidebarLeft, SidebarRight} from './SandyIcons'; -import {useDispatch, useStore} from '../utils/useStore'; -import { - toggleLeftSidebarVisible, - toggleRightSidebarVisible, -} from '../reducers/application'; -import { - theme, - Layout, - withTrackingScope, - Dialog, - useTrackedCallback, - NUX, -} from 'flipper-plugin'; -import SetupDoctorScreen, {checkHasNewProblem} from './SetupDoctorScreen'; -import SettingsSheet from '../chrome/SettingsSheet'; -import WelcomeScreen from './WelcomeScreen'; -import {errorCounterAtom} from '../chrome/ConsoleLogs'; -import {ToplevelProps} from './SandyApp'; -import {useValue} from 'flipper-plugin'; -import {logout} from '../reducers/user'; -import config from '../fb-stubs/config'; -import styled from '@emotion/styled'; -import {showEmulatorLauncher} from './appinspect/LaunchEmulator'; -import {setStaticView} from '../reducers/connections'; -import {getLogger} from 'flipper-common'; -import {SandyRatingButton} from '../chrome/RatingButton'; -import {filterNotifications} from './notification/notificationUtils'; -import {useMemoize} from 'flipper-plugin'; -import isProduction from '../utils/isProduction'; -import NetworkGraph from '../chrome/NetworkGraph'; -import FpsGraph from '../chrome/FpsGraph'; -import UpdateIndicator from '../chrome/UpdateIndicator'; -import PluginManager from '../chrome/plugin-manager/PluginManager'; -import {showLoginDialog} from '../chrome/fb-stubs/SignInSheet'; -import SubMenu from 'antd/lib/menu/SubMenu'; -import constants from '../fb-stubs/constants'; -import { - canFileExport, - canOpenDialog, - showOpenDialog, - startFileExport, - startLinkExport, - startLogsExport, -} from '../utils/exportData'; -import {openDeeplinkDialog} from '../deeplink'; -import {css} from '@emotion/css'; -import {getRenderHostInstance} from 'flipper-frontend-core'; -import openSupportRequestForm from '../fb-stubs/openSupportRequestForm'; -import {StyleGuide} from './StyleGuide'; - -const LeftRailButtonElem = styled(Button)<{kind?: 'small'}>(({kind}) => ({ - width: kind === 'small' ? 32 : 36, - height: kind === 'small' ? 32 : 36, - padding: '5px 0', - border: 'none', - boxShadow: 'none', -})); -LeftRailButtonElem.displayName = 'LeftRailButtonElem'; - -export function LeftRailButton({ - icon, - small, - selected, - toggled, - count, - title, - onClick, - disabled, -}: { - icon?: React.ReactElement; - small?: boolean; - toggled?: boolean; - selected?: boolean; - disabled?: boolean; - count?: number | true; - title?: string; - onClick?: React.MouseEventHandler; -}) { - const iconElement = - icon && cloneElement(icon, {style: {fontSize: small ? 16 : 24}}); - - let res = ( - - ); - - if (count !== undefined) { - res = - count === true ? ( - - {res} - - ) : ( - - {res} - - ); - } - - if (title) { - res = ( - - {res} - - ); - } - - return res; -} - -const LeftRailDivider = styled(Divider)({ - margin: `10px 0`, - width: 32, - minWidth: 32, -}); -LeftRailDivider.displayName = 'LeftRailDividier'; - -export const LeftRail = withTrackingScope(function LeftRail({ - toplevelSelection, - setToplevelSelection, -}: ToplevelProps) { - return ( - - - - } - title="App Inspect" - selected={toplevelSelection === 'appinspect'} - onClick={() => { - setToplevelSelection('appinspect'); - }} - /> - } - title="Plugin Manager" - onClick={() => { - Dialog.showModal((onHide) => ); - }} - /> - - - - - - {!isProduction() && ( -

- - -
- )} - - - - - - - - {config.showLogin && } - - - - ); -}); - -const menu = css` - border: none; -`; -const submenu = css` - .ant-menu-submenu-title { - width: 32px; - height: 32px !important; - line-height: 32px !important; - padding: 0; - margin: 0; - } - .ant-menu-submenu-arrow { - display: none; - } -`; - -function ExtrasMenu() { - const store = useStore(); - - const startFileExportTracked = useTrackedCallback( - 'File export', - () => startFileExport(store.dispatch), - [store.dispatch], - ); - const startLogsExportTracked = useTrackedCallback( - 'Logs export', - startLogsExport, - [], - ); - const startLinkExportTracked = useTrackedCallback( - 'Link export', - () => startLinkExport(store.dispatch), - [store.dispatch], - ); - const startImportTracked = useTrackedCallback( - 'File import', - () => showOpenDialog(store), - [store], - ); - - const [showSettings, setShowSettings] = useState(false); - const onSettingsClose = useCallback(() => setShowSettings(false), []); - - const settings = useStore((state) => state.settingsState); - const {showWelcomeAtStartup} = settings; - const [welcomeVisible, setWelcomeVisible] = useState(showWelcomeAtStartup); - - const fullState = useStore((state) => state); - - return ( - <> - - - } small />} - className={submenu}> - {canFileExport() ? ( - - Export Flipper logs - - ) : null} - {canOpenDialog() ? ( - - Import Flipper file - - ) : null} - {canFileExport() ? ( - - Export Flipper file - - ) : null} - {constants.ENABLE_SHAREABLE_LINK ? ( - - Export shareable link - - ) : null} - - - { - store.dispatch(setStaticView(StyleGuide)); - }}> - Flipper Style Guide - - openDeeplinkDialog(store)}> - Trigger deeplink - - - - {config.isFBBuild ? ( - <> - { - getLogger().track('usage', 'support-form-source', { - source: 'sidebar', - group: undefined, - }); - openSupportRequestForm(fullState); - }}> - Feedback - - - ) : null} - setShowSettings(true)}> - Settings - - setWelcomeVisible(true)}> - Help - - - - - {showSettings && ( - - )} - setWelcomeVisible(false)} - showAtStartup={showWelcomeAtStartup} - onCheck={(value) => - store.dispatch({ - type: 'UPDATE_SETTINGS', - payload: {...settings, showWelcomeAtStartup: value}, - }) - } - /> - - ); -} - -function LeftSidebarToggleButton() { - const dispatch = useDispatch(); - const mainMenuVisible = useStore( - (state) => state.application.leftSidebarVisible, - ); - - return ( - } - small - title="Left Sidebar Toggle" - toggled={mainMenuVisible} - onClick={() => { - dispatch(toggleLeftSidebarVisible()); - }} - /> - ); -} - -function RightSidebarToggleButton() { - const dispatch = useDispatch(); - const rightSidebarAvailable = useStore( - (state) => state.application.rightSidebarAvailable, - ); - const rightSidebarVisible = useStore( - (state) => state.application.rightSidebarVisible, - ); - - return ( - } - small - title="Right Sidebar Toggle" - toggled={rightSidebarVisible} - disabled={!rightSidebarAvailable} - onClick={() => { - dispatch(toggleRightSidebarVisible()); - }} - /> - ); -} - -function NotificationButton({ - toplevelSelection, - setToplevelSelection, -}: ToplevelProps) { - const notifications = useStore((state) => state.notifications); - const activeNotifications = useMemoize(filterNotifications, [ - notifications.activeNotifications, - notifications.blocklistedPlugins, - notifications.blocklistedCategories, - ]); - return ( - } - title="Notifications" - selected={toplevelSelection === 'notification'} - count={activeNotifications.length} - onClick={() => setToplevelSelection('notification')} - /> - ); -} - -function DebugLogsButton({ - toplevelSelection, - setToplevelSelection, -}: ToplevelProps) { - const errorCount = useValue(errorCounterAtom); - return ( - } - title="Flipper Logs" - selected={toplevelSelection === 'flipperlogs'} - count={errorCount} - onClick={() => { - setToplevelSelection('flipperlogs'); - }} - /> - ); -} - -function LaunchEmulatorButton() { - const store = useStore(); - - return ( - } - title="Start Emulator / Simulator" - onClick={() => { - showEmulatorLauncher(store); - }} - small - /> - ); -} - -function SetupDoctorButton() { - const [visible, setVisible] = useState(false); - const result = useStore( - (state) => state.healthchecks.healthcheckReport.result, - ); - const hasNewProblem = useMemo(() => checkHasNewProblem(result), [result]); - const onClose = useCallback(() => setVisible(false), []); - return ( - <> - } - small - title="Setup Doctor" - count={hasNewProblem ? true : undefined} - onClick={() => setVisible(true)} - /> - - - ); -} - -function LoginButton() { - const dispatch = useDispatch(); - const user = useStore((state) => state.user); - const login = (user?.id ?? null) !== null; - const profileUrl = user?.profile_picture?.uri; - const [showLogout, setShowLogout] = useState(false); - const onHandleVisibleChange = useCallback( - (visible) => setShowLogout(visible), - [], - ); - - return login ? ( - { - onHandleVisibleChange(false); - dispatch(logout()); - }}> - Log Out - - } - trigger="click" - placement="right" - visible={showLogout} - overlayStyle={{padding: 0}} - onVisibleChange={onHandleVisibleChange}> - - - - - ) : ( - <> - } - title="Log In" - onClick={() => showLoginDialog()} - /> - - ); -} diff --git a/desktop/flipper-ui-core/src/sandy-chrome/LeftSidebar.tsx b/desktop/flipper-ui-core/src/sandy-chrome/LeftSidebar.tsx index 48d81eefe..6afbd57a7 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/LeftSidebar.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/LeftSidebar.tsx @@ -15,11 +15,7 @@ import {Button, Tooltip, Typography} from 'antd'; import {InfoCircleOutlined} from '@ant-design/icons'; export const LeftSidebar: React.FC = ({children}) => ( - + {children} ); diff --git a/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx b/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx new file mode 100644 index 000000000..4327b7bf9 --- /dev/null +++ b/desktop/flipper-ui-core/src/sandy-chrome/Navbar.tsx @@ -0,0 +1,691 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import { + Dialog, + Layout, + NUX, + theme, + useMemoize, + useTrackedCallback, + useValue, + withTrackingScope, +} from 'flipper-plugin'; +import {getRenderHostInstance} from 'flipper-frontend-core'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import {useDispatch, useStore} from '../utils/useStore'; +import config from '../fb-stubs/config'; +import {currentUser, isConnected, logoutUser} from '../fb-stubs/user'; +import {Badge, Button, Menu, Modal} from 'antd'; +import { + BellOutlined, + BugOutlined, + LayoutOutlined, + RocketOutlined, + SettingOutlined, + WarningOutlined, +} from '@ant-design/icons'; +import { + toggleConnectivityModal, + toggleLeftSidebarVisible, + toggleRightSidebarVisible, +} from '../reducers/application'; +import PluginManager from '../chrome/plugin-manager/PluginManager'; +import {showEmulatorLauncher} from './appinspect/LaunchEmulator'; +import SetupDoctorScreen, {checkHasNewProblem} from './SetupDoctorScreen'; +import {isProduction} from 'flipper-common'; +import FpsGraph from '../chrome/FpsGraph'; +import NetworkGraph from '../chrome/NetworkGraph'; +import {errorCounterAtom} from '../chrome/ConsoleLogs'; +import {filterNotifications} from './notification/notificationUtils'; +import { + exportEverythingEverywhereAllAtOnce, + ExportEverythingEverywhereAllAtOnceStatus, + startFileImport, + startFileExport, + startLinkExport, +} from '../utils/exportData'; +import UpdateIndicator from '../chrome/UpdateIndicator'; +import {css} from '@emotion/css'; +import constants from '../fb-stubs/constants'; +import {setStaticView} from '../reducers/connections'; +import {StyleGuide} from './StyleGuide'; +import {openDeeplinkDialog} from '../deeplink'; +import SettingsSheet from '../chrome/SettingsSheet'; +import WelcomeScreen from './WelcomeScreen'; +import {AppSelector} from './appinspect/AppSelector'; +import { + NavbarScreenRecordButton, + NavbarScreenshotButton, +} from '../chrome/ScreenCaptureButtons'; +import {StatusMessage} from './appinspect/AppInspect'; +import {TroubleshootingGuide} from './appinspect/fb-stubs/TroubleshootingGuide'; +import {FlipperDevTools} from '../chrome/FlipperDevTools'; +import {TroubleshootingHub} from '../chrome/TroubleshootingHub'; +import {Notification} from './notification/Notification'; + +export const Navbar = withTrackingScope(function Navbar() { + return ( + + + + + + + + + {!isProduction() && ( +
+ + +
+ )} +
+ + + + + + + + + +
+ ); +}); + +function ExportEverythingEverywhereAllAtOnceStatusModal({ + status, + setStatus, +}: { + status: ExportEverythingEverywhereAllAtOnceStatus | undefined; + setStatus: ( + newStatus: ExportEverythingEverywhereAllAtOnceStatus | undefined, + ) => void; +}) { + const [statusMessage, setStatusMessage] = useState(); + + useEffect(() => { + switch (status?.[0]) { + case 'logs': { + setStatusMessage(

Exporting Flipper logs...

); + return; + } + case 'files': { + let sheepCount = 0; + const setFileExportMessage = () => { + setStatusMessage( + <> +

Exporting Flipper debug files from all devices...

+

It could take a long time!

+

Let's count sheep while we wait: {sheepCount++}.

+

We'll skip it automatically if it exceeds 3 minutes.

+ , + ); + }; + + setFileExportMessage(); + + const interval = setInterval(setFileExportMessage, 3000); + return () => clearInterval(interval); + } + case 'state': { + let dinosaursCount = 0; + const setStateExportMessage = () => { + setStatusMessage( + <> +

Exporting Flipper state...

+

It also could take a long time!

+

This time we could count dinosaurs: {dinosaursCount++}.

+

We'll skip it automatically if it exceeds 2 minutes.

+ , + ); + }; + + setStateExportMessage(); + + const interval = setInterval(setStateExportMessage, 2000); + return () => clearInterval(interval); + } + case 'archive': { + setStatusMessage(

Creating an archive...

); + return; + } + case 'upload': { + setStatusMessage(

Uploading the archive...

); + return; + } + case 'support': { + setStatusMessage(

Creating a support request...

); + return; + } + case 'error': { + setStatusMessage( + <> +

Oops! Something went wrong.

+

{status[1]}

+ , + ); + return; + } + case 'done': { + setStatusMessage(

Done!

); + return; + } + case 'cancelled': { + setStatusMessage(

Cancelled! Why? 😱🤯👏

); + return; + } + } + }, [status]); + + return ( + { + setStatus(undefined); + }} + title="Exporting everything everywhere all at once" + footer={null}> + {statusMessage} + + ); +} + +function NotificationButton() { + const store = useStore(); + const isOpen = useStore((store) => store.application.isNotificationModalOpen); + const notifications = useStore((state) => state.notifications); + const activeNotifications = useMemoize(filterNotifications, [ + notifications.activeNotifications, + notifications.blocklistedPlugins, + notifications.blocklistedCategories, + ]); + return ( + <> + { + store.dispatch({type: 'isNotificationModalOpen', payload: true}); + }} + /> + + store.dispatch({type: 'isNotificationModalOpen', payload: false}) + } + width="70vw" + footer={null}> +
+ +
+
+ + ); +} + +function LeftSidebarToggleButton() { + const dispatch = useDispatch(); + const mainMenuVisible = useStore( + (state) => state.application.leftSidebarVisible, + ); + + return ( + { + dispatch(toggleLeftSidebarVisible()); + }} + /> + ); +} + +function RightSidebarToggleButton() { + const dispatch = useDispatch(); + const rightSidebarAvailable = useStore( + (state) => state.application.rightSidebarAvailable, + ); + const rightSidebarVisible = useStore( + (state) => state.application.rightSidebarVisible, + ); + + return ( + { + dispatch(toggleRightSidebarVisible()); + }} + /> + ); +} + +function LaunchVirtualDeviceButton() { + const store = useStore(); + + return ( + { + showEmulatorLauncher(store); + }} + /> + ); +} + +const badgeDotClassname = css` + sup { + right: calc(50% - 14px); + margin-top: 8px; + } +`; +const badgeCountClassname = css` + sup { + right: calc(50% - 22px); + margin-top: 8px; + } +`; + +const hideBorderWhenDisabled = css` + :disabled { + border-color: transparent !important; + } + :disabled:hover { + border-color: ${theme.disabledColor} !important; + } +`; + +export function NavbarButton({ + icon: Icon, + label, + toggled = false, + onClick, + count, + disabled = false, + flipIcon = false, + zIndex, + colorOverride, +}: { + icon: (props: any) => any; + label: string; + // TODO remove optional + colorOverride?: string; + zIndex?: number; + onClick?: () => void; + toggled?: boolean; + count?: number | true; + disabled?: boolean; + flipIcon?: boolean; +}) { + const color = toggled ? theme.primaryColor : theme.textColorActive; + const button = ( + + ); + + if (count !== undefined) { + return ( + + {button} + + ); + } else { + return button; + } +} + +function NoConnectivityWarning() { + const connected = useValue(isConnected()); + + if (!connected) { + return ( + + ); + } + + return null; +} + +const menu = css` + border: none; + height: 56px; + + .ant-menu-submenu-title { + hieght: 56px; + } +`; +const submenu = css` + height: 56px; + + .ant-menu-submenu-title { + height: 56px !important; + padding: 0; + margin: 0; + } + .ant-menu-submenu-arrow { + display: none; + } +`; + +const AlertsZIndex = 101; +const TroubleShootZIndex = 100; + +function TroubleshootMenu() { + const store = useStore(); + const [status, setStatus] = useState< + ExportEverythingEverywhereAllAtOnceStatus | undefined + >(); + const [isFlipperDevToolsModalOpen, setFlipperDevToolsModalOpen] = + useState(false); + + const exportEverythingEverywhereAllAtOnceTracked = useTrackedCallback( + 'Debug data export', + () => + exportEverythingEverywhereAllAtOnce( + store, + (...args) => setStatus(args), + config.isFBBuild, + ), + [store, setStatus], + ); + const [isDoctorVisible, setIsDoctorVisible] = useState(false); + const result = useStore( + (state) => state.healthchecks.healthcheckReport.result, + ); + const hasNewProblem = useMemo(() => checkHasNewProblem(result), [result]); + const flipperErrorLogCount = useValue(errorCounterAtom); + + const count = flipperErrorLogCount || hasNewProblem || 0; + + const badgeProps: Parameters[0] = + count === true ? {dot: true, offset: [-8, 8]} : {count, offset: [-6, 5]}; + + return ( + <> + {/* using Badge **here** as NavbarButton badge is being cut off by Menu component */} + + + } + className={submenu}> + setIsDoctorVisible(true)}> + Setup Doctor + + { + store.dispatch(toggleConnectivityModal()); + }}> + Troubleshoot Connectivity + + + + + Export All + + { + setFlipperDevToolsModalOpen(true); + }}> + + Flipper Logs + + + + + + setIsDoctorVisible(false)} + /> + + setFlipperDevToolsModalOpen(false)} + /> + + + ); +} + +function TroubleshootingModal() { + const store = useStore(); + const isOpen = useStore( + (state) => state.application.isTroubleshootingModalOpen, + ); + return ( + store.dispatch(toggleConnectivityModal())} + width="100%" + footer={null} + style={{ + // override default `top: 100px` + top: '5vh', + }}> +
+ +
+
+ ); +} + +function FlipperDevToolsModal({ + isOpen, + onClose, +}: { + isOpen: boolean; + onClose: () => void; +}) { + return ( + +
+ +
+
+ ); +} + +function ExtrasMenu() { + const store = useStore(); + + const startFileExportTracked = useTrackedCallback( + 'File export', + () => startFileExport(store.dispatch), + [store.dispatch], + ); + const startLinkExportTracked = useTrackedCallback( + 'Link export', + () => startLinkExport(store.dispatch), + [store.dispatch], + ); + const startFileImportTracked = useTrackedCallback( + 'File import', + () => startFileImport(store), + [store], + ); + + const [showSettings, setShowSettings] = useState(false); + const onSettingsClose = useCallback(() => setShowSettings(false), []); + + const settings = useStore((state) => state.settingsState); + const {showWelcomeAtStartup} = settings; + const [welcomeVisible, setWelcomeVisible] = useState(showWelcomeAtStartup); + const loggedIn = useValue(currentUser()); + + return ( + <> + + + } + className={submenu}> + { + Dialog.showModal((onHide) => ); + }}> + Add Plugins + + + Import Flipper file + + + Export Flipper file + + {constants.ENABLE_SHAREABLE_LINK ? ( + + Export shareable link + + ) : null} + + + { + store.dispatch(setStaticView(StyleGuide)); + }}> + Flipper Style Guide + + openDeeplinkDialog(store)}> + Trigger deeplink + + + + setShowSettings(true)}> + Settings + + setWelcomeVisible(true)}> + Help + + {config.showLogin && loggedIn && ( + await logoutUser()}> + Logout + + )} + + + + {showSettings && ( + + )} + setWelcomeVisible(false)} + showAtStartup={showWelcomeAtStartup} + onCheck={(value) => + store.dispatch({ + type: 'UPDATE_SETTINGS', + payload: {...settings, showWelcomeAtStartup: value}, + }) + } + /> + + ); +} diff --git a/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx b/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx index d18ba1faf..173913251 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/SandyApp.tsx @@ -7,7 +7,7 @@ * @format */ -import React, {useEffect, useState, useCallback} from 'react'; +import React, {useEffect} from 'react'; import { TrackingScope, useLogger, @@ -20,19 +20,18 @@ import {Link, styled} from '../ui'; import {theme} from 'flipper-plugin'; import {Logger} from 'flipper-common'; -import {LeftRail} from './LeftRail'; -import {useStore, useDispatch} from '../utils/useStore'; -import {FlipperDevTools} from '../chrome/FlipperDevTools'; -import {setStaticView} from '../reducers/connections'; -import {toggleLeftSidebarVisible} from '../reducers/application'; +import {Navbar} from './Navbar'; +import {useStore} from '../utils/useStore'; import {AppInspect} from './appinspect/AppInspect'; import PluginContainer from '../PluginContainer'; import {ContentContainer} from './ContentContainer'; -import {Notification} from './notification/Notification'; import {showChangelog} from '../chrome/ChangelogSheet'; import PlatformSelectWizard, { hasPlatformWizardBeenDone, } from '../chrome/PlatformSelectWizard'; +import PWAInstallationWizard, { + shouldShowPWAInstallationWizard, +} from '../chrome/PWAppInstallationWizard'; import {getVersionString} from '../utils/versionString'; import config from '../fb-stubs/config'; import {WelcomeScreenStaticView} from './WelcomeScreen'; @@ -41,63 +40,22 @@ import {isFBEmployee} from '../utils/fbEmployee'; import {notification} from 'antd'; import isProduction from '../utils/isProduction'; import {getRenderHostInstance} from 'flipper-frontend-core'; - -export type ToplevelNavItem = - | 'appinspect' - | 'flipperlogs' - | 'notification' - | undefined; -export type ToplevelProps = { - toplevelSelection: ToplevelNavItem; - setToplevelSelection: (_newSelection: ToplevelNavItem) => void; -}; +import {uiPerfTracker} from '../utils/UIPerfTracker'; export function SandyApp() { const logger = useLogger(); - const dispatch = useDispatch(); const leftSidebarVisible = useStore( (state) => state.application.leftSidebarVisible, ); const staticView = useStore((state) => state.connections.staticView); - /** - * top level navigation uses two pieces of state, selection stored here, and selection that is based on what is stored in the reducer (which might be influenced by redux action dispatches to different means). - * The logic here is to sync both, but without modifying the navigation related reducers to not break classic Flipper. - * It is possible to simplify this in the future. - */ - const [toplevelSelection, setStoredToplevelSelection] = - useState('appinspect'); - - // Handle toplevel nav clicks from LeftRail - const setToplevelSelection = useCallback( - (newSelection: ToplevelNavItem) => { - // toggle sidebar visibility if needed - const hasLeftSidebar = - newSelection === 'appinspect' || newSelection === 'notification'; - if (hasLeftSidebar) { - if (newSelection === toplevelSelection) { - dispatch(toggleLeftSidebarVisible()); - } else { - dispatch(toggleLeftSidebarVisible(true)); - } - } - switch (newSelection) { - case 'flipperlogs': - dispatch(setStaticView(FlipperDevTools)); - break; - default: - } - setStoredToplevelSelection(newSelection); - }, - [dispatch, toplevelSelection], - ); - useEffect(() => { document.title = `Flipper (${getVersionString()}${ config.isFBBuild ? '@FB' : '' })`; registerStartupTime(logger); + uiPerfTracker.track('ui-perf-sandy-container-rendered'); if (hasPlatformWizardBeenDone(window.localStorage)) { Dialog.showModal((onHide) => ( @@ -110,6 +68,11 @@ export function SandyApp() { )); } + if (shouldShowPWAInstallationWizard()) { + console.info('Attempt to install PWA, launch installation wizard.'); + Dialog.showModal((onHide) => ); + } + showChangelog(true); // don't warn about logger, even with a new logger we don't want to re-register @@ -144,55 +107,47 @@ export function SandyApp() { } }, []); - const leftMenuContent = !leftSidebarVisible ? null : toplevelSelection === - 'appinspect' ? ( - - ) : toplevelSelection === 'notification' ? ( - - ) : null; - return ( - - - - <_Sidebar width={250} minWidth={220} maxWidth={800} gutter> - {leftMenuContent && ( - - {leftMenuContent} + + + + + <_Sidebar width={250} minWidth={220} maxWidth={800} gutter> + {leftSidebarVisible ? : null} + + + + {staticView ? ( + + {staticView === WelcomeScreenStaticView ? ( + React.createElement(staticView) /* avoid shadow */ + ) : ( + + {React.createElement(staticView, { + logger: logger, + })} + + )} + ) : ( + )} - - - - {staticView ? ( - - {staticView === WelcomeScreenStaticView ? ( - React.createElement(staticView) /* avoid shadow */ - ) : ( - - {React.createElement(staticView, { - logger: logger, - })} - - )} - - ) : ( - - )} - {outOfContentsContainer} - - + {outOfContentsContainer} + + + <_PortalsManager /> @@ -223,13 +178,14 @@ const outOfContentsContainer = ( const MainContainer = styled(Layout.Container)({ background: theme.backgroundWash, - padding: `${theme.space.large}px ${theme.space.large}px ${theme.space.large}px 0`, + padding: `0 ${theme.space.large}px ${theme.space.large}px 0`, overflow: 'hidden', }); const RootElement = styled.div({ display: 'flex', height: '100%', + background: theme.backgroundWash, }); RootElement.displayName = 'SandyAppRootElement'; @@ -242,5 +198,4 @@ function registerStartupTime(logger: Logger) { }); renderHost.sendIpcEvent('getLaunchTime'); - renderHost.sendIpcEvent('componentDidMount'); } diff --git a/desktop/flipper-ui-core/src/sandy-chrome/SetupDoctorScreen.tsx b/desktop/flipper-ui-core/src/sandy-chrome/SetupDoctorScreen.tsx index 687b0861e..29d75620e 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/SetupDoctorScreen.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/SetupDoctorScreen.tsx @@ -9,7 +9,15 @@ import React, {useEffect, useCallback, useMemo, useState} from 'react'; import {useDispatch, useStore} from '../utils/useStore'; -import {Typography, Collapse, Button, Modal, Checkbox, Alert} from 'antd'; +import { + Typography, + Collapse, + Button, + Modal, + Checkbox, + Alert, + Space, +} from 'antd'; import { CheckCircleFilled, CloseCircleFilled, @@ -167,6 +175,7 @@ const FooterContainer = styled(Layout.Horizontal)({ }); function SetupDoctorFooter(props: { + closable: boolean; onClose: () => void; onRerunDoctor: () => Promise; showAcknowledgeCheckbox: boolean; @@ -190,7 +199,7 @@ function SetupDoctorFooter(props: { )} - + {props.closable && }
, +
+ Loading... +
, + ] `); }); @@ -89,7 +92,7 @@ test('Can render and launch android apps - no SDKs', async () => { .toMatchInlineSnapshot(`
No Mobile SDKs Enabled
@@ -125,7 +128,13 @@ test('Can render and launch android apps', async () => { await sleep(1); // give exec time to resolve expect(await renderer.findAllByText(/emulator/)).toMatchInlineSnapshot(` - Array [ + [ +

+ Android emulators +

, emulator1 , @@ -140,11 +149,11 @@ test('Can render and launch android apps', async () => { await sleep(1000); expect(onClose).toBeCalled(); expect(exec.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ + [ + [ "android-get-emulators", ], - Array [ + [ "android-launch-emulator", "emulator2", false, @@ -152,3 +161,69 @@ test('Can render and launch android apps', async () => { ] `); }); + +test('Favouriting a virtual device brings it to the top', async () => { + const store = createStore(createRootReducer()); + + const exec = jest.fn().mockImplementation(async (cmd) => { + if (cmd === 'android-get-emulators') { + return ['emulator1', 'emulator2']; + } + }); + + getRenderHostInstance().flipperServer.exec = exec; + + store.dispatch({ + type: 'UPDATE_SETTINGS', + payload: { + ...store.getState().settingsState, + enableAndroid: true, + }, + }); + const onClose = jest.fn(); + + const renderer = render( + + + , + ); + + await sleep(1); // give exec time to resolve + + expect(await renderer.findAllByText(/emulator/)).toMatchInlineSnapshot(` + [ +

+ Android emulators +

, + + emulator1 + , + + emulator2 + , + ] + `); + + const lastFavourite = last(renderer.getAllByLabelText('not-favorite'))!; + fireEvent.click(lastFavourite); + + expect(await renderer.findAllByText(/emulator/)).toMatchInlineSnapshot(` + [ +

+ Android emulators +

, + + emulator2 + , + + emulator1 + , + ] + `); +}); diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/fb-stubs/TroubleshootingGuide.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/fb-stubs/TroubleshootingGuide.tsx index 456004303..05f09c447 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/fb-stubs/TroubleshootingGuide.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/fb-stubs/TroubleshootingGuide.tsx @@ -7,15 +7,6 @@ * @format */ -import React from 'react'; -import {NoDevices} from '../NoDevices'; - -export function TroubleshootingGuide(_props: { - showGuide: boolean; - devicesDetected: number; -}) { - if (_props.devicesDetected == 0) return ; - else { - return <>; - } +export function TroubleshootingGuide() { + return null; } diff --git a/desktop/flipper-ui-core/src/sandy-chrome/notification/BlocklistSettingButton.tsx b/desktop/flipper-ui-core/src/sandy-chrome/notification/BlocklistSettingButton.tsx index dfe09fffa..c17102a85 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/notification/BlocklistSettingButton.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/notification/BlocklistSettingButton.tsx @@ -31,7 +31,7 @@ export default function BlocklistSettingButton(props: { /> setShowModal(false)}> diff --git a/desktop/flipper-ui-core/src/sandy-chrome/notification/Notification.tsx b/desktop/flipper-ui-core/src/sandy-chrome/notification/Notification.tsx index 3454fc8f1..f7b4493cf 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/notification/Notification.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/notification/Notification.tsx @@ -115,6 +115,7 @@ function NotificationEntry({notification}: {notification: PluginNotification}) { pluginName, iconName, } = notification; + const store = useStore(); const actions = useMemo( () => ( @@ -163,7 +164,13 @@ function NotificationEntry({notification}: {notification: PluginNotification}) { ? `${clientName}/${appName}` : clientName ?? appName ?? 'Not Connected'} - @@ -307,6 +314,7 @@ export function Notification() { export function openNotification(store: Store, noti: PluginNotificationOrig) { const client = getClientById(store, noti.client); if (client) { + store.dispatch({type: 'isNotificationModalOpen', payload: true}); store.dispatch( selectPlugin({ selectedPlugin: noti.pluginId, @@ -318,6 +326,7 @@ export function openNotification(store: Store, noti: PluginNotificationOrig) { } else { const device = getDeviceById(store, noti.client); if (device) { + store.dispatch({type: 'isNotificationModalOpen', payload: true}); store.dispatch( selectPlugin({ selectedPlugin: noti.pluginId, diff --git a/desktop/flipper-ui-core/src/selectors/connections.tsx b/desktop/flipper-ui-core/src/selectors/connections.tsx index aa75b253b..e5a5a7e8f 100644 --- a/desktop/flipper-ui-core/src/selectors/connections.tsx +++ b/desktop/flipper-ui-core/src/selectors/connections.tsx @@ -86,7 +86,6 @@ export const getPluginLists = createSelector( plugins: { clientPlugins, devicePlugins, - bundledPlugins, marketplacePlugins, loadedPlugins, disabledPlugins, @@ -96,7 +95,6 @@ export const getPluginLists = createSelector( }: State) => ({ clientPlugins, devicePlugins, - bundledPlugins, marketplacePlugins, loadedPlugins, disabledPlugins, diff --git a/desktop/flipper-ui-core/src/startFlipperDesktop.tsx b/desktop/flipper-ui-core/src/startFlipperDesktop.tsx index f3048503c..7be42ec0f 100644 --- a/desktop/flipper-ui-core/src/startFlipperDesktop.tsx +++ b/desktop/flipper-ui-core/src/startFlipperDesktop.tsx @@ -8,8 +8,6 @@ */ import {Provider} from 'react-redux'; -import {createRoot} from 'react-dom/client'; - import {init as initLogger} from './fb-stubs/Logger'; import {SandyApp} from './sandy-chrome/SandyApp'; import {Persistor, persistStore} from 'redux-persist'; @@ -39,12 +37,14 @@ import styled from '@emotion/styled'; import {CopyOutlined} from '@ant-design/icons'; import {getVersionString} from './utils/versionString'; import {PersistGate} from 'redux-persist/integration/react'; -import {setLoggerInstance, FlipperServer} from 'flipper-common'; +import {setLoggerInstance, FlipperServer, initLogTailer} from 'flipper-common'; import {getRenderHostInstance} from 'flipper-frontend-core'; import {startGlobalErrorHandling} from './utils/globalErrorHandling'; import {loadTheme} from './utils/loadTheme'; import {connectFlipperServerToStore} from './dispatcher/flipperServer'; +import {enableConnectivityHook} from './chrome/ConnectivityLogs'; import ReactDOM from 'react-dom'; +import {uiPerfTracker} from './utils/UIPerfTracker'; class AppFrame extends React.Component< {logger: Logger; persistor: Persistor}, @@ -123,6 +123,10 @@ class AppFrame extends React.Component< ); } + componentDidMount(): void { + uiPerfTracker.track('ui-perf-root-rendered'); + } + componentDidCatch(error: any, errorInfo: any) { console.error( `Flipper chrome crash: ${error}`, @@ -139,16 +143,26 @@ class AppFrame extends React.Component< function init(flipperServer: FlipperServer) { const settings = getRenderHostInstance().serverConfig.settings; const store = getStore(); + + initLogTailer(); + const logger = initLogger(store); + uiPerfTracker._init(); setLoggerInstance(logger); startGlobalErrorHandling(); loadTheme(settings.darkMode); // rehydrate app state before exposing init - const persistor = persistStore(store, undefined, () => { + const persistor = persistStore(store, undefined, async () => { // Make sure process state is set before dispatchers run - dispatcher(store, logger); + await dispatcher(store, logger); + getRenderHostInstance().sendIpcEvent('storeRehydrated'); + uiPerfTracker.track('ui-perf-store-rehydrated'); + // We could potentially merge ui-perf-store-rehydrated and ui-perf-everything-finally-loaded-jeeeez, + // but what if at some point in the future we relalize that store rehydration is not actually the last event? + // Keep it separate for the time being (evil laugh as there is nothing more permanent than temporary stuff) + uiPerfTracker.track('ui-perf-everything-finally-loaded-jeeeez'); }); setPersistor(persistor); @@ -165,16 +179,17 @@ function init(flipperServer: FlipperServer) { connectFlipperServerToStore(flipperServer, store, logger); + enableConsoleHook(); + enableConnectivityHook(flipperServer); + // TODO T116224873: Return the following code back instead of ReactDOM.react when the following issue is fixed: https://github.com/react-component/trigger/issues/288 // const root = createRoot(document.getElementById('root')!); // root.render(); - ReactDOM.render( - , - document.getElementById('root')!, - ); - - enableConsoleHook(); + const root = document.getElementById('root'); + if (root) { + ReactDOM.render(, root); + } const launcherMessage = getRenderHostInstance().serverConfig.processConfig.launcherMsg; @@ -189,8 +204,8 @@ function init(flipperServer: FlipperServer) { } } -export async function startFlipperDesktop(flipperServer: FlipperServer) { - getRenderHostInstance(); // renderHost instance should be set at this point! +export function startFlipperDesktop(flipperServer: FlipperServer) { + getRenderHostInstance(); init(flipperServer); } diff --git a/desktop/flipper-ui-core/src/ui/components/Button.tsx b/desktop/flipper-ui-core/src/ui/components/Button.tsx index 0dd8dd982..804634885 100644 --- a/desktop/flipper-ui-core/src/ui/components/Button.tsx +++ b/desktop/flipper-ui-core/src/ui/components/Button.tsx @@ -9,10 +9,9 @@ import React, {useCallback} from 'react'; import styled from '@emotion/styled'; -import {Button as AntdButton} from 'antd'; +import {Button as AntdButton, ButtonProps} from 'antd'; import Glyph, {IconSize} from './Glyph'; -import type {ButtonProps} from 'antd/lib/button'; import {theme, getFlipperLib} from 'flipper-plugin'; type ButtonType = 'primary' | 'success' | 'warning' | 'danger'; diff --git a/desktop/flipper-ui-core/src/ui/components/ErrorBoundary.tsx b/desktop/flipper-ui-core/src/ui/components/ErrorBoundary.tsx index 3d2a646ff..11fea18b6 100644 --- a/desktop/flipper-ui-core/src/ui/components/ErrorBoundary.tsx +++ b/desktop/flipper-ui-core/src/ui/components/ErrorBoundary.tsx @@ -8,7 +8,7 @@ */ import {CodeBlock} from 'flipper-plugin'; -import {Component} from 'react'; +import {Component, ErrorInfo} from 'react'; import Heading from './Heading'; import Button from './Button'; import View from './View'; @@ -54,8 +54,9 @@ export default class ErrorBoundary extends Component< this.state = {error: null}; } - componentDidCatch(err: Error) { - console.error(err.toString(), 'ErrorBoundary'); + componentDidCatch(err: Error, errorInfo: ErrorInfo) { + // eslint-disable-next-line flipper/no-console-error-without-context + console.error(err, errorInfo.componentStack, 'ErrorBoundary'); this.setState({error: err}); } @@ -78,11 +79,9 @@ export default class ErrorBoundary extends Component< return ( {heading} - {this.props.showStack !== false && ( - {`${ - error.stack ?? error.toString() - }`} - )} + {this.props.showStack !== false && + 'Look in the console for correct stack traces.'} +
); diff --git a/desktop/flipper-ui-core/src/ui/components/Glyph.tsx b/desktop/flipper-ui-core/src/ui/components/Glyph.tsx index 45f23c7b9..dd6208cd9 100644 --- a/desktop/flipper-ui-core/src/ui/components/Glyph.tsx +++ b/desktop/flipper-ui-core/src/ui/components/Glyph.tsx @@ -11,7 +11,7 @@ import React from 'react'; import styled from '@emotion/styled'; import {getIconURL} from '../../utils/icons'; -export type IconSize = 8 | 10 | 12 | 16 | 18 | 20 | 24 | 32; +export type IconSize = 8 | 10 | 12 | 16 | 18 | 20 | 24 | 28 | 32; const ColoredIconBlack = styled.img<{size: number}>(({size}) => ({ height: size, diff --git a/desktop/flipper-ui-core/src/ui/components/NotificationBody.tsx b/desktop/flipper-ui-core/src/ui/components/NotificationBody.tsx new file mode 100644 index 000000000..e38f027bb --- /dev/null +++ b/desktop/flipper-ui-core/src/ui/components/NotificationBody.tsx @@ -0,0 +1,49 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {CopyOutlined} from '@ant-design/icons'; +import {Typography} from 'antd'; +import {getRenderHostInstance} from 'flipper-frontend-core'; +import {theme} from 'flipper-plugin'; +import * as React from 'react'; + +type NotificationbodyProps = { + text: string; +}; + +export const NotificationBody: React.FC = ({text}) => { + const messageLoggedRef = React.useRef(false); + + return ( + { + if (messageLoggedRef.current) { + return; + } + if (ellipsis) { + console.warn( + 'Message is too long to fit in the notification box. Original text:', + text, + ); + messageLoggedRef.current = true; + } + }, + }} + copyable={{ + onCopy: () => getRenderHostInstance().writeTextToClipboard(text), + icon: , + }}> + {text} + + ); +}; diff --git a/desktop/flipper-ui-core/src/ui/components/RequireLogin.tsx b/desktop/flipper-ui-core/src/ui/components/RequireLogin.tsx index aabe63516..e931edbd0 100644 --- a/desktop/flipper-ui-core/src/ui/components/RequireLogin.tsx +++ b/desktop/flipper-ui-core/src/ui/components/RequireLogin.tsx @@ -7,7 +7,7 @@ * @format */ -import {isLoggedIn} from '../../fb-stubs/user'; +import {currentUser} from '../../fb-stubs/user'; import {Layout, useValue} from 'flipper-plugin'; import React from 'react'; import config from '../../fb-stubs/config'; @@ -15,7 +15,7 @@ import {Alert} from 'antd'; import {LoginOutlined} from '@ant-design/icons'; export const RequireLogin: React.FC<{}> = ({children}) => { - const loggedIn = useValue(isLoggedIn()); + const loggedIn = useValue(currentUser()); if (!config.isFBBuild) { return ( diff --git a/desktop/flipper-ui-core/src/ui/components/StackTrace.tsx b/desktop/flipper-ui-core/src/ui/components/StackTrace.tsx index df6692967..a925a7818 100644 --- a/desktop/flipper-ui-core/src/ui/components/StackTrace.tsx +++ b/desktop/flipper-ui-core/src/ui/components/StackTrace.tsx @@ -9,7 +9,6 @@ import {Component} from 'react'; import Text from './Text'; -import {colors} from './colors'; import ManagedTable from './table/ManagedTable'; import FlexRow from './FlexRow'; import Glyph from './Glyph'; @@ -22,6 +21,7 @@ import { TableColumns, TableBodyColumn, } from './table/types'; +import {theme} from 'flipper-plugin'; const Padder = styled.div<{ padded?: boolean; @@ -34,9 +34,8 @@ Padder.displayName = 'StackTrace:Padder'; const Container = styled.div<{isCrash?: boolean; padded?: boolean}>( ({isCrash, padded}) => ({ - backgroundColor: isCrash ? colors.redTint : 'transprent', border: padded - ? `1px solid ${isCrash ? colors.red : colors.light15}` + ? `1px solid ${isCrash ? theme.errorColor : theme.dividerColor}` : 'none', borderRadius: padded ? 5 : 0, overflow: 'hidden', @@ -45,7 +44,7 @@ const Container = styled.div<{isCrash?: boolean; padded?: boolean}>( Container.displayName = 'StackTrace:Container'; const Title = styled(FlexRow)<{isCrash?: boolean}>(({isCrash}) => ({ - color: isCrash ? colors.red : 'inherit', + color: isCrash ? theme.errorColor : 'inherit', padding: 8, alignItems: 'center', minHeight: 32, @@ -53,18 +52,21 @@ const Title = styled(FlexRow)<{isCrash?: boolean}>(({isCrash}) => ({ Title.displayName = 'StackTrace:Title'; const Reason = styled(Text)<{isCrash?: boolean}>(({isCrash}) => ({ - color: isCrash ? colors.red : colors.light80, + color: isCrash ? theme.errorColor : theme.textColorPrimary, fontWeight: 'bold', fontSize: 13, })); Reason.displayName = 'StackTrace:Reason'; -const Line = styled(Text)<{isCrash?: boolean; isBold?: boolean}>( - ({isCrash, isBold}) => ({ - color: isCrash ? colors.red : colors.light80, - fontWeight: isBold ? 'bold' : 'normal', - }), -); +const Line = styled(Text)<{ + isCrash?: boolean; + isBold?: boolean; + color?: string; +}>(({isBold, color}) => ({ + color: color ?? theme.textColorPrimary, + fontWeight: isBold ? 'bold' : 'normal', + overflow: 'visible', +})); Line.displayName = 'StackTrace:Line'; const Icon = styled(Glyph)({marginRight: 5}); @@ -148,6 +150,18 @@ export default class StackTrace extends Component<{ return acc; }, {}); + const colorForColumn = (column: string): string | undefined => { + switch (column) { + case 'lineNumber': + return theme.semanticColors.numberValue; + case 'library': + return theme.textColorPrimary; + case 'address': + return theme.semanticColors.stringValue; + case 'caller': + return theme.textColorSecondary; + } + }; const rows: TableBodyRow[] = children.map((l, i) => ({ key: String(i), columns: (Object.keys(columns) as Array).reduce<{ @@ -156,7 +170,11 @@ export default class StackTrace extends Component<{ acc[cv] = { align: cv === 'lineNumber' ? 'right' : 'left', value: ( - + {String(l[cv])} ), @@ -177,7 +195,7 @@ export default class StackTrace extends Component<{ name="stop" variant="filled" size={16} - color={colors.red} + color={theme.errorColor} /> )} diff --git a/desktop/flipper-ui-core/src/ui/components/table/ManagedTable.tsx b/desktop/flipper-ui-core/src/ui/components/table/ManagedTable.tsx index 6ed2652c3..f9c3919af 100644 --- a/desktop/flipper-ui-core/src/ui/components/table/ManagedTable.tsx +++ b/desktop/flipper-ui-core/src/ui/components/table/ManagedTable.tsx @@ -155,7 +155,7 @@ type ManagedTableState = { }; const Container = styled(FlexColumn)<{canOverflow?: boolean}>((props) => ({ - overflow: props.canOverflow ? 'auto' : 'visible', + overflow: props.canOverflow ? 'scroll' : 'visible', flexGrow: 1, height: '100%', })); diff --git a/desktop/flipper-ui-core/src/ui/components/table/TableHead.tsx b/desktop/flipper-ui-core/src/ui/components/table/TableHead.tsx index 5ef4370d5..eeeb6562b 100644 --- a/desktop/flipper-ui-core/src/ui/components/table/TableHead.tsx +++ b/desktop/flipper-ui-core/src/ui/components/table/TableHead.tsx @@ -111,7 +111,7 @@ class TableHeadColumn extends PureComponent<{ componentDidMount() { if (this.props.horizontallyScrollable && this.ref) { // measure initial width - this.onResize(this.ref.offsetWidth); + this.onResize(this.ref.getBoundingClientRect().width); } } @@ -146,14 +146,16 @@ class TableHeadColumn extends PureComponent<{ throw new Error('expected there to be parentElement'); } - const parentWidth = parentElement.clientWidth; + const parentMeasures = parentElement.getBoundingClientRect(); + const parentWidth = parentMeasures.width; const {childNodes} = parentElement; const lastElem = childNodes[childNodes.length - 1]; - const right = - lastElem instanceof HTMLElement - ? lastElem.offsetLeft + lastElem.clientWidth + 1 - : 0; + let right = 0; + if (lastElem instanceof HTMLElement) { + const lastElemMeasures = lastElem.getBoundingClientRect(); + right = lastElemMeasures.left + lastElemMeasures.width; + } if (right < parentWidth) { normalizedWidth = calculatePercentage(parentWidth, newWidth); diff --git a/desktop/flipper-ui-core/src/utils/UIPerfTracker.tsx b/desktop/flipper-ui-core/src/utils/UIPerfTracker.tsx new file mode 100644 index 000000000..1da3db5d3 --- /dev/null +++ b/desktop/flipper-ui-core/src/utils/UIPerfTracker.tsx @@ -0,0 +1,33 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {getLogger} from 'flipper-common'; + +type UIPerfEvents = + | 'ui-perf-store-rehydrated' + | 'ui-perf-root-rendered' + | 'ui-perf-sandy-container-rendered' + | 'ui-perf-everything-finally-loaded-jeeeez'; + +class UIPerfTracker { + private t0!: number; + + _init() { + this.t0 = performance.now(); + } + + track(event: UIPerfEvents) { + const tx = performance.now(); + getLogger().track('performance', event, { + time: tx - this.t0, + }); + } +} + +export const uiPerfTracker = new UIPerfTracker(); diff --git a/desktop/flipper-ui-core/src/utils/__tests__/exportData.node.tsx b/desktop/flipper-ui-core/src/utils/__tests__/exportData.node.tsx index a0e11b023..d1529ff43 100644 --- a/desktop/flipper-ui-core/src/utils/__tests__/exportData.node.tsx +++ b/desktop/flipper-ui-core/src/utils/__tests__/exportData.node.tsx @@ -105,7 +105,13 @@ function generateClientFromClientWithSalt( const identifier = generateClientIdentifierWithSalt(client.id, salt); return { id: identifier, - query: {app, os, device, device_id: salt + '-' + device_id}, + query: { + app, + os, + device, + device_id: salt + '-' + device_id, + medium: client.query.medium, + }, }; } @@ -114,7 +120,7 @@ function generateClientFromDevice(device: Device, app: string): ClientExport { const identifier = generateClientIdentifier(device, app); return { id: identifier, - query: {app, os, device: deviceType, device_id: serial}, + query: {app, os, device: deviceType, device_id: serial, medium: 'NONE'}, }; } @@ -149,6 +155,7 @@ test('test generateClientFromClientWithSalt helper function', () => { os: 'iOS', device: 'emulator', device_id: 'salt-serial', + medium: 'NONE', }, }); expect(client).toEqual({ @@ -158,6 +165,7 @@ test('test generateClientFromClientWithSalt helper function', () => { os: 'iOS', device: 'emulator', device_id: 'serial', + medium: 'NONE', }, }); }); @@ -178,6 +186,7 @@ test('test generateClientFromDevice helper function', () => { os: 'iOS', device: 'emulator', device_id: 'serial', + medium: 'NONE', }, }); }); @@ -727,6 +736,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present os: 'iOS', device: 'TestiPhone', device_id: 'serial1', + medium: 'NONE', }, null, logger, @@ -742,6 +752,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present os: 'iOS', device: 'TestiPhone', device_id: 'serial1', + medium: 'NONE', }, null, logger, @@ -757,6 +768,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present os: 'iOS', device: 'TestiPhone', device_id: 'serial1', + medium: 'NONE', }, null, logger, @@ -776,7 +788,6 @@ test('test determinePluginsToProcess for mutilple clients having plugins present ['RandomPlugin', TestPlugin.details], ['TestDevicePlugin', TestDevicePlugin.details], ]), - bundledPlugins: new Map(), gatekeepedPlugins: [], disabledPlugins: [], failedPlugins: [], @@ -817,6 +828,7 @@ test('test determinePluginsToProcess for no selected plugin present in any clien os: 'iOS', device: 'TestiPhone', device_id: 'serial1', + medium: 'NONE', }, null, logger, @@ -832,6 +844,7 @@ test('test determinePluginsToProcess for no selected plugin present in any clien os: 'iOS', device: 'TestiPhone', device_id: 'serial1', + medium: 'NONE', }, null, logger, @@ -851,7 +864,6 @@ test('test determinePluginsToProcess for no selected plugin present in any clien ['RandomPlugin', TestPlugin.details], ['TestDevicePlugin', TestDevicePlugin.details], ]), - bundledPlugins: new Map(), gatekeepedPlugins: [], disabledPlugins: [], failedPlugins: [], @@ -875,6 +887,7 @@ test('test determinePluginsToProcess for multiple clients on same device', async os: 'iOS', device: 'TestiPhone', device_id: 'serial1', + medium: 'NONE', }, null, logger, @@ -890,6 +903,7 @@ test('test determinePluginsToProcess for multiple clients on same device', async os: 'iOS', device: 'TestiPhone', device_id: 'serial1', + medium: 'NONE', }, null, logger, @@ -905,7 +919,6 @@ test('test determinePluginsToProcess for multiple clients on same device', async ['TestPlugin', TestPlugin.details], ['TestDevicePlugin', TestDevicePlugin.details], ]), - bundledPlugins: new Map(), gatekeepedPlugins: [], disabledPlugins: [], failedPlugins: [], @@ -938,6 +951,7 @@ test('test determinePluginsToProcess for multiple clients on different device', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', + medium: 'NONE', }, null, logger, @@ -953,6 +967,7 @@ test('test determinePluginsToProcess for multiple clients on different device', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', + medium: 'NONE', }, null, logger, @@ -968,6 +983,7 @@ test('test determinePluginsToProcess for multiple clients on different device', os: 'iOS', device: 'TestiPhone', device_id: 'serial2', + medium: 'NONE', }, null, logger, @@ -983,6 +999,7 @@ test('test determinePluginsToProcess for multiple clients on different device', os: 'iOS', device: 'TestiPhone', device_id: 'serial2', + medium: 'NONE', }, null, logger, @@ -998,7 +1015,6 @@ test('test determinePluginsToProcess for multiple clients on different device', ['TestPlugin', TestPlugin.details], ['TestDevicePlugin', TestDevicePlugin.details], ]), - bundledPlugins: new Map(), gatekeepedPlugins: [], disabledPlugins: [], failedPlugins: [], @@ -1055,6 +1071,7 @@ test('test determinePluginsToProcess to ignore archived clients', async () => { os: 'iOS', device: 'TestiPhone', device_id: 'serial', + medium: 'NONE', }, null, logger, @@ -1070,6 +1087,7 @@ test('test determinePluginsToProcess to ignore archived clients', async () => { os: 'iOS', device: 'TestiPhone', device_id: 'serial-archived', + medium: 'NONE', }, null, logger, @@ -1085,7 +1103,6 @@ test('test determinePluginsToProcess to ignore archived clients', async () => { ['TestPlugin', TestPlugin.details], ['TestDevicePlugin', TestDevicePlugin.details], ]), - bundledPlugins: new Map(), gatekeepedPlugins: [], disabledPlugins: [], failedPlugins: [], @@ -1235,9 +1252,9 @@ test('Non sandy plugins are exported properly if they are still queued', async ( const serial = storeExport.exportStoreData.device!.serial; expect(serial).not.toBeFalsy(); expect(storeExport.exportStoreData.pluginStates2).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#00000000-0000-0000-0000-000000000000-serial": Object { - "TestPlugin": "{\\"counter\\":3}", + { + "TestApp#Android#MockAndroidDevice#00000000-0000-0000-0000-000000000000-serial": { + "TestPlugin": "{"counter":3}", }, } `); @@ -1328,18 +1345,18 @@ test('Sandy plugins are imported properly', async () => { expect(client.sandyPluginStates.get(TestPlugin.id)!.exportStateSync()) .toMatchInlineSnapshot(` - Object { + { "counter": 0, - "otherState": Object { + "otherState": { "testCount": 0, }, } `); expect(client2.sandyPluginStates.get(TestPlugin.id)!.exportStateSync()) .toMatchInlineSnapshot(` - Object { + { "counter": 3, - "otherState": Object { + "otherState": { "testCount": -3, }, } @@ -1433,9 +1450,9 @@ test('Sandy device plugins are exported / imported properly', async () => { ]) )[sandyDeviceTestPlugin.id], ).toMatchInlineSnapshot(` - Object { + { "counter": 0, - "otherState": Object { + "otherState": { "testCount": 0, }, } @@ -1445,10 +1462,10 @@ test('Sandy device plugins are exported / imported properly', async () => { sandyDeviceTestPlugin.id, ]), ).toMatchInlineSnapshot(` - Object { - "TestPlugin": Object { + { + "TestPlugin": { "counter": 4, - "otherState": Object { + "otherState": { "testCount": -3, }, }, @@ -1666,7 +1683,7 @@ test('Sandy plugins with complex data are imported / exported correctly', async `); expect(api.s.get()).toMatchInlineSnapshot(` Set { - Object { + { "x": 2, }, } @@ -1735,7 +1752,7 @@ test('Sandy device plugins with complex data are imported / exported correctly' `); expect(api.s.get()).toMatchInlineSnapshot(` Set { - Object { + { "x": 2, }, } diff --git a/desktop/flipper-ui-core/src/utils/__tests__/info.node.tsx b/desktop/flipper-ui-core/src/utils/__tests__/info.node.tsx index 7f230ca71..8adffd860 100644 --- a/desktop/flipper-ui-core/src/utils/__tests__/info.node.tsx +++ b/desktop/flipper-ui-core/src/utils/__tests__/info.node.tsx @@ -79,7 +79,7 @@ describe('info', () => { ); const inspectorPluginSelectionInfo = getInfo(); expect(networkPluginSelectionInfo.selection).toMatchInlineSnapshot(` - Object { + { "app": "TestApp", "archived": false, "device": "MockAndroidDevice", @@ -94,7 +94,7 @@ describe('info', () => { } `); expect(inspectorPluginSelectionInfo.selection).toMatchInlineSnapshot(` - Object { + { "app": "TestApp", "archived": false, "device": "MockAndroidDevice", diff --git a/desktop/flipper-ui-core/src/utils/__tests__/messageQueueSandy.node.tsx b/desktop/flipper-ui-core/src/utils/__tests__/messageQueueSandy.node.tsx index 9691e5e1c..8ba958fba 100644 --- a/desktop/flipper-ui-core/src/utils/__tests__/messageQueueSandy.node.tsx +++ b/desktop/flipper-ui-core/src/utils/__tests__/messageQueueSandy.node.tsx @@ -118,13 +118,11 @@ test('queue - events are processed immediately if plugin is selected', async () sendMessage('noop', {}); client.flushMessageBuffer(); expect(getTestPluginState(client)).toMatchInlineSnapshot(` - Object { - "count": 5, - } - `); - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot( - `Object {}`, - ); + { + "count": 5, + } + `); + expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`{}`); }); test('queue - events are NOT processed immediately if plugin is NOT selected (but enabled)', async () => { @@ -139,42 +137,42 @@ test('queue - events are NOT processed immediately if plugin is NOT selected (bu expect(getTestPluginState(client).count).toBe(0); // the first message is already visible cause of the leading debounce expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - ], - } - `); + { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": [ + { + "api": "TestPlugin", + "method": "inc", + "params": {}, + }, + ], + } + `); client.flushMessageBuffer(); expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 3, - }, - }, - ], - } - `); + { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": [ + { + "api": "TestPlugin", + "method": "inc", + "params": {}, + }, + { + "api": "TestPlugin", + "method": "inc", + "params": { + "delta": 2, + }, + }, + { + "api": "TestPlugin", + "method": "inc", + "params": { + "delta": 3, + }, + }, + ], + } + `); // process the message const pluginKey = getPluginKey(client.id, device, TestPlugin.id); @@ -204,7 +202,7 @@ test('queue - events are NOT processed immediately if plugin is NOT selected (bu selectDeviceLogs(store); sendMessage('inc', {delta: 4}); client.flushMessageBuffer(); - expect(client.messageBuffer).toMatchInlineSnapshot(`Object {}`); + expect(client.messageBuffer).toMatchInlineSnapshot(`{}`); expect(store.getState().pluginMessageQueue).toEqual({}); // star again, plugin still not selected, message is queued @@ -243,8 +241,8 @@ test('queue - events ARE processed immediately if plugin is NOT selected / enabl }), ); expect(store.getState().connections.enabledPlugins).toMatchInlineSnapshot(` - Object { - "TestApp": Array [], + { + "TestApp": [], } `); @@ -258,9 +256,7 @@ test('queue - events ARE processed immediately if plugin is NOT selected / enabl // the first message is already visible cause of the leading debounce expect(pluginState().count).toBe(1); // message queue was never involved due to the bypass... - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot( - `Object {}`, - ); + expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`{}`); // flush will make the others visible client.flushMessageBuffer(); expect(pluginState().count).toBe(6); @@ -280,18 +276,18 @@ test('queue - events are queued for plugins that are favorite when app is not se sendMessage('inc', {delta: 2}); expect(getTestPluginState(client)).toEqual({count: 0}); expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - ], - } - `); + { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": [ + { + "api": "TestPlugin", + "method": "inc", + "params": { + "delta": 2, + }, + }, + ], + } + `); }); test('queue - events are queued for plugins that are favorite when app is selected on different device', async () => { @@ -313,21 +309,21 @@ test('queue - events are queued for plugins that are favorite when app is select client2.flushMessageBuffer(); expect(getTestPluginState(client)).toEqual({count: 0}); expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { + { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": [ + { "api": "TestPlugin", "method": "inc", - "params": Object { + "params": { "delta": 2, }, }, ], - "TestApp#Android#MockAndroidDevice#serial2#TestPlugin": Array [ - Object { + "TestApp#Android#MockAndroidDevice#serial2#TestPlugin": [ + { "api": "TestPlugin", "method": "inc", - "params": Object { + "params": { "delta": 3, }, }, @@ -527,43 +523,43 @@ test('client - incoming messages are buffered and flushed together', async () => expect(getTestPluginState(client).count).toBe(0); // the first message is already visible cause of the leading debounce expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - ], - } - `); + { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": [ + { + "api": "TestPlugin", + "method": "inc", + "params": {}, + }, + ], + } + `); expect(client.messageBuffer).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#StubPlugin": Object { - "messages": Array [ - Object { + { + "TestApp#Android#MockAndroidDevice#serial#StubPlugin": { + "messages": [ + { "api": "StubPlugin", "method": "log", - "params": Object { + "params": { "line": "suff", }, }, ], "plugin": "[SandyPluginInstance]", }, - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { - "messages": Array [ - Object { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": { + "messages": [ + { "api": "TestPlugin", "method": "inc", - "params": Object { + "params": { "delta": 2, }, }, - Object { + { "api": "TestPlugin", "method": "inc", - "params": Object { + "params": { "delta": 3, }, }, @@ -578,42 +574,42 @@ test('client - incoming messages are buffered and flushed together', async () => await sleep(500); expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#StubPlugin": Array [ - Object { - "api": "StubPlugin", - "method": "log", - "params": Object { - "line": "suff", - }, - }, - ], - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 3, - }, - }, - ], - } - `); - expect(client.messageBuffer).toMatchInlineSnapshot(`Object {}`); + { + "TestApp#Android#MockAndroidDevice#serial#StubPlugin": [ + { + "api": "StubPlugin", + "method": "log", + "params": { + "line": "suff", + }, + }, + ], + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": [ + { + "api": "TestPlugin", + "method": "inc", + "params": {}, + }, + { + "api": "TestPlugin", + "method": "inc", + "params": { + "delta": 2, + }, + }, + { + "api": "TestPlugin", + "method": "inc", + "params": { + "delta": 3, + }, + }, + ], + } + `); + expect(client.messageBuffer).toMatchInlineSnapshot(`{}`); expect(StubPlugin.persistedStateReducer.mock.calls).toMatchInlineSnapshot( - `Array []`, + `[]`, ); // tigger processing the queue @@ -625,43 +621,43 @@ test('client - incoming messages are buffered and flushed together', async () => ); expect(StubPlugin.persistedStateReducer.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - undefined, - "log", - Object { - "line": "suff", - }, - ], - ] - `); + [ + [ + undefined, + "log", + { + "line": "suff", + }, + ], + ] + `); expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#StubPlugin": Array [], - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object {}, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 2, - }, - }, - Object { - "api": "TestPlugin", - "method": "inc", - "params": Object { - "delta": 3, - }, - }, - ], - } - `); + { + "TestApp#Android#MockAndroidDevice#serial#StubPlugin": [], + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": [ + { + "api": "TestPlugin", + "method": "inc", + "params": {}, + }, + { + "api": "TestPlugin", + "method": "inc", + "params": { + "delta": 2, + }, + }, + { + "api": "TestPlugin", + "method": "inc", + "params": { + "delta": 3, + }, + }, + ], + } + `); }); test('queue - messages that have not yet flushed be lost when disabling the plugin', async () => { @@ -673,13 +669,13 @@ test('queue - messages that have not yet flushed be lost when disabling the plug sendMessage('inc', {delta: 2}); expect(client.messageBuffer).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { - "messages": Array [ - Object { + { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": { + "messages": [ + { "api": "TestPlugin", "method": "inc", - "params": Object { + "params": { "delta": 2, }, }, @@ -689,12 +685,12 @@ test('queue - messages that have not yet flushed be lost when disabling the plug } `); expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` - Object { - "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ - Object { + { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": [ + { "api": "TestPlugin", "method": "inc", - "params": Object {}, + "params": {}, }, ], } @@ -702,10 +698,8 @@ test('queue - messages that have not yet flushed be lost when disabling the plug // disable await switchTestPlugin(store, client); - expect(client.messageBuffer).toMatchInlineSnapshot(`Object {}`); - expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot( - `Object {}`, - ); + expect(client.messageBuffer).toMatchInlineSnapshot(`{}`); + expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`{}`); // re-enable, no messages arrive await switchTestPlugin(store, client); diff --git a/desktop/flipper-ui-core/src/utils/__tests__/safeFilename.node.tsx b/desktop/flipper-ui-core/src/utils/__tests__/safeFilename.node.tsx new file mode 100644 index 000000000..251636bfb --- /dev/null +++ b/desktop/flipper-ui-core/src/utils/__tests__/safeFilename.node.tsx @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {safeFilename} from '../safeFilename'; + +describe('safeFilename', () => { + test('replaces special chars in a string to make it a safe file name', async () => { + expect(safeFilename('/data/data/0/files/sonar/spec!al file name%')).toBe( + '-data-data-0-files-sonar-spec-al-file-name-', + ); + }); +}); diff --git a/desktop/flipper-ui-core/src/utils/__tests__/sideEffect.node.tsx b/desktop/flipper-ui-core/src/utils/__tests__/sideEffect.node.tsx index efdbd899e..fc1386e30 100644 --- a/desktop/flipper-ui-core/src/utils/__tests__/sideEffect.node.tsx +++ b/desktop/flipper-ui-core/src/utils/__tests__/sideEffect.node.tsx @@ -12,8 +12,6 @@ import {createStore, Store} from 'redux'; import produce from 'immer'; import {sleep} from 'flipper-plugin'; -jest.useFakeTimers(); - const initialState = { counter: {count: 0}, somethingUnrelated: false, @@ -33,10 +31,12 @@ function reducer(state: State, action: Action): State { }); } +jest.useFakeTimers(); + describe('sideeffect', () => { let store: Store; let events: string[]; - let unsubscribe: undefined | (() => void) = undefined; + let unsubscribe: undefined | (() => void) = () => {}; let warn: jest.Mock; let error: jest.Mock; const origWarning = console.warn; @@ -69,17 +69,14 @@ describe('sideeffect', () => { events.push(`counter: ${s.counter.count}`); }, ); - store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'}); expect(events.length).toBe(0); - // arrive as a single effect jest.advanceTimersByTime(10); expect(events).toEqual(['counter: 2']); - // no more events arrive after unsubscribe - unsubscribe(); + unsubscribe?.(); store.dispatch({type: 'inc'}); jest.advanceTimersByTime(10); expect(events).toEqual(['counter: 2']); @@ -96,17 +93,13 @@ describe('sideeffect', () => { events.push(`counter: ${count}`); }, ); - store.dispatch({type: 'unrelated'}); expect(events.length).toBe(0); - // unrelated event doesn't trigger jest.advanceTimersByTime(10); expect(events.length).toBe(0); - // counter increment does store.dispatch({type: 'inc'}); - jest.advanceTimersByTime(10); expect(events).toEqual(['counter: 1']); expect(warn).not.toBeCalled(); @@ -122,17 +115,13 @@ describe('sideeffect', () => { events.push(`counter: ${number}`); }, ); - store.dispatch({type: 'unrelated'}); expect(events.length).toBe(0); - // unrelated event doesn't trigger jest.advanceTimersByTime(10); expect(events.length).toBe(0); - // counter increment does store.dispatch({type: 'inc'}); - jest.advanceTimersByTime(10); expect(events).toEqual(['counter: 1']); expect(warn).not.toBeCalled(); @@ -148,15 +137,13 @@ describe('sideeffect', () => { throw new Error('oops'); }, ); - expect(() => { store.dispatch({type: 'inc'}); }).not.toThrow(); - jest.advanceTimersByTime(10); expect(error.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ + [ + [ "Error while running side effect 'test': Error: oops", [Error: oops], ], @@ -164,27 +151,6 @@ describe('sideeffect', () => { `); }); - test('warns about long running effects', async () => { - let done = false; - unsubscribe = sideEffect( - store, - {name: 'test', throttleMs: 10}, - (s) => s, - () => { - const end = Date.now() + 100; - while (Date.now() < end) { - // block - } - done = true; - }, - ); - - store.dispatch({type: 'inc'}); - jest.advanceTimersByTime(200); - expect(done).toBe(true); - expect(warn.mock.calls[0][0]).toContain("Side effect 'test' took"); - }); - test('throttles correctly', async () => { unsubscribe = sideEffect( store, @@ -194,50 +160,39 @@ describe('sideeffect', () => { events.push(`counter: ${number}`); }, ); - // Fires immediately store.dispatch({type: 'inc'}); jest.advanceTimersByTime(100); expect(events).toEqual(['counter: 1']); - // no new tick in the next 100 ms jest.advanceTimersByTime(300); store.dispatch({type: 'inc'}); - jest.advanceTimersByTime(300); store.dispatch({type: 'inc'}); - expect(events).toEqual(['counter: 1']); jest.advanceTimersByTime(1000); expect(events).toEqual(['counter: 1', 'counter: 3']); - // long time no effect, it will fire right away again // N.b. we need call sleep here to create a timeout, as time wouldn't progress otherwise const p = sleep(2000); jest.advanceTimersByTime(2000); await p; - // ..but firing an event that doesn't match the selector doesn't reset the timer store.dispatch({type: 'unrelated'}); expect(events).toEqual(['counter: 1', 'counter: 3']); - jest.advanceTimersByTime(100); - store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'}); jest.advanceTimersByTime(100); - const p2 = sleep(2000); jest.advanceTimersByTime(2000); await p2; - expect(events).toEqual(['counter: 1', 'counter: 3', 'counter: 5']); }); test('can fire immediately', async () => { store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'}); - unsubscribe = sideEffect( store, {name: 'test', throttleMs: 1, fireImmediately: true}, @@ -246,7 +201,6 @@ describe('sideeffect', () => { events.push(`counter: ${s.counter.count}`); }, ); - expect(events).toEqual(['counter: 2']); store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'}); diff --git a/desktop/flipper-ui-core/src/utils/exportData.tsx b/desktop/flipper-ui-core/src/utils/exportData.tsx index 20cf3aed7..f2de950a4 100644 --- a/desktop/flipper-ui-core/src/utils/exportData.tsx +++ b/desktop/flipper-ui-core/src/utils/exportData.tsx @@ -8,7 +8,13 @@ */ import * as React from 'react'; -import {getLogger} from 'flipper-common'; +import { + getLogger, + DeviceDebugFile, + DeviceDebugCommand, + timeout, + getStringFromErrorLike, +} from 'flipper-common'; import {Store, MiddlewareAPI} from '../reducers'; import {DeviceExport} from 'flipper-frontend-core'; import {selectedPlugins, State as PluginsState} from '../reducers/plugins'; @@ -20,19 +26,24 @@ import {DevicePluginMap, ClientPluginMap} from '../plugin'; import {BaseDevice} from 'flipper-frontend-core'; import {ArchivedDevice} from 'flipper-frontend-core'; import {v4 as uuidv4} from 'uuid'; -import {tryCatchReportPlatformFailures} from 'flipper-common'; import {TestIdler} from './Idler'; import {processMessageQueue} from './messageQueue'; import {getPluginTitle} from './pluginUtils'; import {capture} from './screenshot'; -import {Dialog, getFlipperLib, Idler, path} from 'flipper-plugin'; +import {Dialog, getFlipperLib, Idler} from 'flipper-plugin'; import {ClientQuery} from 'flipper-common'; import ShareSheetExportUrl from '../chrome/ShareSheetExportUrl'; import ShareSheetExportFile from '../chrome/ShareSheetExportFile'; import ExportDataPluginSheet from '../chrome/ExportDataPluginSheet'; import {getRenderHostInstance} from 'flipper-frontend-core'; import {uploadFlipperMedia} from '../fb-stubs/user'; -import {logsAtom} from '../chrome/ConsoleLogs'; +import {exportLogs} from '../chrome/ConsoleLogs'; +import JSZip from 'jszip'; +import {safeFilename} from './safeFilename'; +import {getExportablePlugins} from '../selectors/connections'; +import {notification} from 'antd'; +import openSupportRequestForm from '../fb-stubs/openSupportRequestForm'; +import {getStore} from '../store'; export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace'; export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace'; @@ -516,7 +527,7 @@ export const exportStoreToFile = ( export async function importDataToStore( source: string, data: string, - store: Store, + store: Store = getStore(), ) { getLogger().track('usage', IMPORT_FLIPPER_TRACE_EVENT); const json: ExportType = JSON.parse(data); @@ -583,56 +594,174 @@ export const importFileToStore = async (file: string, store: Store) => { } }; -export function canOpenDialog() { - return !!getRenderHostInstance().showOpenDialog; +export async function startFileImport(store: Store) { + const file = await getRenderHostInstance().importFile({ + extensions: ['flipper', 'json', 'txt'], + }); + if (!file || typeof file.data !== 'string') { + return; + } + importDataToStore(file.name, file.data as string, store); } -export function showOpenDialog(store: Store) { - return getRenderHostInstance() - .showOpenDialog?.({ - filter: {extensions: ['flipper', 'json', 'txt'], name: 'Flipper files'}, - }) - .then((filePath) => { - if (filePath) { - tryCatchReportPlatformFailures(() => { - importFileToStore(filePath, store); - }, `${IMPORT_FLIPPER_TRACE_EVENT}:UI`); - } - }); +async function startDeviceFlipperFolderExport() { + return await getRenderHostInstance().flipperServer.exec( + {timeout: 3 * 60 * 1000}, + 'fetch-debug-data', + ); } -export function canFileExport() { - return !!getRenderHostInstance().showSaveDialog; -} +export type ExportEverythingEverywhereAllAtOnceStatus = + | ['logs'] + | ['files'] + | ['state'] + | ['archive'] + | ['upload'] + | ['support'] + | ['done'] + | ['error', string] + | ['cancelled']; +export async function exportEverythingEverywhereAllAtOnce( + store: MiddlewareAPI, + onStatusUpdate?: (...args: ExportEverythingEverywhereAllAtOnceStatus) => void, + openSupportRequest?: boolean, +) { + const zip = new JSZip(); -export async function startLogsExport() { - const serializedLogs = logsAtom - .get() + // Step 1: Export Flipper logs + onStatusUpdate?.('logs'); + const serializedLogs = exportLogs .map((item) => JSON.stringify(item)) .join('\n'); - await getRenderHostInstance().exportFile?.(serializedLogs); + + zip.file('flipper_logs.txt', serializedLogs); + + try { + // Step 2: Export device logs + onStatusUpdate?.('files'); + const flipperFolderContent = await startDeviceFlipperFolderExport(); + + const deviceFlipperFolder = zip.folder('device_flipper_folder')!; + flipperFolderContent.forEach((deviceDebugItem) => { + const deviceAppFolder = deviceFlipperFolder.folder( + safeFilename(`${deviceDebugItem.serial}__${deviceDebugItem.appId}`), + )!; + + deviceDebugItem.data.forEach((appDebugItem) => { + const appDebugItemIsFile = ( + item: DeviceDebugFile | DeviceDebugCommand, + ): item is DeviceDebugFile => !!(appDebugItem as DeviceDebugFile).path; + + if (appDebugItemIsFile(appDebugItem)) { + deviceAppFolder.file( + safeFilename(appDebugItem.path), + appDebugItem.data, + ); + } else { + deviceAppFolder.file( + safeFilename(appDebugItem.command), + appDebugItem.result, + ); + } + }); + }); + } catch (e) { + console.error( + 'exportEverythingEverywhereAllAtOnce -> failed to export Flipper device debug data', + e, + ); + } + + try { + // Step 3: Export Flipper State + onStatusUpdate?.('state'); + const exportablePlugins = getExportablePlugins(store.getState()); + // TODO: no need to put this in the store, + // need to be cleaned up later in combination with SupportForm + store.dispatch(selectedPlugins(exportablePlugins.map(({id}) => id))); + const {serializedString} = await timeout(2 * 60 * 1000, exportStore(store)); + + zip.file('flipper_export', serializedString); + } catch (e) { + console.error( + 'exportEverythingEverywhereAllAtOnce -> failed to export Flipper state', + e, + ); + } + + onStatusUpdate?.('archive'); + const archiveData = await zip.generateAsync({type: 'uint8array'}); + + const exportedFilePath = await getRenderHostInstance().exportFileBinary?.( + archiveData, + { + defaultPath: 'flipper_EEAaO_export.zip', + }, + ); + + if (openSupportRequest) { + if (exportedFilePath) { + onStatusUpdate?.('upload'); + + let everythingEverywhereAllAtOnceExportDownloadURL: string | undefined; + try { + everythingEverywhereAllAtOnceExportDownloadURL = + await getRenderHostInstance().flipperServer.exec( + 'intern-cloud-upload', + exportedFilePath, + ); + } catch (e) { + console.error( + 'exportEverythingEverywhereAllAtOnce -> failed to upload export to intern', + exportedFilePath, + ); + notification.warn({ + message: 'Failed to upload debug data', + description: `Flipper failed to upload debug export (${exportedFilePath}) automatically. Please, attach it to the support request manually in the comments after it is created.`, + duration: null, + }); + } + + onStatusUpdate?.('support'); + try { + await openSupportRequestForm(store.getState(), { + everythingEverywhereAllAtOnceExportDownloadURL, + }); + onStatusUpdate?.('done'); + } catch (e) { + console.error( + 'exportEverythingEverywhereAllAtOnce -> failed to create a support request', + e, + ); + onStatusUpdate?.('error', getStringFromErrorLike(e)); + } + } else { + notification.warn({ + message: 'Export cancelled', + description: `Exporting Flipper debug data was cancelled. Flipper team will not be able to help you without this data. Please, restart the export.`, + duration: null, + }); + onStatusUpdate?.('cancelled'); + } + } else { + if (exportedFilePath) { + onStatusUpdate?.('done'); + } else { + onStatusUpdate?.('cancelled'); + } + } } export async function startFileExport(dispatch: Store['dispatch']) { - const file = await getRenderHostInstance().showSaveDialog?.({ - title: 'FlipperExport', - defaultPath: path.join( - getRenderHostInstance().serverConfig.paths.homePath, - 'FlipperExport.flipper', - ), - }); - if (!file) { - return; - } const plugins = await selectPlugins(); if (plugins === false) { - return; // cancelled + return; } // TODO: no need to put this in the store, - // need to be cleaned up later in combination with SupportForm + // need to be cleaned up later in combination with SupportForm. dispatch(selectedPlugins(plugins)); Dialog.showModal((onHide) => ( - + )); } diff --git a/desktop/flipper-ui-core/src/utils/flipperLibImplementation/index.tsx b/desktop/flipper-ui-core/src/utils/flipperLibImplementation/index.tsx index f74f3fc92..d10cc5307 100644 --- a/desktop/flipper-ui-core/src/utils/flipperLibImplementation/index.tsx +++ b/desktop/flipper-ui-core/src/utils/flipperLibImplementation/index.tsx @@ -17,14 +17,28 @@ import {addNotification} from '../../reducers/notifications'; import {deconstructPluginKey} from 'flipper-common'; import {RenderHost} from 'flipper-frontend-core'; import {setMenuEntries} from '../../reducers/connections'; +import { + currentUser, + internGraphGETAPIRequestRaw, + internGraphPOSTAPIRequestRaw, + isConnected, +} from '../../fb-stubs/user'; export function initializeFlipperLibImplementation( renderHost: RenderHost, store: Store, logger: Logger, ) { + const base = baseFlipperLibImplementation(renderHost, logger); _setFlipperLibImplementation({ - ...baseFlipperLibImplementation(renderHost, logger), + ...base, + intern: { + ...base.intern, + graphGet: internGraphGETAPIRequestRaw, + graphPost: internGraphPOSTAPIRequestRaw, + currentUser, + isConnected, + }, enableMenuEntries(entries) { store.dispatch(setMenuEntries(entries)); }, @@ -52,5 +66,16 @@ export function initializeFlipperLibImplementation( ); }, DetailsSidebarImplementation: DetailSidebarImpl, + settings() { + const darkModeState = store.getState().settingsState.darkMode; + let isDarkMode = darkModeState === 'dark'; + if ( + darkModeState === 'system' && + window.matchMedia('(prefers-color-scheme:dark)').matches + ) { + isDarkMode = true; + } + return {isDarkMode}; + }, }); } diff --git a/desktop/flipper-ui-core/src/utils/globalErrorHandling.tsx b/desktop/flipper-ui-core/src/utils/globalErrorHandling.tsx index 140cdc22b..4d5eb6f3b 100644 --- a/desktop/flipper-ui-core/src/utils/globalErrorHandling.tsx +++ b/desktop/flipper-ui-core/src/utils/globalErrorHandling.tsx @@ -9,11 +9,23 @@ export const startGlobalErrorHandling = () => { if (typeof window !== 'undefined') { - window.addEventListener('error', (event) => { - console.error('"error" event intercepted:', event.error); - }); - window.addEventListener('unhandledrejection', (event) => { + window.onerror = ( + event: Event | string, + source?: string, + lineno?: number, + colno?: number, + error?: Error, + ) => { + console.error('"onerror" event intercepted:', event, { + source, + lineno, + colno, + error, + stack: (error as any)?.stack, + }); + }; + window.onunhandledrejection = (event) => { console.error('"unhandledrejection" event intercepted:', event.reason); - }); + }; } }; diff --git a/desktop/flipper-ui-core/src/utils/icons.tsx b/desktop/flipper-ui-core/src/utils/icons.tsx index 2478ba405..b79333e6e 100644 --- a/desktop/flipper-ui-core/src/utils/icons.tsx +++ b/desktop/flipper-ui-core/src/utils/icons.tsx @@ -10,7 +10,7 @@ import {getRenderHostInstance} from 'flipper-frontend-core'; import {IconSize} from '../ui/components/Glyph'; -const AVAILABLE_SIZES: IconSize[] = [8, 10, 12, 16, 18, 20, 24, 32]; +const AVAILABLE_SIZES: IconSize[] = [8, 10, 12, 16, 18, 20, 24, 28, 32]; const DENSITIES = [1, 1.5, 2, 3, 4]; export type Icon = { @@ -59,7 +59,7 @@ function normalizeIcon(icon: Icon): Icon { } export function getPublicIconUrl({name, variant, size, density}: Icon) { - return `https://facebook.com/assets/?name=${name}&variant=${variant}&size=${size}&set=facebook_icons&density=${density}x`; + return `https://facebook.com/images/assets_DO_NOT_HARDCODE/facebook_icons/${name}_${variant}_${size}.png`; } export function getIconURL(icon: Icon) { diff --git a/desktop/flipper-ui-core/src/utils/isPluginVersionMoreRecent.tsx b/desktop/flipper-ui-core/src/utils/isPluginVersionMoreRecent.tsx index 2a710f5eb..b0cc69776 100644 --- a/desktop/flipper-ui-core/src/utils/isPluginVersionMoreRecent.tsx +++ b/desktop/flipper-ui-core/src/utils/isPluginVersionMoreRecent.tsx @@ -26,13 +26,6 @@ export function isPluginVersionMoreRecent( if (semver.gt(versionDetails.version, otherVersionDetails.version)) { return true; } - if ( - semver.eq(versionDetails.version, otherVersionDetails.version) && - versionDetails.isBundled - ) { - // prefer bundled versions - return true; - } if ( semver.eq(versionDetails.version, otherVersionDetails.version) && versionDetails.isActivatable && diff --git a/desktop/flipper-ui-core/src/utils/messageQueue.tsx b/desktop/flipper-ui-core/src/utils/messageQueue.tsx index 0dcb03aa2..8c6470a06 100644 --- a/desktop/flipper-ui-core/src/utils/messageQueue.tsx +++ b/desktop/flipper-ui-core/src/utils/messageQueue.tsx @@ -29,7 +29,11 @@ function processMessagesImmediately( const reducerStartTime = Date.now(); try { plugin.receiveMessages(messages); - addBackgroundStat(plugin.definition.id, Date.now() - reducerStartTime); + addBackgroundStat( + plugin.definition.id, + messages, + Date.now() - reducerStartTime, + ); } catch (e) { console.error( `Failed to process event for plugin ${plugin.definition.id}`, diff --git a/desktop/flipper-ui-core/src/utils/notifications.tsx b/desktop/flipper-ui-core/src/utils/notifications.tsx index 1109dca20..8044809f6 100644 --- a/desktop/flipper-ui-core/src/utils/notifications.tsx +++ b/desktop/flipper-ui-core/src/utils/notifications.tsx @@ -14,6 +14,7 @@ import {setStaticView} from '../reducers/connections'; import {getStore} from '../store'; import {Layout} from '../ui'; import {v4 as uuid} from 'uuid'; +import {NotificationBody} from '../ui/components/NotificationBody'; const {Link} = Typography; @@ -22,9 +23,10 @@ export function showErrorNotification(message: string, description?: string) { notification.error({ key, message, - description: ( + description: description ? ( + + ) : ( - {description ??

{description}

}

See{' '} MAX_BACKGROUND_TASK_TIME) { console.warn( `Plugin ${plugin} took too much time while doing background: ${cpuTime}ms. Handling background messages should take less than ${MAX_BACKGROUND_TASK_TIME}ms.`, + messages, ); } } diff --git a/desktop/flipper-ui-core/src/utils/pluginUtils.tsx b/desktop/flipper-ui-core/src/utils/pluginUtils.tsx index 56117e160..3f53ff33e 100644 --- a/desktop/flipper-ui-core/src/utils/pluginUtils.tsx +++ b/desktop/flipper-ui-core/src/utils/pluginUtils.tsx @@ -17,7 +17,6 @@ import { import type Client from '../Client'; import type { ActivatablePluginDetails, - BundledPluginDetails, DownloadablePluginDetails, PluginDetails, } from 'flipper-common'; @@ -30,7 +29,7 @@ export type PluginLists = { enabledPlugins: PluginDefinition[]; disabledPlugins: PluginDefinition[]; unavailablePlugins: [plugin: PluginDetails, reason: string][]; - downloadablePlugins: (DownloadablePluginDetails | BundledPluginDetails)[]; + downloadablePlugins: DownloadablePluginDetails[]; }; export type ActivePluginListItem = @@ -46,7 +45,7 @@ export type ActivePluginListItem = } | { status: 'uninstalled'; - details: DownloadablePluginDetails | BundledPluginDetails; + details: DownloadablePluginDetails; } | { status: 'unavailable'; @@ -162,7 +161,6 @@ export function computePluginLists( >, plugins: Pick< State['plugins'], - | 'bundledPlugins' | 'marketplacePlugins' | 'loadedPlugins' | 'devicePlugins' @@ -180,12 +178,12 @@ export function computePluginLists( enabledPlugins: PluginDefinition[]; disabledPlugins: PluginDefinition[]; unavailablePlugins: [plugin: PluginDetails, reason: string][]; - downloadablePlugins: (DownloadablePluginDetails | BundledPluginDetails)[]; + downloadablePlugins: DownloadablePluginDetails[]; } { const enabledDevicePluginsState = connections.enabledDevicePlugins; const enabledPluginsState = connections.enabledPlugins; const uninstalledMarketplacePlugins = getLatestCompatibleVersionOfEachPlugin( - [...plugins.bundledPlugins.values(), ...plugins.marketplacePlugins], + [...plugins.marketplacePlugins], getAppVersion(), ).filter((p) => !plugins.loadedPlugins.has(p.id)); const devicePlugins: PluginDefinition[] = [...plugins.devicePlugins.values()] @@ -205,10 +203,7 @@ export function computePluginLists( ) .filter((p) => !enabledDevicePluginsState.has(p.id)); const unavailablePlugins: [plugin: PluginDetails, reason: string][] = []; - const downloadablePlugins: ( - | DownloadablePluginDetails - | BundledPluginDetails - )[] = []; + const downloadablePlugins: DownloadablePluginDetails[] = []; if (device) { // find all device plugins that aren't part of the current device / metro @@ -431,7 +426,6 @@ export type PluginStatus = | 'unknown' | 'failed' | 'gatekeeped' - | 'bundle_installable' | 'marketplace_installable'; export function getPluginStatus( @@ -452,9 +446,6 @@ export function getPluginStatus( if (failedPluginEntry) { return ['failed', failedPluginEntry[1]]; } - if (state.bundledPlugins.has(id)) { - return ['bundle_installable']; - } if (state.marketplacePlugins.find((d) => d.id === id)) { return ['marketplace_installable']; } diff --git a/desktop/flipper-ui-core/src/utils/runHealthchecks.tsx b/desktop/flipper-ui-core/src/utils/runHealthchecks.tsx index 81c2f4639..7385d7331 100644 --- a/desktop/flipper-ui-core/src/utils/runHealthchecks.tsx +++ b/desktop/flipper-ui-core/src/utils/runHealthchecks.tsx @@ -58,7 +58,7 @@ async function launchHealthchecks(options: HealthcheckOptions): Promise { h.key, ) .catch((e) => { - console.error('Failed to run doctor check', e); + console.warn('Failed to run doctor check', e); return { status: 'FAILED', isAcknowledged: false, diff --git a/desktop/flipper-ui-core/src/utils/safeFilename.tsx b/desktop/flipper-ui-core/src/utils/safeFilename.tsx new file mode 100644 index 000000000..287939820 --- /dev/null +++ b/desktop/flipper-ui-core/src/utils/safeFilename.tsx @@ -0,0 +1,11 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +export const safeFilename = (rawFilename: string) => + rawFilename.replace(/(\W+)/gi, '-'); diff --git a/desktop/flipper-ui-core/src/utils/testUtils.tsx b/desktop/flipper-ui-core/src/utils/testUtils.tsx index ae41b88ec..6dfc57add 100644 --- a/desktop/flipper-ui-core/src/utils/testUtils.tsx +++ b/desktop/flipper-ui-core/src/utils/testUtils.tsx @@ -56,7 +56,6 @@ export function createMockDownloadablePluginDetails( version: version, downloadUrl: `http://localhost/${lowercasedID}/${version}`, lastUpdated: lastUpdated, - isBundled: false, isActivatable: false, isEnabledByDefault: false, }; @@ -69,7 +68,6 @@ export function createMockActivatablePluginDetails( return { id: 'Hello', specVersion: 2, - isBundled: false, isActivatable: true, dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-sample1', entry: './test/index.js', diff --git a/desktop/jest.config.js b/desktop/jest.config.js index 926b4370f..619f6e052 100644 --- a/desktop/jest.config.js +++ b/desktop/jest.config.js @@ -20,6 +20,7 @@ module.exports = { '^flipper-(server-core|ui-core|frontend-core|common)$': '/flipper-$1/src', '^flipper-(pkg|pkg-lib|doctor|test-utils)$': '/$1/src', + '^.+\\.(css|scss)$': '/scripts/jest-css-stub.js', }, clearMocks: true, coverageReporters: [ @@ -29,6 +30,6 @@ module.exports = { ...(process.env.COVERAGE_TEXT === 'detailed' ? ['text'] : []), ], testMatch: ['**/**.(node|spec).(js|jsx|ts|tsx)'], - testEnvironment: 'jest-environment-jsdom-sixteen', + testEnvironment: 'jsdom', resolver: '/jest.resolver.js', }; diff --git a/desktop/package.json b/desktop/package.json index 5366adbd8..37b292ded 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -67,12 +67,12 @@ "category": "facebook-intern", "description": "Mobile development tool", "devDependencies": { - "@babel/eslint-parser": "^7.17.0", + "@babel/eslint-parser": "^7.22.15", "@jest-runner/electron": "^3.0.1", "@testing-library/react": "^12.1.4", - "@types/jest": "^26.0.24", + "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^5.22.0", - "@typescript-eslint/parser": "^5.22.0", + "@typescript-eslint/parser": "^5.55.0", "babel-eslint": "^10.1.0", "cross-env": "^7.0.3", "electron": "18.2.0", @@ -89,21 +89,20 @@ "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.5.0", "eslint-plugin-rulesdir": "^0.2.1", - "jest": "^26.6.3", - "jest-environment-jsdom-sixteen": "^2.0.0", + "jest": "^29.5.0", "jest-fetch-mock": "^3.0.3", "less": "^4.1.2", "patch-package": "^6.4.7", - "prettier": "^2.6.2", + "prettier": "^2.8.7", "pretty-format": "^27.5.0", "rimraf": "^3.0.2", - "ts-jest": "^26.5.6", - "ts-node": "^9.1.1", - "typescript": "^4.6.4" + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^4.9.5" }, "homepage": "https://fbflipper.com/", "icon": "icon.png", @@ -116,6 +115,7 @@ "productName": "Flipper", "resolutions": { "@jest-runner/electron/electron": "18.2.0", + "jest-environment-jsdom": "29.5.0", "adbkit-logcat": "^2.0.1", "minimist": "1.2.6", "node-forge": "^1.0.6" @@ -130,16 +130,17 @@ "build:tsc": "tsc -b tsc-root/tsconfig.json && ./ts-node ./scripts/compute-package-checksum.tsx -d ./babel-transformer -o ./lib/checksum.txt", "bump-versions": "./ts-node scripts/bump-versions.tsx", "bundle-all-plugins": "./ts-node scripts/bundle-all-plugins.tsx", - "dev-server": "cross-env NODE_ENV=development ./ts-node scripts/start-dev-server.tsx", + "start-electron": "cross-env NODE_ENV=development ./ts-node scripts/start-dev-server.tsx", "docs": "cd ../website && yarn start", "fix": "eslint . --fix --ext .js,.ts,.tsx", "flipper-server": "cross-env NODE_ENV=development ./ts-node scripts/start-flipper-server-dev.tsx", - "lint": "yarn lint:eslint && yarn lint:tsc && yarn tsc-plugins", + "lint": "yarn lint:eslint && yarn lint:tsc && yarn tsc-plugins && yarn run lint:types-deps", "lint:eslint": "eslint . --ext .js,.ts,.tsx", "lint:tsc": "tsc && tsc -p tsc-root/tsconfig.json --noemit", + "lint:types-deps": "./ts-node ./scripts/verify-types-dependencies.tsx", "list-plugins": "./ts-node scripts/list-plugins.tsx", "open-dist": "open ../dist/mac/Flipper.app --args --launcher=false --inspect=9229", - "postinstall": "patch-package && yarn --cwd plugins install --mutex network:30331 && yarn tsc -b pkg-lib/tsconfig.json && ./ts-node scripts/generate-plugin-entry-points.tsx && yarn build:tsc && yarn build:themes", + "postinstall": "patch-package && yarn --cwd plugins install --mutex network:30331 && yarn tsc -b pkg-lib/tsconfig.json && ./ts-node scripts/remove-plugin-entry-points.tsx && yarn build:tsc && yarn build:themes", "prebuild": "yarn build:tsc && yarn rm-dist && yarn build:themes", "predev-server": "yarn build:tsc", "preflipper-server": "yarn build:tsc", @@ -156,8 +157,8 @@ "rm-modules": "rimraf **/*/node_modules node_modules", "rm-temp": "rimraf $TMPDIR/jest* $TMPDIR/react-native-packager*", "rm-watches": "watchman watch-del-all", - "start": "yarn dev-server --inspect=9229", - "start:break": "yarn dev-server --inspect-brk=9229", + "start": "yarn flipper-server --inspect=9229", + "start:break": "yarn flipper-server --inspect-brk=9229", "start:no-bundled-plugins": "yarn start --no-bundled-plugins", "test": "cross-env TZ=Pacific/Pohnpei jest", "test:debug": "yarn build:tsc && cross-env TZ=Pacific/Pohnpei node --inspect node_modules/.bin/jest --runInBand", @@ -165,11 +166,11 @@ "watch": "cross-env TZ=Pacific/Pohnpei node --expose-gc --stack-trace-limit=40 ./node_modules/.bin/jest --watch" }, "engines": { - "node": ">=16", + "node": ">=18", "npm": "use yarn instead", "yarn": "^1.16" }, - "version": "0.162.0", + "version": "0.227.0", "workspaces": { "packages": [ "scripts", @@ -182,6 +183,8 @@ "flipper-common", "flipper-frontend-core", "flipper-plugin", + "flipper-plugin-core", + "flipper-server-client", "flipper-server-companion", "flipper-server-core", "flipper-ui-core", @@ -198,6 +201,6 @@ ] }, "dependencies": { - "js-flipper": "^0.146.1" + "js-flipper": "^0.182.0" } } diff --git a/desktop/patches/@emotion+cache+11.5.0.patch b/desktop/patches/@emotion+cache+11.7.1.patch similarity index 91% rename from desktop/patches/@emotion+cache+11.5.0.patch rename to desktop/patches/@emotion+cache+11.7.1.patch index c77129dd9..1d28e46b2 100644 --- a/desktop/patches/@emotion+cache+11.5.0.patch +++ b/desktop/patches/@emotion+cache+11.7.1.patch @@ -1,8 +1,8 @@ diff --git a/node_modules/@emotion/cache/dist/emotion-cache.cjs.dev.js b/node_modules/@emotion/cache/dist/emotion-cache.cjs.dev.js -index 9a906b4..57f9d9d 100644 +index af9ac5a..195c3ff 100644 --- a/node_modules/@emotion/cache/dist/emotion-cache.cjs.dev.js +++ b/node_modules/@emotion/cache/dist/emotion-cache.cjs.dev.js -@@ -158,9 +158,10 @@ var createUnsafeSelectorsAlarm = function createUnsafeSelectorsAlarm(cache) { +@@ -159,9 +159,10 @@ var createUnsafeSelectorsAlarm = function createUnsafeSelectorsAlarm(cache) { return; } @@ -17,10 +17,10 @@ index 9a906b4..57f9d9d 100644 }; }; diff --git a/node_modules/@emotion/cache/dist/emotion-cache.esm.js b/node_modules/@emotion/cache/dist/emotion-cache.esm.js -index 5e5fa89..781c69a 100644 +index fd65cd3..40899c2 100644 --- a/node_modules/@emotion/cache/dist/emotion-cache.esm.js +++ b/node_modules/@emotion/cache/dist/emotion-cache.esm.js -@@ -149,9 +149,10 @@ var createUnsafeSelectorsAlarm = function createUnsafeSelectorsAlarm(cache) { +@@ -150,9 +150,10 @@ var createUnsafeSelectorsAlarm = function createUnsafeSelectorsAlarm(cache) { return; } diff --git a/desktop/patches/adbkit+2.11.1.patch b/desktop/patches/adbkit+2.11.1.patch deleted file mode 100644 index 6ed26002c..000000000 --- a/desktop/patches/adbkit+2.11.1.patch +++ /dev/null @@ -1,47 +0,0 @@ -diff --git a/node_modules/adbkit/lib/adb/parser.js b/node_modules/adbkit/lib/adb/parser.js -index 1b66bda..c41037d 100644 ---- a/node_modules/adbkit/lib/adb/parser.js -+++ b/node_modules/adbkit/lib/adb/parser.js -@@ -52,7 +52,7 @@ Parser = (function() { - - Parser.prototype.readAll = function() { - var all, endListener, errorListener, resolver, tryRead; -- all = new Buffer(0); -+ all = Buffer.alloc(0); - resolver = Promise.defer(); - tryRead = (function(_this) { - return function() { -@@ -108,7 +108,7 @@ Parser = (function() { - return resolver.reject(new Parser.PrematureEOFError(howMany)); - } - } else { -- return resolver.resolve(new Buffer(0)); -+ return resolver.resolve(Buffer.alloc(0)); - } - }; - })(this); -@@ -196,7 +196,7 @@ Parser = (function() { - - Parser.prototype.readUntil = function(code) { - var read, skipped; -- skipped = new Buffer(0); -+ skipped = Buffer.alloc(0); - read = (function(_this) { - return function() { - return _this.readBytes(1).then(function(chunk) { -diff --git a/node_modules/adbkit/lib/adb/protocol.js b/node_modules/adbkit/lib/adb/protocol.js -index d1377d0..2edd7ba 100644 ---- a/node_modules/adbkit/lib/adb/protocol.js -+++ b/node_modules/adbkit/lib/adb/protocol.js -@@ -33,9 +33,9 @@ Protocol = (function() { - - Protocol.encodeData = function(data) { - if (!Buffer.isBuffer(data)) { -- data = new Buffer(data); -+ data = Buffer.from(data); - } -- return Buffer.concat([new Buffer(Protocol.encodeLength(data.length)), data]); -+ return Buffer.concat([Buffer.from(Protocol.encodeLength(data.length)), data]); - }; - - return Protocol; diff --git a/desktop/patches/adbkit-logcat+2.0.1.patch b/desktop/patches/adbkit-logcat+2.0.1.patch deleted file mode 100644 index 96f5a8756..000000000 --- a/desktop/patches/adbkit-logcat+2.0.1.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/adbkit-logcat/lib/logcat/parser/binary.js b/node_modules/adbkit-logcat/lib/logcat/parser/binary.js -index 12fa8ec..e122933 100644 ---- a/node_modules/adbkit-logcat/lib/logcat/parser/binary.js -+++ b/node_modules/adbkit-logcat/lib/logcat/parser/binary.js -@@ -10,7 +10,7 @@ const HEADER_SIZE_MAX = 100 - class Binary extends EventEmitter { - constructor(options) { - super(options) -- this.buffer = new Buffer(0) -+ this.buffer = Buffer.alloc(0) - } - - parse(chunk) { diff --git a/desktop/patches/antd+4.19.2.patch b/desktop/patches/antd+4.24.13.patch similarity index 56% rename from desktop/patches/antd+4.19.2.patch rename to desktop/patches/antd+4.24.13.patch index 6c6d293e8..4bdebb262 100644 --- a/desktop/patches/antd+4.19.2.patch +++ b/desktop/patches/antd+4.24.13.patch @@ -1,20 +1,19 @@ diff --git a/node_modules/antd/es/button/button.js b/node_modules/antd/es/button/button.js -index a1d8a28..f35bec9 100644 +index 635e056..611d5b4 100644 --- a/node_modules/antd/es/button/button.js +++ b/node_modules/antd/es/button/button.js -@@ -197,6 +197,8 @@ var InternalButton = function InternalButton(props, ref) { +@@ -165,6 +165,7 @@ var InternalButton = function InternalButton(props, ref) { + }; }, [loadingOrDelay]); React.useEffect(fixTwoCNChar, [buttonRef]); - + var scope = React.useContext(global.FlipperTrackingScopeContext); -+ var handleClick = function handleClick(e) { - var onClick = props.onClick, - disabled = props.disabled; // https://github.com/ant-design/ant-design/issues/30207 -@@ -206,7 +208,13 @@ var InternalButton = function InternalButton(props, ref) { + var onClick = props.onClick; + // https://github.com/ant-design/ant-design/issues/30207 +@@ -172,7 +173,13 @@ var InternalButton = function InternalButton(props, ref) { + e.preventDefault(); return; } - - onClick === null || onClick === void 0 ? void 0 : onClick(e); + if (onClick !== null && onClick !== void 0) { + global.flipperTrackInteraction( @@ -24,16 +23,16 @@ index a1d8a28..f35bec9 100644 + ); + } }; - - devWarning(!(typeof icon === 'string' && icon.length > 2), 'Button', "`icon` is using ReactNode instead of string naming in v4. Please check `".concat(icon, "` at https://ant.design/components/icon")); + process.env.NODE_ENV !== "production" ? warning(!(typeof icon === 'string' && icon.length > 2), 'Button', "`icon` is using ReactNode instead of string naming in v4. Please check `".concat(icon, "` at https://ant.design/components/icon")) : void 0; + process.env.NODE_ENV !== "production" ? warning(!(ghost && isUnBorderedButtonType(type)), 'Button', "`link` or `text` button can't be a `ghost` button.") : void 0; diff --git a/node_modules/antd/es/typography/Link.js b/node_modules/antd/es/typography/Link.js -index 5079ee6..43bf942 100644 +index 9156f1d..3978c6a 100644 --- a/node_modules/antd/es/typography/Link.js +++ b/node_modules/antd/es/typography/Link.js -@@ -34,12 +34,23 @@ var Link = function Link(_a, ref) { - }); // https://github.com/ant-design/ant-design/issues/26622 - // @ts-ignore - +@@ -19,12 +19,24 @@ var Link = /*#__PURE__*/React.forwardRef(function (_a, ref) { + var mergedProps = _extends(_extends({}, restProps), { + rel: rel === undefined && restProps.target === '_blank' ? 'noopener noreferrer' : rel + }); + var onClick = React.useCallback((e) => { + if (mergedProps.onClick) { + return mergedProps.onClick(e); @@ -44,14 +43,15 @@ index 5079ee6..43bf942 100644 + e.stopPropagation(); + }; + }, [mergedProps.href, mergedProps.onClick]) - ++ + // @ts-expect-error: https://github.com/ant-design/ant-design/issues/26622 delete mergedProps.navigate; return /*#__PURE__*/React.createElement(Base, _extends({}, mergedProps, { - ref: baseRef, + ref: ref, ellipsis: !!ellipsis, - component: "a" + component: "a", + onClick: onClick, })); - }; - + }); + export default Link; diff --git a/desktop/patches/metro+0.69.0.patch b/desktop/patches/metro+0.70.2.patch similarity index 100% rename from desktop/patches/metro+0.69.0.patch rename to desktop/patches/metro+0.70.2.patch diff --git a/desktop/patches/rc-collapse+3.1.0.patch b/desktop/patches/rc-collapse+3.1.0.patch deleted file mode 100644 index 4db47eb57..000000000 --- a/desktop/patches/rc-collapse+3.1.0.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/node_modules/rc-collapse/es/Panel.js b/node_modules/rc-collapse/es/Panel.js -index 7be6854..b8fd6b3 100644 ---- a/node_modules/rc-collapse/es/Panel.js -+++ b/node_modules/rc-collapse/es/Panel.js -@@ -86,7 +86,9 @@ var CollapsePanel = /*#__PURE__*/function (_React$Component) { - className: itemCls, - style: style, - id: id -- }, React.createElement("div", { -+ }, -+ React.createElement(global.FlipperTracked, { action: 'collapse:' + header }, -+ React.createElement("div", { - className: headerCls, - onClick: function onClick() { - return collapsible !== 'header' && _this2.handleItemClick(); -@@ -100,7 +102,7 @@ var CollapsePanel = /*#__PURE__*/function (_React$Component) { - className: "".concat(prefixCls, "-header-text") - }, header) : header, extra && React.createElement("div", { - className: "".concat(prefixCls, "-extra") -- }, extra)), React.createElement(CSSMotion, Object.assign({ -+ }, extra))), React.createElement(CSSMotion, Object.assign({ - visible: isActive, - leavedClassName: "".concat(prefixCls, "-content-hidden") - }, openMotion, { diff --git a/desktop/patches/rc-collapse+3.4.2.patch b/desktop/patches/rc-collapse+3.4.2.patch new file mode 100644 index 000000000..497ece4df --- /dev/null +++ b/desktop/patches/rc-collapse+3.4.2.patch @@ -0,0 +1,18 @@ +diff --git a/node_modules/rc-collapse/es/Panel.js b/node_modules/rc-collapse/es/Panel.js +index d908840..3b5926b 100644 +--- a/node_modules/rc-collapse/es/Panel.js ++++ b/node_modules/rc-collapse/es/Panel.js +@@ -137,9 +137,11 @@ var CollapsePanel = /*#__PURE__*/function (_React$Component) { + className: itemCls, + style: style, + id: id +- }), /*#__PURE__*/React.createElement("div", headerProps, this.renderIcon(), this.renderTitle(), ifExtraExist && /*#__PURE__*/React.createElement("div", { ++ }), ++ React.createElement(global.FlipperTracked, { action: 'collapse:' + _this$props4.header }, ++ /*#__PURE__*/React.createElement("div", headerProps, this.renderIcon(), this.renderTitle(), ifExtraExist && /*#__PURE__*/React.createElement("div", { + className: "".concat(prefixCls, "-extra") +- }, extra)), /*#__PURE__*/React.createElement(CSSMotion, _extends({ ++ }, extra))), /*#__PURE__*/React.createElement(CSSMotion, _extends({ + visible: isActive, + leavedClassName: "".concat(prefixCls, "-content-hidden") + }, openMotion, { diff --git a/desktop/patches/rc-tabs+11.10.5.patch b/desktop/patches/rc-tabs+12.5.10.patch similarity index 77% rename from desktop/patches/rc-tabs+11.10.5.patch rename to desktop/patches/rc-tabs+12.5.10.patch index a82943632..d55715ffd 100644 --- a/desktop/patches/rc-tabs+11.10.5.patch +++ b/desktop/patches/rc-tabs+12.5.10.patch @@ -1,18 +1,16 @@ diff --git a/node_modules/rc-tabs/es/TabNavList/TabNode.js b/node_modules/rc-tabs/es/TabNavList/TabNode.js -index eca033e..7a818d8 100644 +index b655b3d..783f686 100644 --- a/node_modules/rc-tabs/es/TabNavList/TabNode.js +++ b/node_modules/rc-tabs/es/TabNavList/TabNode.js -@@ -27,13 +27,21 @@ function TabNode(_ref, ref) { - return onRemove; - }, []); +@@ -22,11 +22,19 @@ function TabNode(_ref) { + style = _ref.style; + var tabPrefix = "".concat(prefixCls, "-tab"); var removable = editable && closable !== false && !disabled; + var scope = React.useContext(global.FlipperTrackingScopeContext); - function onInternalClick(e) { if (disabled) { return; } - - onClick(e); + global.flipperTrackInteraction( + 'Tabs', @@ -23,5 +21,5 @@ index eca033e..7a818d8 100644 + e + ); } - function onRemoveTab(event) { + event.preventDefault(); diff --git a/desktop/pkg-lib/package.json b/desktop/pkg-lib/package.json index 2598a0336..4ddd84909 100644 --- a/desktop/pkg-lib/package.json +++ b/desktop/pkg-lib/package.json @@ -9,21 +9,25 @@ "license": "MIT", "bugs": "https://github.com/facebook/flipper/issues", "dependencies": { - "flipper-babel-transformer": "0.0.0", + "chalk": "^4", + "esbuild": "^0.15.7", + "fb-watchman": "^2.0.2", + "flipper-common": "0.0.0", "flipper-plugin-lib": "0.0.0", - "fs-extra": "^10.1.0", + "fs-extra": "^11.1.1", "metro": "^0.70.2", "metro-cache": "^0.70.2", - "metro-minify-terser": "^0.70.2", - "npm-packlist": "^4.0.0" + "metro-minify-terser": "^0.75.0", + "npm-packlist": "^4.0.0", + "p-map": "^4" }, "devDependencies": { - "@types/fs-extra": "^9.0.13", + "@types/fs-extra": "^11.0.1", "@types/mock-fs": "^4.13.1", "@types/node": "^17.0.31", "@types/npm-packlist": "^1.1.2", "flipper-test-utils": "0.0.0", - "mock-fs": "^5.1.2" + "mock-fs": "^5.2.0" }, "scripts": { "reset": "rimraf lib *.tsbuildinfo", diff --git a/desktop/pkg-lib/src/__tests__/getWatchFolders.node.tsx b/desktop/pkg-lib/src/__tests__/getWatchFolders.node.tsx index 08f1877b0..61ce26869 100644 --- a/desktop/pkg-lib/src/__tests__/getWatchFolders.node.tsx +++ b/desktop/pkg-lib/src/__tests__/getWatchFolders.node.tsx @@ -99,12 +99,13 @@ describe('getWatchFolders', () => { }; const readReadJson = fs.readJson; try { + // @ts-expect-error readJson is read only and it is fine, this is a test fs.readJson = readJsonMock as any; const resolvedFolders = await getWatchFolders( path.join(rootDir, 'local_module_2'), ); expect(resolvedFolders.map(normalizePath)).toMatchInlineSnapshot(` - Array [ + [ "/test/root/local_module_2", "/test/root/node_modules", "/test/root/plugins/fb/fb_plugin_module_1", @@ -115,6 +116,7 @@ describe('getWatchFolders', () => { ] `); } finally { + // @ts-expect-error readJson is read only and it is fine, this is a test fs.readJson = readReadJson; } }); diff --git a/desktop/pkg-lib/src/buildDefaultPlugins.tsx b/desktop/pkg-lib/src/buildDefaultPlugins.tsx new file mode 100644 index 000000000..20e060b94 --- /dev/null +++ b/desktop/pkg-lib/src/buildDefaultPlugins.tsx @@ -0,0 +1,55 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {InstalledPluginDetails} from 'flipper-common'; +import pMap from 'p-map'; +import path from 'path'; +import fs from 'fs-extra'; +import runBuild from './runBuild'; + +const defaultPluginsDir = path.join(__dirname, '../../static/defaultPlugins'); + +export async function buildDefaultPlugins( + defaultPlugins: InstalledPluginDetails[], + dev: boolean, + intern: boolean, +) { + if (process.env.FLIPPER_NO_REBUILD_PLUGINS) { + console.log( + `⚙️ Including ${ + defaultPlugins.length + } plugins into the default plugins list. Skipping rebuilding because "no-rebuild-plugins" option provided. List of default plugins: ${defaultPlugins + .map((p) => p.id) + .join(', ')}`, + ); + } + await pMap( + defaultPlugins, + async function (plugin) { + try { + if (!process.env.FLIPPER_NO_REBUILD_PLUGINS) { + console.log( + `⚙️ Building plugin ${plugin.id} to include it into the default plugins list...`, + ); + await runBuild(plugin.dir, dev, intern); + } + await fs.ensureSymlink( + plugin.dir, + path.join(defaultPluginsDir, plugin.name), + 'junction', + ); + } catch (err) { + console.error(`✖ Failed to build plugin ${plugin.id}`, err); + } + }, + { + concurrency: 16, + }, + ); +} diff --git a/desktop/pkg-lib/src/getDefaultPlugins.tsx b/desktop/pkg-lib/src/getDefaultPlugins.tsx new file mode 100644 index 000000000..e84defa30 --- /dev/null +++ b/desktop/pkg-lib/src/getDefaultPlugins.tsx @@ -0,0 +1,43 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {getSourcePlugins} from 'flipper-plugin-lib'; + +// For insiders builds we bundle top 5 popular device plugins, +// plus top 10 popular "universal" plugins enabled by more than 100 users. +const hardcodedPlugins = new Set([ + // Popular device plugins + 'DeviceLogs', + 'CrashReporter', + 'MobileBuilds', + 'Hermesdebuggerrn', + 'React', + // Popular client plugins + 'Inspector', + 'Network', + 'AnalyticsLogging', + 'GraphQL', + 'UIPerf', + 'MobileConfig', + 'Databases', + 'FunnelLogger', + 'Navigation', + 'Fresco', + 'Preferences', +]); + +export const getDefaultPlugins = async (isInsidersBuild: boolean) => { + const sourcePlugins = await getSourcePlugins(); + const defaultPlugins = sourcePlugins + // we only include headless plugins and a predefined set of regular plugins into insiders release + .filter( + (p) => !isInsidersBuild || hardcodedPlugins.has(p.id) || p.headless, + ); + return defaultPlugins; +}; diff --git a/desktop/pkg-lib/src/index.tsx b/desktop/pkg-lib/src/index.tsx index e4b6e4b8a..8102e05ae 100644 --- a/desktop/pkg-lib/src/index.tsx +++ b/desktop/pkg-lib/src/index.tsx @@ -11,3 +11,7 @@ export {default as runBuild} from './runBuild'; export {default as getWatchFolders} from './getWatchFolders'; export {default as computePackageChecksum} from './computePackageChecksum'; export {default as stripSourceMapComment} from './stripSourceMap'; +export {default as startWatchPlugins} from './startWatchPlugins'; +export {default as Watchman} from './watchman'; +export {buildDefaultPlugins} from './buildDefaultPlugins'; +export {getDefaultPlugins} from './getDefaultPlugins'; diff --git a/desktop/pkg-lib/src/runBuild.tsx b/desktop/pkg-lib/src/runBuild.tsx index 932045968..ac07f17f4 100644 --- a/desktop/pkg-lib/src/runBuild.tsx +++ b/desktop/pkg-lib/src/runBuild.tsx @@ -7,123 +7,98 @@ * @format */ -import Metro from 'metro'; -import getWatchFolders from './getWatchFolders'; import path from 'path'; import fs from 'fs-extra'; import {getInstalledPluginDetails} from 'flipper-plugin-lib'; -import {FileStore} from 'metro-cache'; -import stripSourceMapComment from './stripSourceMap'; -import os from 'os'; +import {build, Plugin} from 'esbuild'; -let metroDir: string | undefined; -const metroDirPromise = getMetroDir().then((dir) => (metroDir = dir)); +// https://github.com/evanw/esbuild/issues/1979#issuecomment-1026988439 +const resolveFbStubsToFbPlugin: Plugin = { + name: 'resolve-fb-stubs-to-fb', + setup({onResolve}) { + onResolve({filter: /fb-stubs/}, (args) => { + let moduleName = args.path.replace('fb-stubs', 'fb'); + if (!moduleName.endsWith('.tsx')) { + moduleName = `${moduleName}.tsx`; + } + return { + path: path.resolve(args.resolveDir, moduleName), + }; + }); + }, +}; -// We need to include metro-runtime to the watched folders list because it contains modules which are included into the final bundle. -async function getMetroDir() { - let dir = __dirname; - while (true) { - const dirToCheck = path.join(dir, 'node_modules', 'metro-runtime'); - if (await fs.pathExists(dirToCheck)) return dirToCheck; - const nextDir = path.dirname(dir); - if (!nextDir || nextDir === '' || nextDir === dir) { - break; - } - dir = nextDir; - } - return __dirname; -} - -interface RunMetroConfig { +interface RunBuildConfig { pluginDir: string; - baseConfig: any; entry: string; out: string; dev: boolean; + node?: boolean; sourceMapPath?: string; - babelTransformerPath: string; + intern: boolean; } -async function runMetro({ +async function runBuild({ pluginDir, - baseConfig, entry, out, dev, + node, sourceMapPath, - babelTransformerPath, -}: RunMetroConfig) { - const config = Object.assign({}, baseConfig, { - reporter: {update: () => {}}, - projectRoot: pluginDir, - watchFolders: [metroDir || (await metroDirPromise)].concat( - await getWatchFolders(pluginDir), - ), - serializer: { - ...baseConfig.serializer, - getRunModuleStatement: (moduleID: string) => - `module.exports = global.__r(${moduleID});`, - }, - transformer: { - ...baseConfig.transformer, - babelTransformerPath, - minifierPath: require.resolve('metro-minify-terser'), - minifierConfig: { - // see: https://www.npmjs.com/package/terser - keep_fnames: true, - module: true, - warnings: true, - mangle: false, - compress: false, - }, - }, - resolver: { - ...baseConfig.resolver, - resolverMainFields: ['flipperBundlerEntry', 'module', 'main'], - sourceExts: ['js', 'jsx', 'ts', 'tsx', 'json', 'mjs', 'cjs'], - blacklistRE: /\.native\.js$/, - }, - cacheStores: [ - new FileStore({ - root: - process.env.FLIPPER_METRO_CACHE ?? - path.join(os.tmpdir(), 'metro-cache'), - }), + intern, +}: RunBuildConfig) { + await build({ + entryPoints: [path.join(pluginDir, entry)], + bundle: true, + outfile: out, + platform: node ? 'node' : 'browser', + format: 'cjs', + // This list should match `dispatcher/plugins.tsx` and `builtInModules` in `desktop/.eslintrc.js` + external: [ + 'flipper', + 'flipper-plugin', + 'react', + 'react-dom', + 'react-dom/client', + 'react-is', + 'antd', + 'immer', + '@emotion/styled', + '@emotion/css', + '@ant-design/icons', + // It is an optional dependency for rollup that we use in react-devtools + 'fsevents', + // Allow desktop plugins that require electron to build (deprecated) + 'electron', ], - }); - const sourceMapUrl = out.replace(/\.js$/, '.map'); - const sourceMap = dev || !!sourceMapPath; - await Metro.runBuild(config, { - dev, - sourceMap, - sourceMapUrl, + sourcemap: dev ? 'inline' : 'external', minify: !dev, - inlineSourceMap: dev, - resetCache: false, - entry, - out, + plugins: intern ? [resolveFbStubsToFbPlugin] : undefined, + loader: { + '.ttf': 'dataurl', + }, }); - if (sourceMap && !dev) { - await stripSourceMapComment(out); - } + + const sourceMapUrl = `${out}.map`; if ( sourceMapPath && path.resolve(sourceMapPath) !== path.resolve(sourceMapUrl) ) { - console.log(`Moving plugin sourcemap to ${sourceMapPath}`); + console.info(`Moving plugin sourcemap to ${sourceMapPath}`); await fs.ensureDir(path.dirname(sourceMapPath)); await fs.move(sourceMapUrl, sourceMapPath, {overwrite: true}); } } type Options = { - sourceMapPath?: string | undefined; - sourceMapPathServerAddOn?: string | undefined; + sourceMapPath?: string; + sourceMapPathServerAddOn?: string; }; export default async function bundlePlugin( pluginDir: string, dev: boolean, + intern: boolean, options?: Options, ) { const stat = await fs.lstat(pluginDir); @@ -137,19 +112,24 @@ export default async function bundlePlugin( ); } const plugin = await getInstalledPluginDetails(pluginDir); - const baseConfig = await Metro.loadConfig(); - const bundleConfigs: RunMetroConfig[] = []; + if (typeof plugin.deprecated === 'string') { + console.warn( + `Skip bundling plugin source ${pluginDir} is deprecated: ${plugin.deprecated}`, + ); + return; + } + + const bundleConfigs: RunBuildConfig[] = []; await fs.ensureDir(path.dirname(plugin.entry)); bundleConfigs.push({ pluginDir, - baseConfig, entry: plugin.source, out: plugin.entry, dev, sourceMapPath: options?.sourceMapPath, - babelTransformerPath: require.resolve('flipper-babel-transformer'), + intern, }); if ( @@ -160,16 +140,14 @@ export default async function bundlePlugin( await fs.ensureDir(path.dirname(plugin.serverAddOnEntry)); bundleConfigs.push({ pluginDir, - baseConfig, entry: plugin.serverAddOnSource, out: plugin.serverAddOnEntry, dev, + node: true, sourceMapPath: options?.sourceMapPathServerAddOn, - babelTransformerPath: require.resolve( - 'flipper-babel-transformer/lib/transform-server-add-on', - ), + intern, }); } - await Promise.all(bundleConfigs.map((config) => runMetro(config))); + await Promise.all(bundleConfigs.map((config) => runBuild(config))); } diff --git a/desktop/pkg-lib/src/startWatchPlugins.tsx b/desktop/pkg-lib/src/startWatchPlugins.tsx new file mode 100644 index 000000000..1fbfbd567 --- /dev/null +++ b/desktop/pkg-lib/src/startWatchPlugins.tsx @@ -0,0 +1,130 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import Watchman from './watchman'; +import { + getInstalledPluginDetails, + getPluginSourceFolders, + isPluginDir, +} from 'flipper-plugin-lib'; +import path from 'path'; +import chalk from 'chalk'; +import runBuild from './runBuild'; +import {InstalledPluginDetails} from 'flipper-common'; +import {getDefaultPlugins} from './getDefaultPlugins'; +import {buildDefaultPlugins} from './buildDefaultPlugins'; + +async function rebuildPlugin(pluginPath: string, intern: boolean) { + try { + await runBuild(pluginPath, true, intern); + console.info(chalk.green('Rebuilt plugin'), pluginPath); + } catch (e) { + console.error( + chalk.red( + 'Failed to compile a plugin, waiting for additional changes...', + ), + e, + ); + } +} + +export default async function startWatchPlugins( + isInsidersBuild: boolean, + intern: boolean, + onChanged?: ( + changedPlugins: InstalledPluginDetails[], + ) => void | Promise, +) { + // eslint-disable-next-line no-console + console.log('🕵️‍ Watching for plugin changes'); + + let delayedCompilation: NodeJS.Timeout | undefined; + const kCompilationDelayMillis = 1000; + const onPluginChangeDetected = (root: string, files: string[]) => { + if (!delayedCompilation) { + delayedCompilation = setTimeout(async () => { + delayedCompilation = undefined; + // eslint-disable-next-line no-console + console.log(`🕵️‍ Detected plugin change`); + try { + const changedDirs = await Promise.all( + // https://facebook.github.io/watchman/docs/nodejs.html#subscribing-to-changes + files.map(async (file: string) => { + const filePathAbs = path.resolve(root, file); + let dirPath = path.dirname(filePathAbs); + while ( + // Stop when we reach plugin root + !(await isPluginDir(dirPath)) + ) { + const relative = path.relative(root, dirPath); + // Stop when we reach desktop/plugins folder + if (!relative || relative.startsWith('..')) { + console.info( + chalk.yellow( + 'Failed to find a plugin root for path. Rebuilding all plugins', + ), + filePathAbs, + ); + throw new Error('REBUILD_ALL'); + } + dirPath = path.resolve(dirPath, '..'); + } + await rebuildPlugin(dirPath, intern); + return dirPath; + }), + ); + const changedPlugins = await Promise.all( + changedDirs.map((dirPath) => getInstalledPluginDetails(dirPath)), + ); + onChanged?.(changedPlugins); + } catch (e) { + if (e instanceof Error && e.message === 'REBUILD_ALL') { + const defaultPlugins = await getDefaultPlugins(isInsidersBuild); + await buildDefaultPlugins(defaultPlugins, true, intern); + onChanged?.(defaultPlugins); + return; + } + throw e; + } + }, kCompilationDelayMillis); + } + }; + try { + await startWatchingPluginsUsingWatchman(onPluginChangeDetected); + } catch (err) { + console.error( + 'Failed to start watching plugin files using Watchman, continue without hot reloading', + err, + ); + } +} + +async function startWatchingPluginsUsingWatchman( + onChange: (root: string, files: string[]) => void, +) { + const pluginFolders = await getPluginSourceFolders(); + await Promise.all( + pluginFolders.map(async (pluginFolder) => { + const watchman = new Watchman(pluginFolder); + await watchman.initialize(); + await watchman.startWatchFiles( + '.', + ({files}) => onChange(pluginFolder, files), + { + excludes: [ + '**/__tests__/**/*', + '**/node_modules/**/*', + '**/dist/*', + '**/.*', + ], + }, + ); + }), + ); +} diff --git a/desktop/scripts/watchman.tsx b/desktop/pkg-lib/src/watchman.tsx similarity index 98% rename from desktop/scripts/watchman.tsx rename to desktop/pkg-lib/src/watchman.tsx index e3d558e4e..e193382db 100644 --- a/desktop/scripts/watchman.tsx +++ b/desktop/pkg-lib/src/watchman.tsx @@ -8,7 +8,7 @@ */ import {Client} from 'fb-watchman'; -import {v4 as uuid} from 'uuid'; +import {uuid} from 'flipper-common'; import path from 'path'; const watchmanTimeout = 60 * 1000; diff --git a/desktop/pkg-lib/tsconfig.json b/desktop/pkg-lib/tsconfig.json index fe62a0d3b..0984a625f 100644 --- a/desktop/pkg-lib/tsconfig.json +++ b/desktop/pkg-lib/tsconfig.json @@ -9,6 +9,9 @@ { "path": "../babel-transformer" }, + { + "path": "../flipper-common" + }, { "path": "../plugin-lib" } diff --git a/desktop/pkg/package.json b/desktop/pkg/package.json index cec3efc68..96e965dae 100644 --- a/desktop/pkg/package.json +++ b/desktop/pkg/package.json @@ -12,24 +12,24 @@ }, "bugs": "https://github.com/facebook/flipper/issues", "dependencies": { - "@oclif/command": "^1", - "@oclif/config": "^1", - "@oclif/parser": "^3.8.5", - "@oclif/plugin-help": "^5.1.12", - "@oclif/plugin-warn-if-update-available": "^2.0.3", + "@oclif/command": "^1.8.36", + "@oclif/config": "^1.18.17", + "@oclif/parser": "^3.8.17", + "@oclif/plugin-help": "^5.2.20", + "@oclif/plugin-warn-if-update-available": "^2.1.1", "ajv": "^6.12.2", "ajv-errors": "^1.0.1", "cli-ux": "^6.0.9", "flipper-pkg-lib": "0.0.0", "flipper-plugin-lib": "0.0.0", - "fs-extra": "^10.1.0", + "fs-extra": "^11.1.1", "inquirer": "^8.2.4", "lodash": "^4.17.21", - "recursive-readdir": "^2.2.2" + "recursive-readdir": "^2.2.3" }, "devDependencies": { "@oclif/dev-cli": "^1", - "@types/fs-extra": "^9.0.13", + "@types/fs-extra": "^11.0.0", "@types/inquirer": "^7.3.3", "@types/node": "^17.0.31", "@types/recursive-readdir": "^2.2.1", diff --git a/desktop/pkg/src/__tests__/runLint.node.tsx b/desktop/pkg/src/__tests__/runLint.node.tsx index 3192fea48..18082ceec 100644 --- a/desktop/pkg/src/__tests__/runLint.node.tsx +++ b/desktop/pkg/src/__tests__/runLint.node.tsx @@ -30,21 +30,28 @@ const validPackageJson = { }, }; -beforeEach(() => { - jest.mock('fs-extra', () => jest.fn()); - fs.pathExists = jest.fn().mockResolvedValue(true); - fs.pathExistsSync = jest.fn().mockReturnValue(true); - // Required by some inconsistent node types for rw access. - (fs.lstatSync as any) = jest.fn().mockReturnValue({ - isFile: function () { - return true; - }, - }); +jest.mock('fs-extra', () => { + const mod = { + ...jest.requireActual('fs-extra'), + readFile: jest.fn(), + pathExists: jest.fn().mockResolvedValue(true), + pathExistsSync: jest.fn().mockResolvedValue(true), + lstatSync: jest.fn().mockReturnValue({ + isFile: function () { + return true; + }, + }), + }; + + return { + ...mod, + default: mod, + }; }); test('valid package json', async () => { const json = JSON.stringify(validPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toBe(null); }); @@ -53,7 +60,7 @@ test('valid scoped package json', async () => { const testPackageJson = Object.assign({}, validPackageJson); testPackageJson.name = '@test/flipper-plugin-package'; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toBe(null); }); @@ -63,14 +70,14 @@ test('$schema field is required', async () => { // @ts-ignore cannot delete non-optional fields delete testPackageJson.$schema; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` - Array [ - ". should have required property \\"$schema\\" pointing to a supported schema URI, e.g.: + [ + ". should have required property "$schema" pointing to a supported schema URI, e.g.: { - \\"$schema\\": \\"https://fbflipper.com/schemas/plugin-package/v2.json\\", - \\"name\\": \\"flipper-plugin-example\\", + "$schema": "https://fbflipper.com/schemas/plugin-package/v2.json", + "name": "flipper-plugin-example", ... }", ] @@ -82,10 +89,10 @@ test('supported schema is required', async () => { testPackageJson.$schema = 'https://fbflipper.com/schemas/plugin-package/v1.json'; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` - Array [ + [ ".$schema should point to a supported schema. Currently supported schemas: - https://fbflipper.com/schemas/plugin-package/v2.json", ] @@ -97,10 +104,10 @@ test('name is required', async () => { // @ts-ignore cannot delete non-optional fields delete testPackageJson.name; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` - Array [ + [ ". should have required property 'name'", ] `); @@ -110,11 +117,11 @@ test('name must start with "flipper-plugin-"', async () => { const testPackageJson = Object.assign({}, validPackageJson); testPackageJson.name = 'test-plugin'; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` - Array [ - "/name should start with \\"flipper-plugin-\\", e.g. \\"flipper-plugin-example\\"", + [ + "/name should start with "flipper-plugin-", e.g. "flipper-plugin-example"", ] `); }); @@ -123,11 +130,11 @@ test('keywords must contain "flipper-plugin"', async () => { const testPackageJson = Object.assign({}, validPackageJson); testPackageJson.keywords = ['flipper', 'network']; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` - Array [ - "/keywords should contain keyword \\"flipper-plugin\\"", + [ + "/keywords should contain keyword "flipper-plugin"", ] `); }); @@ -135,16 +142,14 @@ test('keywords must contain "flipper-plugin"', async () => { test('flippeBundlerEntry must point to an existing file', async () => { const testPackageJson = Object.assign({}, validPackageJson); testPackageJson.flipperBundlerEntry = 'unexisting/file'; - fs.pathExistsSync = jest - .fn() - .mockImplementation( - (filePath) => !filePath.includes(path.join('unexisting', 'file')), - ); + (fs.pathExistsSync as any as jest.Mock).mockImplementation( + (filePath) => !filePath.includes(path.join('unexisting', 'file')), + ); const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` - Array [ + [ "/flipperBundlerEntry should point to a valid file", ] `); @@ -156,12 +161,12 @@ test('multiple validation errors reported', async () => { // @ts-ignore cannot delete non-optional fields delete testPackageJson.flipperBundlerEntry; const json = JSON.stringify(testPackageJson); - fs.readFile = jest.fn().mockResolvedValue(new Buffer(json)); + (fs.readFile as any as jest.Mock).mockReturnValueOnce(new Buffer(json)); const result = await runLint('dir'); expect(result).toMatchInlineSnapshot(` - Array [ + [ ". should have required property 'flipperBundlerEntry'", - "/keywords should contain keyword \\"flipper-plugin\\"", + "/keywords should contain keyword "flipper-plugin"", ] `); }); diff --git a/desktop/pkg/src/__tests__/runMigrate.node.tsx b/desktop/pkg/src/__tests__/runMigrate.node.tsx index 24e3d514e..090cc8a22 100644 --- a/desktop/pkg/src/__tests__/runMigrate.node.tsx +++ b/desktop/pkg/src/__tests__/runMigrate.node.tsx @@ -10,25 +10,6 @@ import runMigrate from '../utils/runMigrate'; import fs from 'fs-extra'; -const packageJsonV1 = { - name: 'Fresco', - version: '1.0.0', - main: 'index.tsx', - license: 'MIT', - keywords: ['images'], - dependencies: { - flipper: 'latest', - }, - scripts: { - prepack: 'yarn reset && yarn build', - }, - title: 'Images', - icon: 'profile', - bugs: { - email: 'example@test.com', - }, -}; - const packageJsonV2 = { $schema: 'https://fbflipper.com/schemas/plugin-package/v2.json', name: 'flipper-plugin-network', @@ -53,18 +34,52 @@ const packageJsonV2 = { let convertedPackageJsonString: string | undefined; +jest.mock('fs-extra', () => { + const packageJsonV1 = { + name: 'Fresco', + version: '1.0.0', + main: 'index.tsx', + license: 'MIT', + keywords: ['images'], + dependencies: { + flipper: 'latest', + }, + scripts: { + prepack: 'yarn reset && yarn build', + }, + title: 'Images', + icon: 'profile', + bugs: { + email: 'example@test.com', + }, + }; + + const mod = { + ...jest.requireActual('fs-extra'), + readFile: jest + .fn() + .mockResolvedValue(new Buffer(JSON.stringify(packageJsonV1))), + pathExists: jest.fn().mockResolvedValue(true), + pathExistsSync: jest.fn().mockResolvedValue(true), + readJson: jest.fn().mockResolvedValue(packageJsonV1), + writeFile: jest.fn(async (_path, content) => { + convertedPackageJsonString = content; + }), + lstatSync: jest.fn().mockReturnValue({ + isFile: function () { + return true; + }, + }), + }; + + return { + ...mod, + default: mod, + }; +}); + beforeEach(() => { - jest.mock('fs-extra', () => jest.fn()); - fs.pathExists = jest.fn().mockResolvedValue(true); - fs.pathExistsSync = jest.fn().mockReturnValue(true); - fs.readJson = jest.fn().mockResolvedValue(packageJsonV1); - fs.readFile = jest - .fn() - .mockResolvedValue(new Buffer(JSON.stringify(packageJsonV1))); convertedPackageJsonString = undefined; - fs.writeFile = jest.fn().mockImplementation(async (_path, content) => { - convertedPackageJsonString = content; - }); }); test('converts package.json and adds dependencies', async () => { @@ -72,31 +87,31 @@ test('converts package.json and adds dependencies', async () => { expect(error).toBeUndefined(); expect(convertedPackageJsonString).toMatchInlineSnapshot(` "{ - \\"$schema\\": \\"https://fbflipper.com/schemas/plugin-package/v2.json\\", - \\"name\\": \\"flipper-plugin-fresco\\", - \\"id\\": \\"Fresco\\", - \\"version\\": \\"1.0.0\\", - \\"main\\": \\"dist/bundle.js\\", - \\"flipperBundlerEntry\\": \\"index.tsx\\", - \\"license\\": \\"MIT\\", - \\"keywords\\": [ - \\"flipper-plugin\\", - \\"images\\" + "$schema": "https://fbflipper.com/schemas/plugin-package/v2.json", + "name": "flipper-plugin-fresco", + "id": "Fresco", + "version": "1.0.0", + "main": "dist/bundle.js", + "flipperBundlerEntry": "index.tsx", + "license": "MIT", + "keywords": [ + "flipper-plugin", + "images" ], - \\"peerDependencies\\": { - \\"flipper\\": \\"latest\\" + "peerDependencies": { + "flipper": "latest" }, - \\"devDependencies\\": { - \\"flipper\\": \\"latest\\", - \\"flipper-pkg\\": \\"latest\\" + "devDependencies": { + "flipper": "latest", + "flipper-pkg": "latest" }, - \\"scripts\\": { - \\"prepack\\": \\"yarn reset && yarn build && flipper-pkg lint && flipper-pkg bundle\\" + "scripts": { + "prepack": "yarn reset && yarn build && flipper-pkg lint && flipper-pkg bundle" }, - \\"title\\": \\"Images\\", - \\"icon\\": \\"profile\\", - \\"bugs\\": { - \\"email\\": \\"example@test.com\\" + "title": "Images", + "icon": "profile", + "bugs": { + "email": "example@test.com" } }" `); @@ -107,37 +122,36 @@ test('converts package.json without changing dependencies', async () => { expect(error).toBeUndefined(); expect(convertedPackageJsonString).toMatchInlineSnapshot(` "{ - \\"$schema\\": \\"https://fbflipper.com/schemas/plugin-package/v2.json\\", - \\"name\\": \\"flipper-plugin-fresco\\", - \\"id\\": \\"Fresco\\", - \\"version\\": \\"1.0.0\\", - \\"main\\": \\"dist/bundle.js\\", - \\"flipperBundlerEntry\\": \\"index.tsx\\", - \\"license\\": \\"MIT\\", - \\"keywords\\": [ - \\"flipper-plugin\\", - \\"images\\" + "$schema": "https://fbflipper.com/schemas/plugin-package/v2.json", + "name": "flipper-plugin-fresco", + "id": "Fresco", + "version": "1.0.0", + "main": "dist/bundle.js", + "flipperBundlerEntry": "index.tsx", + "license": "MIT", + "keywords": [ + "flipper-plugin", + "images" ], - \\"dependencies\\": { - \\"flipper\\": \\"latest\\" + "dependencies": { + "flipper": "latest" }, - \\"scripts\\": { - \\"prepack\\": \\"yarn reset && yarn build && flipper-pkg lint && flipper-pkg bundle\\" + "scripts": { + "prepack": "yarn reset && yarn build && flipper-pkg lint && flipper-pkg bundle" }, - \\"title\\": \\"Images\\", - \\"icon\\": \\"profile\\", - \\"bugs\\": { - \\"email\\": \\"example@test.com\\" + "title": "Images", + "icon": "profile", + "bugs": { + "email": "example@test.com" } }" `); }); test('does not migrate already migrated packages', async () => { - fs.readJson = jest.fn().mockResolvedValue(packageJsonV2); - fs.readFile = jest - .fn() - .mockResolvedValue(new Buffer(JSON.stringify(packageJsonV2))); + (fs.readFile as any as jest.Mock).mockResolvedValue( + new Buffer(JSON.stringify(packageJsonV2)), + ); const error = await runMigrate('dir'); expect(error).toBeUndefined(); expect(convertedPackageJsonString).toBeUndefined(); diff --git a/desktop/pkg/src/commands/bundle.tsx b/desktop/pkg/src/commands/bundle.tsx index bf77e6c9f..4a09d9471 100644 --- a/desktop/pkg/src/commands/bundle.tsx +++ b/desktop/pkg/src/commands/bundle.tsx @@ -39,23 +39,35 @@ export default class Bundle extends Command { 'Force env.NODE_ENV=production, enable minification and disable producing source maps.', default: false, }), + intern: flags.boolean({ + description: 'Force inten build which replaces fb-stubs with fb.', + default: false, + }), }; public async run() { const {args, flags} = this.parse(Bundle); const inputDirectory: string = path.resolve(process.cwd(), args.directory); - const success = await runBuildOnce(inputDirectory, !flags.production); + const success = await runBuildOnce( + inputDirectory, + !flags.production, + flags.intern, + ); if (!flags.watch) { process.exit(success ? 0 : 1); } else { - enterWatchMode(inputDirectory, !flags.production); + enterWatchMode(inputDirectory, !flags.production, flags.intern); } } } -async function runBuildOnce(inputDirectory: string, dev: boolean) { +async function runBuildOnce( + inputDirectory: string, + dev: boolean, + intern: boolean, +) { try { - await runBuild(inputDirectory, dev); + await runBuild(inputDirectory, dev, intern); console.log('✅ Build succeeded'); return true; } catch (e) { @@ -65,7 +77,7 @@ async function runBuildOnce(inputDirectory: string, dev: boolean) { } } -function enterWatchMode(inputDirectory: string, dev: boolean) { +function enterWatchMode(inputDirectory: string, dev: boolean, intern: boolean) { console.log(`⏳ Waiting for changes...`); let isBuilding = false; let pendingChanges = false; @@ -82,7 +94,7 @@ function enterWatchMode(inputDirectory: string, dev: boolean) { isBuilding = true; while (pendingChanges) { pendingChanges = false; - await runBuildOnce(inputDirectory, dev); + await runBuildOnce(inputDirectory, dev, intern); } isBuilding = false; console.log(`⏳ Waiting for changes...`); diff --git a/desktop/pkg/src/commands/pack.tsx b/desktop/pkg/src/commands/pack.tsx index b68f860ab..0b68bca42 100644 --- a/desktop/pkg/src/commands/pack.tsx +++ b/desktop/pkg/src/commands/pack.tsx @@ -40,6 +40,10 @@ export default class Pack extends Command { 'Force env.NODE_ENV=production, enable minification and disable producing source maps.', default: false, }), + intern: flags.boolean({ + description: 'Force inten build which replaces fb-stubs with fb.', + default: false, + }), }; public static args: args.IArg[] = [ @@ -115,7 +119,7 @@ export default class Pack extends Command { cli.action.stop(); cli.action.start(`Compiling`); - await runBuild(inputDirectory, parsedFlags.production); + await runBuild(inputDirectory, parsedFlags.production, parsedFlags.intern); cli.action.stop(); cli.action.start(`Packing to ${outputFile}`); diff --git a/desktop/pkg/src/utils/runLint.tsx b/desktop/pkg/src/utils/runLint.tsx index 828eaa626..7f566dda8 100644 --- a/desktop/pkg/src/utils/runLint.tsx +++ b/desktop/pkg/src/utils/runLint.tsx @@ -58,10 +58,10 @@ export default async function runLint( ]; } - const packageJsonSchema = await fs.readJson(packageJsonSchemaPath); - const pluginPackageJsonSchema = await fs.readJson( - pluginPackageJsonSchemaPath, - ); + const [packageJsonSchema, pluginPackageJsonSchema] = await Promise.all([ + fs.readJson(packageJsonSchemaPath), + fs.readJson(pluginPackageJsonSchemaPath), + ]); const ajv = new Ajv({ allErrors: true, loadSchema, diff --git a/desktop/plugin-lib/package.json b/desktop/plugin-lib/package.json index 17cecdc41..6be73d584 100644 --- a/desktop/plugin-lib/package.json +++ b/desktop/plugin-lib/package.json @@ -9,26 +9,26 @@ "license": "MIT", "bugs": "https://github.com/facebook/flipper/issues", "dependencies": { - "algoliasearch": "^4.13.0", + "algoliasearch": "^4.14.3", "decompress": "^4.2.1", "decompress-targz": "^4.1.1", "decompress-unzip": "^4.0.1", "flipper-common": "^0.0.0", - "fs-extra": "^10.1.0", + "fs-extra": "^11.1.1", "live-plugin-manager": "^0.17.1", "npm-api": "^1.0.1", "p-filter": "^2.1.0", "p-map": "^4.0.0", - "semver": "^7.3.7", + "semver": "^7.5.4", "tmp": "^0.2.1" }, "devDependencies": { "@types/decompress": "4.2.4", - "@types/fs-extra": "^9.0.13", + "@types/fs-extra": "^11.0.1", "@types/mock-fs": "^4.13.1", "@types/node": "^17.0.31", "flipper-test-utils": "0.0.0", - "mock-fs": "^5.1.2" + "mock-fs": "^5.2.0" }, "scripts": { "reset": "rimraf lib *.tsbuildinfo", diff --git a/desktop/plugin-lib/src/__tests__/getPluginDetails.node.tsx b/desktop/plugin-lib/src/__tests__/getPluginDetails.node.tsx index cee1b1b57..e04838e13 100644 --- a/desktop/plugin-lib/src/__tests__/getPluginDetails.node.tsx +++ b/desktop/plugin-lib/src/__tests__/getPluginDetails.node.tsx @@ -12,7 +12,6 @@ import path from 'path'; import {getInstalledPluginDetails} from '../getPluginDetails'; import {pluginInstallationDir} from '../pluginPaths'; import {normalizePath} from 'flipper-test-utils'; -import {mocked} from 'ts-jest/utils'; jest.mock('fs-extra'); @@ -32,12 +31,13 @@ test('getPluginDetailsV1', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV1); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); details.entry = normalizePath(details.entry); expect(details).toMatchInlineSnapshot(` - Object { + { "bugs": undefined, "category": undefined, "deprecated": undefined, @@ -50,7 +50,6 @@ test('getPluginDetailsV1', async () => { "icon": undefined, "id": "flipper-plugin-test", "isActivatable": true, - "isBundled": false, "main": "dist/bundle.js", "name": "flipper-plugin-test", "pluginType": undefined, @@ -76,12 +75,13 @@ test('getPluginDetailsV2', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); details.entry = normalizePath(details.entry); expect(details).toMatchInlineSnapshot(` - Object { + { "bugs": undefined, "category": undefined, "deprecated": undefined, @@ -95,7 +95,6 @@ test('getPluginDetailsV2', async () => { "icon": undefined, "id": "flipper-plugin-test", "isActivatable": true, - "isBundled": false, "main": "dist/bundle.js", "name": "flipper-plugin-test", "pluginType": undefined, @@ -124,12 +123,13 @@ test('id used as title if the latter omited', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); details.entry = normalizePath(details.entry); expect(details).toMatchInlineSnapshot(` - Object { + { "bugs": undefined, "category": undefined, "deprecated": undefined, @@ -143,7 +143,6 @@ test('id used as title if the latter omited', async () => { "icon": undefined, "id": "test", "isActivatable": true, - "isBundled": false, "main": "dist/bundle.js", "name": "flipper-plugin-test", "pluginType": undefined, @@ -171,12 +170,13 @@ test('name without "flipper-plugin-" prefix is used as title if the latter omite description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); details.entry = normalizePath(details.entry); expect(details).toMatchInlineSnapshot(` - Object { + { "bugs": undefined, "category": undefined, "deprecated": undefined, @@ -190,7 +190,6 @@ test('name without "flipper-plugin-" prefix is used as title if the latter omite "icon": undefined, "id": "flipper-plugin-test", "isActivatable": true, - "isBundled": false, "main": "dist/bundle.js", "name": "flipper-plugin-test", "pluginType": undefined, @@ -221,12 +220,13 @@ test('flipper-plugin-version is parsed', async () => { 'flipper-plugin': '^0.45', }, }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); details.entry = normalizePath(details.entry); expect(details).toMatchInlineSnapshot(` - Object { + { "bugs": undefined, "category": undefined, "deprecated": undefined, @@ -240,7 +240,6 @@ test('flipper-plugin-version is parsed', async () => { "icon": undefined, "id": "flipper-plugin-test", "isActivatable": true, - "isBundled": false, "main": "dist/bundle.js", "name": "flipper-plugin-test", "pluginType": undefined, @@ -275,12 +274,13 @@ test('plugin type and supported devices parsed', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); details.entry = normalizePath(details.entry); expect(details).toMatchInlineSnapshot(` - Object { + { "bugs": undefined, "category": undefined, "deprecated": undefined, @@ -294,7 +294,6 @@ test('plugin type and supported devices parsed', async () => { "icon": undefined, "id": "flipper-plugin-test", "isActivatable": true, - "isBundled": false, "main": "dist/bundle.js", "name": "flipper-plugin-test", "pluginType": "device", @@ -305,19 +304,19 @@ test('plugin type and supported devices parsed', async () => { "source": "src/index.tsx", "specVersion": 2, "supportedApps": undefined, - "supportedDevices": Array [ - Object { + "supportedDevices": [ + { "archived": false, "os": "Android", }, - Object { + { "os": "Android", - "specs": Array [ + "specs": [ "KaiOS", ], "type": "physical", }, - Object { + { "os": "iOS", "type": "emulator", }, @@ -345,12 +344,13 @@ test('plugin type and supported apps parsed', async () => { description: 'Description of Test Plugin', gatekeeper: 'GK_flipper_plugin_test', }; + // @ts-expect-error this is read only and it is fine, this is a test fs.readJson = jest.fn().mockImplementation(() => pluginV2); const details = await getInstalledPluginDetails(pluginPath); details.dir = normalizePath(details.dir); details.entry = normalizePath(details.entry); expect(details).toMatchInlineSnapshot(` - Object { + { "bugs": undefined, "category": undefined, "deprecated": undefined, @@ -364,7 +364,6 @@ test('plugin type and supported apps parsed', async () => { "icon": undefined, "id": "flipper-plugin-test", "isActivatable": true, - "isBundled": false, "main": "dist/bundle.js", "name": "flipper-plugin-test", "pluginType": "client", @@ -374,18 +373,18 @@ test('plugin type and supported apps parsed', async () => { "serverAddOnSource": undefined, "source": "src/index.tsx", "specVersion": 2, - "supportedApps": Array [ - Object { + "supportedApps": [ + { "appID": "Messenger", "os": "Android", "type": "emulator", }, - Object { + { "appID": "Instagram", "os": "Android", "type": "physical", }, - Object { + { "appID": "Facebook", "os": "iOS", "type": "emulator", @@ -424,8 +423,8 @@ test('can merge two package.json files', async () => { email: 'flippersupport@example.localhost', }, }; - const mockedFs = mocked(fs); - mockedFs.readJson.mockImplementation((file) => { + const mockedFs = jest.mocked(fs); + mockedFs.readJson.mockImplementation((file): any => { if (file === path.join(pluginPath, 'package.json')) { return pluginBase; } else if (file === path.join(pluginPath, 'fb', 'package.json')) { @@ -437,8 +436,8 @@ test('can merge two package.json files', async () => { details.dir = normalizePath(details.dir); details.entry = normalizePath(details.entry); expect(details).toMatchInlineSnapshot(` - Object { - "bugs": Object { + { + "bugs": { "email": "flippersupport@example.localhost", "url": "https://fb.com/groups/flippersupport", }, @@ -454,11 +453,10 @@ test('can merge two package.json files', async () => { "icon": undefined, "id": "flipper-plugin-test", "isActivatable": true, - "isBundled": false, "main": "dist/bundle.js", "name": "flipper-plugin-test", "pluginType": "device", - "publishedDocs": Object { + "publishedDocs": { "overview": true, "setup": true, }, @@ -468,19 +466,19 @@ test('can merge two package.json files', async () => { "source": "src/index.tsx", "specVersion": 2, "supportedApps": undefined, - "supportedDevices": Array [ - Object { + "supportedDevices": [ + { "archived": false, "os": "Android", }, - Object { + { "os": "Android", - "specs": Array [ + "specs": [ "KaiOS", ], "type": "physical", }, - Object { + { "os": "iOS", "type": "emulator", }, diff --git a/desktop/plugin-lib/src/__tests__/getUpdatablePlugins.node.tsx b/desktop/plugin-lib/src/__tests__/getUpdatablePlugins.node.tsx index 62c219750..511c5825d 100644 --- a/desktop/plugin-lib/src/__tests__/getUpdatablePlugins.node.tsx +++ b/desktop/plugin-lib/src/__tests__/getUpdatablePlugins.node.tsx @@ -16,7 +16,6 @@ import { NpmPackageDescriptor, } from '../getNpmHostedPlugins'; import {getInstalledPlugins} from '../pluginInstaller'; -import {mocked} from 'ts-jest/utils'; import type {Package} from 'npm-api'; import {InstalledPluginDetails} from 'flipper-common'; @@ -67,7 +66,6 @@ const installedPlugins: InstalledPluginDetails[] = [ id: 'Hello', title: 'Hello', description: 'World?', - isBundled: false, isActivatable: true, }, { @@ -82,7 +80,6 @@ const installedPlugins: InstalledPluginDetails[] = [ id: 'World', title: 'World', description: 'Hello?', - isBundled: false, isActivatable: true, }, ]; @@ -93,10 +90,10 @@ const updates: NpmPackageDescriptor[] = [ ]; test('annotatePluginsWithUpdates', async () => { - const getInstalledPluginsMock = mocked(getInstalledPlugins); + const getInstalledPluginsMock = jest.mocked(getInstalledPlugins); getInstalledPluginsMock.mockReturnValue(Promise.resolve(installedPlugins)); - const getNpmHostedPluginsMock = mocked(getNpmHostedPlugins); + const getNpmHostedPluginsMock = jest.mocked(getNpmHostedPlugins); getNpmHostedPluginsMock.mockReturnValue(Promise.resolve(updates)); const res = await getUpdatablePlugins(); @@ -107,9 +104,9 @@ test('annotatePluginsWithUpdates', async () => { version: res[0].version, updateStatus: res[0].updateStatus, }).toMatchInlineSnapshot(` - Object { + { "name": "flipper-plugin-hello", - "updateStatus": Object { + "updateStatus": { "kind": "up-to-date", }, "version": "0.1.0", @@ -121,9 +118,9 @@ test('annotatePluginsWithUpdates', async () => { version: res[1].version, updateStatus: res[1].updateStatus, }).toMatchInlineSnapshot(` - Object { + { "name": "flipper-plugin-world", - "updateStatus": Object { + "updateStatus": { "kind": "update-available", "version": "0.3.0", }, diff --git a/desktop/plugin-lib/src/__tests__/pluginInstaller.node.tsx b/desktop/plugin-lib/src/__tests__/pluginInstaller.node.tsx index adb11a997..c8a666e4d 100644 --- a/desktop/plugin-lib/src/__tests__/pluginInstaller.node.tsx +++ b/desktop/plugin-lib/src/__tests__/pluginInstaller.node.tsx @@ -119,24 +119,32 @@ function collectFileContent( } } -describe('pluginInstaller', () => { - let readJson: any; - beforeEach(() => { - mockfs(installedPluginFiles); - readJson = fs.readJson; - fs.readJson = (file: string) => { +jest.mock('fs-extra', () => { + const mod = { + ...jest.requireActual('fs-extra'), + readJson: jest.fn((file: string) => { const content = fileContent.get(normalizePath(file)); if (content) { return Promise.resolve(JSON.parse(content)); } else { return Promise.resolve(undefined); } - }; + }), + }; + + return { + ...mod, + default: mod, + }; +}); + +describe('pluginInstaller', () => { + beforeEach(() => { + mockfs(installedPluginFiles); }); afterEach(() => { mockfs.restore(); - fs.readJson = readJson; }); test('getInstalledPlugins', async () => { diff --git a/desktop/plugin-lib/src/getPluginDetails.tsx b/desktop/plugin-lib/src/getPluginDetails.tsx index 9a58ca771..3d3ed85e5 100644 --- a/desktop/plugin-lib/src/getPluginDetails.tsx +++ b/desktop/plugin-lib/src/getPluginDetails.tsx @@ -68,7 +68,6 @@ export async function getInstalledPluginDetails( : undefined; return { ...pluginDetails, - isBundled: false, isActivatable: true, dir, entry, diff --git a/desktop/plugin-lib/src/pluginInstaller.tsx b/desktop/plugin-lib/src/pluginInstaller.tsx index 6cc04ae9e..365a44d6d 100644 --- a/desktop/plugin-lib/src/pluginInstaller.tsx +++ b/desktop/plugin-lib/src/pluginInstaller.tsx @@ -22,7 +22,6 @@ import {InstalledPluginDetails} from 'flipper-common'; import {getInstalledPluginDetails, isPluginDir} from './getPluginDetails'; import { getPluginVersionInstallationDir, - getPluginDirNameFromPackageName, getPluginInstallationDir, pluginInstallationDir, legacyPluginInstallationDir, @@ -102,19 +101,15 @@ export async function installPluginFromNpm(name: string) { await fs.ensureDir(tmpDir); const plugManNoDep = providePluginManagerNoDependencies(); plugManNoDep.options.pluginsPath = tmpDir; - await plugManNoDep.install(name); - const pluginTempDir = path.join( - tmpDir, - getPluginDirNameFromPackageName(name), - ); - return await installPluginFromTempDir(pluginTempDir); + const pluginInfo = await plugManNoDep.install(name); + return await installPluginFromTempDir(pluginInfo.location); } finally { await fs.remove(tmpDir); } } -export async function installPluginFromFile( - packagePath: string, +export async function installPluginFromFileOrBuffer( + packagePath: string | Buffer, ): Promise { const tmpDir = await promisify(tmp.dir)(); try { diff --git a/desktop/plugin-lib/src/pluginPaths.tsx b/desktop/plugin-lib/src/pluginPaths.tsx index 10c45b20d..382f14087 100644 --- a/desktop/plugin-lib/src/pluginPaths.tsx +++ b/desktop/plugin-lib/src/pluginPaths.tsx @@ -28,12 +28,6 @@ export const pluginCacheDir = path.join(flipperDataDir, 'plugins'); export async function getPluginSourceFolders(): Promise { const pluginFolders: string[] = []; - if (process.env.FLIPPER_NO_DEFAULT_PLUGINS) { - console.log( - '🥫 Skipping default plugins because "--no-default-plugins" flag provided', - ); - return pluginFolders; - } const flipperConfigPath = path.join(homedir(), '.flipper', 'config.json'); if (await fs.pathExists(flipperConfigPath)) { const config = await fs.readJson(flipperConfigPath); diff --git a/desktop/plugins/package.json b/desktop/plugins/package.json index 0999fcf5d..cb40f1649 100644 --- a/desktop/plugins/package.json +++ b/desktop/plugins/package.json @@ -17,7 +17,7 @@ "@types/node": "*", "@types/react": "*", "@types/react-dom": "*", - "antd": "*", + "antd": "^4.24", "flipper": "*", "flipper-plugin": "*", "react": "*", diff --git a/desktop/plugins/public/crash_reporter/docs/overview.mdx b/desktop/plugins/public/crash_reporter/docs/overview.mdx index f6054e055..daffef9d7 100644 --- a/desktop/plugins/public/crash_reporter/docs/overview.mdx +++ b/desktop/plugins/public/crash_reporter/docs/overview.mdx @@ -1,6 +1,6 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; -The Crash Reporter Plugin shows a notification in Flipper whenever an app crashes. You can click on the notification to see crash information such as stacktrace and other metadata. +The Crash Reporter plugin shows a notification in Flipper whenever an app crashes. You can click on the notification to see crash information such as stacktrace and other metadata. For Android, clicking on the 'Open in Logs' button jumps to the relevant row in the Logs plugin containing the crash information, as shown in the following screenshots. diff --git a/desktop/plugins/public/crash_reporter/docs/setup.mdx b/desktop/plugins/public/crash_reporter/docs/setup.mdx index 47eaefe4f..4099cdfaf 100644 --- a/desktop/plugins/public/crash_reporter/docs/setup.mdx +++ b/desktop/plugins/public/crash_reporter/docs/setup.mdx @@ -10,7 +10,7 @@ In order to send custom notifications, take the steps detailed below. ## Android -Instantiate and add the plugin in `FlipperClient`. +1. Instantiate and add the plugin in `FlipperClient`. ```java import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; @@ -18,7 +18,7 @@ import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; client.addPlugin(CrashReporterPlugin.getInstance()); ``` -Use the following API to trigger your custom crash notification. +2. Use the following API to trigger your custom crash notification. ```java import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; diff --git a/desktop/plugins/public/databases/docs/setup.mdx b/desktop/plugins/public/databases/docs/setup.mdx index aefccf1d3..3153aa68f 100644 --- a/desktop/plugins/public/databases/docs/setup.mdx +++ b/desktop/plugins/public/databases/docs/setup.mdx @@ -21,7 +21,7 @@ client.addPlugin(new DatabasesFlipperPlugin(new SqliteDatabaseDriver(context, ne for (String databaseName : context.databaseList()) { databaseFiles.add(context.getDatabasePath(databaseName)); } - databaseFiles.add("...path_to_your_db...") + databaseFiles.add("...path_to_your_db..."); return databaseFiles; } }))); diff --git a/desktop/plugins/public/databases/index.tsx b/desktop/plugins/public/databases/index.tsx index d5f20076b..f2879dfbb 100644 --- a/desktop/plugins/public/databases/index.tsx +++ b/desktop/plugins/public/databases/index.tsx @@ -108,7 +108,7 @@ export function plugin(client: PluginClient) { const updateDatabases = (event: { databases: Array<{name: string; id: number; tables: Array}>; }) => { - const updates = event.databases; + const updates = event.databases ?? []; const state = pluginState.get(); const databases = updates.sort((db1, db2) => db1.id - db2.id); const selectedDatabase = diff --git a/desktop/plugins/public/fresco/MultipleSelect.tsx b/desktop/plugins/public/fresco/MultipleSelect.tsx index e0a5fcd1c..404ae7184 100644 --- a/desktop/plugins/public/fresco/MultipleSelect.tsx +++ b/desktop/plugins/public/fresco/MultipleSelect.tsx @@ -11,6 +11,8 @@ import React, {Component} from 'react'; import {Layout} from 'flipper-plugin'; import {Button, Menu, Checkbox, Dropdown} from 'antd'; import {DownOutlined} from '@ant-design/icons'; +// This import is OK since it is a type-only import +// eslint-disable-next-line no-restricted-imports import type {CheckboxChangeEvent} from 'antd/lib/checkbox'; export default class MultipleSelect extends Component<{ diff --git a/desktop/plugins/public/fresco/docs/overview.mdx b/desktop/plugins/public/fresco/docs/overview.mdx index 71b7bb19f..6b160b36d 100644 --- a/desktop/plugins/public/fresco/docs/overview.mdx +++ b/desktop/plugins/public/fresco/docs/overview.mdx @@ -12,8 +12,8 @@ Images are grouped by the different caching layers they are stored in. The curre ## Attribution -Images can be annotated with attributes that can help to determine the context in which an image was loaded and displayed. You can use that information to filter by a particular surface or only inspect images that are in the critical path of your application, for instance during a cold start. +Images can be annotated with attributes that can help to determine the context in which an image was loaded and displayed. You can use that information to filter by a particular surface or only inspect images that are in the critical path of your application (such as during a cold start). ## Leak Tracking -Dealing with large resources can require special APIs to be used that circumvent usual garbage collection. The plugin allows tracking `CloseableReference`s for Fresco on Android that weren't properly closed, which can help you improve the performance of your app. +Dealing with large resources can require special APIs to be used that circumvent usual garbage collection. The plugin enables the tracking of `CloseableReference`s for Fresco on Android that weren't properly closed, which can help you improve the performance of your app. diff --git a/desktop/plugins/public/fresco/docs/setup.mdx b/desktop/plugins/public/fresco/docs/setup.mdx index b47478566..aff899d41 100644 --- a/desktop/plugins/public/fresco/docs/setup.mdx +++ b/desktop/plugins/public/fresco/docs/setup.mdx @@ -1,7 +1,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; import Link from '@docusaurus/Link'; -Currently, the Image plugin only supports [Fresco](https://frescolib.org/) for Android as backend. +Currently, the Images plugin only supports [Fresco](https://frescolib.org/) for Android as backend. If you'd like to see support for other image loading libraries, please post your request in the [Flipper Support](https://fb.workplace.com/groups/flippersupport) Workplace group. diff --git a/desktop/plugins/public/layout/Inspector.tsx b/desktop/plugins/public/layout/Inspector.tsx index 06684aa75..96d7054e9 100644 --- a/desktop/plugins/public/layout/Inspector.tsx +++ b/desktop/plugins/public/layout/Inspector.tsx @@ -108,7 +108,9 @@ export default class Inspector extends Component { label: 'Focus', click: (id: ElementID) => { if (this.props.client.isConnected) { - this.props.client.call('onRequestAXFocus', {id}); + this.props.client + .call('onRequestAXFocus', {id}) + .catch((e) => console.warn('Unable to request AX focus', e)); } }, }, @@ -341,20 +343,29 @@ export default class Inspector extends Component { } getElementLeaves(tree: ElementSelectorNode): Array { - return tree - ? Object.entries(tree).reduce( - ( - currLeafNode: Array, - [id, children]: [ElementID, ElementSelectorNode], - ): Array => - currLeafNode.concat( - Object.keys(children).length > 0 - ? this.getElementLeaves(children) - : [id], - ), - [], - ) - : []; + if (!tree) { + return []; + } + const leavesSet = new Set(); + + const treeIteratorStack: [ElementID, ElementSelectorNode][] = [ + ...Object.entries(tree), + ]; + while (treeIteratorStack.length) { + const [id, children] = treeIteratorStack.pop()!; + + if (leavesSet.has(id)) { + continue; + } + + if (Object.keys(children).length) { + treeIteratorStack.push(...Object.entries(children)); + } else { + leavesSet.add(id); + } + } + + return [...leavesSet]; } /// Return path from given tree structure and id if id is not null; otherwise return any path diff --git a/desktop/plugins/public/layout/docs/overview.mdx b/desktop/plugins/public/layout/docs/overview.mdx index 6a4489ddd..fde245b9f 100644 --- a/desktop/plugins/public/layout/docs/overview.mdx +++ b/desktop/plugins/public/layout/docs/overview.mdx @@ -1,8 +1,6 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; -The Layout Inspector in Flipper is useful for a wide variety of debugging scenarios. - -You can inspect what views the hierarchy is made up of as well as what properties each view has; this is incredibly useful when debugging issues with your product. +The Layout Inspector is useful for a wide variety of debugging scenarios. You can inspect what views the hierarchy is made up of as well as what properties each view has; this is incredibly useful when debugging issues with your product. In addition to Flipper, the Layout tab supports [Litho](https://fblitho.com) and [ComponentKit](https://componentkit.org) components; it integrates with these frameworks to present components in the hierarchy just as if they were native views, exposing all the layout properties, props, and state of the components. The Layout Inspector is further extensible to support other UI frameworks. diff --git a/desktop/plugins/public/layout/docs/setup.mdx b/desktop/plugins/public/layout/docs/setup.mdx index 49c15af23..e2411c8ee 100644 --- a/desktop/plugins/public/layout/docs/setup.mdx +++ b/desktop/plugins/public/layout/docs/setup.mdx @@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.162.0' + debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.227.0' debugImplementation 'com.facebook.litho:litho-annotations:0.19.0' // ... } diff --git a/desktop/plugins/public/layout/index.tsx b/desktop/plugins/public/layout/index.tsx index 126d2eb4d..88e2fe897 100644 --- a/desktop/plugins/public/layout/index.tsx +++ b/desktop/plugins/public/layout/index.tsx @@ -34,7 +34,7 @@ import { IDEFileResolver, IDEType, } from 'flipper'; -import {message} from 'antd'; +import {message, notification} from 'antd'; import {getFlipperLib} from 'flipper-plugin'; type State = { @@ -216,6 +216,10 @@ export default class LayoutPlugin extends FlipperPlugin< private static isMylesInvoked = false; + componentDidMount() { + this.onSuggestUIDebugger(); + } + init() { if (!this.props.persistedState) { // If the selected plugin from the previous session was layout, then while importing the flipper export, the redux store doesn't get updated in the first render, due to which the plugin crashes, as it has no persisted state @@ -325,6 +329,71 @@ export default class LayoutPlugin extends FlipperPlugin< } }; + onSuggestUIDebugger = () => { + if (!getFlipperLib().GK('flipper_ui_debugger')) { + return; + } + + const lastShownTimestampKey = + 'layout-plugin-UIDebuggerBannerLastShownTimestamp'; + let lastShownTimestampFromStorage = undefined; + try { + lastShownTimestampFromStorage = window.localStorage.getItem( + lastShownTimestampKey, + ); + } catch (e) {} + + if (lastShownTimestampFromStorage) { + const WithinOneDay = (timestamp: number) => { + const Day = 1 * 24 * 60 * 60 * 1000; + const DayAgo = Date.now() - Day; + + return timestamp > DayAgo; + }; + const lastShownTimestamp = Number(lastShownTimestampFromStorage); + if (WithinOneDay(lastShownTimestamp)) { + // The banner was shown less than 24-hours ago, don't show it again. + return; + } + } + + const lastShownTimestamp = Date.now(); + try { + window.localStorage.setItem( + lastShownTimestampKey, + String(lastShownTimestamp), + ); + } catch (e) {} + + const key = `open-ui-debugger-${lastShownTimestamp}`; + const btn = ( + + ); + + notification.open({ + message: 'Layout plugin is being deprecated.', + description: `The new replacement plugin, UI Debugger, is available for use now. + Find it on the left panel`, + duration: 30, + type: 'warning', + btn, + key, + }); + }; + onToggleSnapshotMode = () => { this.setState((prevState) => ({inSnapshotMode: !prevState.inSnapshotMode})); }; @@ -340,6 +409,7 @@ export default class LayoutPlugin extends FlipperPlugin< }) : this.client; } + onToggleAlignmentMode = () => { if (this.state.selectedElement) { if (this.client.isConnected) { diff --git a/desktop/plugins/public/leak_canary/docs/setup.mdx b/desktop/plugins/public/leak_canary/docs/setup.mdx index 983db1bd6..c370bc8c9 100644 --- a/desktop/plugins/public/leak_canary/docs/setup.mdx +++ b/desktop/plugins/public/leak_canary/docs/setup.mdx @@ -2,13 +2,13 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; import Link from '@docusaurus/Link'; -To setup the Leak Canary plugin, take the following steps: +To setup the LeakCanary plugin, take the following steps: 1. Ensure that you have an explicit dependency in your application's `build.gradle` including the plugin dependency, such as is shown in the following snippet: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.162.0' + debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.227.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' } ``` @@ -16,7 +16,7 @@ dependencies { 2. Update your the `onCreate` method in you `Application` to add the LeakCanary2 plugin to Flipper and the Flipper listener to LeakCanary: ```kt -import com.facebook.flipper.plugins.leakcanary2.FlipperLeakListener +import com.facebook.flipper.plugins.leakcanary2.FlipperLeakEventListener import com.facebook.flipper.plugins.leakcanary2.LeakCanary2FlipperPlugin ... @@ -27,9 +27,9 @@ import com.facebook.flipper.plugins.leakcanary2.LeakCanary2FlipperPlugin /* set the flipper listener in leak canary config */ - LeakCanary.config = LeakCanary.config.copy( - onHeapAnalyzedListener = FlipperLeakListener() - ) + LeakCanary.config = LeakCanary.config.run { + copy(eventListeners = eventListeners + FlipperLeakEventListener()) + } SoLoader.init(this, false) diff --git a/desktop/plugins/public/logs/__tests__/logs.node.tsx b/desktop/plugins/public/logs/__tests__/logs.node.tsx index 09b476a5a..96532406c 100644 --- a/desktop/plugins/public/logs/__tests__/logs.node.tsx +++ b/desktop/plugins/public/logs/__tests__/logs.node.tsx @@ -50,8 +50,8 @@ test('it will merge equal rows', () => { sendLogEntry(entry3); expect(instance.rows.records()).toMatchInlineSnapshot(` - Array [ - Object { + [ + { "app": "X", "count": 1, "date": 2021-01-28T17:15:12.859Z, @@ -61,7 +61,7 @@ test('it will merge equal rows', () => { "tid": 1, "type": "error", }, - Object { + { "app": "Y", "count": 2, "date": 2021-01-28T17:15:17.859Z, @@ -71,7 +71,7 @@ test('it will merge equal rows', () => { "tid": 3, "type": "warn", }, - Object { + { "app": "X", "count": 1, "date": 2021-01-28T17:15:12.859Z, @@ -130,9 +130,9 @@ test('export / import plugin does work', async () => { const data = await exportStateAsync(); expect(data).toMatchInlineSnapshot(` - Object { - "logs": Array [ - Object { + { + "logs": [ + { "app": "X", "count": 1, "date": 2021-01-28T17:15:12.859Z, @@ -142,7 +142,7 @@ test('export / import plugin does work', async () => { "tid": 1, "type": "error", }, - Object { + { "app": "Y", "count": 1, "date": 2021-01-28T17:15:17.859Z, diff --git a/desktop/plugins/public/logs/docs/overview.mdx b/desktop/plugins/public/logs/docs/overview.mdx index 3fabf0500..c93e207dd 100644 --- a/desktop/plugins/public/logs/docs/overview.mdx +++ b/desktop/plugins/public/logs/docs/overview.mdx @@ -18,6 +18,6 @@ Clicking on a tag, PID or TID in the table filters only for logs with the same v ### Expression Watcher -The Expression Watcher () in the sidebar can be used to 'watch' for certain logs to happen and count how often they occur. An expression can be a simple string, or a regular expression, matched against the logs. +The Expression Watcher in the sidebar can be used to 'watch' for certain logs to happen and count how often they occur. An expression can be a simple string, or a regular expression, matched against the logs. When the notify checkbox is enabled, Flipper sends notifications once the log is being processed. This lets you know when the 'watcher' triggered, even if Flipper is in the background. diff --git a/desktop/plugins/public/logs/index.tsx b/desktop/plugins/public/logs/index.tsx index ccea40ca4..bd7b62c98 100644 --- a/desktop/plugins/public/logs/index.tsx +++ b/desktop/plugins/public/logs/index.tsx @@ -184,7 +184,9 @@ export function devicePlugin(client: DevicePluginClient) { } async function clearLogs() { - await client.device.clearLogs(); + if (client.device.connected.get()) { + await client.device.clearLogs(); + } rows.clear(); tableManagerRef.current?.clearSelection(); } diff --git a/desktop/plugins/public/navigation/__tests__/testURI.node.tsx b/desktop/plugins/public/navigation/__tests__/testURI.node.tsx index 8bac54e30..78eb83048 100644 --- a/desktop/plugins/public/navigation/__tests__/testURI.node.tsx +++ b/desktop/plugins/public/navigation/__tests__/testURI.node.tsx @@ -28,6 +28,14 @@ test('parse required numeric parameters from uri', () => { expect(getRequiredParameters(testURI)).toEqual(expectedResult); }); +// https://fb.workplace.com/groups/flippersupport/permalink/1513232162490770/ +test('ignore params with JSON values', () => { + const testURI = + 'fb://test_uri/?parameter1={"test":"value"}¶meter2="{\\"test\\":\\"value\\"}"'; + const expectedResult: string[] = []; + expect(getRequiredParameters(testURI)).toEqual(expectedResult); +}); + test('replace required parameters with values', () => { const testURI = 'fb://test_uri/?parameter1={parameter1}¶meter2={parameter2}'; diff --git a/desktop/plugins/public/navigation/components/RequiredParametersDialog.tsx b/desktop/plugins/public/navigation/components/RequiredParametersDialog.tsx index dead39be2..cf050dd4a 100644 --- a/desktop/plugins/public/navigation/components/RequiredParametersDialog.tsx +++ b/desktop/plugins/public/navigation/components/RequiredParametersDialog.tsx @@ -34,7 +34,7 @@ export function RequiredParametersDialog(props: Props) { useRequiredParameterFormValidator(requiredParameters); return ( + {(onHide: () => void) => { return ( diff --git a/desktop/plugins/public/navigation/docs/overview.mdx b/desktop/plugins/public/navigation/docs/overview.mdx index 1ce99d050..3aaa1438e 100644 --- a/desktop/plugins/public/navigation/docs/overview.mdx +++ b/desktop/plugins/public/navigation/docs/overview.mdx @@ -1,9 +1,12 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; -The Navigation Plugin enables users to quickly navigate to deep links within their mobile applications to help speed up the development cycle. The plugin is designed to integrate easily within your existing navigation framework or as a standalone tool. Users can bookmark deep links and jump to them via the button in the tool bar, as shown in the following screenshot. +The Navigation Plugin enables users to quickly navigate to deep links within their mobile applications to help speed up the development cycle. The plugin is designed to integrate easily within your existing navigation framework or as a standalone tool. + +Users can bookmark deep links and jump to them via the button in the tool bar, as shown in the following screenshot. Navigation Plugin Button -Navigation events within the app can also be logged to Flipper. This enables the user to view past navigation events and jump straight to them (see the following screenshot) or export the navigation events for reporting. + +Navigation events within the app can also be logged to Flipper, which enables the user to view past navigation events and jump straight to them (see the following screenshot) or export the navigation events for reporting. Navigation Plugin UI diff --git a/desktop/plugins/public/navigation/docs/setup.mdx b/desktop/plugins/public/navigation/docs/setup.mdx index b552f261c..94e24217d 100644 --- a/desktop/plugins/public/navigation/docs/setup.mdx +++ b/desktop/plugins/public/navigation/docs/setup.mdx @@ -18,7 +18,7 @@ This enables the Navigation Plugin to be integrated into existing navigation fra ### Using Android deep links -The Navigation Plugin can be used with built in [deep links for Android](https://developer.android.com/training/app-links/deep-linking). +The Navigation Plugin can be used with built-in [Deep Links for Android](https://developer.android.com/training/app-links/deep-linking). To deep link to an activity, edit the AndroidManifest.xml and add the intent filter for the given activity, as follows: @@ -50,7 +50,7 @@ The Navigation Plugin can easily be integrated into a third-party navigation fra #### AirBnB deep link dispatch -[Deep Link Dispatch](https://github.com/airbnb/DeepLinkDispatch) will work out of the box with Flipper for navigating to links, including support for url parameters. +[DeepLinkDispatch](https://github.com/airbnb/DeepLinkDispatch) will work out of the box with Flipper for navigating to links, including support for url parameters. To add logging, simply add a BroadcastReceiver to your app that is called on any incoming deep links: @@ -74,7 +74,3 @@ public class DeepLinkApplication extends Application { } } ``` - -## iOS - -iOS support is coming soon. diff --git a/desktop/plugins/public/navigation/util/uri.tsx b/desktop/plugins/public/navigation/util/uri.tsx index 3e43f75e5..fae2610f3 100644 --- a/desktop/plugins/public/navigation/util/uri.tsx +++ b/desktop/plugins/public/navigation/util/uri.tsx @@ -7,8 +7,6 @@ * @format */ -import querystring from 'querystring'; - export const validateParameter = (value: string, parameter: string) => { return ( value && @@ -26,10 +24,10 @@ export const filterOptionalParameters = (uri: string) => { export const parseURIParameters = (query: string) => { // get parameters from query string and store in Map const parameters = query.split('?').splice(1).join(''); - const parametersObj = querystring.parse(parameters); + const parametersObj = new URLSearchParams(parameters); const parametersMap = new Map(); for (const key in parametersObj) { - parametersMap.set(key, parametersObj[key] as string); + parametersMap.set(key, parametersObj.get(key) as string); } return parametersMap; }; @@ -62,12 +60,20 @@ export const replaceRequiredParametersWithValues = ( }; export const getRequiredParameters = (uri: string) => { - const parameterRegExp = /{[^?]*?}/g; + // Add = to the matching group to filter out stringified JSON parameters + const parameterRegExp = /={[^?]*?}/g; const matches: Array = []; let match = parameterRegExp.exec(uri); while (match != null) { if (match[0]) { - matches.push(match[0]); + // Remove = from the match + const target = match[0].substring(1); + try { + // If the value could be parsed asa valid JSON, ignore it + JSON.parse(target); + } catch { + matches.push(target); + } } match = parameterRegExp.exec(uri); } diff --git a/desktop/plugins/public/network/RequestDetails.tsx b/desktop/plugins/public/network/RequestDetails.tsx index ecb912cc7..62050ef4f 100644 --- a/desktop/plugins/public/network/RequestDetails.tsx +++ b/desktop/plugins/public/network/RequestDetails.tsx @@ -9,7 +9,6 @@ import React from 'react'; import {Component} from 'react'; -import querystring from 'querystring'; import xmlBeautifier from 'xml-beautifier'; import {Base64} from 'js-base64'; @@ -23,7 +22,13 @@ import { } from 'flipper-plugin'; import {Select, Typography} from 'antd'; -import {bodyAsBinary, bodyAsString, formatBytes, getHeaderValue} from './utils'; +import { + bodyAsBinary, + bodyAsString, + formatBytes, + getHeaderValue, + queryToObj, +} from './utils'; import {Request, Header, Insights, RetryInsights} from './types'; import {BodyOptions} from './index'; import {ProtobufDefinitionsRepository} from './ProtobufDefinitionsRepository'; @@ -523,7 +528,7 @@ class JSONFormatter { class LogEventFormatter { formatRequest(request: Request) { if (request.url.indexOf('logging_client_event') > 0) { - const data = querystring.parse(bodyAsString(request.requestData)); + const data = queryToObj(bodyAsString(request.requestData)); if (typeof data.message === 'string') { data.message = JSON.parse(data.message); } @@ -535,7 +540,7 @@ class LogEventFormatter { class GraphQLBatchFormatter { formatRequest(request: Request) { if (request.url.indexOf('graphqlbatch') > 0) { - const data = querystring.parse(bodyAsString(request.requestData)); + const data = queryToObj(bodyAsString(request.requestData)); if (typeof data.queries === 'string') { data.queries = JSON.parse(data.queries); } @@ -575,7 +580,7 @@ class GraphQLFormatter { if (!decoded) { return undefined; } - const data = querystring.parse(bodyAsString(decoded)); + const data = queryToObj(bodyAsString(decoded)); if (typeof data.variables === 'string') { data.variables = JSON.parse(data.variables); } @@ -636,10 +641,7 @@ class FormUrlencodedFormatter { return undefined; } return ( - + ); } }; diff --git a/desktop/plugins/public/network/__tests__/chunks.node.tsx b/desktop/plugins/public/network/__tests__/chunks.node.tsx index 92a0121e2..88f8e796e 100644 --- a/desktop/plugins/public/network/__tests__/chunks.node.tsx +++ b/desktop/plugins/public/network/__tests__/chunks.node.tsx @@ -47,11 +47,11 @@ test('Reducer correctly adds initial chunk', () => { }); expect(instance.partialResponses.get()['1']).toMatchInlineSnapshot(` - Object { - "followupChunks": Object {}, - "initialResponse": Object { + { + "followupChunks": {}, + "initialResponse": { "data": "hello", - "headers": Array [], + "headers": [], "id": "1", "index": 0, "insights": null, @@ -76,8 +76,8 @@ test('Reducer correctly adds followup chunk', () => { data: 'hello', }); expect(instance.partialResponses.get()['1']).toMatchInlineSnapshot(` - Object { - "followupChunks": Object { + { + "followupChunks": { "1": "hello", }, } @@ -113,13 +113,13 @@ test('Reducer correctly combines initial response and followup chunk', () => { totalChunks: 2, }); expect(instance.partialResponses.get()).toMatchInlineSnapshot(` - Object { - "1": Object { - "followupChunks": Object {}, - "initialResponse": Object { + { + "1": { + "followupChunks": {}, + "initialResponse": { "data": "aGVs", - "headers": Array [ - Object { + "headers": [ + { "key": "Content-Type", "value": "text/plain", }, diff --git a/desktop/plugins/public/network/__tests__/customheaders.node.tsx b/desktop/plugins/public/network/__tests__/customheaders.node.tsx index 6f2e3b768..63e860d44 100644 --- a/desktop/plugins/public/network/__tests__/customheaders.node.tsx +++ b/desktop/plugins/public/network/__tests__/customheaders.node.tsx @@ -75,13 +75,13 @@ test('Can handle custom headers', async () => { // verify internal storage expect(instance.columns.get().slice(-2)).toMatchInlineSnapshot(` - Array [ - Object { + [ + { "key": "request_header_test-header", "title": "test-header (request)", "width": 200, }, - Object { + { "key": "response_header_second-test-header", "title": "second-test-header (response)", "width": 200, @@ -138,13 +138,13 @@ test('Can handle custom headers', async () => { // verify internal storage expect(instance2.columns.get().slice(-2)).toMatchInlineSnapshot(` - Array [ - Object { + [ + { "key": "request_header_test-header", "title": "test-header (request)", "width": 200, }, - Object { + { "key": "response_header_second-test-header", "title": "second-test-header (response)", "width": 200, diff --git a/desktop/plugins/public/network/docs/setup.mdx b/desktop/plugins/public/network/docs/setup.mdx index 1dcfb6f6e..8c97345dc 100644 --- a/desktop/plugins/public/network/docs/setup.mdx +++ b/desktop/plugins/public/network/docs/setup.mdx @@ -12,7 +12,7 @@ The network plugin is shipped as a separate Maven artifact, as follows: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.162.0' + debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.227.0' } ``` diff --git a/desktop/plugins/public/network/index.tsx b/desktop/plugins/public/network/index.tsx index b9ce02eb1..e056dd712 100644 --- a/desktop/plugins/public/network/index.tsx +++ b/desktop/plugins/public/network/index.tsx @@ -55,9 +55,9 @@ import { formatDuration, requestsToText, decodeBody, + formatOperationName, } from './utils'; import RequestDetails from './RequestDetails'; -import {URL} from 'url'; import {assembleChunksIfResponseIsComplete} from './chunks'; import {DeleteOutlined} from '@ant-design/icons'; import {ManageMockResponsePanel} from './request-mocking/ManageMockResponsePanel'; @@ -240,11 +240,15 @@ export function plugin(client: PluginClient) { function init() { supportsMocks(client.device) .then((result) => { - const newRoutes = JSON.parse( + const newRouteArray: [any] = JSON.parse( localStorage.getItem( LOCALSTORAGE_MOCK_ROUTE_LIST_KEY + client.appId, - ) || '{}', + ) || '[]', ); + const newRoutes: {[id: string]: any} = {}; + newRouteArray.forEach((value, index) => { + newRoutes[index.toString()] = value; + }); batch(() => { routes.set(newRoutes); isMockResponseSupported.set(result); @@ -254,7 +258,11 @@ export function plugin(client: PluginClient) { informClientMockChange(routes.get()); }) - .catch((e) => console.error('[network] Failed to init mocks:', e)); + .catch((e) => { + if (client.device.connected.get()) { + console.error('[network] Failed to init mocks:', e); + } + }); // declare new variable to be called inside the interface networkRouteManager.set( @@ -469,7 +477,7 @@ function showCustomColumnDialog( return ( { const header = form.getFieldValue('header'); const type = form.getFieldValue('type'); @@ -606,7 +614,7 @@ export function Component() { } /> [] = [ width: 120, visible: false, }, + { + key: 'requestData', + title: 'GraphQL operation name', + width: 120, + visible: false, + formatters: formatOperationName, + }, { key: 'domain', }, diff --git a/desktop/plugins/public/network/package.json b/desktop/plugins/public/network/package.json index 9807b1629..2821132b0 100644 --- a/desktop/plugins/public/network/package.json +++ b/desktop/plugins/public/network/package.json @@ -16,6 +16,8 @@ "url": "https://github.com/facebook/flipper/issues" }, "dependencies": { + "brotli": "^1.3.3", + "buffer": "^6.0.3", "lodash": "^4.17.21", "pako": "^2.0.3", "protobufjs": "^6.10.2", @@ -26,7 +28,8 @@ "flipper-plugin": "*" }, "devDependencies": { - "@types/pako": "^1.0.1", + "@types/brotli": "^1.3.1", + "@types/pako": "^2.0.0", "js-base64": "^3.6.0" } } diff --git a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx index 24f0a2bd6..9322d0bc8 100644 --- a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx +++ b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx @@ -139,6 +139,9 @@ export function createNetworkManager( }) .then((res) => { if (res) { + if (res.encoding !== 'utf-8' || typeof res.data !== 'string') { + return; + } const importedRoutes = JSON.parse(res.data); importedRoutes?.forEach((importedRoute: Route) => { if (importedRoute != null) { diff --git a/desktop/plugins/public/network/utils.tsx b/desktop/plugins/public/network/utils.tsx index fdd64b6de..a6a62c681 100644 --- a/desktop/plugins/public/network/utils.tsx +++ b/desktop/plugins/public/network/utils.tsx @@ -7,6 +7,8 @@ * @format */ +import {Buffer} from 'buffer'; +import decompress from 'brotli/decompress'; import pako from 'pako'; import {Request, Header, ResponseInfo} from './types'; import {Base64} from 'js-base64'; @@ -99,26 +101,44 @@ export function decodeBody( } try { - const isGzip = getHeaderValue(headers, 'Content-Encoding') === 'gzip'; - if (isGzip) { - try { - // The request is gzipped, so convert the raw bytes back to base64 first. - const dataArr = Base64.toUint8Array(data); - // then inflate. - return isTextual(headers, dataArr) - ? // pako will detect the BOM headers and return a proper utf-8 string right away - pako.inflate(dataArr, {to: 'string'}) - : pako.inflate(dataArr); - } catch (e) { - // on iOS, the stream send to flipper is already inflated, so the content-encoding will not - // match the actual data anymore, and we should skip inflating. - // In that case, we intentionally fall-through - if (!('' + e).includes('incorrect header check')) { - throw e; + const contentEncoding = getHeaderValue(headers, 'Content-Encoding'); + switch (contentEncoding) { + // Gzip encoding + case 'gzip': { + try { + // The request is gzipped, so convert the raw bytes back to base64 first. + const dataArr = Base64.toUint8Array(data); + // then inflate. + return isTextual(headers, dataArr) + ? // pako will detect the BOM headers and return a proper utf-8 string right away + pako.inflate(dataArr, {to: 'string'}) + : pako.inflate(dataArr); + } catch (e) { + // on iOS, the stream send to flipper is already inflated, so the content-encoding will not + // match the actual data anymore, and we should skip inflating. + // In that case, we intentionally fall-through + if (!('' + e).includes('incorrect header check')) { + throw e; + } + break; } } + + // Brotli encoding (https://github.com/facebook/flipper/issues/2578) + case 'br': { + const inflated = decompress(Buffer.from(Base64.toUint8Array(data))); + + // on iOS, the stream send to flipper is already inflated, so the content-encoding will not + // match the actual data anymore, so a 0-length result without an error is expected. + // In that case, we intentionally fall-through + if (inflated.length === 0 && data.length > 0) { + break; + } + + return new TextDecoder().decode(inflated); + } } - // If this is not a gzipped request, assume we are interested in a proper utf-8 string. + // If this is not a gzipped or brotli-encoded request, assume we are interested in a proper utf-8 string. // - If the raw binary data in is needed, in base64 form, use data directly // - either directly use data (for example) const bytes = Base64.toUint8Array(data); @@ -170,6 +190,15 @@ export function bodyAsBinary( return undefined; } +export const queryToObj = (query: string) => { + const params = new URLSearchParams(query); + const obj: Record = {}; + params.forEach((value, key) => { + obj[key] = value; + }); + return obj; +}; + function escapeCharacter(x: string) { const code = x.charCodeAt(0); return code < 16 ? '\\u0' + code.toString(16) : '\\u' + code.toString(16); @@ -243,6 +272,15 @@ export function formatStatus(status: number | undefined) { return status ? '' + status : ''; } +export function formatOperationName(requestData: string): string { + try { + const parsedData = JSON.parse(requestData); + return parsedData?.operationName; + } catch (_err) { + return ''; + } +} + export function requestsToText(requests: Request[]): string { const request = requests[0]; if (!request || !request.url) { diff --git a/desktop/plugins/public/package.json b/desktop/plugins/public/package.json index e6473ecc8..133ae4999 100644 --- a/desktop/plugins/public/package.json +++ b/desktop/plugins/public/package.json @@ -7,7 +7,8 @@ "*" ], "nohoist": [ - "flipper-plugin-kaios-big-allocations/**" + "flipper-plugin-kaios-big-allocations/**", + "flipper-plugin-ui-debugger/**" ] }, "bugs": { diff --git a/desktop/plugins/public/reactdevtools/index.tsx b/desktop/plugins/public/reactdevtools/index.tsx index b07c124db..f14262d07 100644 --- a/desktop/plugins/public/reactdevtools/index.tsx +++ b/desktop/plugins/public/reactdevtools/index.tsx @@ -78,8 +78,9 @@ export function devicePlugin(client: DevicePluginClient) { if (newGlobalDevToolsSource) { globalDevToolsInstance = { type: 'global', + // https://esbuild.github.io/content-types/#direct-eval // eslint-disable-next-line no-eval - module: eval(newGlobalDevToolsSource), + module: (0, eval)(newGlobalDevToolsSource), }; globalDevToolsAvailable.set(true); @@ -218,7 +219,9 @@ export function devicePlugin(client: DevicePluginClient) { }, send(event: any, payload: any) { const data = {event, payload}; - client.sendToServerAddOn('message', data); + client.sendToServerAddOn('message', data).catch((e) => { + console.warn(`Failed to send message to React devtools`, e); + }); }, }; diff --git a/desktop/plugins/public/reactdevtools/package.json b/desktop/plugins/public/reactdevtools/package.json index 63d1f59b7..32139443a 100644 --- a/desktop/plugins/public/reactdevtools/package.json +++ b/desktop/plugins/public/reactdevtools/package.json @@ -21,7 +21,8 @@ "dependencies": { "@rollup/plugin-commonjs": "^21.0.3", "@rollup/plugin-node-resolve": "^13.1.3", - "react-devtools-inline": "^4.24.3", + "react-devtools-core": "^4.28.0", + "react-devtools-inline": "^4.28.0", "rollup": "^2.70.1", "ws": "^8.5.0" }, @@ -30,8 +31,14 @@ "bugs": { "url": "https://github.com/facebook/flipper/issues" }, - "devDependencies": {}, + "devDependencies": { + "fs-extra": "^11.1.1", + "ts-node": "^10.9.1" + }, "peerDependencies": { "flipper-plugin": "*" + }, + "scripts": { + "postinstall": "ts-node scripts/remove-sourcemap-reference.tsx || true" } } diff --git a/desktop/plugins/public/reactdevtools/scripts/remove-sourcemap-reference.tsx b/desktop/plugins/public/reactdevtools/scripts/remove-sourcemap-reference.tsx new file mode 100644 index 000000000..8641083fc --- /dev/null +++ b/desktop/plugins/public/reactdevtools/scripts/remove-sourcemap-reference.tsx @@ -0,0 +1,51 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +/* eslint-disable no-restricted-imports */ + +/* + * This script attempts to get rid of a sourcemap reference in react-devtools-frontend + * that is causing a rather annoying non-fetal error at Flipper startup. + * + * It will gracefully fail as Flipper will still work without it, just show the error again. + */ + +import fs from 'fs-extra'; +import path from 'path'; + +const SOURCEMAP_REFERENCE = + '//# sourceMappingURL=importFile.worker.worker.js.map'; + +async function main() { + const frontendPath = path.resolve( + __dirname, + '../../node_modules/react-devtools-inline/dist/frontend.js', + ); + console.log(`Looking up ${frontendPath} for patching ...`); + // Check if path exists + if (!(await fs.pathExists(frontendPath))) { + console.warn('react-devtools-inline frontend not found, skipping patching'); + return 0; + } + + const content = await fs.readFile(frontendPath, 'utf-8'); + if (!content.includes(SOURCEMAP_REFERENCE)) { + console.log('react-devtools-inline appears to already be patched.'); + return 0; + } + + await fs.writeFile(frontendPath, content.replaceAll(SOURCEMAP_REFERENCE, '')); + + console.log('react-devtools-inline patched successfully.'); + return 0; +} + +(async () => { + process.exit(await main()); +})(); diff --git a/desktop/plugins/public/reactdevtools/scripts/tsconfig.json b/desktop/plugins/public/reactdevtools/scripts/tsconfig.json new file mode 100644 index 000000000..a8d67065a --- /dev/null +++ b/desktop/plugins/public/reactdevtools/scripts/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2021", + "module": "nodenext", + "jsx": "react", + "noEmit": true, + "strict": true, + "moduleResolution": "node", + "esModuleInterop": true + } +} diff --git a/desktop/plugins/public/seamammals/src/__tests__/seamammals.spec.tsx b/desktop/plugins/public/seamammals/src/__tests__/seamammals.spec.tsx index b51e3670f..ae4f18e1a 100644 --- a/desktop/plugins/public/seamammals/src/__tests__/seamammals.spec.tsx +++ b/desktop/plugins/public/seamammals/src/__tests__/seamammals.spec.tsx @@ -28,13 +28,13 @@ test('It can store rows', () => { }); expect(instance.rows.get()).toMatchInlineSnapshot(` - Object { - "1": Object { + { + "1": { "id": 1, "title": "Dolphin", "url": "http://dolphin.png", }, - "2": Object { + "2": { "id": 2, "title": "Turtle", "url": "http://turtle.png", diff --git a/desktop/plugins/public/shared_preferences/src/__test__/shared_preferences.spec.tsx b/desktop/plugins/public/shared_preferences/src/__test__/shared_preferences.spec.tsx index 27575b4c3..3d661287e 100644 --- a/desktop/plugins/public/shared_preferences/src/__test__/shared_preferences.spec.tsx +++ b/desktop/plugins/public/shared_preferences/src/__test__/shared_preferences.spec.tsx @@ -37,16 +37,16 @@ test('general plugin logic testing', async () => { await sleep(1000); expect(onSend).toBeCalledWith('getAllSharedPreferences', {}); expect(instance.sharedPreferences.get()).toMatchInlineSnapshot(` - Object { - "other_sample": Object { - "changesList": Array [], - "preferences": Object { + { + "other_sample": { + "changesList": [], + "preferences": { "SomeKey": 1337, }, }, - "sample": Object { - "changesList": Array [], - "preferences": Object { + "sample": { + "changesList": [], + "preferences": { "Hello": "world", }, }, @@ -77,16 +77,16 @@ test('general plugin logic testing', async () => { 5555, ); expect(instance.sharedPreferences.get()).toMatchInlineSnapshot(` - Object { - "other_sample": Object { - "changesList": Array [], - "preferences": Object { + { + "other_sample": { + "changesList": [], + "preferences": { "SomeKey": 5555, }, }, - "sample": Object { - "changesList": Array [ - Object { + "sample": { + "changesList": [ + { "deleted": false, "name": "SomeKey", "preferences": "sample", @@ -94,7 +94,7 @@ test('general plugin logic testing', async () => { "value": 5555, }, ], - "preferences": Object { + "preferences": { "Hello": "world", "SomeKey": 5555, }, @@ -120,20 +120,20 @@ test('general plugin logic testing', async () => { instance.sharedPreferences.get().sample.preferences.SomeKey, ).toBeUndefined(); expect(instance.sharedPreferences.get()).toMatchInlineSnapshot(` - Object { - "other_sample": Object { - "changesList": Array [], - "preferences": Object {}, + { + "other_sample": { + "changesList": [], + "preferences": {}, }, - "sample": Object { - "changesList": Array [ - Object { + "sample": { + "changesList": [ + { "deleted": true, "name": "SomeKey", "preferences": "sample", "time": 2, }, - Object { + { "deleted": false, "name": "SomeKey", "preferences": "sample", @@ -141,7 +141,7 @@ test('general plugin logic testing', async () => { "value": 5555, }, ], - "preferences": Object { + "preferences": { "Hello": "world", }, }, diff --git a/desktop/plugins/public/shared_preferences/src/index.tsx b/desktop/plugins/public/shared_preferences/src/index.tsx index 2eed60f74..a60dbd2b5 100644 --- a/desktop/plugins/public/shared_preferences/src/index.tsx +++ b/desktop/plugins/public/shared_preferences/src/index.tsx @@ -18,11 +18,17 @@ import { styled, Select, } from 'flipper'; -import {PluginClient, createState, usePlugin, useValue} from 'flipper-plugin'; +import { + PluginClient, + createState, + usePlugin, + useValue, + getFlipperLib, +} from 'flipper-plugin'; import {clone} from 'lodash'; import React from 'react'; - +import {Button, notification} from 'antd'; type SharedPreferencesChangeEvent = { preferences: string; name: string; @@ -96,6 +102,55 @@ export function plugin(client: PluginClient) { }); } + async function saveToFile() { + if (selectedPreferences.get() != null) { + try { + const name = selectedPreferences.get() as string; + await getFlipperLib().exportFile( + JSON.stringify(sharedPreferences.get()[name]), + { + defaultPath: name, + }, + ); + } catch (e) { + notification.error({ + message: 'Save failed', + description: `Could not save shared preferences to file`, + duration: 15, + }); + } + } + } + async function loadFromFile() { + const file = await getFlipperLib().importFile(); + if (file && file.encoding === 'utf-8' && typeof file.data === 'string') { + try { + const preferences = JSON.parse(file.data) as SharedPreferencesEntry; + const name = selectedPreferences.get(); + if (name != null) { + updateSharedPreferences({ + name: name, + preferences: preferences.preferences, + }); + + for (const key in preferences.preferences) { + await client.send('setSharedPreference', { + sharedPreferencesName: name, + preferenceName: key, + preferenceValue: preferences.preferences[key], + }); + } + } + } catch (e) { + console.warn('Unable to import shared preferences', e); + } + } else { + console.warn( + 'The loaded file either has wrong encoding or is not a valid json file', + ); + } + } + client.onMessage('sharedPreferencesChange', (change) => sharedPreferences.update((draft) => { const entry = draft[change.preferences]; @@ -124,6 +179,8 @@ export function plugin(client: PluginClient) { setSelectedPreferences, setSharedPreference, deleteSharedPreference, + saveToFile, + loadFromFile, }; } @@ -179,6 +236,16 @@ export function Component() { selected={selectedPreferences} onChange={instance.setSelectedPreferences} /> + Options + + diff --git a/desktop/plugins/public/ui-debugger/ClientTypes.tsx b/desktop/plugins/public/ui-debugger/ClientTypes.tsx new file mode 100644 index 000000000..94e2c297c --- /dev/null +++ b/desktop/plugins/public/ui-debugger/ClientTypes.tsx @@ -0,0 +1,288 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {TraversalMode} from './DesktopTypes'; + +export type Events = { + init: InitEvent; + subtreeUpdate: SubtreeUpdateEvent; + frameScan: FrameScanEvent; + perfStats: PerfStatsEvent; + performanceStats: PerformanceStatsEvent; + metadataUpdate: UpdateMetadataEvent; + setTraversalMode: SetTraversalModeEvent; +}; + +export type Methods = { + onTraversalModeChange(params: {mode: TraversalMode}): Promise; +}; + +export type SetTraversalModeEvent = { + mode: TraversalMode; +}; + +export type FrameScanEvent = { + frameTime: number; + nodes: ClientNode[]; + snapshot?: SnapshotInfo; + frameworkEvents?: FrameworkEvent[]; +}; + +/** + * @deprecated This event should not be used and soon will + * be removed. FrameScan should be used instead. + */ +export type SubtreeUpdateEvent = { + txId: number; + rootId: Id; + nodes: ClientNode[]; + snapshot: Snapshot; + frameworkEvents?: FrameworkEvent[]; +}; + +export type NodeMap = Map; +export type FrameworkEventType = string; + +export type FrameworkEventMetadata = { + type: FrameworkEventType; + documentation: string; +}; + +type JsonObject = { + [key: string]: JSON; +}; + +type JSON = string | number | boolean | null | JSON[] | JsonObject; + +type Stacktrace = {type: 'stacktrace'; stacktrace: string[]}; +type Reason = {type: 'reason'; reason: string}; +type UpstreamEvent = {type: 'upstreamEvent'; eventId: Id}; +type FrameworkEventAttribution = Stacktrace | Reason | UpstreamEvent; + +export type FrameworkEvent = { + id: number; + treeId?: Id; //todo should be mandatory once ios implements this + nodeId: Id; + type: FrameworkEventType; + timestamp: number; + payload?: JsonObject; + duration?: number; + attribution?: FrameworkEventAttribution; + thread?: 'main' | string; //todo should be mandatory once ios implements this +}; + +export type InitEvent = { + rootId: Id; + frameworkEventMetadata?: FrameworkEventMetadata[]; + supportedTraversalModes?: TraversalMode[]; + currentTraversalMode?: TraversalMode; +}; + +/** + * @deprecated This performance event should not be used and soon will + * be removed. PerformanceStatsEvent should be used instead. + */ +export type PerfStatsEvent = { + txId: number; + observerType: string; + start: number; + traversalComplete: number; + snapshotComplete: number; + queuingComplete: number; + deferredComputationComplete: number; + serializationComplete: number; + socketComplete: number; + nodesCount: number; +}; + +export type PerformanceStatsEvent = { + txId: number; + observerType: string; + nodesCount: number; + start: number; + traversalMS: number; + snapshotMS: number; + queuingMS: number; + deferredComputationMS: number; + serializationMS: number; + socketMS: number; + payloadSize?: number; +}; + +export type DynamicPerformanceStatsEvent = PerformanceStatsEvent & { + [key: string]: any; +}; + +export type UpdateMetadataEvent = { + attributeMetadata: Record; +}; + +export type UpdateAvailableTraversalModeEvent = { + modes: TraversalMode[]; +}; + +export type ClientNode = { + id: Id; + parent?: Id; + qualifiedName: string; //this is the name of the component plus qualification so myles has a chance of finding it. E.g com.facebook.MyView + lineNumber?: number; + name: string; + attributes: Record; + inlineAttributes: Record; + hiddenAttributes?: any; + children: Id[]; + bounds: Bounds; + tags: Tag[]; + activeChild?: Id; +}; + +export type Metadata = { + id: MetadataId; + type: string; + namespace: string; + name: string; + mutable: boolean; + customAttributes?: Record; +}; + +export type Bounds = { + x: number; + y: number; + width: number; + height: number; +}; + +export type Size = { + width: number; + height: number; +}; + +export type SpaceBox = { + top: number; + right: number; + bottom: number; + left: number; +}; + +export type Coordinate = { + x: number; + y: number; +}; + +export type Coordinate3D = { + x: number; + y: number; + z: number; +}; + +export type Color = { + r: number; + g: number; + b: number; + a: number; +}; + +export type Snapshot = string; +export type SnapshotInfo = {nodeId: Id; data: Snapshot}; +export type Id = number | string; + +export type MetadataId = number; + +export type Tag = + | 'Native' + | 'Declarative' + | 'Android' + | 'Litho' + | 'LithoMountable' + | 'CK' + | 'iOS' + | 'BloksBoundTree' + | 'BloksDerived' + | 'TreeRoot' + | 'Warning'; + +export type Inspectable = + | InspectableObject + | InspectableArray + | InspectableText + | InspectableNumber + | InspectableColor + | InspectableBoolean + | InspectableEnum + | InspectableCoordinate + | InspectableCoordinate3D + | InspectableSize + | InspectableBounds + | InspectableSpaceBox + | InspectableUnknown; + +export type InspectableText = { + type: 'text'; + value: string; +}; + +export type InspectableNumber = { + type: 'number'; + value: number; +}; + +export type InspectableBoolean = { + type: 'boolean'; + value: boolean; +}; + +export type InspectableEnum = { + type: 'enum'; + value: string; +}; + +export type InspectableColor = { + type: 'color'; + value: Color; +}; + +export type InspectableBounds = { + type: 'bounds'; + value: Bounds; +}; + +export type InspectableSize = { + type: 'size'; + value: Size; +}; + +export type InspectableCoordinate = { + type: 'coordinate'; + value: Coordinate; +}; + +export type InspectableCoordinate3D = { + type: 'coordinate3d'; + value: Coordinate3D; +}; + +export type InspectableSpaceBox = { + type: 'space'; + value: SpaceBox; +}; + +export type InspectableObject = { + type: 'object'; + fields: Record; +}; + +export type InspectableArray = { + type: 'array'; + items: Inspectable[]; +}; + +export type InspectableUnknown = { + type: 'unknown'; + value: string; +}; diff --git a/desktop/plugins/public/ui-debugger/DesktopTypes.tsx b/desktop/plugins/public/ui-debugger/DesktopTypes.tsx new file mode 100644 index 000000000..b79f8a228 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/DesktopTypes.tsx @@ -0,0 +1,163 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Atom, _ReadOnlyAtom} from 'flipper-plugin'; +import { + Id, + FrameworkEventType, + FrameworkEvent, + Inspectable, + Bounds, + Tag, + ClientNode, + Metadata, + SnapshotInfo, +} from './ClientTypes'; +import TypedEmitter from 'typed-emitter'; + +export type LiveClientState = { + snapshotInfo: SnapshotInfo | null; + nodes: Map; +}; + +export type Color = string; + +export type UIState = { + viewMode: Atom; + wireFrameMode: Atom; + isConnected: Atom; + isPaused: Atom; + streamState: Atom; + searchTerm: Atom; + isContextMenuOpen: Atom; + hoveredNodes: Atom; + selectedNode: Atom; + highlightedNodes: Atom>; + focusedNode: Atom; + expandedNodes: Atom>; + visualiserWidth: Atom; + frameworkEventMonitoring: Atom>; + filterMainThreadMonitoring: Atom; + + supportedTraversalModes: Atom; + traversalMode: Atom; +}; + +//enumerates the keys of input type and casts each to ReadOnlyAtom, this is so we only expose read only atoms to the UI +//and all writes come through UIActions +type TransformToReadOnly = { + [P in keyof T]: T[P] extends Atom ? _ReadOnlyAtom : T[P]; +}; + +export type WireFrameMode = 'All' | 'SelectedAndChildren' | 'SelectedOnly'; + +export type ReadOnlyUIState = TransformToReadOnly; + +export type StreamFlowState = {paused: boolean}; + +export type NestedNode = { + id: Id; + name: string; + attributes: Record; + children: NestedNode[]; + bounds: Bounds; + tags: Tag[]; + activeChildIdx?: number; +}; + +export type TraversalMode = 'view-hierarchy' | 'accessibility-hierarchy'; + +export type ViewMode = + | {mode: 'default'} + | {mode: 'frameworkEventsTable'; nodeId: Id; isTree: boolean}; + +export type NodeSelection = { + id: Id; + source: SelectionSource; +}; + +export type AugmentedFrameworkEvent = FrameworkEvent & { + nodeName?: string; + rootComponentName?: string; +}; + +export type OnSelectNode = ( + node: Id | undefined, + source: SelectionSource, +) => void; + +export type UIActions = { + onHoverNode: (...node: Id[]) => void; + onFocusNode: (focused?: Id) => void; + onContextMenuOpen: (open: boolean) => void; + onSelectNode: OnSelectNode; + onExpandNode: (node: Id) => void; + onCollapseNode: (node: Id) => void; + setVisualiserWidth: (width: number) => void; + onSetFilterMainThreadMonitoring: (toggled: boolean) => void; + onSetViewMode: (viewMode: ViewMode) => void; + onSetFrameworkEventMonitored: ( + eventType: FrameworkEventType, + monitored: boolean, + ) => void; + onPlayPauseToggled: () => void; + onSearchTermUpdated: (searchTerm: string) => void; + onSetWireFrameMode: (WireFrameMode: WireFrameMode) => void; + onExpandAllRecursively: (nodeId: Id) => void; + onCollapseAllNonAncestors: (nodeId: Id) => void; + onCollapseAllRecursively: (nodeId: Id) => void; + ensureAncestorsExpanded: (nodeId: Id) => void; + onSetTraversalMode: (mode: TraversalMode) => void; +}; + +export type SelectionSource = + | 'visualiser' + | 'tree' + | 'keyboard' + | 'context-menu'; + +export type StreamState = + | {state: 'Ok'} + | {state: 'RetryingAfterError'} + | { + state: 'StreamInterceptorRetryableError'; + error: StreamInterceptorError; + retryCallback: () => Promise; + } + | { + state: 'FatalError'; + error: Error; + clearCallBack: () => Promise; + }; + +export type DesktopFrame = { + nodes: Map; + snapshot?: SnapshotInfo; + frameTime: number; +}; + +export type StreamInterceptorEventEmitter = TypedEmitter<{ + /* one of these event will be emitted when frame comes from client */ + frameReceived: (frame: DesktopFrame) => void; + /* at leat one these events will be emitted in reponse to frame received from client */ + frameUpdated: (frame: DesktopFrame) => void; + /* one of these events will be emitted when metadata comes from client */ + metadataReceived: (metadata: Metadata[]) => void; + /* at leat one these events will be emitted in reponse to frame received from client */ + metadataUpdated: (metadata: Metadata[]) => void; +}>; + +export class StreamInterceptorError extends Error { + title: string; + + constructor(title: string, message: string) { + super(message); + this.title = title; + } +} diff --git a/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx b/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx new file mode 100644 index 000000000..01466dd58 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/FrameworkEventsTable.tsx @@ -0,0 +1,176 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {DeleteOutlined, PartitionOutlined} from '@ant-design/icons'; +import { + DataTable, + DataTableColumn, + DataTableManager, + DetailSidebar, + Layout, + usePlugin, + useValue, +} from 'flipper-plugin'; +import React, {useCallback, useEffect, useMemo, useRef} from 'react'; +import {FrameworkEvent, Id, NodeMap} from '../ClientTypes'; +import {plugin} from '../index'; +import {Button, Result, Tooltip} from 'antd'; +import {AugmentedFrameworkEvent} from '../DesktopTypes'; +import {formatDuration, formatTimestampMillis} from '../utils/timeUtils'; +import {eventTypeToName} from './sidebar/inspector/FrameworkEventsInspector'; +import {startCase} from 'lodash'; +import {Visualization2D} from './visualizer/Visualization2D'; +import {getNode} from '../utils/map'; +import {tracker} from '../utils/tracker'; + +export function FrameworkEventsTable({ + nodeId, + isTree, + nodes, +}: { + nodeId: Id; + nodes: NodeMap; + isTree: boolean; +}) { + const instance = usePlugin(plugin); + + const focusedNode = useValue(instance.uiState.focusedNode); + const managerRef = useRef | null>( + null, + ); + + useEffect(() => { + tracker.track('framework-event-table-opened', {}); + instance.uiActions.onSelectNode(undefined, 'tree'); + if (nodeId != null) { + managerRef.current?.resetFilters(); + if (isTree) { + managerRef.current?.addColumnFilter('treeId', nodeId as string, { + exact: true, + }); + } else { + managerRef.current?.addColumnFilter('nodeId', nodeId as string, { + exact: true, + }); + } + } + }, [instance.uiActions, isTree, nodeId]); + + const allColumns = useMemo(() => { + const customColumnKeys = instance.frameworkEventsCustomColumns.get(); + + const customColumns = [...customColumnKeys].map( + (customKey: string) => + ({ + key: customKey, + title: startCase(customKey), + onRender: (row: AugmentedFrameworkEvent) => row.payload?.[customKey], + } as DataTableColumn), + ); + + return staticColumns.concat(customColumns); + }, [instance.frameworkEventsCustomColumns]); + + const onSelectRow = useCallback( + (event: FrameworkEvent | undefined): void => { + if (event != null) { + tracker.track('framework-event-table-row-selected', { + eventType: event.type, + }); + } + + instance.uiActions.onFocusNode(event?.nodeId); + }, + [instance.uiActions], + ); + return ( + + + enableAutoScroll + dataSource={instance.frameworkEvents} + tableManagerRef={managerRef} + onSelect={onSelectRow} + columns={allColumns} + extraActions={ + <> + + + + + + + + } + /> + + {getNode(focusedNode, nodes) != null ? ( + + ) : ( + + )} + + + ); +} + +const staticColumns: DataTableColumn[] = [ + { + key: 'timestamp', + onRender: (row: FrameworkEvent) => formatTimestampMillis(row.timestamp), + title: 'Timestamp', + }, + { + key: 'type', + title: 'Event type', + onRender: (row: FrameworkEvent) => eventTypeToName(row.type), + }, + { + key: 'duration', + title: 'Duration', + onRender: (row: FrameworkEvent) => + row.duration != null ? formatDuration(row.duration) : null, + }, + { + key: 'treeId', + title: 'TreeId', + }, + { + key: 'rootComponentName', + title: 'Root component name', + }, + { + key: 'nodeId', + title: 'Component ID', + }, + { + key: 'nodeName', + title: 'Component name', + }, + { + key: 'thread', + title: 'Thread', + onRender: (row: FrameworkEvent) => startCase(row.thread), + }, +]; diff --git a/desktop/plugins/public/ui-debugger/components/PerfStats.tsx b/desktop/plugins/public/ui-debugger/components/PerfStats.tsx new file mode 100644 index 000000000..270cd4d5e --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/PerfStats.tsx @@ -0,0 +1,173 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import { + PerformanceStatsEvent, + DynamicPerformanceStatsEvent, + Id, + ClientNode, + FrameworkEvent, +} from '../ClientTypes'; +import {ReadOnlyUIState} from '../DesktopTypes'; +import React, {useMemo} from 'react'; +import { + DataInspector, + DataSource, + DataTable, + DataTableColumn, + DetailSidebar, + Layout, +} from 'flipper-plugin'; + +export function PerfStats(props: { + uiState: ReadOnlyUIState; + nodes: Map; + rootId?: Id; + events: DataSource; + frameworkEvents: DataSource; +}) { + const uiStateValues = Object.entries(props.uiState).map(([key, value]) => [ + key, + value.get(), + ]); + + const allColumns = useMemo(() => { + if (props.events.size > 0) { + const row = props.events.get(0); + + const unknownKeys = Object.keys(row).filter( + (property) => !knownKeys.has(property), + ); + + const unknownColumns = unknownKeys.map((unknwonKey) => ({ + key: unknwonKey, + title: formatKey(unknwonKey), + onRender: (row: DynamicPerformanceStatsEvent) => { + if (unknwonKey.endsWith('MS')) { + return formatDiff(row[unknwonKey]); + } + return row[unknwonKey]; + }, + })); + + return columns.concat(...unknownColumns); + } + return columns; + }, [props.events]); + + return ( + + + dataSource={props.events} + columns={allColumns} + /> + + + + + ); +} + +function formatDiff(ms: number): string { + return `${ms.toFixed(0)}ms`; +} + +function formatSize(bytes: number): string { + return `${(bytes / 1000).toFixed()}`; +} + +function formatKey(key: string): string { + const pascalCase = key.replace(/([a-z])([A-Z])/g, '$1 $2'); + return pascalCase.charAt(0).toUpperCase() + pascalCase.slice(1); +} + +const columns: DataTableColumn[] = [ + { + key: 'txId', + title: 'TXID', + onRender: (row: PerformanceStatsEvent) => { + return row.txId.toFixed(0); + }, + }, + { + key: 'observerType', + title: 'Type', + }, + { + key: 'nodesCount', + title: 'Total nodes', + }, + { + key: 'start', + title: 'Start', + onRender: (row: PerformanceStatsEvent) => { + return new Date(row.start).toISOString(); + }, + }, + { + key: 'traversalMS', + title: 'Traversal time (Main thread)', + onRender: (row: PerformanceStatsEvent) => { + return formatDiff(row.traversalMS); + }, + }, + { + key: 'snapshotMS', + title: 'Snapshot time (Main thread)', + onRender: (row: PerformanceStatsEvent) => { + return formatDiff(row.snapshotMS); + }, + }, + { + key: 'queuingMS', + title: 'Queuing time', + onRender: (row: PerformanceStatsEvent) => { + return formatDiff(row.queuingMS); + }, + }, + { + key: 'deferredComputationMS', + title: 'Deferred processing time', + onRender: (row: PerformanceStatsEvent) => { + return formatDiff(row.deferredComputationMS); + }, + }, + { + key: 'serializationMS', + title: 'Serialization time', + onRender: (row: PerformanceStatsEvent) => { + return formatDiff(row.serializationMS); + }, + }, + { + key: 'socketMS', + title: 'Socket send time', + onRender: (row: PerformanceStatsEvent) => { + return formatDiff(row.socketMS); + }, + }, + { + key: 'payloadSize', + title: 'Payload Size (KB)', + onRender: (row: PerformanceStatsEvent) => { + if (!row.payloadSize) { + return 'NaN'; + } + return formatSize(row.payloadSize); + }, + }, +]; +const knownKeys = new Set(columns.map((column) => column.key)); diff --git a/desktop/plugins/public/ui-debugger/components/StreamInterceptorErrorView.tsx b/desktop/plugins/public/ui-debugger/components/StreamInterceptorErrorView.tsx new file mode 100644 index 000000000..0d2c66609 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/StreamInterceptorErrorView.tsx @@ -0,0 +1,25 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Result} from 'antd'; +import * as React from 'react'; + +export function StreamInterceptorErrorView({ + button, + title, + message, +}: { + title: string; + message: string; + button: React.ReactNode; +}): React.ReactElement { + return ( + + ); +} diff --git a/desktop/plugins/public/ui-debugger/components/fb-stubs/IDEContextMenu.tsx b/desktop/plugins/public/ui-debugger/components/fb-stubs/IDEContextMenu.tsx new file mode 100644 index 000000000..a80b8c651 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/fb-stubs/IDEContextMenu.tsx @@ -0,0 +1,27 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {MenuProps} from 'antd'; + +import {ClientNode} from '../../ClientTypes'; + +export async function prefetchSourceFileLocation(_: ClientNode) {} + +type MenuItems = MenuProps['items']; + +export function ideContextMenuItems( + _node: ClientNode, + _onResultsUpdated: () => void, +): MenuItems { + return []; +} + +export function bigGrepContextMenuItems(_: ClientNode): MenuItems { + return []; +} diff --git a/desktop/flipper-ui-core/src/fb-stubs/ScribeLogger.tsx b/desktop/plugins/public/ui-debugger/components/fb-stubs/feedback.tsx similarity index 56% rename from desktop/flipper-ui-core/src/fb-stubs/ScribeLogger.tsx rename to desktop/plugins/public/ui-debugger/components/fb-stubs/feedback.tsx index 325042c64..2662c3dfb 100644 --- a/desktop/flipper-ui-core/src/fb-stubs/ScribeLogger.tsx +++ b/desktop/plugins/public/ui-debugger/components/fb-stubs/feedback.tsx @@ -7,12 +7,10 @@ * @format */ -export type ScribeMessage = { - category: string; - message: string; +import React from 'react'; + +const FeedbackRequest: React.FC<{}> = () => { + return <>; }; -export default class ScribeLogger { - constructor() {} - send(_message: ScribeMessage) {} -} +export default FeedbackRequest; diff --git a/desktop/plugins/public/ui-debugger/components/main.tsx b/desktop/plugins/public/ui-debugger/components/main.tsx index e13bf1965..fc3ab0f22 100644 --- a/desktop/plugins/public/ui-debugger/components/main.tsx +++ b/desktop/plugins/public/ui-debugger/components/main.tsx @@ -7,138 +7,242 @@ * @format */ -import React, {useState} from 'react'; -import {PerfStatsEvent, plugin} from '../index'; +import React, {ReactNode, useEffect, useRef, useState} from 'react'; +import {plugin} from '../index'; import { - DataInspector, - DataTable, - DataTableColumn, DetailSidebar, Layout, usePlugin, useValue, + _Sidebar as ResizablePanel, + theme, } from 'flipper-plugin'; -import {Tree, Typography} from 'antd'; -import type {DataNode} from 'antd/es/tree'; -import {DownOutlined} from '@ant-design/icons'; import {useHotkeys} from 'react-hotkeys-hook'; -import {Id, UINode} from '../types'; - -function nodesToAntTree(root: Id, nodes: Map): DataNode { - function uiNodeToAntNode(id: Id): DataNode { - const node = nodes.get(id); - return { - key: id, - title: node?.name, - children: node?.children.map((id) => uiNodeToAntNode(id)), - }; - } - - return uiNodeToAntNode(root); -} - -function formatDiff(start: number, end: number): string { - const ms = end - start; - return `${ms.toFixed(0)}ms`; -} - -export const columns: DataTableColumn[] = [ - { - key: 'txId', - title: 'TXID', - }, - { - key: 'nodesCount', - title: 'Total nodes', - }, - { - key: 'start', - title: 'Start', - onRender: (row: PerfStatsEvent) => { - console.log(row.start); - return new Date(row.start).toISOString(); - }, - }, - { - key: 'scanComplete', - title: 'Scan time', - onRender: (row: PerfStatsEvent) => { - return formatDiff(row.start, row.scanComplete); - }, - }, - { - key: 'serializationComplete', - title: 'Serialization time', - onRender: (row: PerfStatsEvent) => { - return formatDiff(row.scanComplete, row.serializationComplete); - }, - }, - { - key: 'socketComplete', - title: 'Socket send time', - onRender: (row: PerfStatsEvent) => { - return formatDiff(row.serializationComplete, row.socketComplete); - }, - }, -]; +import {Id, Metadata, MetadataId, ClientNode} from '../ClientTypes'; +import {PerfStats} from './PerfStats'; +import {Visualization2D} from './visualizer/Visualization2D'; +import {Inspector} from './sidebar/Inspector'; +import {TreeControls} from './tree/TreeControls'; +import {Button, Spin, Typography} from 'antd'; +import {QueryClientProvider} from 'react-query'; +import {Tree2} from './tree/Tree'; +import {StreamInterceptorErrorView} from './StreamInterceptorErrorView'; +import {queryClient} from '../utils/reactQuery'; +import {FrameworkEventsTable} from './FrameworkEventsTable'; +import {Centered} from './shared/Centered'; export function Component() { const instance = usePlugin(plugin); const rootId = useValue(instance.rootId); - const nodes = useValue(instance.nodes); + const streamState = useValue(instance.uiState.streamState); + const visualiserWidth = useValue(instance.uiState.visualiserWidth); + const nodes: Map = useValue(instance.nodes); + const metadata: Map = useValue(instance.metadata); const [showPerfStats, setShowPerfStats] = useState(false); - const [selectedNode, setSelectedNode] = useState( - undefined, - ); useHotkeys('ctrl+i', () => setShowPerfStats((show) => !show)); - function renderAttributesInspector(node: UINode | undefined) { - if (!node) { - return; - } - return ( - <> - - - Attributes Inspector - - - - - ); - } + const viewMode = useValue(instance.uiState.viewMode); + const [bottomPanel, setBottomPanel] = useState< + {title: string; component: ReactNode} | undefined + >(); + const openBottomPanelWithContent = (title: string, component: ReactNode) => { + setBottomPanel({title, component}); + }; + const dismissBottomPanel = () => { + setBottomPanel(undefined); + }; + + const [bottomPanelHeight, setBottomPanelHeight] = useState(400); if (showPerfStats) return ( - - dataSource={instance.perfEvents} - columns={columns} + ); - if (rootId) { - const antTree = nodesToAntTree(rootId, nodes); + if (streamState.state === 'FatalError') { return ( - <> - - { - setSelectedNode(selected[0] as string); - }} - defaultExpandAll - expandedKeys={[...nodes.keys()]} - switcherIcon={} - treeData={[antTree]} - /> - - {selectedNode && renderAttributesInspector(nodes.get(selectedNode))} - + + Reset + + } + /> ); } - return

Nothing yet
; + if (streamState.state === 'StreamInterceptorRetryableError') { + return ( + + Retry + + } + /> + ); + } + + if (rootId == null || streamState.state === 'RetryingAfterError') { + return ( + + + + ); + } + + if (viewMode.mode === 'frameworkEventsTable') { + return ( + + ); + } + + return ( + + + + + + + + + { + instance.uiActions.setVisualiserWidth(width); + }} + gutter> + + + + + + + {bottomPanel && ( + + {bottomPanel.component} + + )} + + + ); +} + +type BottomPanelProps = { + title: string; + dismiss: () => void; + children: React.ReactNode; + height: number; + setHeight: (height: number) => void; +}; +export function BottomPanel({ + title, + dismiss, + children, + height, + setHeight, +}: BottomPanelProps) { + const bottomPanelRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + bottomPanelRef.current && + !bottomPanelRef.current.contains(event.target) + ) { + setTimeout(() => { + //push to back of event queue so that you can still select item in the tree + dismiss(); + }, 0); + } + }; + // Add event listener when the component is mounted. + document.addEventListener('mousedown', handleClickOutside); + + // Remove event listener when component is unmounted. + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [bottomPanelRef, dismiss]); + + if (!children) { + return <>; + } + + return ( +
+ setHeight(height)} + gutter> + + + {title} + + + + {children} + + + +
+ ); } diff --git a/desktop/plugins/public/ui-debugger/components/shared/Centered.tsx b/desktop/plugins/public/ui-debugger/components/shared/Centered.tsx new file mode 100644 index 000000000..657a0b7bb --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/shared/Centered.tsx @@ -0,0 +1,21 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Layout} from 'flipper-plugin'; +import React from 'react'; + +export function Centered(props: {children: React.ReactNode}) { + return ( + + + {props.children} + + + ); +} diff --git a/desktop/plugins/public/ui-debugger/components/shared/FrameworkEventsTreeSelect.tsx b/desktop/plugins/public/ui-debugger/components/shared/FrameworkEventsTreeSelect.tsx new file mode 100644 index 000000000..c598a5c14 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/shared/FrameworkEventsTreeSelect.tsx @@ -0,0 +1,163 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React, {ReactNode} from 'react'; +import {InfoCircleOutlined} from '@ant-design/icons'; +import {Tooltip, TreeSelect} from 'antd'; +import {Layout, theme} from 'flipper-plugin'; +import {FrameworkEventMetadata, FrameworkEventType} from '../../ClientTypes'; + +export function FrameworkEventsTreeSelect({ + treeData, + onSetEventSelected, + selected, + placeholder, + width, +}: { + width?: number; + placeholder: string; + selected: string[]; + treeData: TreeSelectNode[]; + onSetEventSelected: ( + eventType: FrameworkEventType, + selected: boolean, + ) => void; +}) { + return ( + { + for (const leaf of getAllLeaves(node)) { + onSetEventSelected(leaf, true); + } + }} + onDeselect={(_: any, node: any) => { + for (const leaf of getAllLeaves(node)) { + onSetEventSelected(leaf, false); + } + }} + /> + ); +} + +type TreeSelectNode = { + title: ReactNode; + titleValue: string; + key: string; + value: string; + children: TreeSelectNode[]; +}; + +/** + * In tree select you can select a parent which implicitly selects all children, we find them all here as the real state + * is in terms of the leaf nodes + */ +function getAllLeaves(treeSelectNode: TreeSelectNode) { + const result: string[] = []; + function getAllLeavesRec(node: TreeSelectNode) { + if (node.children.length > 0) { + for (const child of node.children) { + getAllLeavesRec(child); + } + } else { + result.push(node.key); + } + } + getAllLeavesRec(treeSelectNode); + return result; +} + +export const frameworkEventSeparator = '.'; +/** + * transformed flat event type data structure into tree + */ +export function buildTreeSelectData( + eventTypes: string[], + metadata: Map, +): TreeSelectNode[] { + const root: TreeSelectNode = buildTreeSelectNode('root', 'root', metadata); + + eventTypes.forEach((eventType) => { + const eventSubtypes = eventType.split(frameworkEventSeparator); + let currentNode = root; + + // Find the parent node for the current id + for (let i = 0; i < eventSubtypes.length - 1; i++) { + let foundChild = false; + for (const child of currentNode.children) { + if (child.titleValue === eventSubtypes[i]) { + currentNode = child; + foundChild = true; + break; + } + } + if (!foundChild) { + const newNode: TreeSelectNode = buildTreeSelectNode( + eventSubtypes[i], + eventSubtypes.slice(0, i + 1).join(frameworkEventSeparator), + metadata, + ); + + currentNode.children.push(newNode); + currentNode = newNode; + } + } + // Add the current id as a child of the parent node + currentNode.children.push( + buildTreeSelectNode( + eventSubtypes[eventSubtypes.length - 1], + eventSubtypes + .slice(0, eventSubtypes.length) + .join(frameworkEventSeparator), + metadata, + ), + ); + }); + + return root.children; +} + +function buildTreeSelectNode( + title: string, + fullValue: string, + metadata: Map, +): TreeSelectNode { + const documentation = metadata.get(fullValue)?.documentation; + return { + title: ( + + {title} + {documentation && ( + + + + )} + + ), + titleValue: title, + key: fullValue, + value: fullValue, + children: [], + }; +} diff --git a/desktop/plugins/public/ui-debugger/components/shared/MultiSelectableDropDownItem.tsx b/desktop/plugins/public/ui-debugger/components/shared/MultiSelectableDropDownItem.tsx new file mode 100644 index 000000000..e499f89c4 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/shared/MultiSelectableDropDownItem.tsx @@ -0,0 +1,51 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {Checkbox, Typography} from 'antd'; +import {Layout, styled, theme} from 'flipper-plugin'; + +export function MultiSelectableDropDownItem({ + value, + selectedValues, + onSelect, + text, +}: { + value: T; + selectedValues: Set; + onSelect: (value: T, selected: boolean) => void; + text: string; +}) { + const isSelected = selectedValues.has(value); + return ( + { + e.stopPropagation(); + onSelect(value, !isSelected); + }}> + { + onSelect(value, !isSelected); + }} + /> + {text} + + ); +} + +export const StyledMultiSelectDropDownItem = styled(Layout.Horizontal)({ + ':hover': { + backgroundColor: theme.backgroundWash, + }, + height: 32, +}); diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/Inspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/Inspector.tsx new file mode 100644 index 000000000..9a7a0c620 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/sidebar/Inspector.tsx @@ -0,0 +1,136 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React, {ReactNode} from 'react'; +// eslint-disable-next-line rulesdir/no-restricted-imports-clone +import {Glyph} from 'flipper'; +import { + DeviceOS, + Layout, + Tab, + Tabs, + theme, + usePlugin, + useValue, +} from 'flipper-plugin'; +import {Id, Metadata, MetadataId, ClientNode} from '../../ClientTypes'; +import {IdentityInspector} from './inspector/IdentityInspector'; +import {AttributesInspector} from './inspector/AttributesInspector'; +import {Tooltip} from 'antd'; +import {NoData} from './inspector/NoData'; +import {plugin} from '../../index'; +import {FrameworkEventsInspector} from './inspector/FrameworkEventsInspector'; +import {DashboardOutlined} from '@ant-design/icons'; + +type Props = { + os: DeviceOS; + nodes: Map; + metadata: Map; + showExtra: (title: string, element: ReactNode) => void; +}; + +export const Inspector: React.FC = ({ + os, + nodes, + metadata, + showExtra, +}) => { + const instance = usePlugin(plugin); + const selectedNodeId = useValue(instance.uiState.selectedNode)?.id; + + const frameworkEventMetadata = useValue(instance.frameworkEventMetadata); + const selectedNode = selectedNodeId ? nodes.get(selectedNodeId) : undefined; + if (!selectedNode) { + return ; + } + + const selectedFrameworkEvents = selectedNodeId + ? instance.frameworkEvents.getAllRecordsByIndex({nodeId: selectedNodeId}) + : []; + + return ( + + + + + + + + }> + + + + + + + + + }> + + + {os !== 'Android' && ( + + + + + + }> + + + )} + {selectedFrameworkEvents?.length > 0 && ( + + + + + + }> + + + )} + + + ); +}; diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/AttributesInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/AttributesInspector.tsx new file mode 100644 index 000000000..33270e383 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/AttributesInspector.tsx @@ -0,0 +1,302 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import { + Inspectable, + InspectableObject, + Metadata, + MetadataId, + ClientNode, +} from '../../../ClientTypes'; +import {DataInspector, Panel, styled} from 'flipper-plugin'; +import {Col, Row} from 'antd'; +import {displayableName} from '../utilities/displayableName'; +import ColorInspector from './ColorInspector'; +import SizeInspector from './SizeInspector'; +import SpaceBoxInspector from './SpaceBoxInspector'; +import BoundsInspector from './BoundsInspector'; +import Coordinate3DInspector from './Coordinate3DInspector'; +import CoordinateInspector from './CoordinateInspector'; +import { + AutoMarginStyle, + BooleanAttributeValueStyle, + EnumAttributeValueStyle, + NumberAttributeValueStyle, + ObjectContainerStyle, + RowStyle, + TextAttributeValueStyle, +} from './Styles'; +import {transform} from '../../../utils/dataTransform'; +import {NoData} from './NoData'; + +const NumberValue = styled.span(NumberAttributeValueStyle); +const BooleanValue = styled.span(BooleanAttributeValueStyle); +const TextValue = styled.span(TextAttributeValueStyle); +const EnumValue = styled.span(EnumAttributeValueStyle); +const ObjectContainer = styled.div(ObjectContainerStyle); +const CenteredContentContainer = styled.div(AutoMarginStyle); + +type NamedAttributeInspectorProps = { + name: string; +}; + +const NamedAttributeInspector: React.FC = ({ + name, + children, +}) => { + return ( + + + {name} + + + {children} + + + ); +}; + +const ObjectAttributeInspector: React.FC<{ + metadata: Map; + name: string; + fields: Record; + level: number; +}> = ({metadata, name, fields, level}) => { + return ( +
+ {name} + {Object.entries(fields).map(([key, value]) => { + const metadataId: number = Number(key); + const attributeName = metadata.get(metadataId)?.name ?? key; + return ( + + {create(metadata, attributeName, value, level + 5)} + + ); + })} +
+ ); +}; + +const ArrayAttributeInspector: React.FC<{ + metadata: Map; + name: string; + items: Inspectable[]; + level: number; +}> = ({metadata, name, items, level}) => { + return ( +
+ {name} + {items.map(function (item, idx) { + const inspectableValue = item; + const attributeName = idx.toString(); + return ( + + {create(metadata, attributeName, inspectableValue, level + 5)} + + ); + })} +
+ ); +}; + +function create( + metadata: Map, + name: string, + inspectable: Inspectable, + level: number = 2, +) { + switch (inspectable?.type) { + case 'boolean': + return ( + + {inspectable.value ? 'TRUE' : 'FALSE'} + + ); + case 'enum': + return ( + + {inspectable.value} + + ); + case 'text': + return ( + + {inspectable.value} + + ); + case 'number': + return ( + + {inspectable.value} + + ); + case 'color': + return ( + + ); + case 'size': + return ( + + + + ); + case 'bounds': + return ( + + + + ); + case 'coordinate': + return ( + + + + ); + case 'coordinate3d': + return ( + + + + ); + case 'space': + return ( + + + + ); + case 'unknown': + return ( + + {inspectable.value} + + ); + case 'array': + return ( + + ); + case 'object': + return ( + + ); + default: + return ( + + {JSON.stringify(inspectable)} + + ); + } +} + +function createSection( + mode: InspectorMode, + metadata: Map, + name: string, + inspectable: InspectableObject, +) { + const children: any[] = []; + Object.keys(inspectable.fields).forEach((key, _index) => { + const metadataId: number = Number(key); + const attributeMetadata = metadata.get(metadataId); + if (attributeMetadata && attributeMetadata.type === mode) { + const attributeValue = inspectable.fields[metadataId]; + children.push(create(metadata, attributeMetadata.name, attributeValue)); + } + }); + + if (children.length > 0) { + return ( + + {...children} + + ); + } +} + +type InspectorMode = 'layout' | 'attribute'; +type Props = { + node: ClientNode; + metadata: Map; + mode: InspectorMode; + rawEnabled?: boolean; +}; +export const AttributesInspector: React.FC = ({ + node, + metadata, + mode, + rawEnabled = true, +}) => { + const keys = Object.keys(node.attributes); + const sections = keys + .map(function (key, _) { + /** + * The node top-level attributes refer to the displayable panels. + * The panel name is obtained by querying the metadata. + * The inspectable contains the actual attributes belonging to each panel. + */ + const metadataId: number = Number(key); + const sectionMetadata = metadata.get(metadataId); + if (!sectionMetadata) { + return; + } + const sectionAttributes = node.attributes[ + metadataId + ] as InspectableObject; + + return createSection( + mode, + metadata, + sectionMetadata.name, + sectionAttributes, + ); + }) + .filter((section) => section !== undefined); + + if (sections.length === 0) { + return ; + } + + return ( + <> + {...sections} + {rawEnabled && ( + + + + )} + + ); +}; diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/BoundsInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/BoundsInspector.tsx new file mode 100644 index 000000000..65648efef --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/BoundsInspector.tsx @@ -0,0 +1,226 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {Bounds} from '../../../ClientTypes'; +import {InspectorStyle} from './Styles'; + +type Props = { + size?: number; + strokeColor?: string; + outerBoxColor?: string; + innerBoxColor?: string; + multiplier?: number; + margin?: number; + separator?: number; + value: Bounds; +}; + +const BoundsInspector: React.FC = ({ + size = InspectorStyle.bounds.size, + strokeColor = InspectorStyle.strokeColor, + outerBoxColor = InspectorStyle.outerFillColor, + innerBoxColor = InspectorStyle.innerFillColor, + multiplier = InspectorStyle.bounds.multiplier, + margin = InspectorStyle.bounds.margin, + separator = InspectorStyle.bounds.separator, + value, +}) => { + const scale = + Math.min(size / (value.width + value.x), size / (value.height + value.y)) * + multiplier; + + const width = value.width * scale; + const height = value.height * scale; + + const origin = size / 2; + const originX = origin - width / 2; + const originY = origin - height / 2; + + const midX = originX + width / 2; + const midY = originY + height / 2; + + const lineStyle = {stroke: strokeColor, strokeWidth: '2'}; + + return ( + + {' '} + {/** outer-box */} + {' '} + {/** bounded-box */} + {/** left */} + {/** top */} + {' '} + {/** left-bottom */} + {' '} + {/** right-top */} + {' '} + {/** right-bottom */} + + {value.x} + + {/** x */} + + {/** left */} + + {/** bezel */} + + {/** bezel */} + + {value.width} + + {/** width */} + + {/** bottom */} + + {/** bezel */} + + {/** bezel */} + + {value.y} + + {/** y */} + + {/** top */} + + {/** bezel */} + + {/** bezel */} + + {value.height} + + {/** height */} + + {/** right */} + + {/** bezel */} + + {/** bezel */} + + ); +}; + +export default BoundsInspector; diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/ColorInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/ColorInspector.tsx new file mode 100644 index 000000000..3eef54560 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/ColorInspector.tsx @@ -0,0 +1,138 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {Col, Popover, Row} from 'antd'; +import {Color} from '../../../ClientTypes'; +import {SketchPicker, ColorResult} from 'react-color'; +import {styled} from 'flipper-plugin'; +import { + AutoMarginStyle, + ColorInnerButtonStyle, + ColorOuterButtonStyle, + NumberAttributeValueStyle, + ObjectContainerStyle, + RowStyle, +} from './Styles'; +import {theme} from 'flipper-plugin'; + +type Props = { + name: string; + color: Color; +}; + +const DefaultColor: Color = { + r: 255, + g: 255, + b: 255, + a: 1, +}; + +const CenteredContentContainer = styled.div(AutoMarginStyle); +const ObjectContainer = styled.div(ObjectContainerStyle); +const NumberValue = styled.span(NumberAttributeValueStyle); +const OuterColorButton = styled.div(ColorOuterButtonStyle); +const InnerColorButton = styled.div(ColorInnerButtonStyle); + +const RGBA = styled.span({color: theme.semanticColors.numberValue}); + +const RGBAtoHEX = (color: Color) => { + const hex = + (color.r | (1 << 8)).toString(16).slice(1) + + (color.g | (1 << 8)).toString(16).slice(1) + + (color.b | (1 << 8)).toString(16).slice(1); + + return '#' + hex.toUpperCase(); +}; + +class ColorPicker extends React.Component<{color: Color}> { + handleChange = (_color: ColorResult) => { + // No color changes to be applied at this stage. + }; + render() { + return ( + + } + trigger="click"> + + + + + ); + } +} + +const ColorHEX: React.FC<{color: Color}> = ({color}) => ( + {RGBAtoHEX(color)} +); + +const ColorRGBA: React.FC<{color: Color}> = ({color}) => ( + <> + r: {color.r} g: {color.g} b:{' '} + {color.b} a: {color.a} + +); + +class ColorInspector extends React.Component { + render() { + return ( + <> + + + {this.props.name} + + + + + + + + + + + Hex + + + + + + + + + + + + RGBA + + + + + + + + + + ); + } +} + +export default ColorInspector; diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/Coordinate3DInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/Coordinate3DInspector.tsx new file mode 100644 index 000000000..0c401478b --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/Coordinate3DInspector.tsx @@ -0,0 +1,53 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {Coordinate3D} from '../../../ClientTypes'; +import {Col, Row} from 'antd'; +import { + CenteredContentStyle, + CenteredHeadingContentStyle, + CenteredNumberStyle, + CenteredTextStyle, +} from './Styles'; + +type Props = { + value: Coordinate3D; +}; + +const Coordinate3DInspector: React.FC = ({value}) => { + return ( + <> + + + x + + + y + + + z + + + + + {value.x} + + + {value.y} + + + {value.z} + + + + ); +}; + +export default Coordinate3DInspector; diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/CoordinateInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/CoordinateInspector.tsx new file mode 100644 index 000000000..f57e3b075 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/CoordinateInspector.tsx @@ -0,0 +1,47 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {Coordinate} from '../../../ClientTypes'; +import {Col, Row} from 'antd'; +import { + CenteredContentStyle, + CenteredHeadingContentStyle, + CenteredNumberStyle, + CenteredTextStyle, +} from './Styles'; + +type Props = { + value: Coordinate; +}; + +const CoordinateInspector: React.FC = ({value}) => { + return ( + <> + + + x + + + y + + + + + {value.x} + + + {value.y} + + + + ); +}; + +export default CoordinateInspector; diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/DefaultInspectorContainer.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/DefaultInspectorContainer.tsx new file mode 100644 index 000000000..24dae988f --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/DefaultInspectorContainer.tsx @@ -0,0 +1,28 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {Col, Row} from 'antd'; +import {styled} from 'flipper-plugin'; +import {DefaultInputContainerStyle} from './Styles'; + +type Props = { + name: string; +}; + +export const DefaultInputContainer = styled.div(DefaultInputContainerStyle); + +export const DefaultInspectorContainer: React.FC = (props) => { + return ( + + {props.name} + {props.children} + + ); +}; diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/DocumentationInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/DocumentationInspector.tsx new file mode 100644 index 000000000..eb07ae9b3 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/DocumentationInspector.tsx @@ -0,0 +1,19 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {ClientNode} from '../../../ClientTypes'; + +type Props = { + node: ClientNode; +}; + +export const DocumentationInspector: React.FC = () => { + return

Quick Help and Documentation

; +}; diff --git a/desktop/plugins/public/ui-debugger/components/sidebar/inspector/FrameworkEventsInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/FrameworkEventsInspector.tsx new file mode 100644 index 000000000..36dd25a1a --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/sidebar/inspector/FrameworkEventsInspector.tsx @@ -0,0 +1,287 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import { + DataInspector, + Layout, + produce, + theme, + TimelineDataDescription, +} from 'flipper-plugin'; +import { + FrameworkEvent, + ClientNode, + FrameworkEventType, + FrameworkEventMetadata, +} from '../../../ClientTypes'; +import React, {ReactNode, useState} from 'react'; +import {StackTraceInspector} from './StackTraceInspector'; +import { + Badge, + Button, + Descriptions, + Dropdown, + Tag, + Tooltip, + Typography, +} from 'antd'; +import {frameworkEventSeparator} from '../../shared/FrameworkEventsTreeSelect'; +import {last, startCase, uniqBy} from 'lodash'; +import {FilterOutlined, TableOutlined} from '@ant-design/icons'; +import {ViewMode} from '../../../DesktopTypes'; +import {MultiSelectableDropDownItem} from '../../shared/MultiSelectableDropDownItem'; +import {formatDuration, formatTimestampMillis} from '../../../utils/timeUtils'; +import {tracker} from '../../../utils/tracker'; + +type Props = { + node: ClientNode; + events: readonly FrameworkEvent[]; + showExtra?: (title: string, element: ReactNode) => void; + frameworkEventMetadata: Map; + onSetViewMode: (viewMode: ViewMode) => void; +}; + +export const FrameworkEventsInspector: React.FC = ({ + node, + events, + showExtra, + frameworkEventMetadata, + onSetViewMode, +}) => { + const allThreads = uniqBy(events, 'thread').map((event) => event.thread); + const [filteredThreads, setFilteredThreads] = useState>( + new Set(), + ); + + const allEventTypes = uniqBy(events, 'type').map((event) => event.type); + const [filteredEventTypes, setFilteredEventTypes] = useState>( + new Set(), + ); + + const filteredEvents = events + .filter( + (event) => + filteredEventTypes.size === 0 || filteredEventTypes.has(event.type), + ) + .filter( + (event) => + filteredThreads.size === 0 || filteredThreads.has(event.thread!), + ); + + const showThreadsSection = allThreads.length > 1; + const showEventTypesSection = allEventTypes.length > 1; + return ( + + + Event timeline + + + + + {supportedTraversalModes.length > 1 && + supportedTraversalModes.includes('accessibility-hierarchy') && ( + + + + )} + {frameworkEventMonitoring.size > 0 && ( + <> + val === true, + ).length + }> + + + setShowFrameworkEventsModal(false)} + /> + + )} + + ); +}; + +function FrameworkEventsMonitoringModal({ + visible, + onCancel, + onSetEventMonitored, + onSetFilterMainThreadMonitoring, + filterMainThreadMonitoring, + frameworkEventTypes, + metadata, +}: { + metadata: Map; + visible: boolean; + onCancel: () => void; + onSetEventMonitored: ( + eventType: FrameworkEventType, + monitored: boolean, + ) => void; + filterMainThreadMonitoring: boolean; + onSetFilterMainThreadMonitoring: (toggled: boolean) => void; + frameworkEventTypes: [FrameworkEventType, boolean][]; +}) { + const selectedFrameworkEvents = frameworkEventTypes + .filter(([, selected]) => selected) + .map(([eventType]) => eventType); + + const treeData = buildTreeSelectData( + frameworkEventTypes.map(([type]) => type), + metadata, + ); + + return ( + + + + Monitoring an event will cause the relevant node in the visualizer and + tree to highlight briefly. Additionally counter will show the number + of matching events in the tree + + + + + + { + onSetFilterMainThreadMonitoring(event); + }} + /> + + Only highlight events that occured on the main thread + + + + + ); +} diff --git a/desktop/plugins/public/ui-debugger/components/tree/toTreeList.tsx b/desktop/plugins/public/ui-debugger/components/tree/toTreeList.tsx new file mode 100644 index 000000000..f6fb1faa5 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/tree/toTreeList.tsx @@ -0,0 +1,153 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ +import { + FrameworkEvent, + FrameworkEventType, + Id, + ClientNode, +} from '../../ClientTypes'; +import {DataSource} from 'flipper-plugin'; +import {concat, last} from 'lodash'; +import {reverse} from 'lodash/fp'; +import {TreeNode} from './Tree'; + +type TreeListStackItem = { + node: ClientNode; + depth: number; + parentIndentGuideDepths: number[]; + isChildOfSelectedNode: boolean; + selectedNodeDepth: number; +}; +export function toTreeList( + nodes: Map, + rootId: Id, + expandedNodes: Set, + selectedNode: Id | undefined, + frameworkEvents: DataSource, + frameworkEventsMonitoring: Map, + filterMainThreadMonitoring: boolean, +): TreeNode[] { + const root = nodes.get(rootId); + if (root == null) { + return []; + } + const stack = [ + { + node: root, + depth: 0, + isChildOfSelectedNode: false, + selectedNodeDepth: 0, + parentIndentGuideDepths: [], + }, + ] as TreeListStackItem[]; + + const treeNodes = [] as TreeNode[]; + + let i = 0; + while (stack.length > 0) { + const stackItem = stack.pop()!!; + + const {node, depth} = stackItem; + + const prevItemLine = last(treeNodes); + //trim all the guides that have now ended + if (prevItemLine != null) { + for (let i = depth; i < prevItemLine.depth; i++) { + prevItemLine.indentGuides[i].trimBottom = true; + } + } + + const isExpanded = expandedNodes.has(node.id); + const isSelected = node.id === selectedNode; + + let events = frameworkEvents.getAllRecordsByIndex({nodeId: node.id}); + if (events) { + events = events + .filter((e) => frameworkEventsMonitoring.get(e.type)) + .filter( + (e) => filterMainThreadMonitoring === false || e.thread === 'main', + ); + } + + treeNodes.push({ + ...node, + idx: i, + depth, + isExpanded, + frameworkEvents: events.length > 0 ? events.length : null, + indentGuides: stackItem.parentIndentGuideDepths.map( + (parentGuideDepth, idx) => { + const isLastGuide = + idx === stackItem.parentIndentGuideDepths.length - 1; + return { + depth: parentGuideDepth, + addHorizontalMarker: isLastGuide, + trimBottom: false, + + color: + stackItem.isChildOfSelectedNode && + parentGuideDepth === stackItem.selectedNodeDepth + ? 'primary' + : 'secondary', + }; + }, + ), + }); + i++; + + let isChildOfSelectedNode = stackItem.isChildOfSelectedNode; + let selectedNodeDepth = stackItem.selectedNodeDepth; + if (isSelected) { + isChildOfSelectedNode = true; + selectedNodeDepth = depth; + // walk back through tree nodes, while depth is greater or equal than current it is your + // parents child / your previous cousin so set dashed line + for (let i = treeNodes.length - 1; i >= 0; i--) { + const prevNode = treeNodes[i]; + if (prevNode.depth < depth) { + break; + } + const selectedDepthIndentGuide = + prevNode.indentGuides[selectedNodeDepth - 1]; + if (selectedDepthIndentGuide) { + selectedDepthIndentGuide.color = 'primary'; + } + } + } + + if (isExpanded) { + //since we do dfs and use a stack we have to reverse children to get the order correct + for (const childId of reverse(node.children)) { + const child = nodes.get(childId); + if (child != null) { + stack.push({ + node: child, + depth: depth + 1, + parentIndentGuideDepths: concat( + stackItem.parentIndentGuideDepths, + depth, + ), + isChildOfSelectedNode: isChildOfSelectedNode, + selectedNodeDepth: selectedNodeDepth, + }); + } + } + } + } + + //always trim last indent guides since they have 'ended' + const prevItemLine = last(treeNodes); + if (prevItemLine != null) { + prevItemLine.indentGuides.forEach((guide) => { + guide.trimBottom = true; + }); + } + + return treeNodes; +} diff --git a/desktop/plugins/public/ui-debugger/components/tree/useKeyboardControls.tsx b/desktop/plugins/public/ui-debugger/components/tree/useKeyboardControls.tsx new file mode 100644 index 000000000..b5302a2ea --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/tree/useKeyboardControls.tsx @@ -0,0 +1,186 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Id} from '../../ClientTypes'; +import {OnSelectNode} from '../../DesktopTypes'; +import {TreeNode} from './Tree'; +import {Virtualizer} from '@tanstack/react-virtual'; +import {usePlugin} from 'flipper-plugin'; +import {plugin} from '../../index'; +import {useEffect} from 'react'; + +export type MillisSinceEpoch = number; + +export function useKeyboardControls( + treeNodes: TreeNode[], + rowVirtualizer: Virtualizer, + selectedNodeId: Id | undefined, + hoveredNodeId: Id | undefined, + onSelectNode: OnSelectNode, + onHoverNode: (...id: Id[]) => void, + onExpandNode: (id: Id) => void, + onCollapseNode: (id: Id) => void, + isUsingKBToScrollUntill: React.MutableRefObject, +) { + const instance = usePlugin(plugin); + + useEffect(() => { + const listener = (event: KeyboardEvent) => { + const kbTargetNodeId = selectedNodeId ?? hoveredNodeId; + const kbTargetNode = treeNodes.find((item) => item.id === kbTargetNodeId); + + switch (event.key) { + case 'Enter': { + if (hoveredNodeId != null) { + onSelectNode(hoveredNodeId, 'keyboard'); + } + + break; + } + + case 'ArrowRight': + event.preventDefault(); + if (kbTargetNode) { + if (kbTargetNode.isExpanded) { + moveSelectedNodeUpOrDown( + 'ArrowDown', + treeNodes, + rowVirtualizer, + kbTargetNode.id, + selectedNodeId, + onSelectNode, + onHoverNode, + isUsingKBToScrollUntill, + ); + } else { + onExpandNode(kbTargetNode.id); + } + } + break; + case 'ArrowLeft': { + event.preventDefault(); + + if (kbTargetNode) { + if (kbTargetNode.isExpanded) { + onCollapseNode(kbTargetNode.id); + } else { + const parentIdx = treeNodes.findIndex( + (treeNode) => treeNode.id === kbTargetNode.parent, + ); + moveSelectedNodeViaKeyBoard( + parentIdx, + treeNodes, + rowVirtualizer, + onSelectNode, + onHoverNode, + isUsingKBToScrollUntill, + ); + } + } + break; + } + + case 'ArrowUp': + case 'ArrowDown': + event.preventDefault(); + + moveSelectedNodeUpOrDown( + event.key, + treeNodes, + rowVirtualizer, + hoveredNodeId, + selectedNodeId, + onSelectNode, + onHoverNode, + isUsingKBToScrollUntill, + ); + + break; + } + }; + window.addEventListener('keydown', listener); + return () => { + window.removeEventListener('keydown', listener); + }; + }, [ + treeNodes, + onSelectNode, + selectedNodeId, + isUsingKBToScrollUntill, + onExpandNode, + onCollapseNode, + instance.uiState.hoveredNodes, + hoveredNodeId, + rowVirtualizer, + onHoverNode, + ]); +} + +export type UpOrDown = 'ArrowDown' | 'ArrowUp'; + +function moveSelectedNodeUpOrDown( + direction: UpOrDown, + treeNodes: TreeNode[], + rowVirtualizer: Virtualizer, + hoveredNode: Id | undefined, + selectedNode: Id | undefined, + onSelectNode: OnSelectNode, + onHoverNode: (...id: Id[]) => void, + isUsingKBToScrollUntill: React.MutableRefObject, +) { + const nodeToUse = selectedNode != null ? selectedNode : hoveredNode; + const curIdx = treeNodes.findIndex((item) => item.id === nodeToUse); + if (curIdx != -1) { + const increment = direction === 'ArrowDown' ? 1 : -1; + const newIdx = curIdx + increment; + + moveSelectedNodeViaKeyBoard( + newIdx, + treeNodes, + rowVirtualizer, + onSelectNode, + onHoverNode, + isUsingKBToScrollUntill, + ); + } +} + +function moveSelectedNodeViaKeyBoard( + newIdx: number, + treeNodes: TreeNode[], + rowVirtualizer: Virtualizer, + onSelectNode: OnSelectNode, + onHoverNode: (...id: Id[]) => void, + isUsingKBToScrollUntil: React.MutableRefObject, +) { + if (newIdx >= 0 && newIdx < treeNodes.length) { + const newNode = treeNodes[newIdx]; + + extendKBControlLease(isUsingKBToScrollUntil); + onSelectNode(newNode.id, 'keyboard'); + onHoverNode(newNode.id); + + rowVirtualizer.scrollToIndex(newIdx, {align: 'auto'}); + } +} + +const KBScrollOverrideTimeMS = 250; +function extendKBControlLease( + isUsingKBToScrollUntil: React.MutableRefObject, +) { + /** + * The reason for this grossness is that when scrolling to an element via keyboard, it will move a new dom node + * under the cursor which will trigger the onmouseenter event for that node even if the mouse never actually was moved. + * This will in turn cause that event handler to hover that node rather than the one the user is trying to get to via keyboard. + * This is a dubious way to work around this. We set this to indicate how long into the future we should disable the + * onmouseenter -> hover behaviour + */ + isUsingKBToScrollUntil.current = + new Date().getTime() + KBScrollOverrideTimeMS; +} diff --git a/desktop/plugins/public/ui-debugger/components/visualizer/Visualization2D.tsx b/desktop/plugins/public/ui-debugger/components/visualizer/Visualization2D.tsx new file mode 100644 index 000000000..89e682ae8 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/visualizer/Visualization2D.tsx @@ -0,0 +1,637 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React, {useEffect, useMemo, useRef, useState} from 'react'; +import {Bounds, Coordinate, Id, ClientNode} from '../../ClientTypes'; +import { + NestedNode, + OnSelectNode, + TraversalMode, + WireFrameMode, +} from '../../DesktopTypes'; + +import { + produce, + styled, + theme, + usePlugin, + useValue, + Layout, +} from 'flipper-plugin'; +import {plugin} from '../../index'; +import {head, isEqual, throttle} from 'lodash'; +import {useDelay} from '../../hooks/useDelay'; +import {Tooltip} from 'antd'; +import {TargetModeState, VisualiserControls} from './VisualizerControls'; +import {getNode} from '../../utils/map'; + +export const Visualization2D: React.FC< + { + width: number; + nodes: Map; + onSelectNode: OnSelectNode; + hideControls?: boolean; + disableInteractivity?: boolean; + } & React.HTMLAttributes +> = ({width, nodes, onSelectNode, hideControls, disableInteractivity}) => { + const rootNodeRef = useRef(); + const instance = usePlugin(plugin); + + const snapshot = useValue(instance.snapshot); + const snapshotNode = snapshot && nodes.get(snapshot.nodeId); + const focusedNodeId = useValue(instance.uiState.focusedNode); + + const selectedNodeId = useValue(instance.uiState.selectedNode); + const hoveredNodes = useValue(instance.uiState.hoveredNodes); + const hoveredNodeId = head(hoveredNodes); + const wireFrameMode = useValue(instance.uiState.wireFrameMode); + const traversalMode = useValue(instance.uiState.traversalMode); + + const [targetMode, setTargetMode] = useState({ + state: 'disabled', + }); + const focusState = useMemo(() => { + //use the snapshot node as root since we cant realistically visualise any node above this + const rootNode = snapshot && toNestedNode(snapshot.nodeId, nodes); + return rootNode && caclulateFocusState(rootNode, focusedNodeId); + }, [snapshot, nodes, focusedNodeId]); + + //this ref is to ensure the mouse has entered the visualiser, otherwise when you have overlapping modals + //the hover state / tooltips all fire + const visualizerActive = useRef(false); + useEffect(() => { + const mouseListener = throttle((ev: MouseEvent) => { + const domRect = rootNodeRef.current?.getBoundingClientRect(); + + if ( + disableInteractivity || + !focusState || + !domRect || + instance.uiState.isContextMenuOpen.get() || + !snapshotNode || + !visualizerActive.current + ) { + return; + } + const rawMouse = {x: ev.clientX, y: ev.clientY}; + + if (!boundsContainsCoordinate(domRect, rawMouse)) { + return; + } + + //make the mouse coord relative to the dom rect of the visualizer + + const pxScaleFactor = calcPxScaleFactor(snapshotNode.bounds, width); + + const offsetMouse = offsetCoordinate(rawMouse, domRect); + const scaledMouse = { + x: offsetMouse.x * pxScaleFactor, + y: offsetMouse.y * pxScaleFactor, + }; + + const hitNodes = hitTest(focusState.focusedRoot, scaledMouse).map( + (node) => node.id, + ); + + if ( + hitNodes.length > 0 && + !isEqual(hitNodes, instance.uiState.hoveredNodes.get()) + ) { + instance.uiActions.onHoverNode(...hitNodes); + } + }, MouseThrottle); + window.addEventListener('mousemove', mouseListener); + + return () => { + window.removeEventListener('mousemove', mouseListener); + }; + }, [ + instance.uiState.hoveredNodes, + focusState, + nodes, + instance.uiState.isContextMenuOpen, + width, + snapshotNode, + instance.uiActions, + disableInteractivity, + ]); + + useEffect(() => { + return instance.uiState.isContextMenuOpen.subscribe((value) => { + if (value === false) { + visualizerActive.current = true; + } + }); + }, [instance.uiState.isContextMenuOpen]); + + if (!focusState || !snapshotNode) { + return null; + } + + const pxScaleFactor = calcPxScaleFactor(snapshotNode.bounds, width); + + const overlayCursor = + targetMode.state === 'disabled' ? 'pointer' : 'crosshair'; + + const onClickOverlay = () => { + instance.uiActions.onSelectNode(hoveredNodeId, 'visualiser'); + if (hoveredNodeId != null) { + instance.uiActions.ensureAncestorsExpanded(hoveredNodeId); + } + + if (targetMode.state !== 'disabled') { + setTargetMode({ + state: 'selected', + targetedNodes: hoveredNodes.slice().reverse(), + sliderPosition: hoveredNodes.length - 1, + }); + } + }; + + return ( + + {!hideControls && ( + + )} + +
{ + e.stopPropagation(); + //the context menu triggers this callback but we dont want to remove hover effect + if (!instance.uiState.isContextMenuOpen.get()) { + instance.uiActions.onHoverNode(); + } + + visualizerActive.current = false; + }} + onMouseEnter={() => { + visualizerActive.current = true; + }} + //this div is to ensure that the size of the visualiser doesnt change when focusings on a subtree + style={ + { + backgroundColor: theme.backgroundWash, + borderRadius: theme.borderRadius, + overflowY: 'auto', + overflowX: 'hidden', + position: 'relative', //this is for the absolutely positioned overlays + [pxScaleFactorCssVar]: pxScaleFactor, + width: toPx(focusState.actualRoot.bounds.width), + height: toPx(focusState.actualRoot.bounds.height), + } as React.CSSProperties + }> + {hoveredNodeId && ( + + + + )} + {selectedNodeId && ( + + )} +
+ {snapshotNode && ( + + )} + +
+
+
+ ); +}; + +const MemoedVisualizationNode2D = React.memo( + Visualization2DNode, + (prev, next) => { + if (prev.node != next.node || prev.wireframeMode != next.wireframeMode) { + return false; + } + if (next.wireframeMode == 'All') { + //if all wire frames are drawn and the root node is the same + //then we are safe + return true; + } else { + //with other modes the selected node affects the drawing + return prev.selectedNode === next.selectedNode; + } + }, +); + +function Visualization2DNode({ + wireframeMode, + isSelectedOrChildOrSelected, + selectedNode, + node, + onSelectNode, + runThroughIndex, + traversalMode, +}: { + wireframeMode: WireFrameMode; + isSelectedOrChildOrSelected: boolean; + selectedNode?: Id; + node: NestedNode; + onSelectNode: OnSelectNode; + runThroughIndex?: number; + traversalMode: TraversalMode; +}) { + const instance = usePlugin(plugin); + + const isSelected = node.id === selectedNode; + const ref = useRef(null); + let nestedChildren: NestedNode[]; + + //if there is an active child don't draw the other children + //this means we don't draw overlapping activities / tabs etc + if ( + node.activeChildIdx != null && + node.activeChildIdx >= 0 && + node.activeChildIdx < node.children.length + ) { + nestedChildren = [node.children[node.activeChildIdx]]; + } else { + nestedChildren = node.children; + } + + const children = nestedChildren.map((child, index) => ( + + )); + + const highLightColor = useValue(instance.uiState.highlightedNodes).get( + node.id, + ); + + const showBorder = + wireframeMode === 'All' || + (wireframeMode === 'SelectedAndChildren' && isSelectedOrChildOrSelected) || + (wireframeMode === 'SelectedOnly' && isSelected); + const showOrdinalIndices = traversalMode == 'accessibility-hierarchy'; + + return ( +
+ {showBorder && } + + {children} + {showOrdinalIndices && ( +
+ {runThroughIndex} +
+ )} +
+ ); +} + +const DelayedHoveredToolTip: React.FC<{ + nodeId: Id; + nodes: Map; + children: JSX.Element; +}> = ({nodeId, nodes, children}) => { + const node = nodes.get(nodeId); + + const isVisible = useDelay(longHoverDelay); + + return ( + + {children} + + ); +}; + +const OverlayBorder = styled.div<{ + cursor: 'pointer' | 'crosshair'; + type: 'selected' | 'hovered'; + nodeId: Id; + nodes: Map; +}>(({type, nodeId, nodes, cursor}) => { + const offset = getTotalOffset(nodeId, nodes); + const node = nodes.get(nodeId); + return { + zIndex: 100, + pointerEvents: type === 'selected' ? 'none' : 'auto', + cursor: cursor, + position: 'absolute', + top: toPx(offset.y), + left: toPx(offset.x), + width: toPx(node?.bounds?.width ?? 0), + height: toPx(node?.bounds?.height ?? 0), + boxSizing: 'border-box', + borderWidth: 3, + borderStyle: 'solid', + color: 'transparent', + borderColor: + type === 'selected' ? theme.primaryColor : theme.textColorPlaceholder, + }; +}); + +/** + * computes the x,y offset of a given node from the root of the visualization + * in node coordinates + */ +function getTotalOffset(id: Id, nodes: Map): Coordinate { + const offset = {x: 0, y: 0}; + let curId: Id | undefined = id; + + while (curId != null) { + const cur = nodes.get(curId); + if (cur != null) { + offset.x += cur.bounds.x; + offset.y += cur.bounds.y; + } + curId = cur?.parent; + } + + return offset; +} + +/** + * this is the border that shows the green or blue line, it is implemented as a sibling to the + * node itself so that it has the same size but the border doesnt affect the sizing of its children + * as border is part of the box model + */ +const NodeBorder = styled.div({ + position: 'absolute', + top: 0, + left: 0, + bottom: 0, + right: 0, + boxSizing: 'border-box', + borderWidth: '1px', + borderStyle: 'solid', + color: 'transparent', + borderColor: theme.disabledColor, +}); + +const longHoverDelay = 500; +const pxScaleFactorCssVar = '--pxScaleFactor'; +const MouseThrottle = 32; + +function toPx(n: number) { + return `calc(${n}px / var(${pxScaleFactorCssVar}))`; +} + +function toNestedNode( + rootId: Id, + nodes: Map, +): NestedNode | undefined { + function uiNodeToNestedNode(node: ClientNode): NestedNode { + const nonNullChildren = node.children.filter( + (childId) => nodes.get(childId) != null, + ); + + if (nonNullChildren.length !== node.children.length) { + console.error( + 'Visualization2D.toNestedNode -> child is nullish!', + node.children, + nonNullChildren.map((childId) => { + const child = nodes.get(childId); + return child && uiNodeToNestedNode(child); + }), + ); + } + + const activeChildIdx = node.activeChild + ? nonNullChildren.indexOf(node.activeChild) + : undefined; + + return { + id: node.id, + name: node.name, + attributes: node.attributes, + children: nonNullChildren.map((childId) => + uiNodeToNestedNode(nodes.get(childId)!), + ), + bounds: node.bounds, + tags: node.tags, + activeChildIdx: activeChildIdx, + }; + } + + const root = nodes.get(rootId); + return root ? uiNodeToNestedNode(root) : undefined; +} + +type FocusState = { + actualRoot: NestedNode; + focusedRoot: NestedNode; + focusedRootGlobalOffset: Coordinate; +}; + +function caclulateFocusState(root: NestedNode, target?: Id): FocusState { + const rootFocusState = { + actualRoot: root, + focusedRoot: root, + focusedRootGlobalOffset: {x: 0, y: 0}, + }; + if (target == null) { + return rootFocusState; + } + return ( + findNodeAndGlobalOffsetRec(root, {x: 0, y: 0}, root, target) || + rootFocusState + ); +} + +function findNodeAndGlobalOffsetRec( + node: NestedNode, + globalOffset: Coordinate, + root: NestedNode, + target: Id, +): FocusState | undefined { + const nextOffset = { + x: globalOffset.x + node.bounds.x, + y: globalOffset.y + node.bounds.y, + }; + if (node.id === target) { + //since we have already applied the this nodes offset to the root node in the visualiser we zero it out here so it isn't counted twice + const focusedRoot = produce(node, (draft) => { + draft.bounds.x = 0; + draft.bounds.y = 0; + }); + return { + actualRoot: root, + focusedRoot, + focusedRootGlobalOffset: nextOffset, + }; + } + + for (const child of node.children) { + const offset = findNodeAndGlobalOffsetRec(child, nextOffset, root, target); + if (offset != null) { + return offset; + } + } + return undefined; +} + +function hitTest(node: NestedNode, mouseCoordinate: Coordinate): NestedNode[] { + const res: NestedNode[] = []; + + function hitTestRec(node: NestedNode, mouseCoordinate: Coordinate): boolean { + const nodeBounds = node.bounds; + + const thisNodeHit = boundsContainsCoordinate(nodeBounds, mouseCoordinate); + + let children = node.children; + + if (node.activeChildIdx != null) { + children = [node.children[node.activeChildIdx]]; + } + const offsetMouseCoord = offsetCoordinate(mouseCoordinate, nodeBounds); + let anyChildHitRecursive = false; + + for (const child of children) { + anyChildHitRecursive = + hitTestRec(child, offsetMouseCoord) || anyChildHitRecursive; + } + + const hit = thisNodeHit && !anyChildHitRecursive; + if (hit) { + res.push(node); + } + + return thisNodeHit || anyChildHitRecursive; + } + + hitTestRec(node, mouseCoordinate); + + return res.sort((a, b) => { + const areaA = a.bounds.height * a.bounds.width; + const areaB = b.bounds.height * b.bounds.width; + if (areaA > areaB) { + return 1; + } else if (areaA < areaB) { + return -1; + } else { + return 0; + } + }); +} + +function boundsContainsCoordinate(bounds: Bounds, coordinate: Coordinate) { + return ( + coordinate.x >= bounds.x && + coordinate.x <= bounds.x + bounds.width && + coordinate.y >= bounds.y && + coordinate.y <= bounds.y + bounds.height + ); +} + +function offsetCoordinate( + coordinate: Coordinate, + offset: Coordinate, +): Coordinate { + return { + x: coordinate.x - offset.x, + y: coordinate.y - offset.y, + }; +} + +function calcPxScaleFactor(snapshotBounds: Bounds, availableWidth: number) { + return snapshotBounds.width / availableWidth; +} diff --git a/desktop/plugins/public/ui-debugger/components/visualizer/VisualizerControls.tsx b/desktop/plugins/public/ui-debugger/components/visualizer/VisualizerControls.tsx new file mode 100644 index 000000000..4cd6f518b --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/visualizer/VisualizerControls.tsx @@ -0,0 +1,182 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Button, Dropdown, Slider, Tooltip, Typography} from 'antd'; +import {Layout, produce, theme, usePlugin} from 'flipper-plugin'; +import {ClientNode, Id} from '../../ClientTypes'; +import {plugin} from '../../index'; +import React from 'react'; +import { + AimOutlined, + FullscreenExitOutlined, + FullscreenOutlined, + PicCenterOutlined, +} from '@ant-design/icons'; +import {tracker} from '../../utils/tracker'; +import {debounce} from 'lodash'; +import {WireFrameMode} from '../../DesktopTypes'; +export type TargetModeState = + | { + state: 'selected'; + targetedNodes: Id[]; + sliderPosition: number; + } + | { + state: 'active'; + } + | { + state: 'disabled'; + }; + +function createItem(wireframeMode: WireFrameMode, label: string) { + return {key: wireframeMode, label: label}; +} + +const wireFrameModeDropDownItems = [ + createItem('All', 'All'), + createItem('SelectedAndChildren', 'Selected and children'), + createItem('SelectedOnly', 'Selected only'), +]; + +export function VisualiserControls({ + targetMode, + setTargetMode, + selectedNode, + focusedNode, + wireFrameMode, + onSetWireFrameMode, +}: { + wireFrameMode: WireFrameMode; + onSetWireFrameMode: (mode: WireFrameMode) => void; + selectedNode?: ClientNode; + focusedNode?: Id; + setTargetMode: (targetMode: TargetModeState) => void; + targetMode: TargetModeState; +}) { + const instance = usePlugin(plugin); + + const focusDisabled = + focusedNode == null && + (selectedNode == null || selectedNode.children.length === 0); + const focusToolTip = focusDisabled + ? 'Select a non leaf node to focus it' + : focusedNode == null + ? 'Focus current node' + : 'Remove focus'; + + const targetToolTip = + targetMode.state === 'disabled' ? 'Target Mode' : 'Exit target mode'; + + return ( + + + {targetMode.state === 'active' && ( + Target mode: Select element + )} + {targetMode.state === 'disabled' && ( + Interactive Visualizer + )} + {targetMode.state === 'selected' && ( + { + setTargetMode( + produce(targetMode, (draft) => { + draft.sliderPosition = value; + }), + ); + instance.uiActions.onSelectNode( + targetMode.targetedNodes[value], + 'visualiser', + ); + + debouncedReportTargetAdjusted(); + }} + /> + )} + + + + { + onSetWireFrameMode(event.selectedKeys[0] as WireFrameMode); + }, + }}> + + + + + + - - + - + - - + - + @@ -100,6 +96,7 @@ + @@ -131,7 +128,6 @@ - @@ -151,13 +147,12 @@ - + - - - - - - + - - - - + - - + + - @@ -276,12 +253,12 @@ - + - - - - + - - + - + @@ -416,7 +392,6 @@ - @@ -429,6 +404,6 @@ - + diff --git a/iOS/Sample/MainViewController.mm b/iOS/Sample/MainViewController.mm index 98eafe48f..d517de842 100644 --- a/iOS/Sample/MainViewController.mm +++ b/iOS/Sample/MainViewController.mm @@ -10,7 +10,6 @@ #import #import "CommunicationDemoViewController.h" #import "NetworkViewController.h" -#import "RootViewController.h" #import "UserDefaultsViewController.h" @interface MainViewController () @@ -29,13 +28,6 @@ [self.navigationController pushViewController:controller animated:true]; } -- (IBAction)tappedComponentKitLayout:(UIButton*)sender { - RootViewController* rootViewController = [RootViewController new]; - - [self.navigationController pushViewController:rootViewController - animated:true]; -} - - (IBAction)tappedNetworkInspector:(UIButton*)sender { UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MainStoryBoard" bundle:nil]; diff --git a/iOS/Sample/Podfile b/iOS/Sample/Podfile index f2212b18f..772305d2f 100644 --- a/iOS/Sample/Podfile +++ b/iOS/Sample/Podfile @@ -3,11 +3,12 @@ source 'https://github.com/facebook/flipper.git' source 'https://github.com/CocoaPods/Specs' target 'Sample' do - platform :ios, '10.0' + platform :ios, '11.0' # See docs/getting-started/ios-native.mdx - pod 'FlipperKit', :path => '../../FlipperKit.podspec', :configuration => 'Debug' - pod 'FlipperKit/FlipperKitLayoutComponentKitSupport', :path => '../../FlipperKit.podspec', :configuration => 'Debug' + pod 'FlipperKit', :path => '../../FlipperKit.podspec', :configuration => 'Debug' + pod 'FlipperKit/FlipperKitLayoutPlugin', :path => '../../FlipperKit.podspec', :configuration => 'Debug' + pod 'FlipperKit/FlipperKitUIDebuggerPlugin', :path => '../../FlipperKit.podspec', :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', :path => '../../FlipperKit.podspec', :configuration => 'Debug' pod 'FlipperKit/FlipperKitUserDefaultsPlugin', :path => '../../FlipperKit.podspec', :configuration => 'Debug' pod 'FlipperKit/FlipperKitExamplePlugin', :path => '../../FlipperKit.podspec', :configuration => 'Debug' @@ -21,7 +22,6 @@ target 'Sample' do pod 'Flipper-Boost-iOSX', :configuration => 'Debug' pod 'OpenSSL-Universal', :configuration => 'Debug' pod 'CocoaAsyncSocket', :configuration => 'Debug' - pod 'ComponentKit', '~> 0.31' - pod 'SocketRocket', '~> 0.6.0' + pod 'SocketRocket', '~> 0.7.0' end diff --git a/iOS/Sample/Podfile.lock b/iOS/Sample/Podfile.lock index 0f1069d2a..8989248a0 100644 --- a/iOS/Sample/Podfile.lock +++ b/iOS/Sample/Podfile.lock @@ -1,9 +1,6 @@ PODS: - CocoaAsyncSocket (7.6.5) - - ComponentKit (0.31): - - RenderCore (= 0.31) - - Yoga (~> 1.14) - - Flipper (0.162.0): + - Flipper (0.222.0): - Flipper-Folly (~> 2.6) - Flipper-Boost-iOSX (1.76.0.1.11) - Flipper-DoubleConversion (3.2.0.1) @@ -17,71 +14,58 @@ PODS: - OpenSSL-Universal (= 1.1.1100) - Flipper-Glog (0.5.0.5) - Flipper-PeerTalk (0.0.4) - - FlipperKit (0.162.0): - - FlipperKit/Core (= 0.162.0) - - FlipperKit/Core (0.162.0): - - Flipper (~> 0.162.0) + - FlipperKit (0.222.0): + - FlipperKit/Core (= 0.222.0) + - FlipperKit/Core (0.222.0): + - Flipper (~> 0.222.0) - FlipperKit/CppBridge - FlipperKit/FBCxxFollyDynamicConvert - FlipperKit/FBDefines - FlipperKit/FKPortForwarding - - SocketRocket (~> 0.6.0) - - FlipperKit/CppBridge (0.162.0): - - Flipper (~> 0.162.0) - - FlipperKit/FBCxxFollyDynamicConvert (0.162.0): + - SocketRocket (~> 0.7.0) + - FlipperKit/CppBridge (0.222.0): + - Flipper (~> 0.222.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.222.0): - Flipper-Folly (~> 2.6) - - FlipperKit/FBDefines (0.162.0) - - FlipperKit/FKPortForwarding (0.162.0): + - FlipperKit/FBDefines (0.222.0) + - FlipperKit/FKPortForwarding (0.222.0): - CocoaAsyncSocket (~> 7.6) - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitExamplePlugin (0.162.0): + - FlipperKit/FlipperKitExamplePlugin (0.222.0): - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay (0.162.0) - - FlipperKit/FlipperKitLayoutComponentKitSupport (0.162.0): - - ComponentKit (= 0.31) - - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay - - FlipperKit/FlipperKitLayoutHelpers - - FlipperKit/FlipperKitLayoutPlugin - - FlipperKit/FlipperKitLayoutTextSearchable - - RenderCore (= 0.31) - - FlipperKit/FlipperKitLayoutHelpers (0.162.0): + - FlipperKit/FlipperKitHighlightOverlay (0.222.0) + - FlipperKit/FlipperKitLayoutHelpers (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutTextSearchable - - FlipperKit/FlipperKitLayoutIOSDescriptors (0.162.0): + - FlipperKit/FlipperKitLayoutIOSDescriptors (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutPlugin (0.162.0): + - FlipperKit/FlipperKitLayoutPlugin (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - FlipperKit/FlipperKitLayoutIOSDescriptors - FlipperKit/FlipperKitLayoutTextSearchable - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.162.0) - - FlipperKit/FlipperKitNetworkPlugin (0.162.0): + - FlipperKit/FlipperKitLayoutTextSearchable (0.222.0) + - FlipperKit/FlipperKitNetworkPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/FlipperKitReactPlugin (0.162.0): + - FlipperKit/FlipperKitReactPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.162.0): + - FlipperKit/FlipperKitUIDebuggerPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.162.0): + - FlipperKit/FlipperKitUserDefaultsPlugin (0.222.0): + - FlipperKit/Core + - FlipperKit/SKIOSNetworkPlugin (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - libevent (2.1.12) - OpenSSL-Universal (1.1.1100) - - RenderCore (0.31) - - SocketRocket (0.6.0) - - Yoga (1.14.0) - - YogaKit (1.18.1): - - Yoga (~> 1.14) + - SocketRocket (0.7.0) DEPENDENCIES: - CocoaAsyncSocket - - ComponentKit (~> 0.31) - Flipper (from `../../Flipper.podspec`) - Flipper-Boost-iOSX - Flipper-DoubleConversion @@ -90,18 +74,18 @@ DEPENDENCIES: - Flipper-PeerTalk - FlipperKit (from `../../FlipperKit.podspec`) - FlipperKit/FlipperKitExamplePlugin (from `../../FlipperKit.podspec`) - - FlipperKit/FlipperKitLayoutComponentKitSupport (from `../../FlipperKit.podspec`) + - FlipperKit/FlipperKitLayoutPlugin (from `../../FlipperKit.podspec`) - FlipperKit/FlipperKitReactPlugin (from `../../FlipperKit.podspec`) + - FlipperKit/FlipperKitUIDebuggerPlugin (from `../../FlipperKit.podspec`) - FlipperKit/FlipperKitUserDefaultsPlugin (from `../../FlipperKit.podspec`) - FlipperKit/SKIOSNetworkPlugin (from `../../FlipperKit.podspec`) - libevent - OpenSSL-Universal - - SocketRocket (~> 0.6.0) + - SocketRocket (~> 0.7.0) SPEC REPOS: https://github.com/CocoaPods/Specs: - CocoaAsyncSocket - - ComponentKit - Flipper-Boost-iOSX - Flipper-DoubleConversion - Flipper-Fmt @@ -110,10 +94,7 @@ SPEC REPOS: - Flipper-PeerTalk - libevent - OpenSSL-Universal - - RenderCore - SocketRocket - - Yoga - - YogaKit EXTERNAL SOURCES: Flipper: @@ -123,22 +104,18 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 - ComponentKit: 7bf7048b9814afc6b6641645a14177f95fd9b9ae - Flipper: 1cdd72ffa916071754a41e9684fb9bcaa4b690bf + Flipper: 2134ddbde14751be413e22c2677d743dffaa9945 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3 Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 - FlipperKit: 87dd306eef9927220672f98266e68bdab6c7cd69 + FlipperKit: 347167ea72c9d718edb15757184384cf31a2cf27 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c - RenderCore: 090beb17b5bff80b86929a7ceb49df789923d23a - SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608 - Yoga: cff67a400f6b74dc38eb0bad4f156673d9aa980c - YogaKit: f782866e155069a2cca2517aafea43200b01fd5a + SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d -PODFILE CHECKSUM: 8f7b8c1a8e7cee47eaef4736990315bfc090e2af +PODFILE CHECKSUM: 183869a0e4a7ff3632408564a06aeb8f4ddc1992 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/iOS/Sample/RootViewController.mm b/iOS/Sample/RootViewController.mm deleted file mode 100644 index dcde5977b..000000000 --- a/iOS/Sample/RootViewController.mm +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RootViewController.h" - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -@interface RootViewController () - -@property(strong, nonatomic) CKComponentHostingView* rootCKHostingView; - -@end - -@implementation RootViewController - -- (instancetype)init { - if (self = [super init]) { - _rootCKHostingView = [[CKComponentHostingView alloc] - initWithComponentProviderFunc:componentForModel - sizeRangeProvider: - [CKComponentFlexibleSizeRangeProvider - providerWithFlexibility: - CKComponentSizeRangeFlexibleHeight]]; - - [self.view addSubview:_rootCKHostingView]; - [self loadViewIfNeeded]; - } - return self; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - self.navigationItem.title = @"ComponentKit Layout"; - self.edgesForExtendedLayout = UIRectEdgeNone; -} - -- (void)viewDidLayoutSubviews { - [super viewDidLayoutSubviews]; - _rootCKHostingView.frame = self.view.bounds; -} - -static CKComponent* componentForModel( - id model, - id context) { - return CK::BackgroundLayoutComponentBuilder() - .component(CK::FlexboxComponentBuilder() - .child( - {.component = CK::ButtonComponentBuilder() - .action(nil) - .title(@"Purple") - .titleColor(UIColor.purpleColor) - .build()}) - .child( - {.component = CK::ButtonComponentBuilder() - .action(nil) - .title(@"Brown") - .titleColor(UIColor.brownColor) - .build()}) - .child( - {.component = CK::ButtonComponentBuilder() - .action(nil) - .title(@"Cyan") - .titleColor(UIColor.cyanColor) - .build()}) - .build()) - .background(CK::ImageComponentBuilder() - .image([UIImage imageNamed:@"sonarpattern"]) - .build()) - .build(); -} - -@end diff --git a/iOS/Sample/Sample.xcodeproj/project.pbxproj b/iOS/Sample/Sample.xcodeproj/project.pbxproj index 2afb5f129..d6642af8f 100644 --- a/iOS/Sample/Sample.xcodeproj/project.pbxproj +++ b/iOS/Sample/Sample.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 53D59DB320ABA18400207065 /* NetworkViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D59DAA20ABA18300207065 /* NetworkViewController.m */; }; 53D59DB420ABA18400207065 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D59DAB20ABA18300207065 /* AppDelegate.m */; }; 53D59DB520ABA18400207065 /* MainViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 53D59DAD20ABA18300207065 /* MainViewController.mm */; }; - 53D59DB620ABA18400207065 /* RootViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 53D59DAF20ABA18300207065 /* RootViewController.mm */; }; 53D59DB720ABA18400207065 /* MainStoryBoard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53D59DB020ABA18400207065 /* MainStoryBoard.storyboard */; }; 53D59DB820ABA18400207065 /* Icons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53D59DB120ABA18400207065 /* Icons.xcassets */; }; 53E0DE5420ABA0E4005682E1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 53E0DE5320ABA0E4005682E1 /* main.m */; }; @@ -29,8 +28,6 @@ 53D59DAB20ABA18300207065 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; }; 53D59DAC20ABA18300207065 /* NetworkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkViewController.h; sourceTree = SOURCE_ROOT; }; 53D59DAD20ABA18300207065 /* MainViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MainViewController.mm; sourceTree = SOURCE_ROOT; }; - 53D59DAE20ABA18300207065 /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = SOURCE_ROOT; }; - 53D59DAF20ABA18300207065 /* RootViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RootViewController.mm; sourceTree = SOURCE_ROOT; }; 53D59DB020ABA18400207065 /* MainStoryBoard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryBoard.storyboard; sourceTree = SOURCE_ROOT; }; 53D59DB120ABA18400207065 /* Icons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Icons.xcassets; sourceTree = SOURCE_ROOT; }; 53D59DB220ABA18400207065 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = SOURCE_ROOT; }; @@ -68,8 +65,6 @@ 53D59DAA20ABA18300207065 /* NetworkViewController.m */, 4E10233F216AD7B400160734 /* UserDefaultsViewController.h */, 4E102340216AD7B400160734 /* UserDefaultsViewController.m */, - 53D59DAE20ABA18300207065 /* RootViewController.h */, - 53D59DAF20ABA18300207065 /* RootViewController.mm */, 53E0DE5220ABA0E4005682E1 /* Info.plist */, 53E0DE5320ABA0E4005682E1 /* main.m */, 534252A9217DECCD0092D02B /* CommunicationDemoViewController.h */, @@ -228,7 +223,6 @@ 53D59DB420ABA18400207065 /* AppDelegate.m in Sources */, 53B4A36B217E2B6200B36A53 /* CommunicationDemoViewController.mm in Sources */, 53D59DB520ABA18400207065 /* MainViewController.mm in Sources */, - 53D59DB620ABA18400207065 /* RootViewController.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -359,7 +353,6 @@ "$(inherited)", "\"${PODS_ROOT}/Headers/Public\"", "\"${PODS_ROOT}/Headers/Public/CocoaAsyncSocket\"", - "\"${PODS_ROOT}/Headers/Public/ComponentKit\"", "\"${PODS_ROOT}/Headers/Public/DoubleConversion\"", "\"${PODS_ROOT}/Headers/Public/Folly\"", "\"${PODS_ROOT}/Headers/Public/PeerTalk\"", @@ -371,7 +364,7 @@ "\"${PODS_ROOT}/Headers/Public/libevent\"", ); INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( /usr/lib/swift, "$(inherited)", @@ -424,7 +417,6 @@ "$(inherited)", "\"${PODS_ROOT}/Headers/Public\"", "\"${PODS_ROOT}/Headers/Public/CocoaAsyncSocket\"", - "\"${PODS_ROOT}/Headers/Public/ComponentKit\"", "\"${PODS_ROOT}/Headers/Public/DoubleConversion\"", "\"${PODS_ROOT}/Headers/Public/Folly\"", "\"${PODS_ROOT}/Headers/Public/PeerTalk\"", @@ -436,7 +428,7 @@ "\"${PODS_ROOT}/Headers/Public/libevent\"", ); INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( /usr/lib/swift, "$(inherited)", diff --git a/iOS/SampleSwift/Podfile b/iOS/SampleSwift/Podfile index 351702af9..6431a9248 100644 --- a/iOS/SampleSwift/Podfile +++ b/iOS/SampleSwift/Podfile @@ -3,11 +3,11 @@ source 'https://github.com/facebook/flipper.git' source 'https://github.com/CocoaPods/Specs' target 'SampleSwift' do - platform :ios, '10.0' + platform :ios, '11.0' # See docs/getting-started/ios-native.mdx pod 'FlipperKit', :path => '../../FlipperKit.podspec', :configuration => 'Debug' - pod 'FlipperKit/FlipperKitLayoutComponentKitSupport', :path => '../../FlipperKit.podspec', :configuration => 'Debug' + pod 'FlipperKit/FlipperKitLayoutPlugin', :path => '../../FlipperKit.podspec', :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', :path => '../../FlipperKit.podspec', :configuration => 'Debug' pod 'FlipperKit/FlipperKitUserDefaultsPlugin', :path => '../../FlipperKit.podspec', :configuration => 'Debug' pod 'FlipperKit/FlipperKitExamplePlugin', :path => '../../FlipperKit.podspec', :configuration => 'Debug' diff --git a/iOS/SampleSwift/Podfile.lock b/iOS/SampleSwift/Podfile.lock index 1cc74c696..2698fb1f0 100644 --- a/iOS/SampleSwift/Podfile.lock +++ b/iOS/SampleSwift/Podfile.lock @@ -1,10 +1,7 @@ PODS: - boost-for-react-native (1.63.0) - CocoaAsyncSocket (7.6.5) - - ComponentKit (0.31): - - RenderCore (= 0.31) - - Yoga (~> 1.14) - - Flipper (0.162.0): + - Flipper (0.222.0): - Flipper-Folly (~> 2.6) - Flipper-Boost-iOSX (1.76.0.1.11) - Flipper-DoubleConversion (3.2.0.1) @@ -18,65 +15,51 @@ PODS: - OpenSSL-Universal (= 1.1.1100) - Flipper-Glog (0.5.0.5) - Flipper-PeerTalk (0.0.4) - - FlipperKit (0.162.0): - - FlipperKit/Core (= 0.162.0) - - FlipperKit/Core (0.162.0): - - Flipper (~> 0.162.0) + - FlipperKit (0.222.0): + - FlipperKit/Core (= 0.222.0) + - FlipperKit/Core (0.222.0): + - Flipper (~> 0.222.0) - FlipperKit/CppBridge - FlipperKit/FBCxxFollyDynamicConvert - FlipperKit/FBDefines - FlipperKit/FKPortForwarding - - SocketRocket (~> 0.6.0) - - FlipperKit/CppBridge (0.162.0): - - Flipper (~> 0.162.0) - - FlipperKit/FBCxxFollyDynamicConvert (0.162.0): + - SocketRocket (~> 0.7.0) + - FlipperKit/CppBridge (0.222.0): + - Flipper (~> 0.222.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.222.0): - Flipper-Folly (~> 2.6) - - FlipperKit/FBDefines (0.162.0) - - FlipperKit/FKPortForwarding (0.162.0): + - FlipperKit/FBDefines (0.222.0) + - FlipperKit/FKPortForwarding (0.222.0): - CocoaAsyncSocket (~> 7.6) - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitExamplePlugin (0.162.0): + - FlipperKit/FlipperKitExamplePlugin (0.222.0): - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay (0.162.0) - - FlipperKit/FlipperKitLayoutComponentKitSupport (0.162.0): - - ComponentKit (= 0.31) - - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay - - FlipperKit/FlipperKitLayoutHelpers - - FlipperKit/FlipperKitLayoutPlugin - - FlipperKit/FlipperKitLayoutTextSearchable - - RenderCore (= 0.31) - - FlipperKit/FlipperKitLayoutHelpers (0.162.0): + - FlipperKit/FlipperKitHighlightOverlay (0.222.0) + - FlipperKit/FlipperKitLayoutHelpers (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutTextSearchable - - FlipperKit/FlipperKitLayoutIOSDescriptors (0.162.0): + - FlipperKit/FlipperKitLayoutIOSDescriptors (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutPlugin (0.162.0): + - FlipperKit/FlipperKitLayoutPlugin (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - FlipperKit/FlipperKitLayoutIOSDescriptors - FlipperKit/FlipperKitLayoutTextSearchable - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.162.0) - - FlipperKit/FlipperKitNetworkPlugin (0.162.0): + - FlipperKit/FlipperKitLayoutTextSearchable (0.222.0) + - FlipperKit/FlipperKitNetworkPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.162.0): + - FlipperKit/FlipperKitUserDefaultsPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.162.0): + - FlipperKit/SKIOSNetworkPlugin (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - libevent (2.1.12) - OpenSSL-Universal (1.1.1100) - - RenderCore (0.31) - - SocketRocket (0.6.0) - - Yoga (1.14.0) - - YogaKit (1.18.1): - - Yoga (~> 1.14) + - SocketRocket (0.7.0) DEPENDENCIES: - boost-for-react-native @@ -88,7 +71,7 @@ DEPENDENCIES: - Flipper-PeerTalk - FlipperKit (from `../../FlipperKit.podspec`) - FlipperKit/FlipperKitExamplePlugin (from `../../FlipperKit.podspec`) - - FlipperKit/FlipperKitLayoutComponentKitSupport (from `../../FlipperKit.podspec`) + - FlipperKit/FlipperKitLayoutPlugin (from `../../FlipperKit.podspec`) - FlipperKit/FlipperKitUserDefaultsPlugin (from `../../FlipperKit.podspec`) - FlipperKit/SKIOSNetworkPlugin (from `../../FlipperKit.podspec`) - libevent @@ -98,7 +81,6 @@ SPEC REPOS: https://github.com/CocoaPods/Specs: - boost-for-react-native - CocoaAsyncSocket - - ComponentKit - Flipper-Boost-iOSX - Flipper-DoubleConversion - Flipper-Fmt @@ -107,10 +89,7 @@ SPEC REPOS: - Flipper-PeerTalk - libevent - OpenSSL-Universal - - RenderCore - SocketRocket - - Yoga - - YogaKit EXTERNAL SOURCES: Flipper: @@ -121,22 +100,18 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 - ComponentKit: 7bf7048b9814afc6b6641645a14177f95fd9b9ae - Flipper: 1cdd72ffa916071754a41e9684fb9bcaa4b690bf + Flipper: 2134ddbde14751be413e22c2677d743dffaa9945 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3 Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 - FlipperKit: 87dd306eef9927220672f98266e68bdab6c7cd69 + FlipperKit: 347167ea72c9d718edb15757184384cf31a2cf27 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c - RenderCore: 090beb17b5bff80b86929a7ceb49df789923d23a - SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608 - Yoga: cff67a400f6b74dc38eb0bad4f156673d9aa980c - YogaKit: f782866e155069a2cca2517aafea43200b01fd5a + SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d -PODFILE CHECKSUM: d2f6b1f5f7b3ca7994bb97a2c78943c23570db33 +PODFILE CHECKSUM: 91787ba7cc4ea0c7dbba9f798e5b572e6f0c5f08 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/iOS/SampleSwift/SampleSwift/AppDelegate.swift b/iOS/SampleSwift/SampleSwift/AppDelegate.swift index 304445eb1..775e8150c 100644 --- a/iOS/SampleSwift/SampleSwift/AppDelegate.swift +++ b/iOS/SampleSwift/SampleSwift/AppDelegate.swift @@ -5,27 +5,24 @@ * LICENSE file in the root directory of this source tree. */ -import UIKit import FlipperKit +import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow() let client = FlipperClient.shared() let layoutDescriptorMapper = SKDescriptorMapper(defaults: ()) - // If you want to debug componentkit view in swift, otherwise you can ignore the next line - FlipperKitLayoutComponentKitSupport.setUpWith(layoutDescriptorMapper) - client?.add(FlipperKitLayoutPlugin(rootNode: application, with: layoutDescriptorMapper!)) + client?.add(FlipperKitLayoutPlugin(rootNode: application, with: layoutDescriptorMapper)) client?.add(FlipperKitNetworkPlugin(networkAdapter: SKIOSNetworkAdapter())) - client?.add(FlipperKitExamplePlugin.sharedInstance()); - client?.add(FKUserDefaultsPlugin.init(suiteName: nil)) + client?.add(FlipperKitExamplePlugin.sharedInstance()) + client?.add(FKUserDefaultsPlugin(suiteName: nil)) client?.start() let storyboard = UIStoryboard(name: "MainStoryBoard", bundle: nil) @@ -65,6 +62,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } - - } diff --git a/iOS/SampleSwift/SampleSwift/MainStoryBoard.storyboard b/iOS/SampleSwift/SampleSwift/MainStoryBoard.storyboard index 461ded24e..964d913c6 100644 --- a/iOS/SampleSwift/SampleSwift/MainStoryBoard.storyboard +++ b/iOS/SampleSwift/SampleSwift/MainStoryBoard.storyboard @@ -1,11 +1,9 @@ - - - - + + - + @@ -19,10 +17,10 @@ - + - - - - + - - - - + - - + + - @@ -105,16 +89,16 @@ - + - + - + - - - - + - - + - + @@ -249,7 +232,6 @@ - @@ -258,40 +240,40 @@ - + - + - + - - + @@ -299,22 +281,21 @@ - + - - - + - + @@ -353,6 +334,7 @@ + @@ -384,7 +366,6 @@ - @@ -394,10 +375,13 @@ - + - + + + + diff --git a/iOS/Tutorial/Podfile b/iOS/Tutorial/Podfile index dc173f0c4..6341cdebe 100644 --- a/iOS/Tutorial/Podfile +++ b/iOS/Tutorial/Podfile @@ -1,10 +1,10 @@ project 'Tutorial.xcodeproj' swift_version = "4.1" -flipperkit_version = '0.162.0' +flipperkit_version = '0.222.0' use_frameworks! target 'Tutorial' do - platform :ios, '10.0' + platform :ios, '11.0' pod 'FlipperKit', '~>' + flipperkit_version # Layout and network plugins are not yet supported for swift projects diff --git a/iOS/Tutorial/Podfile.lock b/iOS/Tutorial/Podfile.lock index 6ac384a97..7c4133f25 100644 --- a/iOS/Tutorial/Podfile.lock +++ b/iOS/Tutorial/Podfile.lock @@ -3,7 +3,7 @@ PODS: - ComponentKit (0.31): - RenderCore (= 0.31) - Yoga (~> 1.14) - - Flipper (0.162.0): + - Flipper (0.222.0): - Flipper-Folly (~> 2.6) - Flipper-Boost-iOSX (1.76.0.1.11) - Flipper-DoubleConversion (3.2.0.1) @@ -17,25 +17,25 @@ PODS: - OpenSSL-Universal (= 1.1.1100) - Flipper-Glog (0.5.0.5) - Flipper-PeerTalk (0.0.4) - - FlipperKit (0.162.0): - - FlipperKit/Core (= 0.162.0) - - FlipperKit/Core (0.162.0): - - Flipper (~> 0.162.0) + - FlipperKit (0.222.0): + - FlipperKit/Core (= 0.222.0) + - FlipperKit/Core (0.222.0): + - Flipper (~> 0.222.0) - FlipperKit/CppBridge - FlipperKit/FBCxxFollyDynamicConvert - FlipperKit/FBDefines - FlipperKit/FKPortForwarding - - SocketRocket (~> 0.6.0) - - FlipperKit/CppBridge (0.162.0): - - Flipper (~> 0.162.0) - - FlipperKit/FBCxxFollyDynamicConvert (0.162.0): + - SocketRocket (~> 0.7.0) + - FlipperKit/CppBridge (0.222.0): + - Flipper (~> 0.222.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.222.0): - Flipper-Folly (~> 2.6) - - FlipperKit/FBDefines (0.162.0) - - FlipperKit/FKPortForwarding (0.162.0): + - FlipperKit/FBDefines (0.222.0) + - FlipperKit/FKPortForwarding (0.222.0): - CocoaAsyncSocket (~> 7.6) - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitHighlightOverlay (0.162.0) - - FlipperKit/FlipperKitLayoutComponentKitSupport (0.162.0): + - FlipperKit/FlipperKitHighlightOverlay (0.222.0) + - FlipperKit/FlipperKitLayoutComponentKitSupport (0.222.0): - ComponentKit (= 0.31) - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay @@ -43,43 +43,39 @@ PODS: - FlipperKit/FlipperKitLayoutPlugin - FlipperKit/FlipperKitLayoutTextSearchable - RenderCore (= 0.31) - - FlipperKit/FlipperKitLayoutHelpers (0.162.0): + - FlipperKit/FlipperKitLayoutHelpers (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutTextSearchable - - FlipperKit/FlipperKitLayoutIOSDescriptors (0.162.0): + - FlipperKit/FlipperKitLayoutIOSDescriptors (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutPlugin (0.162.0): + - FlipperKit/FlipperKitLayoutPlugin (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - FlipperKit/FlipperKitLayoutIOSDescriptors - FlipperKit/FlipperKitLayoutTextSearchable - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.162.0) - - FlipperKit/FlipperKitNetworkPlugin (0.162.0): + - FlipperKit/FlipperKitLayoutTextSearchable (0.222.0) + - FlipperKit/FlipperKitNetworkPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.162.0): + - FlipperKit/FlipperKitUserDefaultsPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.162.0): + - FlipperKit/SKIOSNetworkPlugin (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - libevent (2.1.12) - OpenSSL-Universal (1.1.1100) - RenderCore (0.31) - - SocketRocket (0.6.0) + - SocketRocket (0.7.0) - Yoga (1.14.0) - - YogaKit (1.18.1): - - Yoga (~> 1.14) DEPENDENCIES: - - FlipperKit (~> 0.162.0) - - FlipperKit/FlipperKitLayoutComponentKitSupport (~> 0.162.0) - - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.162.0) - - FlipperKit/SKIOSNetworkPlugin (~> 0.162.0) + - FlipperKit (~> 0.222.0) + - FlipperKit/FlipperKitLayoutComponentKitSupport (~> 0.222.0) + - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.222.0) + - FlipperKit/SKIOSNetworkPlugin (~> 0.222.0) SPEC REPOS: trunk: @@ -98,26 +94,24 @@ SPEC REPOS: - RenderCore - SocketRocket - Yoga - - YogaKit SPEC CHECKSUMS: CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 ComponentKit: 7bf7048b9814afc6b6641645a14177f95fd9b9ae - Flipper: 1cdd72ffa916071754a41e9684fb9bcaa4b690bf + Flipper: 2134ddbde14751be413e22c2677d743dffaa9945 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3 Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 - FlipperKit: 87dd306eef9927220672f98266e68bdab6c7cd69 + FlipperKit: 347167ea72c9d718edb15757184384cf31a2cf27 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c RenderCore: 090beb17b5bff80b86929a7ceb49df789923d23a - SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608 + SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: cff67a400f6b74dc38eb0bad4f156673d9aa980c - YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 501cc41c1247e0a7e1e11e75a98f15d6811266b2 +PODFILE CHECKSUM: 8f946f0af7af514b0220472d59f2b9d79b44d357 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/js/js-flipper/package.json b/js/js-flipper/package.json index 605f352bc..2d4954750 100644 --- a/js/js-flipper/package.json +++ b/js/js-flipper/package.json @@ -1,7 +1,7 @@ { "name": "js-flipper", "title": "JS Flipper Bindings for Web-Socket based clients", - "version": "0.162.0", + "version": "0.227.0", "main": "lib/index.js", "browser": { "os": false @@ -39,34 +39,34 @@ "licenseFilename": "LICENSE", "readmeFilename": "README.md", "devDependencies": { - "@babel/core": "^7.18.2", - "@babel/eslint-parser": "^7.18.2", - "@types/jest": "^27.5.0", - "@types/node": "^17.0.41", - "@types/sinon": "^10.0.11", - "@types/ws": "^8.5.3", - "@typescript-eslint/eslint-plugin": "^5.27.1", - "@typescript-eslint/parser": "^5.27.1", + "@babel/core": "^7.22.10", + "@babel/eslint-parser": "7.22.10", + "@types/jest": "^29.5.3", + "@types/node": "^20.5.0", + "@types/sinon": "^10.0.16", + "@types/ws": "^8.5.5", + "@typescript-eslint/eslint-plugin": "6.4.0", + "@typescript-eslint/parser": "6.4.0", "ansi-to-html": "^0.7.2", "cross-env": "^7.0.3", - "eslint": "^8.17.0", - "eslint-config-fbjs": "^3.1.1", - "eslint-config-prettier": "^8.5.0", - "eslint-import-resolver-typescript": "^2.7.1", + "eslint": "8.47.0", + "eslint-config-fbjs": "^4.0.0", + "eslint-config-prettier": "^9.0.0", + "eslint-import-resolver-typescript": "^3.6.0", "eslint-plugin-babel": "^5.3.0", - "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-ft-flow": "^3.0.0", "eslint-plugin-header": "^3.0.0", - "eslint-plugin-import": "^2.25.4", - "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-import": "^2.28.0", + "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-react": "^7.29.4", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.5.0", - "jest": "^28.1.1", - "prettier": "^2.6.2", - "sinon": "^14.0.0", - "ts-jest": "^28.0.4", - "typescript": "^4.7.3", - "ws": "^8.5.0" + "jest": "^29.6.2", + "prettier": "^3.0.2", + "sinon": "^15.2.0", + "ts-jest": "^29.1.0", + "typescript": "^5.1.6", + "ws": "^8.12.1" } } diff --git a/js/js-flipper/src/client.ts b/js/js-flipper/src/client.ts index b64f134b8..bfe73bc32 100644 --- a/js/js-flipper/src/client.ts +++ b/js/js-flipper/src/client.ts @@ -237,7 +237,7 @@ export class FlipperClient { }; this.ws.onclose = ({code}) => { // Some WS implementations do not properly set `wasClean` - if (code !== WSCloseCode.NormalClosure) { + if (![WSCloseCode.NormalClosure, WSCloseCode.GoingAway].includes(code)) { this.reconnect(); } }; @@ -292,8 +292,8 @@ export class FlipperClient { if (method === 'getBackgroundPlugins') { responder.success({ - plugins: [...this.plugins.keys()].filter((key) => - this.plugins.get(key)?.runInBackground?.(), + plugins: [...this.plugins.keys()].filter( + (key) => this.plugins.get(key)?.runInBackground?.(), ), }); return; diff --git a/js/js-flipper/src/connection.ts b/js/js-flipper/src/connection.ts index 8f9a1f48c..ecc5cca26 100644 --- a/js/js-flipper/src/connection.ts +++ b/js/js-flipper/src/connection.ts @@ -8,11 +8,18 @@ */ import {FlipperErrorMessage, FlipperMessageBus} from './message'; -import {FlipperPluginConnection, FlipperPluginReceiver} from './plugin'; +import { + FlipperPluginConnection, + FlipperPluginReceiver, + FlipperPluginReceiverRes, +} from './plugin'; import {FlipperResponder} from './responder'; import {isPromise, safeJSONStringify} from './util'; -type FlipperReceiver = (data: unknown, responder: FlipperResponder) => void; +type FlipperReceiver = ( + data: FlipperPluginReceiverRes, + responder: FlipperResponder, +) => void; export class FlipperConnection implements FlipperPluginConnection { pluginId: string; @@ -36,7 +43,10 @@ export class FlipperConnection implements FlipperPluginConnection { } receive(method: string, receiver: FlipperPluginReceiver) { - const wrappedReceiver: FlipperReceiver = (data, responder) => { + const wrappedReceiver: FlipperReceiver = ( + data: FlipperPluginReceiverRes, + responder, + ) => { const handleError = (e: unknown) => { const errorMessage: FlipperErrorMessage = e instanceof Error @@ -47,7 +57,9 @@ export class FlipperConnection implements FlipperPluginConnection { try { const response = receiver(data); if (isPromise(response)) { - response.then((data) => responder.success(data)).catch(handleError); + response + .then((data: FlipperPluginReceiverRes) => responder.success(data)) + .catch(handleError); return; } responder.success(response); diff --git a/js/js-flipper/yarn.lock b/js/js-flipper/yarn.lock index 3d0a4dcd1..3f045604a 100644 --- a/js/js-flipper/yarn.lock +++ b/js/js-flipper/yarn.lock @@ -2,7 +2,12 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== @@ -17,72 +22,147 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" - integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== +"@babel/code-frame@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" + integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== + dependencies: + "@babel/highlight" "^7.22.10" + chalk "^2.4.2" -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.18.2": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" - integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== +"@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/compat-data@^7.20.0": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.5.tgz#86f172690b093373a933223b4745deeb6049e733" + integrity sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g== + +"@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113" + integrity sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-module-transforms" "^7.18.9" - "@babel/helpers" "^7.18.9" - "@babel/parser" "^7.18.10" + "@babel/generator" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-module-transforms" "^7.20.2" + "@babel/helpers" "^7.20.5" + "@babel/parser" "^7.20.5" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.18.10" - "@babel/types" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/eslint-parser@^7.18.2": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.18.9.tgz#255a63796819a97b7578751bb08ab9f2a375a031" - integrity sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ== +"@babel/core@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" + integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== dependencies: - eslint-scope "^5.1.1" - eslint-visitor-keys "^2.1.0" - semver "^6.3.0" + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.10" + "@babel/generator" "^7.22.10" + "@babel/helper-compilation-targets" "^7.22.10" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.10" + "@babel/parser" "^7.22.10" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.10" + "@babel/types" "^7.22.10" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.1" -"@babel/generator@^7.18.10", "@babel/generator@^7.7.2": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.10.tgz#794f328bfabdcbaf0ebf9bf91b5b57b61fa77a2a" - integrity sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA== +"@babel/eslint-parser@7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.10.tgz#bfdf3d1b32ad573fe7c1c3447e0b485e3a41fd09" + integrity sha512-0J8DNPRXQRLeR9rPaUMM3fA+RbixjnVLe/MRMYCkp3hzgsSuxCHQ8NN8xQG1wIHKJ4a1DTROTvFJdW+B5/eOsg== dependencies: - "@babel/types" "^7.18.10" + "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" + eslint-visitor-keys "^2.1.0" + semver "^6.3.1" + +"@babel/generator@^7.20.5", "@babel/generator@^7.7.2": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95" + integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA== + dependencies: + "@babel/types" "^7.20.5" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/helper-compilation-targets@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" - integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== +"@babel/generator@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" + integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== dependencies: - "@babel/compat-data" "^7.18.8" + "@babel/types" "^7.22.10" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== + dependencies: + "@babel/compat-data" "^7.20.0" "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.20.2" + browserslist "^4.21.3" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024" + integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.5" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" - integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + +"@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== dependencies: - "@babel/template" "^7.18.6" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -91,6 +171,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" @@ -98,31 +185,56 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" - integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-transforms@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" + integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.18.6" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" + +"@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.5" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.8.0": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== -"@babel/helper-simple-access@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" - integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.20.2" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" @@ -131,29 +243,60 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" - integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" -"@babel/helper-validator-identifier@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" - integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== "@babel/helper-validator-option@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/helpers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" - integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + +"@babel/helpers@^7.20.5": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.6.tgz#e64778046b70e04779dfbdf924e7ebb45992c763" + integrity sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w== dependencies: - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + +"@babel/helpers@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" + integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== + dependencies: + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.10" + "@babel/types" "^7.22.10" "@babel/highlight@^7.18.6": version "7.18.6" @@ -164,10 +307,38 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.10.tgz#94b5f8522356e69e8277276adf67ed280c90ecc1" - integrity sha512-TYk3OA0HKL6qNryUayb5UUEhM/rkOQozIBEA5ITXh5DWrSp0TlUQXMyZmnWxG/DizSWBeeQ0Zbc5z8UGaaqoeg== +"@babel/highlight@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" + integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" + integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== + +"@babel/parser@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" + integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== + +"@babel/parser@^7.22.5": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -204,6 +375,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -260,22 +438,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/runtime-corejs3@^7.10.2": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.13.10.tgz#14c3f4c85de22ba88e8e86685d13e8861a82fe86" - integrity sha512-x/XYVQ1h684pp1mJwOV4CyvqZXqbc8CMsMGUnAbuc82ZCdv1U63w5RSUzgDSXQHG5Rps/kiksH6g2D5BuaKyXg== +"@babel/runtime@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" + integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ== dependencies: - core-js-pure "^3.0.0" - regenerator-runtime "^0.13.4" + regenerator-runtime "^0.13.11" -"@babel/runtime@^7.10.2", "@babel/runtime@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3": +"@babel/template@^7.18.10", "@babel/template@^7.3.3": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== @@ -284,29 +454,72 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.10.tgz#37ad97d1cb00efa869b91dd5d1950f8a6cf0cb08" - integrity sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g== +"@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133" + integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" + "@babel/generator" "^7.20.5" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" + "@babel/parser" "^7.20.5" + "@babel/types" "^7.20.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" - integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== +"@babel/traverse@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" + integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== dependencies: - "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/code-frame" "^7.22.10" + "@babel/generator" "^7.22.10" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.10" + "@babel/types" "^7.22.10" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" + integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@babel/types@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" + integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + +"@babel/types@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -314,34 +527,63 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@eslint/eslintrc@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" - integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== +"@eslint-community/eslint-utils@^4.2.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" + integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" + integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== + +"@eslint-community/regexpp@^4.6.1": + version "4.6.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8" + integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw== + +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.2" - globals "^13.15.0" + espree "^9.6.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@humanwhocodes/config-array@^0.10.4": - version "0.10.4" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c" - integrity sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw== +"@eslint/js@^8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.47.0.tgz#5478fdf443ff8158f9de171c704ae45308696c7d" + integrity sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og== + +"@humanwhocodes/config-array@^0.11.10": + version "0.11.10" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" + integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" -"@humanwhocodes/gitignore-to-minimatch@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" - integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" @@ -364,110 +606,117 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" - integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== +"@jest/console@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.6.2.tgz#bf1d4101347c23e07c029a1b1ae07d550f5cc541" + integrity sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^28.1.3" - jest-util "^28.1.3" + jest-message-util "^29.6.2" + jest-util "^29.6.2" slash "^3.0.0" -"@jest/core@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" - integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== +"@jest/core@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.6.2.tgz#6f2d1dbe8aa0265fcd4fb8082ae1952f148209c8" + integrity sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg== dependencies: - "@jest/console" "^28.1.3" - "@jest/reporters" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/console" "^29.6.2" + "@jest/reporters" "^29.6.2" + "@jest/test-result" "^29.6.2" + "@jest/transform" "^29.6.2" + "@jest/types" "^29.6.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^28.1.3" - jest-config "^28.1.3" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-resolve-dependencies "^28.1.3" - jest-runner "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - jest-watcher "^28.1.3" + jest-changed-files "^29.5.0" + jest-config "^29.6.2" + jest-haste-map "^29.6.2" + jest-message-util "^29.6.2" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.2" + jest-resolve-dependencies "^29.6.2" + jest-runner "^29.6.2" + jest-runtime "^29.6.2" + jest-snapshot "^29.6.2" + jest-util "^29.6.2" + jest-validate "^29.6.2" + jest-watcher "^29.6.2" micromatch "^4.0.4" - pretty-format "^28.1.3" - rimraf "^3.0.0" + pretty-format "^29.6.2" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" - integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== +"@jest/environment@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.6.2.tgz#794c0f769d85e7553439d107d3f43186dc6874a9" + integrity sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q== dependencies: - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/fake-timers" "^29.6.2" + "@jest/types" "^29.6.1" "@types/node" "*" - jest-mock "^28.1.3" + jest-mock "^29.6.2" -"@jest/expect-utils@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" - integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== +"@jest/expect-utils@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.2.tgz#cd0065dfdd8e8a182aa350cc121db97b5eed7b3f" + integrity sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA== dependencies: - jest-get-type "^28.0.2" + jest-get-type "^29.4.2" -"@jest/expect@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" - integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== +"@jest/expect-utils@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.2.tgz#1b97f290d0185d264dd9fdec7567a14a38a90534" + integrity sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg== dependencies: - expect "^28.1.3" - jest-snapshot "^28.1.3" + jest-get-type "^29.4.3" -"@jest/fake-timers@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" - integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== +"@jest/expect@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.6.2.tgz#5a2ad58bb345165d9ce0a1845bbf873c480a4b28" + integrity sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg== dependencies: - "@jest/types" "^28.1.3" - "@sinonjs/fake-timers" "^9.1.2" + expect "^29.6.2" + jest-snapshot "^29.6.2" + +"@jest/fake-timers@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.6.2.tgz#fe9d43c5e4b1b901168fe6f46f861b3e652a2df4" + integrity sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA== + dependencies: + "@jest/types" "^29.6.1" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-util "^28.1.3" + jest-message-util "^29.6.2" + jest-mock "^29.6.2" + jest-util "^29.6.2" -"@jest/globals@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" - integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== +"@jest/globals@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.6.2.tgz#74af81b9249122cc46f1eb25793617eec69bf21a" + integrity sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw== dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.6.2" + "@jest/expect" "^29.6.2" + "@jest/types" "^29.6.1" + jest-mock "^29.6.2" -"@jest/reporters@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" - integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== +"@jest/reporters@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.2.tgz#524afe1d76da33d31309c2c4a2c8062d0c48780a" + integrity sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" + "@jest/console" "^29.6.2" + "@jest/test-result" "^29.6.2" + "@jest/transform" "^29.6.2" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -479,78 +728,115 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - jest-worker "^28.1.3" + jest-message-util "^29.6.2" + jest-util "^29.6.2" + jest-worker "^29.6.2" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" - terminal-link "^2.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" - integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== +"@jest/schemas@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.2.tgz#cf7cfe97c5649f518452b176c47ed07486270fc1" + integrity sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g== dependencies: - "@sinclair/typebox" "^0.24.1" + "@sinclair/typebox" "^0.25.16" -"@jest/source-map@^28.1.2": - version "28.1.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" - integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== dependencies: - "@jridgewell/trace-mapping" "^0.3.13" + "@sinclair/typebox" "^0.25.16" + +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.0.tgz#bd34a05b5737cb1a99d43e1957020ac8e5b9ddb1" + integrity sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" - integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== +"@jest/test-result@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.6.2.tgz#fdd11583cd1608e4db3114e8f0cce277bf7a32ed" + integrity sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw== dependencies: - "@jest/console" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/console" "^29.6.2" + "@jest/types" "^29.6.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" - integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== +"@jest/test-sequencer@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz#585eff07a68dd75225a7eacf319780cb9f6b9bf4" + integrity sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw== dependencies: - "@jest/test-result" "^28.1.3" + "@jest/test-result" "^29.6.2" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" + jest-haste-map "^29.6.2" slash "^3.0.0" -"@jest/transform@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" - integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== +"@jest/transform@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.6.2.tgz#522901ebbb211af08835bc3bcdf765ab778094e3" + integrity sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" + jest-haste-map "^29.6.2" + jest-regex-util "^29.4.3" + jest-util "^29.6.2" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - write-file-atomic "^4.0.1" + write-file-atomic "^4.0.2" -"@jest/types@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" - integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== +"@jest/types@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.2.tgz#8f724a414b1246b2bfd56ca5225d9e1f39540d82" + integrity sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw== dependencies: - "@jest/schemas" "^28.1.3" + "@jest/schemas" "^29.4.2" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== + dependencies: + "@jest/schemas" "^29.4.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jest/types@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" + integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== + dependencies: + "@jest/schemas" "^29.6.0" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -574,7 +860,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== @@ -584,12 +870,12 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12": version "0.3.14" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== @@ -597,6 +883,37 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@jridgewell/trace-mapping@^0.3.18": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" + integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": + version "5.1.1-v1" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" + integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg== + dependencies: + eslint-scope "5.1.1" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -610,7 +927,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -618,31 +935,62 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@sinclair/typebox@^0.24.1": - version "0.24.26" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.26.tgz#84f9e8c1d93154e734a7947609a1dc7c7a81cc22" - integrity sha512-1ZVIyyS1NXDRVT8GjWD5jULjhDyM3IsIHef2VGUMdnWOlX2tkPjyEX/7K0TGSH2S8EaPhp1ylFdjSjUGQ+gecg== +"@pkgr/utils@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.1.tgz#0a9b06ffddee364d6642b3cd562ca76f55b34a03" + integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== + dependencies: + cross-spawn "^7.0.3" + is-glob "^4.0.3" + open "^8.4.0" + picocolors "^1.0.0" + tiny-glob "^0.2.9" + tslib "^2.4.0" -"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== +"@sinclair/typebox@^0.25.16": + version "0.25.21" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" + integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@>=5", "@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== dependencies: - "@sinonjs/commons" "^1.7.0" + type-detect "4.0.8" -"@sinonjs/samsam@^6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-6.1.1.tgz#627f7f4cbdb56e6419fa2c1a3e4751ce4f6a00b1" - integrity sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA== +"@sinonjs/fake-timers@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" + integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== dependencies: - "@sinonjs/commons" "^1.6.0" + "@sinonjs/commons" "^2.0.0" + +"@sinonjs/fake-timers@^10.3.0": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@sinonjs/samsam@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.0.tgz#0d488c91efb3fa1442e26abea81759dfc8b5ac60" + integrity sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew== + dependencies: + "@sinonjs/commons" "^2.0.0" lodash.get "^4.4.2" type-detect "^4.0.8" @@ -710,18 +1058,18 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^27.5.0": - version "27.5.2" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.2.tgz#ec49d29d926500ffb9fd22b84262e862049c026c" - integrity sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA== +"@types/jest@^29.5.3": + version "29.5.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" + integrity sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA== dependencies: - jest-matcher-utils "^27.0.0" - pretty-format "^27.0.0" + expect "^29.0.0" + pretty-format "^29.0.0" -"@types/json-schema@^7.0.9": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/json-schema@^7.0.12": + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" + integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== "@types/json5@^0.0.29": version "0.0.29" @@ -733,20 +1081,20 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.3.tgz#4e4a95b6fe44014563ceb514b2598b3e623d1c98" integrity sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg== -"@types/node@^17.0.41": - version "17.0.45" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" - integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== +"@types/node@^20.5.0": + version "20.5.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.0.tgz#7fc8636d5f1aaa3b21e6245e97d56b7f56702313" + integrity sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q== -"@types/prettier@^2.1.5": - version "2.6.4" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.4.tgz#ad899dad022bab6b5a9f0a0fe67c2f7a4a8950ed" - integrity sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw== +"@types/semver@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== -"@types/sinon@^10.0.11": - version "10.0.13" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.13.tgz#60a7a87a70d9372d0b7b38cc03e825f46981fb83" - integrity sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ== +"@types/sinon@^10.0.16": + version "10.0.16" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.16.tgz#4bf10313bd9aa8eef1e50ec9f4decd3dd455b4d3" + integrity sha512-j2Du5SYpXZjJVJtXBokASpPRj+e2z+VUhCPHmM6WMfe3dpHu6iVKJMU6AiBcMp/XTAYnEj6Wc1trJUWwZ0QaAQ== dependencies: "@types/sinonjs__fake-timers" "*" @@ -760,10 +1108,10 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== -"@types/ws@^8.5.3": - version "8.5.3" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" - integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== +"@types/ws@^8.5.5": + version "8.5.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== dependencies: "@types/node" "*" @@ -779,131 +1127,102 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.27.1": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.0.tgz#059798888720ec52ffa96c5f868e31a8f70fa3ec" - integrity sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg== +"@typescript-eslint/eslint-plugin@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.0.tgz#53428b616f7d80fe879f45a08f11cc0f0b62cf13" + integrity sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg== dependencies: - "@typescript-eslint/scope-manager" "5.33.0" - "@typescript-eslint/type-utils" "5.33.0" - "@typescript-eslint/utils" "5.33.0" + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.4.0" + "@typescript-eslint/type-utils" "6.4.0" + "@typescript-eslint/utils" "6.4.0" + "@typescript-eslint/visitor-keys" "6.4.0" debug "^4.3.4" - functional-red-black-tree "^1.0.1" - ignore "^5.2.0" - regexpp "^3.2.0" - semver "^7.3.7" - tsutils "^3.21.0" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" -"@typescript-eslint/parser@^5.27.1": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.32.0.tgz#1de243443bc6186fb153b9e395b842e46877ca5d" - integrity sha512-IxRtsehdGV9GFQ35IGm5oKKR2OGcazUoiNBxhRV160iF9FoyuXxjY+rIqs1gfnd+4eL98OjeGnMpE7RF/NBb3A== +"@typescript-eslint/parser@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.4.0.tgz#47e7c6e22ff1248e8675d95f488890484de67600" + integrity sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg== dependencies: - "@typescript-eslint/scope-manager" "5.32.0" - "@typescript-eslint/types" "5.32.0" - "@typescript-eslint/typescript-estree" "5.32.0" + "@typescript-eslint/scope-manager" "6.4.0" + "@typescript-eslint/types" "6.4.0" + "@typescript-eslint/typescript-estree" "6.4.0" + "@typescript-eslint/visitor-keys" "6.4.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.32.0.tgz#763386e963a8def470580cc36cf9228864190b95" - integrity sha512-KyAE+tUON0D7tNz92p1uetRqVJiiAkeluvwvZOqBmW9z2XApmk5WSMV9FrzOroAcVxJZB3GfUwVKr98Dr/OjOg== +"@typescript-eslint/scope-manager@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz#3048e4262ba3eafa4e2e69b08912d9037ec646ae" + integrity sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig== dependencies: - "@typescript-eslint/types" "5.32.0" - "@typescript-eslint/visitor-keys" "5.32.0" + "@typescript-eslint/types" "6.4.0" + "@typescript-eslint/visitor-keys" "6.4.0" -"@typescript-eslint/scope-manager@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.0.tgz#509d7fa540a2c58f66bdcfcf278a3fa79002e18d" - integrity sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw== +"@typescript-eslint/type-utils@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.4.0.tgz#c8ac92716ed6a9d5443aa3e342910355b0796ba0" + integrity sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg== dependencies: - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/visitor-keys" "5.33.0" - -"@typescript-eslint/type-utils@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.0.tgz#92ad1fba973c078d23767ce2d8d5a601baaa9338" - integrity sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA== - dependencies: - "@typescript-eslint/utils" "5.33.0" + "@typescript-eslint/typescript-estree" "6.4.0" + "@typescript-eslint/utils" "6.4.0" debug "^4.3.4" - tsutils "^3.21.0" + ts-api-utils "^1.0.1" -"@typescript-eslint/types@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.32.0.tgz#484273021eeeae87ddb288f39586ef5efeb6dcd8" - integrity sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ== +"@typescript-eslint/types@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.4.0.tgz#5b109a59a805f0d8d375895e42d9e5f0037f66ee" + integrity sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg== -"@typescript-eslint/types@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.0.tgz#d41c584831805554b063791338b0220b613a275b" - integrity sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw== - -"@typescript-eslint/typescript-estree@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.32.0.tgz#282943f34babf07a4afa7b0ff347a8e7b6030d12" - integrity sha512-ZVAUkvPk3ITGtCLU5J4atCw9RTxK+SRc6hXqLtllC2sGSeMFWN+YwbiJR9CFrSFJ3w4SJfcWtDwNb/DmUIHdhg== +"@typescript-eslint/typescript-estree@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz#3c58d20632db93fec3d6ab902acbedf593d37276" + integrity sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA== dependencies: - "@typescript-eslint/types" "5.32.0" - "@typescript-eslint/visitor-keys" "5.32.0" + "@typescript-eslint/types" "6.4.0" + "@typescript-eslint/visitor-keys" "6.4.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" -"@typescript-eslint/typescript-estree@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.0.tgz#02d9c9ade6f4897c09e3508c27de53ad6bfa54cf" - integrity sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ== +"@typescript-eslint/utils@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.4.0.tgz#23e996b693603c5924b1fbb733cc73196256baa5" + integrity sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw== dependencies: - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/visitor-keys" "5.33.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.4.0" + "@typescript-eslint/types" "6.4.0" + "@typescript-eslint/typescript-estree" "6.4.0" + semver "^7.5.4" -"@typescript-eslint/utils@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.0.tgz#46797461ce3146e21c095d79518cc0f8ec574038" - integrity sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw== +"@typescript-eslint/visitor-keys@6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz#96a426cdb1add28274abd7a34aefe27f8b7d51ef" + integrity sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA== dependencies: - "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.33.0" - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/typescript-estree" "5.33.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - -"@typescript-eslint/visitor-keys@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.32.0.tgz#b9715d0b11fdb5dd10fd0c42ff13987470525394" - integrity sha512-S54xOHZgfThiZ38/ZGTgB2rqx51CMJ5MCfVT2IplK4Q7hgzGfe0nLzLCcenDnc/cSjP568hdeKfeDcBgqNHD/g== - dependencies: - "@typescript-eslint/types" "5.32.0" - eslint-visitor-keys "^3.3.0" - -"@typescript-eslint/visitor-keys@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.0.tgz#fbcbb074e460c11046e067bc3384b5d66b555484" - integrity sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw== - dependencies: - "@typescript-eslint/types" "5.33.0" - eslint-visitor-keys "^3.3.0" + "@typescript-eslint/types" "6.4.0" + eslint-visitor-keys "^3.4.1" acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== -ajv@^6.10.0, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -971,23 +1290,30 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== +aria-query@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" + deep-equal "^2.0.5" -array-includes@^3.1.4, array-includes@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" - integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-includes@^3.1.5, array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" - get-intrinsic "^1.1.1" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" is-string "^1.0.7" array-union@^2.1.0: @@ -995,49 +1321,98 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.flat@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" - integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== +array.prototype.findlastindex@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz#bc229aef98f6bd0533a2bc61ff95209875526c9b" + integrity sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - -array.prototype.flatmap@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" - integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + +arraybuffer.prototype.slice@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz#9b5ea3868a6eebc30273da577eb888381c0044bb" + integrity sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= -axe-core@^4.4.3: - version "4.4.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" - integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== - -axobject-query@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" - integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== - -babel-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" - integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== +asynciterator.prototype@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62" + integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg== dependencies: - "@jest/transform" "^28.1.3" + has-symbols "^1.0.3" + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +axe-core@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.2.tgz#6e566ab2a3d29e415f5115bc0fd2597a5eb3e5e3" + integrity sha512-b1WlTV8+XKLj9gZy2DZXgQiyDp9xkkoe2a6U6UbYccScq2wgH/YwCeI2/Jq2mgo0HzQxqJOjWZBLeA/mqsk5Mg== + +axobject-query@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" + integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg== + dependencies: + deep-equal "^2.0.5" + +babel-jest@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.2.tgz#cada0a59e07f5acaeb11cbae7e3ba92aec9c1126" + integrity sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A== + dependencies: + "@jest/transform" "^29.6.2" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^28.1.3" + babel-preset-jest "^29.5.0" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -1053,10 +1428,10 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" - integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -1081,12 +1456,12 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" - integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== dependencies: - babel-plugin-jest-hoist "^28.1.3" + babel-plugin-jest-hoist "^29.5.0" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -1109,15 +1484,25 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.20.2: - version "4.21.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" - integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== +browserslist@^4.21.3: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== dependencies: - caniuse-lite "^1.0.30001370" - electron-to-chromium "^1.4.202" + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" node-releases "^2.0.6" - update-browserslist-db "^1.0.5" + update-browserslist-db "^1.0.9" + +browserslist@^4.21.9: + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== + dependencies: + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" + node-releases "^2.0.12" + update-browserslist-db "^1.0.11" bs-logger@0.x: version "0.2.6" @@ -1161,12 +1546,17 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001370: - version "1.0.30001373" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz#2dc3bc3bfcb5d5a929bec11300883040d7b4b4be" - integrity sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ== +caniuse-lite@^1.0.30001400: + version "1.0.30001412" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001412.tgz#30f67d55a865da43e0aeec003f073ea8764d5d7c" + integrity sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA== -chalk@^2.0.0: +caniuse-lite@^1.0.30001503: + version "1.0.30001517" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8" + integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA== + +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1246,17 +1636,17 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" -core-js-pure@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.9.1.tgz#677b322267172bd490e4464696f790cbc355bec5" - integrity sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cross-env@^7.0.3: version "7.0.3" @@ -1279,13 +1669,6 @@ damerau-levenshtein@^1.0.8: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -1300,10 +1683,33 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== + +deep-equal@^2.0.5: + version "2.2.0" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6" + integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw== + dependencies: + call-bind "^1.0.2" + es-get-iterator "^1.1.2" + get-intrinsic "^1.1.3" + is-arguments "^1.1.1" + is-array-buffer "^3.0.1" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" deep-is@^0.1.3: version "0.1.4" @@ -1315,6 +1721,11 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" @@ -1323,25 +1734,33 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.2.tgz#711fe6bd8a5869fe2539cee4a5152425ff671fda" + integrity sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw== -diff-sequences@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" - integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== -diff@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== +diff@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== dir-glob@^3.0.1: version "3.0.1" @@ -1364,15 +1783,20 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -electron-to-chromium@^1.4.202: - version "1.4.208" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.208.tgz#ecb5b47c8cc212a43172ffc5ce50178a638a5d74" - integrity sha512-diMr4t69FigAGUk2KovP0bygEtN/9AkqEVkzjEp0cu+zFFbZMVvwACpTTfuj1mAmFR5kNoSW8wGKDFWIvmThiQ== +electron-to-chromium@^1.4.251: + version "1.4.264" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.264.tgz#2f68a062c38b7a04bf57f3e6954b868672fbdcd3" + integrity sha512-AZ6ZRkucHOQT8wke50MktxtmcWZr67kE17X/nAXFf62NIdMdgY6xfsaJD5Szoy84lnkuPWH+4tTNE3s2+bPCiw== -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== +electron-to-chromium@^1.4.431: + version "1.4.467" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.467.tgz#b0660bf644baff7eedea33b8c742fb53ec60e3c2" + integrity sha512-2qI70O+rR4poYeF2grcuS/bCps5KJh6y1jtZMDDEteyKJQrzLOEhFyXCLcHW6DTBjKjWkk26JhWoAi+Ux9A0fg== + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" @@ -1384,6 +1808,14 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +enhanced-resolve@^5.12.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + entities@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" @@ -1396,35 +1828,151 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: - version "1.20.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" - integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== +es-abstract@^1.19.0, es-abstract@^1.19.5: + version "1.20.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.3.tgz#90b143ff7aedc8b3d189bcfac7f1e3e3f81e9da1" + integrity sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" + get-intrinsic "^1.1.3" get-symbol-description "^1.0.0" has "^1.0.3" has-property-descriptors "^1.0.0" has-symbols "^1.0.3" internal-slot "^1.0.3" - is-callable "^1.2.4" + is-callable "^1.2.6" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" is-weakref "^1.0.2" - object-inspect "^1.12.0" + object-inspect "^1.12.2" object-keys "^1.1.1" - object.assign "^4.1.2" + object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" string.prototype.trimend "^1.0.5" string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" +es-abstract@^1.20.4: + version "1.20.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" + integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.2" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + +es-abstract@^1.21.2, es-abstract@^1.21.3: + version "1.22.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" + integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.1" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.2.1" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.0" + safe-array-concat "^1.0.0" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.10" + +es-get-iterator@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-iterator-helpers@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.12.tgz#272f7270f270f4d63dd8aa718228ffa901fd086e" + integrity sha512-T6Ldv67RYULYtZ1k1omngDTVQSTVNX/ZSjDiwlw0PMokhy8kq2LFElleaEjpvlSaXh9ugJ4zrBgbQ7L+Bjdm3Q== + dependencies: + asynciterator.prototype "^1.0.0" + es-abstract "^1.21.3" + es-set-tostringtag "^2.0.1" + function-bind "^1.1.1" + globalthis "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + iterator.prototype "^1.1.0" + safe-array-concat "^1.0.0" + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -1461,42 +2009,51 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-fbjs@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/eslint-config-fbjs/-/eslint-config-fbjs-3.1.1.tgz#f5b4c1df888693672116f000527a8df25d61b888" - integrity sha512-Vpyqz+ZcyLyiUGUdUfiQmZnxiQ4Nj/KDRKed/Y5qSm+xHbQJ5zcZUQwLUMWHQicpDHewsdXwlpUAblvy1DtGvg== +eslint-config-fbjs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-fbjs/-/eslint-config-fbjs-4.0.0.tgz#9b2e70c57bca58ba4dfafe3a566bdb077146455f" + integrity sha512-GKXvVfpgVgXdxzmYeaqiBJFL58o+T9d5J8EMXYy3AsyD3AtEdsHzwd+4bEqSI2A0RbeF27CAj9+o2v9VhWSLIg== -eslint-config-prettier@^8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== +eslint-config-prettier@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f" + integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw== -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" - resolve "^1.20.0" + is-core-module "^2.11.0" + resolve "^1.22.1" -eslint-import-resolver-typescript@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" - integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== +eslint-import-resolver-typescript@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.0.tgz#36f93e1eb65a635e688e16cae4bead54552e3bbd" + integrity sha512-QTHR9ddNnn35RTxlaEnx2gCxqFlF2SEN0SE2d17SqwyM7YOSI2GHWRYp5BiRkObTUNYPupC/3Fq2a0PpT+EKpg== dependencies: debug "^4.3.4" - glob "^7.2.0" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + fast-glob "^3.3.1" + get-tsconfig "^4.5.0" + is-core-module "^2.11.0" is-glob "^4.0.3" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" -eslint-module-utils@^2.7.3: - version "2.7.3" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" - integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== +eslint-module-utils@^2.7.4: + version "2.7.4" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== + dependencies: + debug "^3.2.7" + +eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" - find-up "^2.1.0" eslint-plugin-babel@^5.3.0: version "5.3.1" @@ -1513,10 +2070,10 @@ eslint-plugin-es@^3.0.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-flowtype@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz#e1557e37118f24734aa3122e7536a038d34a4912" - integrity sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ== +eslint-plugin-ft-flow@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-ft-flow/-/eslint-plugin-ft-flow-3.0.0.tgz#e9cbe4531089c2a85726928baa2d30d24d9af3b5" + integrity sha512-fz5ep5V2jh5aNBHoCKOdq+xxGenxYMfmOq8F6MUnFGyeEncJgV91O7mOWyKTZN0h/ofP+yW1CS7fZq8iEglNxw== dependencies: lodash "^4.17.21" string-natural-compare "^3.0.1" @@ -1526,42 +2083,50 @@ eslint-plugin-header@^3.0.0: resolved "https://registry.yarnpkg.com/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz#6ce512432d57675265fac47292b50d1eff11acd6" integrity sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg== -eslint-plugin-import@^2.25.4: - version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== +eslint-plugin-import@^2.28.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz#8d66d6925117b06c4018d491ae84469eb3cb1005" + integrity sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q== dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" + array-includes "^3.1.6" + array.prototype.findlastindex "^1.2.2" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.8.0" has "^1.0.3" - is-core-module "^2.8.1" + is-core-module "^2.12.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" + object.fromentries "^2.0.6" + object.groupby "^1.0.0" + object.values "^1.1.6" + resolve "^1.22.3" + semver "^6.3.1" + tsconfig-paths "^3.14.2" -eslint-plugin-jsx-a11y@^6.5.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff" - integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q== +eslint-plugin-jsx-a11y@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz#fca5e02d115f48c9a597a6894d5bcec2f7a76976" + integrity sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA== dependencies: - "@babel/runtime" "^7.18.9" - aria-query "^4.2.2" - array-includes "^3.1.5" + "@babel/runtime" "^7.20.7" + aria-query "^5.1.3" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" ast-types-flow "^0.0.7" - axe-core "^4.4.3" - axobject-query "^2.2.0" + axe-core "^4.6.2" + axobject-query "^3.1.1" damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" has "^1.0.3" - jsx-ast-utils "^3.3.2" - language-tags "^1.0.5" + jsx-ast-utils "^3.3.3" + language-tags "=1.0.5" minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" semver "^6.3.0" eslint-plugin-node@^11.1.0: @@ -1576,44 +2141,47 @@ eslint-plugin-node@^11.1.0: resolve "^1.10.1" semver "^6.1.0" -eslint-plugin-prettier@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== +eslint-plugin-prettier@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz#6887780ed95f7708340ec79acfdf60c35b9be57a" + integrity sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w== dependencies: prettier-linter-helpers "^1.0.0" + synckit "^0.8.5" eslint-plugin-react-hooks@^4.5.0: version "4.6.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== -eslint-plugin-react@^7.29.4: - version "7.30.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz#2be4ab23ce09b5949c6631413ba64b2810fd3e22" - integrity sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg== +eslint-plugin-react@^7.33.2: + version "7.33.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608" + integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw== dependencies: - array-includes "^3.1.5" - array.prototype.flatmap "^1.3.0" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" doctrine "^2.1.0" + es-iterator-helpers "^1.0.12" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.5" - object.fromentries "^2.0.5" - object.hasown "^1.1.1" - object.values "^1.1.5" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" prop-types "^15.8.1" - resolve "^2.0.0-next.3" - semver "^6.3.0" - string.prototype.matchall "^4.0.7" + resolve "^2.0.0-next.4" + semver "^6.3.1" + string.prototype.matchall "^4.0.8" eslint-rule-composer@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== -eslint-scope@^5.1.1: +eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -1621,10 +2189,10 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -1636,91 +2204,87 @@ eslint-utils@^2.0.0: dependencies: eslint-visitor-keys "^1.1.0" -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: +eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== -eslint@^8.17.0: - version "8.22.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48" - integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA== +eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@8.47.0: + version "8.47.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.47.0.tgz#c95f9b935463fb4fad7005e626c7621052e90806" + integrity sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q== dependencies: - "@eslint/eslintrc" "^1.3.0" - "@humanwhocodes/config-array" "^0.10.4" - "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" - ajv "^6.10.0" + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "^8.47.0" + "@humanwhocodes/config-array" "^0.11.10" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.3.3" - esquery "^1.4.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" find-up "^5.0.0" - functional-red-black-tree "^1.0.1" - glob-parent "^6.0.1" - globals "^13.15.0" - globby "^11.1.0" - grapheme-splitter "^1.0.4" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" + is-path-inside "^3.0.3" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^9.3.2, espree@^9.3.3: - version "9.3.3" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" - integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.8.0" + acorn "^8.9.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.1" esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -1766,16 +2330,28 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" - integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== +expect@^29.0.0: + version "29.4.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.2.tgz#2ae34eb88de797c64a1541ad0f1e2ea8a7a7b492" + integrity sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ== dependencies: - "@jest/expect-utils" "^28.1.3" - jest-get-type "^28.0.2" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" + "@jest/expect-utils" "^29.4.2" + jest-get-type "^29.4.2" + jest-matcher-utils "^29.4.2" + jest-message-util "^29.4.2" + jest-util "^29.4.2" + +expect@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.2.tgz#7b08e83eba18ddc4a2cf62b5f2d1918f5cd84521" + integrity sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA== + dependencies: + "@jest/expect-utils" "^29.6.2" + "@types/node" "*" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.6.2" + jest-message-util "^29.6.2" + jest-util "^29.6.2" fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -1788,9 +2364,9 @@ fast-diff@^1.1.2: integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -1798,7 +2374,18 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-glob@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -1806,7 +2393,7 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: version "1.13.0" @@ -1836,13 +2423,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -1868,9 +2448,16 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.5" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" - integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" fs.realpath@^1.0.0: version "1.0.0" @@ -1897,12 +2484,7 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== - -functions-have-names@^1.2.2: +functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -1917,15 +2499,25 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== dependencies: function-bind "^1.1.1" has "^1.0.3" has-symbols "^1.0.3" +get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -1944,6 +2536,11 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-tsconfig@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.5.0.tgz#6d52d1c7b299bd3ee9cd7638561653399ac77b0f" + integrity sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ== + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -1951,7 +2548,7 @@ glob-parent@^5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: +glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -1970,30 +2567,30 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: - version "13.15.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" - integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== +globals@^13.19.0: + version "13.19.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" + integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== dependencies: type-fest "^0.20.2" +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globalyzer@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" + integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== + globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -2006,15 +2603,27 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -graceful-fs@^4.2.9: +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" @@ -2038,7 +2647,12 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -2072,7 +2686,12 @@ ignore@^5.1.1, ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== -import-fresh@^3.0.0, import-fresh@^3.2.1: +ignore@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -2115,11 +2734,62 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +internal-slot@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" + integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + side-channel "^1.0.4" + +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" + integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-typed-array "^1.1.10" + +is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -2135,30 +2805,56 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.6, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== +is-core-module@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" -is-date-object@^1.0.1: +is-core-module@^2.12.1, is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +is-core-module@^2.9.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" + integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -2169,6 +2865,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -2176,6 +2879,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -2193,6 +2901,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -2201,6 +2914,11 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -2227,6 +2945,29 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-typed-array@^1.1.10: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -2234,11 +2975,31 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2286,388 +3047,426 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" - integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== +iterator.prototype@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.0.tgz#690c88b043d821f783843aaf725d7ac3b62e3b46" + integrity sha512-rjuhAk1AJ1fssphHD0IFV6TWL40CwRZ53FrztKx43yk2v6rguBYsY4Bj1VU4HmoMmKwZUlx7mfnhDf9cOp4YTw== + dependencies: + define-properties "^1.1.4" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + has-tostringtag "^1.0.0" + reflect.getprototypeof "^1.0.3" + +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== dependencies: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" - integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== +jest-circus@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.2.tgz#1e6ffca60151ac66cad63fce34f443f6b5bb4258" + integrity sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw== dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.6.2" + "@jest/expect" "^29.6.2" + "@jest/test-result" "^29.6.2" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - dedent "^0.7.0" + dedent "^1.0.0" is-generator-fn "^2.0.0" - jest-each "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" + jest-each "^29.6.2" + jest-matcher-utils "^29.6.2" + jest-message-util "^29.6.2" + jest-runtime "^29.6.2" + jest-snapshot "^29.6.2" + jest-util "^29.6.2" p-limit "^3.1.0" - pretty-format "^28.1.3" + pretty-format "^29.6.2" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" - integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== +jest-cli@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.6.2.tgz#edb381763398d1a292cd1b636a98bfa5644b8fda" + integrity sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q== dependencies: - "@jest/core" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/core" "^29.6.2" + "@jest/test-result" "^29.6.2" + "@jest/types" "^29.6.1" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-config "^29.6.2" + jest-util "^29.6.2" + jest-validate "^29.6.2" prompts "^2.0.1" yargs "^17.3.1" -jest-config@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" - integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== +jest-config@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.6.2.tgz#c68723f06b31ca5e63030686e604727d406cd7c3" + integrity sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^28.1.3" - "@jest/types" "^28.1.3" - babel-jest "^28.1.3" + "@jest/test-sequencer" "^29.6.2" + "@jest/types" "^29.6.1" + babel-jest "^29.6.2" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^28.1.3" - jest-environment-node "^28.1.3" - jest-get-type "^28.0.2" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-runner "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-circus "^29.6.2" + jest-environment-node "^29.6.2" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.2" + jest-runner "^29.6.2" + jest-util "^29.6.2" + jest-validate "^29.6.2" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^28.1.3" + pretty-format "^29.6.2" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== +jest-diff@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.2.tgz#b88502d5dc02d97f6512d73c37da8b36f49b4871" + integrity sha512-EK8DSajVtnjx9sa1BkjZq3mqChm2Cd8rIzdXkQMA8e0wuXq53ypz6s5o5V8HRZkoEt2ywJ3eeNWFKWeYr8HK4g== dependencies: chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + diff-sequences "^29.4.2" + jest-get-type "^29.4.2" + pretty-format "^29.4.2" -jest-diff@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" - integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== +jest-diff@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.2.tgz#c36001e5543e82a0805051d3ceac32e6825c1c46" + integrity sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA== dependencies: chalk "^4.0.0" - diff-sequences "^28.1.1" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.6.2" -jest-docblock@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" - integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== dependencies: detect-newline "^3.0.0" -jest-each@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" - integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== +jest-each@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.6.2.tgz#c9e4b340bcbe838c73adf46b76817b15712d02ce" + integrity sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.1" chalk "^4.0.0" - jest-get-type "^28.0.2" - jest-util "^28.1.3" - pretty-format "^28.1.3" + jest-get-type "^29.4.3" + jest-util "^29.6.2" + pretty-format "^29.6.2" -jest-environment-node@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" - integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== +jest-environment-node@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.6.2.tgz#a9ea2cabff39b08eca14ccb32c8ceb924c8bb1ad" + integrity sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ== dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.6.2" + "@jest/fake-timers" "^29.6.2" + "@jest/types" "^29.6.1" "@types/node" "*" - jest-mock "^28.1.3" - jest-util "^28.1.3" + jest-mock "^29.6.2" + jest-util "^29.6.2" -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== +jest-get-type@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.2.tgz#7cb63f154bca8d8f57364d01614477d466fa43fe" + integrity sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg== -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== -jest-haste-map@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" - integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== +jest-haste-map@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.2.tgz#298c25ea5255cfad8b723179d4295cf3a50a70d1" + integrity sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.1" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - jest-worker "^28.1.3" + jest-regex-util "^29.4.3" + jest-util "^29.6.2" + jest-worker "^29.6.2" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" - integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== +jest-leak-detector@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz#e2b307fee78cab091c37858a98c7e1d73cdf5b38" + integrity sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ== dependencies: - jest-get-type "^28.0.2" - pretty-format "^28.1.3" + jest-get-type "^29.4.3" + pretty-format "^29.6.2" -jest-matcher-utils@^27.0.0: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== +jest-matcher-utils@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.2.tgz#08d0bf5abf242e3834bec92c7ef5071732839e85" + integrity sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg== dependencies: chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-diff "^29.4.2" + jest-get-type "^29.4.2" + pretty-format "^29.4.2" -jest-matcher-utils@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" - integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== +jest-matcher-utils@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz#39de0be2baca7a64eacb27291f0bd834fea3a535" + integrity sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ== dependencies: chalk "^4.0.0" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" + jest-diff "^29.6.2" + jest-get-type "^29.4.3" + pretty-format "^29.6.2" -jest-message-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" - integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== +jest-message-util@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.2.tgz#309a2924eae6ca67cf7f25781a2af1902deee717" + integrity sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^28.1.3" + "@jest/types" "^29.4.2" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^28.1.3" + pretty-format "^29.4.2" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" - integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== +jest-message-util@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.2.tgz#af7adc2209c552f3f5ae31e77cf0a261f23dc2bb" + integrity sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ== dependencies: - "@jest/types" "^28.1.3" + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.6.2" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.6.2.tgz#ef9c9b4d38c34a2ad61010a021866dad41ce5e00" + integrity sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg== + dependencies: + "@jest/types" "^29.6.1" "@types/node" "*" + jest-util "^29.6.2" jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" - integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== -jest-resolve-dependencies@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" - integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== +jest-resolve-dependencies@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz#36435269b6672c256bcc85fb384872c134cc4cf2" + integrity sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w== dependencies: - jest-regex-util "^28.0.2" - jest-snapshot "^28.1.3" + jest-regex-util "^29.4.3" + jest-snapshot "^29.6.2" -jest-resolve@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" - integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== +jest-resolve@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.6.2.tgz#f18405fe4b50159b7b6d85e81f6a524d22afb838" + integrity sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" + jest-haste-map "^29.6.2" jest-pnp-resolver "^1.2.2" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-util "^29.6.2" + jest-validate "^29.6.2" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" - integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== +jest-runner@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.6.2.tgz#89e8e32a8fef24781a7c4c49cd1cb6358ac7fc01" + integrity sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w== dependencies: - "@jest/console" "^28.1.3" - "@jest/environment" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/console" "^29.6.2" + "@jest/environment" "^29.6.2" + "@jest/test-result" "^29.6.2" + "@jest/transform" "^29.6.2" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" - emittery "^0.10.2" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^28.1.1" - jest-environment-node "^28.1.3" - jest-haste-map "^28.1.3" - jest-leak-detector "^28.1.3" - jest-message-util "^28.1.3" - jest-resolve "^28.1.3" - jest-runtime "^28.1.3" - jest-util "^28.1.3" - jest-watcher "^28.1.3" - jest-worker "^28.1.3" + jest-docblock "^29.4.3" + jest-environment-node "^29.6.2" + jest-haste-map "^29.6.2" + jest-leak-detector "^29.6.2" + jest-message-util "^29.6.2" + jest-resolve "^29.6.2" + jest-runtime "^29.6.2" + jest-util "^29.6.2" + jest-watcher "^29.6.2" + jest-worker "^29.6.2" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" - integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== +jest-runtime@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.6.2.tgz#692f25e387f982e89ab83270e684a9786248e545" + integrity sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg== dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/globals" "^28.1.3" - "@jest/source-map" "^28.1.2" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.6.2" + "@jest/fake-timers" "^29.6.2" + "@jest/globals" "^29.6.2" + "@jest/source-map" "^29.6.0" + "@jest/test-result" "^29.6.2" + "@jest/transform" "^29.6.2" + "@jest/types" "^29.6.1" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" + jest-haste-map "^29.6.2" + jest-message-util "^29.6.2" + jest-mock "^29.6.2" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.2" + jest-snapshot "^29.6.2" + jest-util "^29.6.2" slash "^3.0.0" strip-bom "^4.0.0" -jest-snapshot@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" - integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== +jest-snapshot@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.6.2.tgz#9b431b561a83f2bdfe041e1cab8a6becdb01af9c" + integrity sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" + "@jest/expect-utils" "^29.6.2" + "@jest/transform" "^29.6.2" + "@jest/types" "^29.6.1" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^28.1.3" + expect "^29.6.2" graceful-fs "^4.2.9" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - jest-haste-map "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" + jest-diff "^29.6.2" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.6.2" + jest-message-util "^29.6.2" + jest-util "^29.6.2" natural-compare "^1.4.0" - pretty-format "^28.1.3" - semver "^7.3.5" + pretty-format "^29.6.2" + semver "^7.5.3" -jest-util@^28.0.0, jest-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" - integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== +jest-util@^29.0.0, jest-util@^29.4.2: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" - integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== +jest-util@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.2.tgz#8a052df8fff2eebe446769fd88814521a517664d" + integrity sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.6.2.tgz#25d972af35b2415b83b1373baf1a47bb266c1082" + integrity sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg== + dependencies: + "@jest/types" "^29.6.1" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^28.0.2" + jest-get-type "^29.4.3" leven "^3.1.0" - pretty-format "^28.1.3" + pretty-format "^29.6.2" -jest-watcher@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" - integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== +jest-watcher@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.6.2.tgz#77c224674f0620d9f6643c4cfca186d8893ca088" + integrity sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA== dependencies: - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/test-result" "^29.6.2" + "@jest/types" "^29.6.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^28.1.3" + emittery "^0.13.1" + jest-util "^29.6.2" string-length "^4.0.1" -jest-worker@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" - integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== +jest-worker@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.2.tgz#682fbc4b6856ad0aa122a5403c6d048b83f3fb44" + integrity sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ== dependencies: "@types/node" "*" + jest-util "^29.6.2" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^28.1.1: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" - integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== +jest@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.6.2.tgz#3bd55b9fd46a161b2edbdf5f1d1bd0d1eab76c42" + integrity sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg== dependencies: - "@jest/core" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/core" "^29.6.2" + "@jest/types" "^29.6.1" import-local "^3.0.2" - jest-cli "^28.1.3" + jest-cli "^29.6.2" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -2707,27 +3506,27 @@ json-schema-traverse@^0.4.1: json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" -json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@^2.2.1, json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz#afe5efe4332cd3515c065072bd4d6b0aa22152bd" - integrity sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q== +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" + integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== dependencies: array-includes "^3.1.5" - object.assign "^4.1.2" + object.assign "^4.1.3" just-extend@^4.0.2: version "4.2.1" @@ -2744,10 +3543,10 @@ language-subtag-registry@~0.3.2: resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg== -language-tags@^1.0.5: +language-tags@=1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" - integrity sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= + integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ== dependencies: language-subtag-registry "~0.3.2" @@ -2769,14 +3568,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -2818,6 +3609,13 @@ loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -2867,7 +3665,7 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -2884,11 +3682,6 @@ minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -2904,13 +3697,13 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -nise@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.1.tgz#ac4237e0d785ecfcb83e20f389185975da5c31f3" - integrity sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A== +nise@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.4.tgz#491ce7e7307d4ec546f5a659b2efe94a18b4bbc0" + integrity sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg== dependencies: - "@sinonjs/commons" "^1.8.3" - "@sinonjs/fake-timers" ">=5" + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers" "^10.0.2" "@sinonjs/text-encoding" "^0.7.1" just-extend "^4.0.2" path-to-regexp "^1.7.0" @@ -2920,6 +3713,11 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== +node-releases@^2.0.12: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + node-releases@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" @@ -2942,60 +3740,83 @@ object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.0, object-inspect@^1.9.0: +object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== +object-inspect@^1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== +object.assign@^4.1.3, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== +object.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" -object.fromentries@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" - integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" -object.hasown@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" - integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== +object.groupby@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.0.tgz#cb29259cf90f37e7bac6437686c1ea8c916d12a9" + integrity sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.21.2" + get-intrinsic "^1.2.1" + +object.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" + integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== dependencies: define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" -object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" once@^1.3.0: version "1.4.0" @@ -3011,24 +3832,26 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +open@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" p-limit@^2.2.0: version "2.3.0" @@ -3044,13 +3867,6 @@ p-limit@^3.0.2, p-limit@^3.1.0: dependencies: yocto-queue "^0.1.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -3065,11 +3881,6 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -3092,11 +3903,6 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -3163,27 +3969,26 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.6.2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== +prettier@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b" + integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ== -pretty-format@^27.0.0, pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== +pretty-format@^29.0.0, pretty-format@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.2.tgz#64bf5ccc0d718c03027d94ac957bdd32b3fb2401" + integrity sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg== dependencies: - ansi-regex "^5.0.1" + "@jest/schemas" "^29.4.2" ansi-styles "^5.0.0" - react-is "^17.0.1" + react-is "^18.0.0" -pretty-format@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" - integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== +pretty-format@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.2.tgz#3d5829261a8a4d89d8b9769064b29c50ed486a47" + integrity sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg== dependencies: - "@jest/schemas" "^28.1.3" - ansi-regex "^5.0.1" + "@jest/schemas" "^29.6.0" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -3209,6 +4014,11 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pure-rand@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.0.tgz#701996ceefa253507923a0e864c17ab421c04a7c" + integrity sha512-rLSBxJjP+4DQOgcJAx6RZHT2he2pkhQdSnofG5VWyVl6GRq/K02ISOuOLcsMOrtKDIJb8JN2zm3FFzWNbezdPw== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -3219,22 +4029,29 @@ react-is@^16.13.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== +reflect.getprototypeof@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.3.tgz#2738fd896fcc3477ffbd4190b40c2458026b6928" + integrity sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.1" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" -regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -3243,7 +4060,16 @@ regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" -regexpp@^3.0.0, regexpp@^3.2.0: +regexp.prototype.flags@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" + +regexpp@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== @@ -3270,21 +4096,12 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== +resolve.exports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" + integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== -resolve@^1.10.1, resolve@^1.22.0: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== - dependencies: - is-core-module "^2.8.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^1.20.0: +resolve@^1.10.1, resolve@^1.20.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -3293,7 +4110,16 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.3: +resolve@^1.22.3: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== @@ -3307,7 +4133,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -3321,23 +4147,54 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +safe-array-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" + integrity sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -semver@7.x, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== dependencies: - lru-cache "^6.0.0" + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" semver@^6.0.0, semver@^6.1.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.3: + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== + dependencies: + lru-cache "^6.0.0" + +semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -3364,16 +4221,16 @@ signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -sinon@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-14.0.0.tgz#203731c116d3a2d58dc4e3cbe1f443ba9382a031" - integrity sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw== +sinon@^15.2.0: + version "15.2.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-15.2.0.tgz#5e44d4bc5a9b5d993871137fd3560bebfac27565" + integrity sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw== dependencies: - "@sinonjs/commons" "^1.8.3" - "@sinonjs/fake-timers" "^9.1.2" - "@sinonjs/samsam" "^6.1.1" - diff "^5.0.0" - nise "^5.1.1" + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers" "^10.3.0" + "@sinonjs/samsam" "^8.0.0" + diff "^5.1.0" + nise "^5.1.4" supports-color "^7.2.0" sisteransi@^1.0.5: @@ -3411,6 +4268,13 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -3433,20 +4297,29 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.matchall@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" - integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== +string.prototype.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" + integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" has-symbols "^1.0.3" internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.1" + regexp.prototype.flags "^1.4.3" side-channel "^1.0.4" +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimend@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" @@ -3456,6 +4329,15 @@ string.prototype.trimend@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimstart@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" @@ -3465,6 +4347,15 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -3487,7 +4378,7 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -3499,7 +4390,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: +supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -3513,26 +4404,23 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== +synckit@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" + "@pkgr/utils" "^2.3.1" + tslib "^2.5.0" + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== test-exclude@^6.0.0: version "6.0.0" @@ -3546,7 +4434,15 @@ test-exclude@^6.0.0: text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +tiny-glob@^0.2.9: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" + integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== + dependencies: + globalyzer "0.1.0" + globrex "^0.1.2" tmpl@1.0.5: version "1.0.5" @@ -3565,41 +4461,44 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -ts-jest@^28.0.4: - version "28.0.8" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.8.tgz#cd204b8e7a2f78da32cf6c95c9a6165c5b99cc73" - integrity sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg== +ts-api-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d" + integrity sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A== + +ts-jest@^29.1.0: + version "29.1.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" - jest-util "^28.0.0" - json5 "^2.2.1" + jest-util "^29.0.0" + json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" - semver "7.x" + semver "^7.5.3" yargs-parser "^21.0.1" -tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== +tsconfig-paths@^3.14.2: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== dependencies: "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" +tslib@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -3623,10 +4522,49 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -typescript@^4.7.3: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typescript@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== unbox-primitive@^1.0.2: version "1.0.2" @@ -3638,10 +4576,18 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -update-browserslist-db@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" - integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +update-browserslist-db@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" + integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -3653,11 +4599,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - v8-to-istanbul@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" @@ -3685,6 +4626,57 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + +which-typed-array@^1.1.10, which-typed-array@^1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -3692,11 +4684,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -3711,24 +4698,29 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" - integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@^8.5.0: - version "8.8.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" - integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== +ws@^8.12.1: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" diff --git a/js/react-flipper-example/package.json b/js/react-flipper-example/package.json index 4beac9b62..9b824e85f 100644 --- a/js/react-flipper-example/package.json +++ b/js/react-flipper-example/package.json @@ -3,19 +3,19 @@ "version": "0.1.0", "private": true, "dependencies": { - "@testing-library/jest-dom": "^5.16.3", - "@testing-library/react": "^12.1.4", - "@testing-library/user-event": "^13.5.0", - "@types/jest": "^27.4.1", - "@types/node": "^16.11.26", - "@types/react": "^17.0.42", - "@types/react-dom": "^17.0.14", - "js-flipper": "^0.140.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "5.0.0", - "typescript": "^4.6.2", - "web-vitals": "^2.1.4" + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.4.3", + "@types/jest": "^29.5.2", + "@types/node": "^20.3.2", + "@types/react": "^18.2.14", + "@types/react-dom": "^18.2.6", + "js-flipper": "*", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "^5.0.1", + "typescript": "^5.1.6", + "web-vitals": "^3.3.2" }, "scripts": { "start": "react-scripts start", diff --git a/js/react-flipper-example/public/index.html b/js/react-flipper-example/public/index.html index aa069f27c..2d6dde7de 100644 --- a/js/react-flipper-example/public/index.html +++ b/js/react-flipper-example/public/index.html @@ -24,7 +24,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + React Flipper WebSocket Example diff --git a/js/react-flipper-example/public/manifest.json b/js/react-flipper-example/public/manifest.json index 080d6c77a..2acb99a21 100644 --- a/js/react-flipper-example/public/manifest.json +++ b/js/react-flipper-example/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "Flipper Tic-Tac-Toe", + "name": "React Flipper WebSocket Example", "icons": [ { "src": "favicon.ico", diff --git a/js/react-flipper-example/src/index.tsx b/js/react-flipper-example/src/index.tsx index 5f4fb9a64..730587743 100644 --- a/js/react-flipper-example/src/index.tsx +++ b/js/react-flipper-example/src/index.tsx @@ -8,13 +8,13 @@ */ import React from 'react'; -import ReactDOM from 'react-dom'; - +import {createRoot} from 'react-dom/client'; import App from './App'; -ReactDOM.render( +const root = createRoot(document.getElementById('root') as HTMLElement); + +root.render( - , - document.getElementById('root'), + ); diff --git a/js/react-flipper-example/src/logo.svg b/js/react-flipper-example/src/logo.svg deleted file mode 100644 index 9dfc1c058..000000000 --- a/js/react-flipper-example/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/js/react-flipper-example/tsconfig.json b/js/react-flipper-example/tsconfig.json index a273b0cfc..a7ff19ff6 100644 --- a/js/react-flipper-example/tsconfig.json +++ b/js/react-flipper-example/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "target": "ES6", + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -20,7 +16,5 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": [ - "src" - ] + "include": ["src"] } diff --git a/js/react-flipper-example/yarn.lock b/js/react-flipper-example/yarn.lock index b19973373..915e21bbc 100644 --- a/js/react-flipper-example/yarn.lock +++ b/js/react-flipper-example/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adobe/css-tools@^4.0.1": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.2.0.tgz#e1a84fca468f4b337816fcb7f0964beb620ba855" + integrity sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA== + "@ampproject/remapping@^2.1.0": version "2.1.2" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" @@ -1241,6 +1246,13 @@ "@types/node" "*" jest-mock "^27.5.1" +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== + dependencies: + jest-get-type "^29.4.3" + "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -1293,6 +1305,13 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + "@jest/source-map@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" @@ -1354,6 +1373,18 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== + dependencies: + "@jest/schemas" "^29.4.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/resolve-uri@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" @@ -1463,6 +1494,11 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.1.tgz#782fa5da44c4f38ae9fd38e9184b54e451936118" integrity sha512-BUyKJGdDWqvWC5GEhyOiUrGNi9iJUr4CU0O2WxJL6QJhHeeA/NVBalH+FeK0r/x/W0rPymXt5s78TDS7d6lCwg== +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -1590,50 +1626,48 @@ "@svgr/plugin-svgo" "^5.5.0" loader-utils "^2.0.0" -"@testing-library/dom@^8.0.0": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.13.0.tgz#bc00bdd64c7d8b40841e27a70211399ad3af46f5" - integrity sha512-9VHgfIatKNXQNaZTtLnalIy0jNZzY35a4S3oi08YAt9Hv1VsfZ/DfA45lM8D/UhtHBGJ4/lGwp0PZkVndRkoOQ== +"@testing-library/dom@^9.0.0": + version "9.3.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.1.tgz#8094f560e9389fb973fe957af41bf766937a9ee9" + integrity sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" - "@types/aria-query" "^4.2.0" - aria-query "^5.0.0" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" chalk "^4.1.0" dom-accessibility-api "^0.5.9" - lz-string "^1.4.4" + lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/jest-dom@^5.16.3": - version "5.16.4" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.4.tgz#938302d7b8b483963a3ae821f1c0808f872245cd" - integrity sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA== +"@testing-library/jest-dom@^5.16.5": + version "5.16.5" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" + integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA== dependencies: + "@adobe/css-tools" "^4.0.1" "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" aria-query "^5.0.0" chalk "^3.0.0" - css "^3.0.0" css.escape "^1.5.1" dom-accessibility-api "^0.5.6" lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react@^12.1.4": - version "12.1.5" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" - integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg== +"@testing-library/react@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.0.0.tgz#59030392a6792450b9ab8e67aea5f3cc18d6347c" + integrity sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^8.0.0" - "@types/react-dom" "<18.0.0" + "@testing-library/dom" "^9.0.0" + "@types/react-dom" "^18.0.0" -"@testing-library/user-event@^13.5.0": - version "13.5.0" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295" - integrity sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg== - dependencies: - "@babel/runtime" "^7.12.5" +"@testing-library/user-event@^14.4.3": + version "14.4.3" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.4.3.tgz#af975e367743fa91989cd666666aec31a8f50591" + integrity sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q== "@tootallnate/once@1": version "1.1.2" @@ -1645,10 +1679,10 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== -"@types/aria-query@^4.2.0": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== +"@types/aria-query@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" + integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.19" @@ -1812,7 +1846,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*", "@types/jest@^27.4.1": +"@types/jest@*": version "27.5.1" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.1.tgz#2c8b6dc6ff85c33bcd07d0b62cb3d19ddfdb3ab9" integrity sha512-fUy7YRpT+rHXto1YlL+J9rs0uLGyiqVt3ZOTQR+4ROc47yNl8WLdVLgUloBRhOxP1PZvguHl44T3H0wAWxahYQ== @@ -1820,6 +1854,14 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" +"@types/jest@^29.5.2": + version "29.5.2" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b" + integrity sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.10" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.10.tgz#9b05b7896166cd00e9cbd59864853abf65d9ac23" @@ -1845,10 +1887,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== -"@types/node@^16.11.26": - version "16.11.36" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.36.tgz#9ab9f8276987132ed2b225cace2218ba794fc751" - integrity sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA== +"@types/node@^20.3.2": + version "20.3.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.2.tgz#fa6a90f2600e052a03c18b8cb3fd83dd4e599898" + integrity sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -1885,17 +1927,17 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-dom@<18.0.0", "@types/react-dom@^17.0.14": - version "17.0.17" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.17.tgz#2e3743277a793a96a99f1bf87614598289da68a1" - integrity sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg== +"@types/react-dom@^18.0.0", "@types/react-dom@^18.2.6": + version "18.2.6" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.6.tgz#ad621fa71a8db29af7c31b41b2ea3d8a6f4144d1" + integrity sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A== dependencies: - "@types/react" "^17" + "@types/react" "*" -"@types/react@^17", "@types/react@^17.0.42": - version "17.0.45" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.45.tgz#9b3d5b661fd26365fefef0e766a1c6c30ccf7b3f" - integrity sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg== +"@types/react@*", "@types/react@^18.2.14": + version "18.2.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.14.tgz#fa7a6fecf1ce35ca94e74874f70c56ce88f7a127" + integrity sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -1976,6 +2018,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin@^5.5.0": version "5.16.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz#78f246dd8d1b528fc5bfca99a8a64d4023a3d86d" @@ -2382,6 +2431,13 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + aria-query@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" @@ -2410,6 +2466,14 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -2525,6 +2589,11 @@ autoprefixer@^10.4.4: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + axe-core@^4.3.5: version "4.4.1" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" @@ -3392,15 +3461,6 @@ css.escape@^1.5.1: resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= -css@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" - integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== - dependencies: - inherits "^2.0.4" - source-map "^0.6.1" - source-map-resolve "^0.6.0" - cssdb@^6.5.0: version "6.5.0" resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-6.5.0.tgz#61264b71f29c834f09b59cb3e5b43c8226590122" @@ -3556,6 +3616,30 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-equal@^2.0.5: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.1.tgz#c72ab22f3a7d3503a4ca87dde976fe9978816739" + integrity sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.0" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.0" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -3585,6 +3669,14 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -3678,6 +3770,11 @@ diff-sequences@^27.5.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -3919,6 +4016,21 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + es-module-lexer@^0.9.0: version "0.9.3" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" @@ -3970,10 +4082,10 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-react-app@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-7.0.0.tgz#0fa96d5ec1dfb99c029b1554362ab3fa1c3757df" - integrity sha512-xyymoxtIt1EOsSaGag+/jmcywRuieQoA2JbPCjnw9HukFj9/97aGPoZVFioaotzk1K5Qt9sHO5EutZbkrAXS0g== +eslint-config-react-app@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz#73ba3929978001c5c86274c017ea57eb5fa644b4" + integrity sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA== dependencies: "@babel/core" "^7.16.0" "@babel/eslint-parser" "^7.16.3" @@ -4281,6 +4393,17 @@ expect@^27.5.1: jest-matcher-utils "^27.5.1" jest-message-util "^27.5.1" +expect@^29.0.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== + dependencies: + "@jest/expect-utils" "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + express@^4.17.1: version "4.17.3" resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" @@ -4519,6 +4642,13 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -4625,6 +4755,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -4644,6 +4779,16 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" +get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -4770,6 +4915,13 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.9" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" @@ -4807,6 +4959,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" @@ -5086,7 +5250,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -5110,6 +5274,15 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +internal-slot@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + ip@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -5139,7 +5312,7 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-arguments@^1.0.4: +is-arguments@^1.0.4, is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== @@ -5147,6 +5320,15 @@ is-arguments@^1.0.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -5179,6 +5361,11 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" @@ -5205,7 +5392,7 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -5276,6 +5463,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -5355,11 +5547,23 @@ is-root@^2.1.0: resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + is-shared-array-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -5379,11 +5583,27 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-typed-array@^1.1.10: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + is-weakref@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -5391,6 +5611,14 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -5408,6 +5636,11 @@ isarray@1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -5569,6 +5802,16 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" @@ -5617,6 +5860,11 @@ jest-get-type@^27.5.1: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + jest-haste-map@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" @@ -5678,6 +5926,16 @@ jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== + dependencies: + chalk "^4.0.0" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -5693,6 +5951,21 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.5.0" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.5.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" @@ -5839,6 +6112,18 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" @@ -5904,10 +6189,10 @@ jest@^27.4.3: import-local "^3.0.2" jest-cli "^27.5.1" -js-flipper@^0.140.0: - version "0.140.0" - resolved "https://registry.yarnpkg.com/js-flipper/-/js-flipper-0.140.0.tgz#f658653e5e34bdfe2f02519e2e1aee2ddfdea747" - integrity sha512-0NbW6n1RAlg+dyc4Plodk5e5UTJHXno9v2J+iMpFqGsQGMAtgOvr3UKMn5V4puZN3QXd4oRt9fBawRlkmWtDrA== +js-flipper@*: + version "0.203.0" + resolved "https://registry.yarnpkg.com/js-flipper/-/js-flipper-0.203.0.tgz#720ba023a717982f0c64d33ddea9a327d0939d05" + integrity sha512-/lEEG9wuQpLoZP3OaeY2dC+Dbbejmn18RajkFXgOjgw9AvHQFsLqum9bwdRmukoq0mIvtUvA/J6SfahqJu4o/Q== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -6213,10 +6498,10 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lz-string@^1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" - integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.9" @@ -6560,7 +6845,7 @@ nwsapi@^2.2.0: object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-copy@^0.1.0: version "0.1.0" @@ -6581,7 +6866,7 @@ object-inspect@^1.11.0, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-is@^1.0.1: +object-is@^1.0.1, object-is@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== @@ -6611,6 +6896,16 @@ object.assign@^4.1.0, object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + object.entries@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" @@ -7533,6 +7828,15 @@ pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^29.0.0, pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== + dependencies: + "@jest/schemas" "^29.4.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -7641,10 +7945,10 @@ react-app-polyfill@^3.0.0: regenerator-runtime "^0.13.9" whatwg-fetch "^3.6.2" -react-dev-utils@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.0.tgz#4eab12cdb95692a077616770b5988f0adf806526" - integrity sha512-xBQkitdxozPxt1YZ9O1097EJiVpwHr9FoAuEVURCKV0Av8NBERovJauzP7bo1ThvuhZ4shsQ1AJiu4vQpoT1AQ== +react-dev-utils@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73" + integrity sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ== dependencies: "@babel/code-frame" "^7.16.0" address "^1.1.2" @@ -7665,25 +7969,24 @@ react-dev-utils@^12.0.0: open "^8.4.0" pkg-up "^3.1.0" prompts "^2.4.2" - react-error-overlay "^6.0.10" + react-error-overlay "^6.0.11" recursive-readdir "^2.2.2" shell-quote "^1.7.3" strip-ansi "^6.0.1" text-table "^0.2.0" -react-dom@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.0" -react-error-overlay@^6.0.10: - version "6.0.10" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6" - integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA== +react-error-overlay@^6.0.11: + version "6.0.11" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" + integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== react-is@^16.13.1: version "16.13.1" @@ -7695,15 +7998,20 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== -react-scripts@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.0.tgz#6547a6d7f8b64364ef95273767466cc577cb4b60" - integrity sha512-3i0L2CyIlROz7mxETEdfif6Sfhh9Lfpzi10CtcGs1emDQStmZfWjJbAIMtRD0opVUjQuFWqHZyRZ9PPzKCFxWg== +react-scripts@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003" + integrity sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ== dependencies: "@babel/core" "^7.16.0" "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" @@ -7721,7 +8029,7 @@ react-scripts@5.0.0: dotenv "^10.0.0" dotenv-expand "^5.1.0" eslint "^8.3.0" - eslint-config-react-app "^7.0.0" + eslint-config-react-app "^7.0.1" eslint-webpack-plugin "^3.1.1" file-loader "^6.2.0" fs-extra "^10.0.0" @@ -7738,7 +8046,7 @@ react-scripts@5.0.0: postcss-preset-env "^7.0.1" prompts "^2.4.2" react-app-polyfill "^3.0.0" - react-dev-utils "^12.0.0" + react-dev-utils "^12.0.1" react-refresh "^0.11.0" resolve "^1.20.0" resolve-url-loader "^4.0.0" @@ -7755,13 +8063,12 @@ react-scripts@5.0.0: optionalDependencies: fsevents "^2.3.2" -react@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" read-pkg-up@^6.0.0: version "6.0.0" @@ -7871,6 +8178,15 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1: call-bind "^1.0.2" define-properties "^1.1.3" +regexp.prototype.flags@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" + regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -8115,13 +8431,12 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" schema-utils@2.7.0: version "2.7.0" @@ -8407,14 +8722,6 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-resolve@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" - integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -8546,6 +8853,13 @@ static-extend@^0.1.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -9047,10 +9361,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.6.2: - version "4.6.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" - integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== +typescript@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== unbox-primitive@^1.0.1: version "1.0.1" @@ -9245,10 +9559,10 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -web-vitals@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.4.tgz#76563175a475a5e835264d373704f9dde718290c" - integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg== +web-vitals@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-3.3.2.tgz#83e8dbd0f8fba43c5fe2e601573e804afc771790" + integrity sha512-qRkpmSeKfEWAzNhtX541xA8gCJ+pqCqBmUlDVkVDSCSYUvfvNqF+k9g8I+uyreRcDBdfiJrd0/aLbTy5ydo49Q== webidl-conversions@^4.0.2: version "4.0.2" @@ -9431,11 +9745,33 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" diff --git a/js/yarn.lock b/js/yarn.lock deleted file mode 100644 index fb57ccd13..000000000 --- a/js/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - diff --git a/packer/Cargo.lock b/packer/Cargo.lock index 140e3132e..7cae92865 100644 --- a/packer/Cargo.lock +++ b/packer/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.61" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "atty" @@ -42,27 +42,31 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bstr" -version = "0.2.17" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", + "serde", ] [[package]] name = "cc" -version = "1.0.73" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -72,16 +76,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.1.17" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47582c09be7c8b32c0ab3a6181825ababb713fde6fff20fc573a3870dd45c6a0" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", - "indexmap", - "lazy_static", + "indexmap 1.9.3", + "once_cell", "strsim", "termcolor", "textwrap", @@ -89,63 +93,53 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.7" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "clap_lex" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] [[package]] name = "console" -version = "0.15.0" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "terminal_size", - "winapi", + "unicode-width", + "windows-sys 0.45.0", ] [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -154,33 +148,31 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.8" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "lazy_static", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", - "lazy_static", ] [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -188,15 +180,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "digest" -version = "0.10.3" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -204,29 +196,30 @@ dependencies = [ [[package]] name = "dirs" -version = "4.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.3.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", + "option-ext", "redox_users", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "either" -version = "1.6.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encode_unicode" @@ -235,15 +228,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] -name = "filetime" -version = "0.2.15" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "filetime" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "winapi", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", ] [[package]] @@ -280,9 +279,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -290,9 +289,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -301,9 +300,9 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.8" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" dependencies = [ "aho-corasick", "bstr", @@ -319,10 +318,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] -name = "heck" -version = "0.4.0" +name = "hashbrown" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -335,11 +340,10 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" dependencies = [ - "crossbeam-utils", "globset", "lazy_static", "log", @@ -353,31 +357,51 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", ] [[package]] name = "indicatif" -version = "0.16.2" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b" +checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" dependencies = [ "console", - "lazy_static", + "instant", "number_prefix", - "regex", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", ] [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "lazy_static" @@ -387,24 +411,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.121" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" -version = "0.4.16" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lzma-sys" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb4b7c3eddad11d3af9e86c487607d2d2442d185d848575365c4856ba96d619" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" dependencies = [ "cc", "libc", @@ -413,29 +434,19 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.6.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "number_prefix" version = "0.4.0" @@ -444,21 +455,33 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "once_cell" -version = "1.10.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "os_str_bytes" -version = "6.0.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "portable-atomic" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" [[package]] name = "proc-macro-error" @@ -469,7 +492,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -486,18 +509,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.17" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -532,26 +555,22 @@ checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rayon" -version = "1.5.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -565,9 +584,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] @@ -579,15 +607,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" dependencies = [ "aho-corasick", "memchr", @@ -596,9 +636,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "remove_dir_all" @@ -611,9 +651,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" @@ -626,35 +666,35 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.143" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.143" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.28", ] [[package]] name = "serde_json" -version = "1.0.80" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f972498cf015f7c0746cac89ebe1d6ef10c293b94175a243a2d9442c163d9944" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -663,11 +703,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.9" +version = "0.9.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50845f68d5c693aac7d72a25415ddd21cb8182c04eafe447b73af55a05f9e1b" +checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" dependencies = [ - "indexmap", + "indexmap 2.0.0", "itoa", "ryu", "serde", @@ -676,9 +716,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -687,9 +727,9 @@ dependencies = [ [[package]] name = "shellexpand" -version = "2.1.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" dependencies = [ "dirs", ] @@ -702,20 +742,31 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.91" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] name = "tar" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" dependencies = [ "filetime", "libc", @@ -734,75 +785,72 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "textwrap" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "d9207952ae1a003f42d3d5e892dac3c6ba42aa6ac0c79a6a91a2b5cb4253e75c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "f1728216d3244de4f14f14f8c15c79be1a7c67867d28d69b719690e2a19fb445" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.28", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unsafe-libyaml" -version = "0.2.2" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931179334a56395bcf64ba5e0ff56781381c1a5832178280c7d7f91d1679aeb0" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" [[package]] name = "version_check" @@ -812,20 +860,19 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" @@ -859,19 +906,151 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "xattr" -version = "0.2.2" +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.2", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" +dependencies = [ + "windows_aarch64_gnullvm 0.48.2", + "windows_aarch64_msvc 0.48.2", + "windows_i686_gnu 0.48.2", + "windows_i686_msvc 0.48.2", + "windows_x86_64_gnu 0.48.2", + "windows_x86_64_gnullvm 0.48.2", + "windows_x86_64_msvc 0.48.2", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" + +[[package]] +name = "xattr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" dependencies = [ "libc", ] [[package]] name = "xz2" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" dependencies = [ "lzma-sys", ] diff --git a/packer/Cargo.toml b/packer/Cargo.toml index 88a1ee92b..5798cdb9d 100644 --- a/packer/Cargo.toml +++ b/packer/Cargo.toml @@ -11,18 +11,18 @@ edition = "2021" [dependencies] clap = { version = "^3", features = ["derive", "cargo"] } -shellexpand = "^2" -tar = "0.4.38" +shellexpand = "^3" +tar = "0.4.39" serde = { version = "^1", features = ["derive"] } -serde_yaml = "0.9.9" +serde_yaml = "0.9.25" anyhow = "^1" -sha2 = "0.10.2" +sha2 = "0.10.7" data-encoding = "^2" serde_json = "^1" -rayon = "^1.5" -indicatif = "^0.16" -xz2 = "0.1.6" -ignore = "0.4.18" +rayon = "^1.8" +indicatif = "^0.17" +xz2 = "0.1.7" +ignore = "0.4.20" [dev-dependencies] tempdir = "0.3.7" diff --git a/packer/src/main.rs b/packer/src/main.rs index 8a5e79a33..260874b91 100644 --- a/packer/src/main.rs +++ b/packer/src/main.rs @@ -106,6 +106,7 @@ fn default_progress_bar(len: u64) -> indicatif::ProgressBar { pb.set_style( indicatif::ProgressStyle::default_bar() .template("{prefix:.bold}▕{bar:.magenta}▏{msg}") + .expect("valid indicatif template") .progress_chars("█▓▒░ "), ); pb @@ -404,7 +405,7 @@ mod test { fn test_included_packlist_parses() { let res: PackList = serde_yaml::from_str(DEFAULT_PACKLIST).expect("Default packlist doesn't deserialize"); - assert_eq!(res.0.len(), 5); + assert_eq!(res.0.len(), 6); } #[test] diff --git a/packer/src/packlist.yaml b/packer/src/packlist.yaml index 6c56a4f99..ff8f54e23 100644 --- a/packer/src/packlist.yaml +++ b/packer/src/packlist.yaml @@ -41,15 +41,26 @@ server-mac-x64: - Flipper.app/Contents/Resources - Flipper.app/Contents/Info.plist +server-mac-aarch64: + mode: exact + basedir: flipper-server-mac-aarch64 + files: + frameworks: + - Flipper.app/Contents/MacOS/ + - Flipper.app/Contents/PkgInfo + core: + - Flipper.app/Contents/Resources + - Flipper.app/Contents/Info.plist + server-linux: mode: glob basedir: flipper-server-linux files: frameworks: - '!*' - - node + - flipper-runtime core: - - '!node' + - '!flipper-runtime - '!static' static: - '!*' diff --git a/react-native/ReactNativeFlipperExample/android/app/build.gradle b/react-native/ReactNativeFlipperExample/android/app/build.gradle index 67357b4fa..f7d168fc3 100644 --- a/react-native/ReactNativeFlipperExample/android/app/build.gradle +++ b/react-native/ReactNativeFlipperExample/android/app/build.gradle @@ -133,8 +133,6 @@ def enableHermes = project.ext.react.get("enableHermes", false); def nativeArchitectures = project.getProperties().get("reactNativeDebugArchitectures") android { - ndkVersion rootProject.ext.ndkVersion - compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { @@ -214,9 +212,10 @@ dependencies { } if (enableHermes) { - def hermesPath = "../../node_modules/hermes-engine/android/"; - debugImplementation files(hermesPath + "hermes-debug.aar") - releaseImplementation files(hermesPath + "hermes-release.aar") + //noinspection GradleDynamicVersion + implementation("com.facebook.react:hermes-engine:+") { // From node_modules + exclude group:'com.facebook.fbjni' + } } else { implementation jscFlavor } diff --git a/react-native/ReactNativeFlipperExample/android/app/src/debug/java/com/reactnativeflipperexample/ReactNativeFlipper.java b/react-native/ReactNativeFlipperExample/android/app/src/debug/java/com/reactnativeflipperexample/ReactNativeFlipper.java index d030fa7c1..28ee0db72 100644 --- a/react-native/ReactNativeFlipperExample/android/app/src/debug/java/com/reactnativeflipperexample/ReactNativeFlipper.java +++ b/react-native/ReactNativeFlipperExample/android/app/src/debug/java/com/reactnativeflipperexample/ReactNativeFlipper.java @@ -22,6 +22,7 @@ import com.facebook.flipper.plugins.react.ReactFlipperPlugin; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; import com.facebook.react.ReactInstanceManager; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.network.CustomClientBuilder; import com.facebook.react.modules.network.NetworkingModule; import okhttp3.OkHttpClient; @@ -38,7 +39,7 @@ public class ReactNativeFlipper { NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); NetworkingModule.setCustomClientBuilder( - new NetworkingModule.CustomClientBuilder() { + new CustomClientBuilder() { @Override public void apply(OkHttpClient.Builder builder) { builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); diff --git a/react-native/ReactNativeFlipperExample/android/build.gradle b/react-native/ReactNativeFlipperExample/android/build.gradle index 8a1102597..850d3eb1c 100644 --- a/react-native/ReactNativeFlipperExample/android/build.gradle +++ b/react-native/ReactNativeFlipperExample/android/build.gradle @@ -12,7 +12,6 @@ buildscript { minSdkVersion = 21 compileSdkVersion = 31 targetSdkVersion = 31 - ndkVersion = '23.0.7599858' } repositories { google() diff --git a/react-native/ReactNativeFlipperExample/android/gradle.properties b/react-native/ReactNativeFlipperExample/android/gradle.properties index f341f823b..91db2646e 100644 --- a/react-native/ReactNativeFlipperExample/android/gradle.properties +++ b/react-native/ReactNativeFlipperExample/android/gradle.properties @@ -30,4 +30,4 @@ android.useAndroidX=true android.enableJetifier=true # Version of flipper SDK to use with React Native -FLIPPER_VERSION=0.125.0 +FLIPPER_VERSION=0.171.1 diff --git a/react-native/ReactNativeFlipperExample/ios/Podfile b/react-native/ReactNativeFlipperExample/ios/Podfile index f0c82e19a..55df1d95c 100644 --- a/react-native/ReactNativeFlipperExample/ios/Podfile +++ b/react-native/ReactNativeFlipperExample/ios/Podfile @@ -2,15 +2,17 @@ require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' source 'https://github.com/CocoaPods/Specs' -platform :ios, '11' +platform :ios, '12.4' # used for automatic bumping -flipperkit_version = '0.162.0' +flipperkit_version = '0.222.0' target 'ReactNativeFlipperExample' do config = use_native_modules! use_react_native!( :path => config["reactNativePath"], + # Versions are explicitly pinned to be able to forcefully test against the latest versions + :flipper_configuration => FlipperConfiguration.enabled(["Debug"], { 'Flipper' => flipperkit_version, 'Flipper-Folly' => '2.6.10', 'Flipper-DoubleConversion' => '3.2.0', 'Flipper-Glog' => '0.5.0.3', 'Flipper-PeerTalk' => '0.0.4', 'OpenSSL-Universal' => '1.1.1100' }), # to enable hermes on iOS, change `false` to `true` and then install pods :hermes_enabled => true ) @@ -20,15 +22,6 @@ target 'ReactNativeFlipperExample' do # Pods for testing end - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable these next few lines. - - # Versions are explicitly pinned to be able to forcefully test against the latest versions. - # use_flipper!() should work as well and pick the React Native defaults - use_flipper!({ 'Flipper' => flipperkit_version, 'Flipper-Folly' => '2.6.10', 'Flipper-DoubleConversion' => '3.2.0', 'Flipper-Glog' => '0.5.0.3', 'Flipper-PeerTalk' => '0.0.4', 'OpenSSL-Universal' => '1.1.1100' }) - post_install do |installer| react_native_post_install(installer) __apply_Xcode_12_5_M1_post_install_workaround(installer) diff --git a/react-native/ReactNativeFlipperExample/ios/Podfile.lock b/react-native/ReactNativeFlipperExample/ios/Podfile.lock index 2ac7d366c..638414ac5 100644 --- a/react-native/ReactNativeFlipperExample/ios/Podfile.lock +++ b/react-native/ReactNativeFlipperExample/ios/Podfile.lock @@ -2,15 +2,15 @@ PODS: - boost (1.76.0) - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) - - FBLazyVector (0.67.4) - - FBReactNativeSpec (0.67.4): + - FBLazyVector (0.69.7) + - FBReactNativeSpec (0.69.7): - RCT-Folly (= 2021.06.28.00-v2) - - RCTRequired (= 0.67.4) - - RCTTypeSafety (= 0.67.4) - - React-Core (= 0.67.4) - - React-jsi (= 0.67.4) - - ReactCommon/turbomodule/core (= 0.67.4) - - Flipper (0.162.0): + - RCTRequired (= 0.69.7) + - RCTTypeSafety (= 0.69.7) + - React-Core (= 0.69.7) + - React-jsi (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - Flipper (0.222.0): - Flipper-Folly (~> 2.6) - Flipper-Boost-iOSX (1.76.0.1.11) - Flipper-DoubleConversion (3.2.0) @@ -26,53 +26,51 @@ PODS: - Flipper-PeerTalk (0.0.4) - Flipper-RSocket (1.4.3): - Flipper-Folly (~> 2.6) - - FlipperKit (0.162.0): - - FlipperKit/Core (= 0.162.0) - - FlipperKit/Core (0.162.0): - - Flipper (~> 0.162.0) + - FlipperKit (0.222.0): + - FlipperKit/Core (= 0.222.0) + - FlipperKit/Core (0.222.0): + - Flipper (~> 0.222.0) - FlipperKit/CppBridge - FlipperKit/FBCxxFollyDynamicConvert - FlipperKit/FBDefines - FlipperKit/FKPortForwarding - - SocketRocket (~> 0.6.0) - - FlipperKit/CppBridge (0.162.0): - - Flipper (~> 0.162.0) - - FlipperKit/FBCxxFollyDynamicConvert (0.162.0): + - SocketRocket (~> 0.7.0) + - FlipperKit/CppBridge (0.222.0): + - Flipper (~> 0.222.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.222.0): - Flipper-Folly (~> 2.6) - - FlipperKit/FBDefines (0.162.0) - - FlipperKit/FKPortForwarding (0.162.0): + - FlipperKit/FBDefines (0.222.0) + - FlipperKit/FKPortForwarding (0.222.0): - CocoaAsyncSocket (~> 7.6) - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitHighlightOverlay (0.162.0) - - FlipperKit/FlipperKitLayoutHelpers (0.162.0): + - FlipperKit/FlipperKitHighlightOverlay (0.222.0) + - FlipperKit/FlipperKitLayoutHelpers (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutTextSearchable - - FlipperKit/FlipperKitLayoutIOSDescriptors (0.162.0): + - FlipperKit/FlipperKitLayoutIOSDescriptors (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutPlugin (0.162.0): + - FlipperKit/FlipperKitLayoutPlugin (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - FlipperKit/FlipperKitLayoutIOSDescriptors - FlipperKit/FlipperKitLayoutTextSearchable - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.162.0) - - FlipperKit/FlipperKitNetworkPlugin (0.162.0): + - FlipperKit/FlipperKitLayoutTextSearchable (0.222.0) + - FlipperKit/FlipperKitNetworkPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/FlipperKitReactPlugin (0.162.0): + - FlipperKit/FlipperKitReactPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.162.0): + - FlipperKit/FlipperKitUserDefaultsPlugin (0.222.0): - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.162.0): + - FlipperKit/SKIOSNetworkPlugin (0.222.0): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - fmt (6.2.1) - glog (0.3.5) - - hermes-engine (0.9.0) + - hermes-engine (0.69.7) - libevent (2.1.12) - OpenSSL-Universal (1.1.1100) - RCT-Folly (2021.06.28.00-v2): @@ -92,282 +90,292 @@ PODS: - fmt (~> 6.2.1) - glog - libevent - - RCTRequired (0.67.4) - - RCTTypeSafety (0.67.4): - - FBLazyVector (= 0.67.4) + - RCTRequired (0.69.7) + - RCTTypeSafety (0.69.7): + - FBLazyVector (= 0.69.7) + - RCTRequired (= 0.69.7) + - React-Core (= 0.69.7) + - React (0.69.7): + - React-Core (= 0.69.7) + - React-Core/DevSupport (= 0.69.7) + - React-Core/RCTWebSocket (= 0.69.7) + - React-RCTActionSheet (= 0.69.7) + - React-RCTAnimation (= 0.69.7) + - React-RCTBlob (= 0.69.7) + - React-RCTImage (= 0.69.7) + - React-RCTLinking (= 0.69.7) + - React-RCTNetwork (= 0.69.7) + - React-RCTSettings (= 0.69.7) + - React-RCTText (= 0.69.7) + - React-RCTVibration (= 0.69.7) + - React-bridging (0.69.7): - RCT-Folly (= 2021.06.28.00-v2) - - RCTRequired (= 0.67.4) - - React-Core (= 0.67.4) - - React (0.67.4): - - React-Core (= 0.67.4) - - React-Core/DevSupport (= 0.67.4) - - React-Core/RCTWebSocket (= 0.67.4) - - React-RCTActionSheet (= 0.67.4) - - React-RCTAnimation (= 0.67.4) - - React-RCTBlob (= 0.67.4) - - React-RCTImage (= 0.67.4) - - React-RCTLinking (= 0.67.4) - - React-RCTNetwork (= 0.67.4) - - React-RCTSettings (= 0.67.4) - - React-RCTText (= 0.67.4) - - React-RCTVibration (= 0.67.4) - - React-callinvoker (0.67.4) - - React-Core (0.67.4): + - React-jsi (= 0.69.7) + - React-callinvoker (0.69.7) + - React-Codegen (0.69.7): + - FBReactNativeSpec (= 0.69.7) + - RCT-Folly (= 2021.06.28.00-v2) + - RCTRequired (= 0.69.7) + - RCTTypeSafety (= 0.69.7) + - React-Core (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - React-Core (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-Core/Default (= 0.67.4) - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-Core/Default (= 0.69.7) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/CoreModulesHeaders (0.67.4): + - React-Core/CoreModulesHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/Default (0.67.4): + - React-Core/Default (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/DevSupport (0.67.4): + - React-Core/DevSupport (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-Core/Default (= 0.67.4) - - React-Core/RCTWebSocket (= 0.67.4) - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-jsinspector (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-Core/Default (= 0.69.7) + - React-Core/RCTWebSocket (= 0.69.7) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-jsinspector (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTActionSheetHeaders (0.67.4): + - React-Core/RCTActionSheetHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTAnimationHeaders (0.67.4): + - React-Core/RCTAnimationHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTBlobHeaders (0.67.4): + - React-Core/RCTBlobHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTImageHeaders (0.67.4): + - React-Core/RCTImageHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTLinkingHeaders (0.67.4): + - React-Core/RCTLinkingHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTNetworkHeaders (0.67.4): + - React-Core/RCTNetworkHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTSettingsHeaders (0.67.4): + - React-Core/RCTSettingsHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTTextHeaders (0.67.4): + - React-Core/RCTTextHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTVibrationHeaders (0.67.4): + - React-Core/RCTVibrationHeaders (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-Core/RCTWebSocket (0.67.4): + - React-Core/RCTWebSocket (0.69.7): - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-Core/Default (= 0.67.4) - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-perflogger (= 0.67.4) + - React-Core/Default (= 0.69.7) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-perflogger (= 0.69.7) - Yoga - - React-CoreModules (0.67.4): - - FBReactNativeSpec (= 0.67.4) + - React-CoreModules (0.69.7): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.67.4) - - React-Core/CoreModulesHeaders (= 0.67.4) - - React-jsi (= 0.67.4) - - React-RCTImage (= 0.67.4) - - ReactCommon/turbomodule/core (= 0.67.4) - - React-cxxreact (0.67.4): + - RCTTypeSafety (= 0.69.7) + - React-Codegen (= 0.69.7) + - React-Core/CoreModulesHeaders (= 0.69.7) + - React-jsi (= 0.69.7) + - React-RCTImage (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - React-cxxreact (0.69.7): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-callinvoker (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsinspector (= 0.67.4) - - React-logger (= 0.67.4) - - React-perflogger (= 0.67.4) - - React-runtimeexecutor (= 0.67.4) - - React-hermes (0.67.4): + - React-callinvoker (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsinspector (= 0.69.7) + - React-logger (= 0.69.7) + - React-perflogger (= 0.69.7) + - React-runtimeexecutor (= 0.69.7) + - React-hermes (0.69.7): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly/Futures (= 2021.06.28.00-v2) - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-jsiexecutor (= 0.67.4) - - React-jsinspector (= 0.67.4) - - React-perflogger (= 0.67.4) - - React-jsi (0.67.4): + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-jsiexecutor (= 0.69.7) + - React-jsinspector (= 0.69.7) + - React-perflogger (= 0.69.7) + - React-jsi (0.69.7): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-jsi/Default (= 0.67.4) - - React-jsi/Default (0.67.4): + - React-jsi/Default (= 0.69.7) + - React-jsi/Default (0.69.7): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-jsiexecutor (0.67.4): + - React-jsiexecutor (0.69.7): - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-perflogger (= 0.67.4) - - React-jsinspector (0.67.4) - - React-logger (0.67.4): + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-perflogger (= 0.69.7) + - React-jsinspector (0.69.7) + - React-logger (0.69.7): - glog - - react-native-flipper (0.159.0): + - react-native-flipper (0.195.0): - React-Core - - React-perflogger (0.67.4) - - React-RCTActionSheet (0.67.4): - - React-Core/RCTActionSheetHeaders (= 0.67.4) - - React-RCTAnimation (0.67.4): - - FBReactNativeSpec (= 0.67.4) + - React-perflogger (0.69.7) + - React-RCTActionSheet (0.69.7): + - React-Core/RCTActionSheetHeaders (= 0.69.7) + - React-RCTAnimation (0.69.7): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.67.4) - - React-Core/RCTAnimationHeaders (= 0.67.4) - - React-jsi (= 0.67.4) - - ReactCommon/turbomodule/core (= 0.67.4) - - React-RCTBlob (0.67.4): - - FBReactNativeSpec (= 0.67.4) + - RCTTypeSafety (= 0.69.7) + - React-Codegen (= 0.69.7) + - React-Core/RCTAnimationHeaders (= 0.69.7) + - React-jsi (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - React-RCTBlob (0.69.7): - RCT-Folly (= 2021.06.28.00-v2) - - React-Core/RCTBlobHeaders (= 0.67.4) - - React-Core/RCTWebSocket (= 0.67.4) - - React-jsi (= 0.67.4) - - React-RCTNetwork (= 0.67.4) - - ReactCommon/turbomodule/core (= 0.67.4) - - React-RCTImage (0.67.4): - - FBReactNativeSpec (= 0.67.4) + - React-Codegen (= 0.69.7) + - React-Core/RCTBlobHeaders (= 0.69.7) + - React-Core/RCTWebSocket (= 0.69.7) + - React-jsi (= 0.69.7) + - React-RCTNetwork (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - React-RCTImage (0.69.7): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.67.4) - - React-Core/RCTImageHeaders (= 0.67.4) - - React-jsi (= 0.67.4) - - React-RCTNetwork (= 0.67.4) - - ReactCommon/turbomodule/core (= 0.67.4) - - React-RCTLinking (0.67.4): - - FBReactNativeSpec (= 0.67.4) - - React-Core/RCTLinkingHeaders (= 0.67.4) - - React-jsi (= 0.67.4) - - ReactCommon/turbomodule/core (= 0.67.4) - - React-RCTNetwork (0.67.4): - - FBReactNativeSpec (= 0.67.4) + - RCTTypeSafety (= 0.69.7) + - React-Codegen (= 0.69.7) + - React-Core/RCTImageHeaders (= 0.69.7) + - React-jsi (= 0.69.7) + - React-RCTNetwork (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - React-RCTLinking (0.69.7): + - React-Codegen (= 0.69.7) + - React-Core/RCTLinkingHeaders (= 0.69.7) + - React-jsi (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - React-RCTNetwork (0.69.7): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.67.4) - - React-Core/RCTNetworkHeaders (= 0.67.4) - - React-jsi (= 0.67.4) - - ReactCommon/turbomodule/core (= 0.67.4) - - React-RCTSettings (0.67.4): - - FBReactNativeSpec (= 0.67.4) + - RCTTypeSafety (= 0.69.7) + - React-Codegen (= 0.69.7) + - React-Core/RCTNetworkHeaders (= 0.69.7) + - React-jsi (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - React-RCTSettings (0.69.7): - RCT-Folly (= 2021.06.28.00-v2) - - RCTTypeSafety (= 0.67.4) - - React-Core/RCTSettingsHeaders (= 0.67.4) - - React-jsi (= 0.67.4) - - ReactCommon/turbomodule/core (= 0.67.4) - - React-RCTText (0.67.4): - - React-Core/RCTTextHeaders (= 0.67.4) - - React-RCTVibration (0.67.4): - - FBReactNativeSpec (= 0.67.4) + - RCTTypeSafety (= 0.69.7) + - React-Codegen (= 0.69.7) + - React-Core/RCTSettingsHeaders (= 0.69.7) + - React-jsi (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - React-RCTText (0.69.7): + - React-Core/RCTTextHeaders (= 0.69.7) + - React-RCTVibration (0.69.7): - RCT-Folly (= 2021.06.28.00-v2) - - React-Core/RCTVibrationHeaders (= 0.67.4) - - React-jsi (= 0.67.4) - - ReactCommon/turbomodule/core (= 0.67.4) - - React-runtimeexecutor (0.67.4): - - React-jsi (= 0.67.4) - - ReactCommon/turbomodule/core (0.67.4): + - React-Codegen (= 0.69.7) + - React-Core/RCTVibrationHeaders (= 0.69.7) + - React-jsi (= 0.69.7) + - ReactCommon/turbomodule/core (= 0.69.7) + - React-runtimeexecutor (0.69.7): + - React-jsi (= 0.69.7) + - ReactCommon/turbomodule/core (0.69.7): - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - - React-callinvoker (= 0.67.4) - - React-Core (= 0.67.4) - - React-cxxreact (= 0.67.4) - - React-jsi (= 0.67.4) - - React-logger (= 0.67.4) - - React-perflogger (= 0.67.4) - - SocketRocket (0.6.0) + - React-bridging (= 0.69.7) + - React-callinvoker (= 0.69.7) + - React-Core (= 0.69.7) + - React-cxxreact (= 0.69.7) + - React-jsi (= 0.69.7) + - React-logger (= 0.69.7) + - React-perflogger (= 0.69.7) + - SocketRocket (0.7.0) - Yoga (1.14.0) - - YogaKit (1.18.1): - - Yoga (~> 1.14) DEPENDENCIES: - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) - - Flipper (= 0.162.0) + - Flipper (= 0.222.0) - Flipper-Boost-iOSX (= 1.76.0.1.11) - Flipper-DoubleConversion (= 3.2.0) - Flipper-Fmt (= 7.1.7) @@ -375,28 +383,30 @@ DEPENDENCIES: - Flipper-Glog (= 0.5.0.3) - Flipper-PeerTalk (= 0.0.4) - Flipper-RSocket (= 1.4.3) - - FlipperKit (= 0.162.0) - - FlipperKit/Core (= 0.162.0) - - FlipperKit/CppBridge (= 0.162.0) - - FlipperKit/FBCxxFollyDynamicConvert (= 0.162.0) - - FlipperKit/FBDefines (= 0.162.0) - - FlipperKit/FKPortForwarding (= 0.162.0) - - FlipperKit/FlipperKitHighlightOverlay (= 0.162.0) - - FlipperKit/FlipperKitLayoutPlugin (= 0.162.0) - - FlipperKit/FlipperKitLayoutTextSearchable (= 0.162.0) - - FlipperKit/FlipperKitNetworkPlugin (= 0.162.0) - - FlipperKit/FlipperKitReactPlugin (= 0.162.0) - - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.162.0) - - FlipperKit/SKIOSNetworkPlugin (= 0.162.0) + - FlipperKit (= 0.222.0) + - FlipperKit/Core (= 0.222.0) + - FlipperKit/CppBridge (= 0.222.0) + - FlipperKit/FBCxxFollyDynamicConvert (= 0.222.0) + - FlipperKit/FBDefines (= 0.222.0) + - FlipperKit/FKPortForwarding (= 0.222.0) + - FlipperKit/FlipperKitHighlightOverlay (= 0.222.0) + - FlipperKit/FlipperKitLayoutPlugin (= 0.222.0) + - FlipperKit/FlipperKitLayoutTextSearchable (= 0.222.0) + - FlipperKit/FlipperKitNetworkPlugin (= 0.222.0) + - FlipperKit/FlipperKitReactPlugin (= 0.222.0) + - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.222.0) + - FlipperKit/SKIOSNetworkPlugin (= 0.222.0) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - - hermes-engine (~> 0.9.0) + - hermes-engine (from `../node_modules/react-native/sdks/hermes/hermes-engine.podspec`) - libevent (~> 2.1.12) - OpenSSL-Universal (= 1.1.1100) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - React (from `../node_modules/react-native/`) + - React-bridging (from `../node_modules/react-native/ReactCommon`) - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) + - React-Codegen (from `build/generated/ios`) - React-Core (from `../node_modules/react-native/`) - React-Core/DevSupport (from `../node_modules/react-native/`) - React-Core/RCTWebSocket (from `../node_modules/react-native/`) @@ -435,11 +445,9 @@ SPEC REPOS: - Flipper-RSocket - FlipperKit - fmt - - hermes-engine - libevent - OpenSSL-Universal - SocketRocket - - YogaKit EXTERNAL SOURCES: boost: @@ -452,6 +460,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/React/FBReactNativeSpec" glog: :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" + hermes-engine: + :podspec: "../node_modules/react-native/sdks/hermes/hermes-engine.podspec" RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTRequired: @@ -460,8 +470,12 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/Libraries/TypeSafety" React: :path: "../node_modules/react-native/" + React-bridging: + :path: "../node_modules/react-native/ReactCommon" React-callinvoker: :path: "../node_modules/react-native/ReactCommon/callinvoker" + React-Codegen: + :path: build/generated/ios React-Core: :path: "../node_modules/react-native/" React-CoreModules: @@ -510,10 +524,10 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: a7c83b31436843459a1961bfd74b96033dc77234 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 - DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 - FBLazyVector: f7b0632c6437e312acf6349288d9aa4cb6d59030 - FBReactNativeSpec: 0f4e1f4cfeace095694436e7c7fcc5bf4b03a0ff - Flipper: 1cdd72ffa916071754a41e9684fb9bcaa4b690bf + DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 + FBLazyVector: 6b7f5692909b4300d50e7359cdefbcd09dd30faa + FBReactNativeSpec: affcf71d996f6b0c01f68883482588297b9d5e6e + Flipper: 2134ddbde14751be413e22c2677d743dffaa9945 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 3d3d04a078d4f3a1b6c6916587f159dc11f232c4 Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b @@ -521,42 +535,43 @@ SPEC CHECKSUMS: Flipper-Glog: 7761f5362d23ead28c19afc2dd1d819f00e40df9 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541 - FlipperKit: 87dd306eef9927220672f98266e68bdab6c7cd69 + FlipperKit: 347167ea72c9d718edb15757184384cf31a2cf27 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: 85ecdd10ee8d8ec362ef519a6a45ff9aa27b2e85 - hermes-engine: bf7577d12ac6ccf53ab8b5af3c6ccf0dd8458c5c + glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a + hermes-engine: 51aaceb1f6dc1aed44b8bbf866f52ec146c00a89 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c - RCT-Folly: 803a9cfd78114b2ec0f140cfa6fa2a6bafb2d685 - RCTRequired: 0aa6c1c27e1d65920df35ceea5341a5fe76bdb79 - RCTTypeSafety: d76a59d00632891e11ed7522dba3fd1a995e573a - React: ab8c09da2e7704f4b3ebad4baa6cfdfcc852dcb5 - React-callinvoker: 216fb96b482da516b8aba4142b145938f6ea92f0 - React-Core: af99b93aff83599485e0e0879879aafa35ceae32 - React-CoreModules: 137a054ce8c547e81dc3502933b1bc0fd08df05d - React-cxxreact: ec5ee6b08664f5b8ac71d8ad912f54d540c4f817 - React-hermes: 644e034cf9eb99c2f867c325c589c85b5c918ef7 - React-jsi: 3e084c80fd364cee64668d5df46d40c39f7973e1 - React-jsiexecutor: cbdf37cebdc4f5d8b3d0bf5ccaa6147fd9de9f3d - React-jsinspector: f4775ea9118cbe1f72b834f0f842baa7a99508d8 - React-logger: a1f028f6d8639a3f364ef80419e5e862e1115250 - react-native-flipper: dc5290261fbeeb2faec1bdc57ae6dd8d562e1de4 - React-perflogger: 0afaf2f01a47fd0fc368a93bfbb5bd3b26db6e7f - React-RCTActionSheet: 59f35c4029e0b532fc42114241a06e170b7431a2 - React-RCTAnimation: aae4f4bed122e78bdab72f7118d291d70a932ce2 - React-RCTBlob: f6fb23394b4f28cd86fa7e9f5f6ae45c23669fda - React-RCTImage: 638815cf96124386dd296067246d91441932ae3f - React-RCTLinking: 254dd06283dd6fdb784285f95e7cec8053c3270f - React-RCTNetwork: 8a4c2d4f357268e520b060572d02bc69a9b991fb - React-RCTSettings: 35d44cbb9972ab933bd0a59ea3e6646dcb030ba3 - React-RCTText: cc5315df8458cfa7b537e621271ef43273955a97 - React-RCTVibration: 3b52a7dced19cdb025b4f88ab26ceb2d85f30ba2 - React-runtimeexecutor: a9d3c82ddf7ffdad9fbe6a81c6d6f8c06385464d - ReactCommon: 07d0c460b9ba9af3eaf1b8f5abe7daaad28c9c4e - SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608 - Yoga: d6b6a80659aa3e91aaba01d0012e7edcbedcbecd - YogaKit: f782866e155069a2cca2517aafea43200b01fd5a + RCT-Folly: b9d9fe1fc70114b751c076104e52f3b1b5e5a95a + RCTRequired: 54bff6aa61efd9598ab59d2a823c382b4fe13d27 + RCTTypeSafety: 47632bfa768df7befde08e339a9847e6cff6ff78 + React: 72a676de573cc5ee0e375e5535238af9a4bd435c + React-bridging: 12b6677a30fbd46555a35aa6096331737a9af598 + React-callinvoker: bb574a923c2281d01be23ed3b5d405caa583f56d + React-Codegen: a5e05592b65963a4a453808d2233a04edb7ac8cd + React-Core: 138385d05068622b2b1873eee7dc5be9762f5383 + React-CoreModules: 3a9be624998677db102b19090b1c33c7564ead6d + React-cxxreact: eb24a767b0b811259947f3d538e7c999467e7131 + React-hermes: 3a08a232d6783e21930b0f10f1c15d209ec9f7ad + React-jsi: 9c1cc1173fc8a24b094e01c54d8e3b567fed7edc + React-jsiexecutor: a73bec0218ba959fc92f811b581ad6c2270c6b6f + React-jsinspector: 8134ee22182b8dd98dc0973db6266c398103ce6c + React-logger: 1e7ac909607ee65fd5c4d8bea8c6e644f66b8843 + react-native-flipper: fd2d49f9859ba09714270b0a81a357592f6bd60d + React-perflogger: 8e832d4e21fdfa613033c76d58d7e617341e804b + React-RCTActionSheet: 9ca778182a9523991bff6381045885b6e808bb73 + React-RCTAnimation: 9ced26ad20b96e532ac791a8ab92a7b1ce2266b8 + React-RCTBlob: 2ca3402386d6ab8e9a9a39117305c7601ba2a7f8 + React-RCTImage: 7be51899367082a49e7a7560247ab3961e4dd248 + React-RCTLinking: 262229106f181d8187a5a041fa0dffe6e9726347 + React-RCTNetwork: 428b6f17bf4684ede387422eb789ca89365e33d3 + React-RCTSettings: eaef83489b80045528f1fe1ea5daefaa586ed763 + React-RCTText: d197cff9d5d7f68bdb88468d94617bbf2aa6a48d + React-RCTVibration: 600a9f8b3537db360563d50fab3d040c262567d4 + React-runtimeexecutor: 65cd2782a57e1d59a68aa5d504edf94278578e41 + ReactCommon: 1e783348b9aa73ae68236271df972ba898560a95 + SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d + Yoga: 0b84a956f7393ef1f37f3bb213c516184e4a689d -PODFILE CHECKSUM: 5b1a0db930809bfeb95709d9d50496ad1169cd7f +PODFILE CHECKSUM: 4e948bc99291e6ee9db9ffa2c78a53e28aab3012 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/react-native/ReactNativeFlipperExample/ios/ReactNativeFlipperExample.xcodeproj/project.pbxproj b/react-native/ReactNativeFlipperExample/ios/ReactNativeFlipperExample.xcodeproj/project.pbxproj index b2d31dc03..9cb5a3ed8 100644 --- a/react-native/ReactNativeFlipperExample/ios/ReactNativeFlipperExample.xcodeproj/project.pbxproj +++ b/react-native/ReactNativeFlipperExample/ios/ReactNativeFlipperExample.xcodeproj/project.pbxproj @@ -615,7 +615,7 @@ "$(inherited)", ); INFOPLIST_FILE = ReactNativeFlipperExampleTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_LDFLAGS = ( "-ObjC", @@ -635,7 +635,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; COPY_PHASE_STRIP = NO; INFOPLIST_FILE = ReactNativeFlipperExampleTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_LDFLAGS = ( "-ObjC", @@ -861,6 +861,7 @@ ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; }; name = Debug; @@ -914,6 +915,7 @@ "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = NO; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; diff --git a/react-native/ReactNativeFlipperExample/package.json b/react-native/ReactNativeFlipperExample/package.json index 01264bfe9..56e59e17c 100644 --- a/react-native/ReactNativeFlipperExample/package.json +++ b/react-native/ReactNativeFlipperExample/package.json @@ -11,20 +11,20 @@ "use-local-deps": "relative-deps" }, "dependencies": { - "react": "^17.0.2", - "react-native": "^0.68.0", - "react-native-flipper": "^0.159.0", - "react-native-windows": "0.68.4" + "react": "^18.0.0", + "react-native": "^0.69.7", + "react-native-flipper": "^0.195.0", + "react-native-windows": "^0.69.0" }, "devDependencies": { - "@babel/core": "^7.18.10", - "@babel/runtime": "^7.18.3", - "babel-jest": "^28.1.3", - "jest": "^28.1.3", - "metro-react-native-babel-preset": "^0.72.0", - "react-test-renderer": "18.1.0", - "relative-deps": "^1.0.7", - "metro-config": "^0.66.2" + "@babel/core": "^7.21.3", + "@babel/runtime": "^7.21.0", + "babel-jest": "^29.5.0", + "jest": "^29.5.0", + "metro-config": "^0.76.0", + "metro-react-native-babel-preset": "0.76.4", + "react-test-renderer": "18.2.0", + "relative-deps": "^1.0.7" }, "jest": { "preset": "react-native" diff --git a/react-native/ReactNativeFlipperExample/yarn.lock b/react-native/ReactNativeFlipperExample/yarn.lock index 53e249a9f..eb553b0af 100644 --- a/react-native/ReactNativeFlipperExample/yarn.lock +++ b/react-native/ReactNativeFlipperExample/yarn.lock @@ -2,12 +2,13 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" - integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== +"@ampproject/remapping@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== dependencies: - "@jridgewell/trace-mapping" "^0.3.0" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" "@azure/abort-controller@^1.0.0": version "1.1.0" @@ -59,10 +60,10 @@ dependencies: tslib "^2.2.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: "@babel/highlight" "^7.18.6" @@ -71,87 +72,45 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== -"@babel/compat-data@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" - integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== +"@babel/compat-data@^7.20.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" + integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.17.10": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.10.tgz#74ef0fbf56b7dfc3f198fc2d927f4f03e12f4b05" - integrity sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA== +"@babel/compat-data@^7.20.5": + version "7.20.10" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" + integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.14.0", "@babel/core@^7.20.0", "@babel/core@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" + integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw== dependencies: - "@ampproject/remapping" "^2.1.0" + "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-module-transforms" "^7.18.9" - "@babel/helpers" "^7.18.9" - "@babel/parser" "^7.18.10" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.18.10" - "@babel/types" "^7.18.10" + "@babel/generator" "^7.21.3" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.3" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.3" + "@babel/types" "^7.21.3" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.1" + json5 "^2.2.2" semver "^6.3.0" -"@babel/core@^7.13.16": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.2.tgz#87b2fcd7cce9becaa7f5acebdc4f09f3dd19d876" - integrity sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ== +"@babel/generator@^7.14.0", "@babel/generator@^7.20.0", "@babel/generator@^7.20.5", "@babel/generator@^7.20.7", "@babel/generator@^7.21.3", "@babel/generator@^7.7.2": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" + integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helpers" "^7.18.2" - "@babel/parser" "^7.18.0" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - -"@babel/generator@^7.14.0", "@babel/generator@^7.17.3", "@babel/generator@^7.7.2": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.7.tgz#8da2599beb4a86194a3b24df6c085931d9ee45ad" - integrity sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w== - dependencies: - "@babel/types" "^7.17.0" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.17.10": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.10.tgz#c281fa35b0c349bbe9d02916f4ae08fc85ed7189" - integrity sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg== - dependencies: - "@babel/types" "^7.18.10" + "@babel/types" "^7.21.3" "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/generator@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" - integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== - dependencies: - "@babel/types" "^7.17.0" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" - integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw== - dependencies: - "@babel/types" "^7.18.2" - "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" "@babel/helper-annotate-as-pure@^7.14.5", "@babel/helper-annotate-as-pure@^7.15.4": @@ -176,38 +135,27 @@ "@babel/helper-explode-assignable-expression" "^7.15.4" "@babel/types" "^7.15.4" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" - integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.15.4": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== dependencies: - "@babel/compat-data" "^7.18.8" + "@babel/compat-data" "^7.20.0" "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.20.2" + browserslist "^4.21.3" semver "^6.3.0" -"@babel/helper-compilation-targets@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b" - integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ== +"@babel/helper-compilation-targets@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" + integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.20.2" + "@babel/compat-data" "^7.20.5" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.4.tgz#7f977c17bd12a5fba363cb19bea090394bf37d2e" - integrity sha512-7ZmzFi+DwJx6A7mHRwbuucEYpyBwmh2Ca0RvI6z2+WLZYCqV0JOaLb+u0zbtmDicebgKBZgqbYfLaKNqSgv5Pw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-member-expression-to-functions" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19" @@ -251,7 +199,7 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9": +"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== @@ -286,13 +234,21 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" - integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== +"@babel/helper-function-name@^7.17.9": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== dependencies: - "@babel/template" "^7.18.6" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + +"@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-get-function-arity@^7.15.4": version "7.15.4" @@ -343,33 +299,33 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" - integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== +"@babel/helper-module-transforms@^7.18.0": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" + integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.18.6" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" -"@babel/helper-module-transforms@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd" - integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA== +"@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.0" - "@babel/types" "^7.18.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" "@babel/helper-optimise-call-expression@^7.15.4": version "7.15.4" @@ -400,6 +356,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== +"@babel/helper-plugin-utils@^7.18.6": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + "@babel/helper-remap-async-to-generator@^7.14.5": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.15.4.tgz#2637c0731e4c90fbf58ac58b50b2b5a192fc970f" @@ -439,20 +400,6 @@ "@babel/traverse" "^7.18.2" "@babel/types" "^7.18.2" -"@babel/helper-simple-access@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b" - integrity sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-simple-access@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" - integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-simple-access@^7.18.2": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9" @@ -460,7 +407,14 @@ dependencies: "@babel/types" "^7.18.2" -"@babel/helper-skip-transparent-expression-wrappers@^7.14.5", "@babel/helper-skip-transparent-expression-wrappers@^7.15.4": +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-skip-transparent-expression-wrappers@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.15.4.tgz#707dbdba1f4ad0fa34f9114fc8197aec7d5da2eb" integrity sha512-BMRLsdh+D1/aap19TycS4eD1qELGrCBJwzaY9IE8LrpJtJb+H7rQkPIdsfgnMtLBA6DJls7X9z93Z4U8h7xw0A== @@ -481,28 +435,38 @@ dependencies: "@babel/types" "^7.15.4" -"@babel/helper-split-export-declaration@^7.18.6": +"@babel/helper-split-export-declaration@^7.16.7", "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" - integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== "@babel/helper-validator-identifier@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== +"@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + "@babel/helper-validator-option@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + "@babel/helper-wrap-function@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.15.4.tgz#6f754b2446cfaf3d612523e6ab8d79c27c3a3de7" @@ -523,52 +487,43 @@ "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helpers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" - integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== dependencies: - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" -"@babel/helpers@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384" - integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" - -"@babel/highlight@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" - integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== dependencies: "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.3": - version "7.17.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.8.tgz#2817fb9d885dd8132ea0f8eb615a6388cca1c240" - integrity sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" + integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== -"@babel/parser@^7.13.16", "@babel/parser@^7.18.0": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef" - integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow== +"@babel/parser@^7.20.0", "@babel/parser@^7.20.13": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.13.tgz#ddf1eb5a813588d2fb1692b70c6fce75b945c088" + integrity sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw== -"@babel/parser@^7.17.10": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78" - integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ== +"@babel/parser@^7.20.5", "@babel/parser@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" + integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== -"@babel/parser@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" - integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== +"@babel/parser@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" + integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== "@babel/plugin-proposal-async-generator-functions@^7.0.0": version "7.16.8" @@ -579,15 +534,7 @@ "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e" - integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-class-properties@^7.13.0": +"@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.13.0": version "7.17.12" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4" integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw== @@ -603,15 +550,7 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-export-default-from" "^7.14.5" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz#ee38589ce00e2cc59b299ec3ea406fcd3a0fdaf6" - integrity sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": version "7.17.12" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be" integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag== @@ -619,6 +558,14 @@ "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" +"@babel/plugin-proposal-numeric-separator@^7.0.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread@^7.0.0": version "7.15.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.15.6.tgz#ef68050c8703d07b25af402cb96cf7f34a68ed11" @@ -638,16 +585,7 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603" - integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.13.12": +"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.13.12": version "7.17.12" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174" integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ== @@ -691,19 +629,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.14.5", "@babel/plugin-syntax-flow@^7.2.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.14.5.tgz#2ff654999497d7d7d142493260005263731da180" - integrity sha512-9WK5ZwKCdWHxVuU13XNT6X73FGmutAXeor5lGFq6qhOFtMFUF4jkbijuyUdZZlpYq6E2hZeZf/u3959X9wsv0Q== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.12.1", "@babel/plugin-syntax-flow@^7.17.12", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.2.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" + integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-flow@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.17.12.tgz#23d852902acd19f42923fca9d0f196984d124e73" - integrity sha512-B8QIgBvkIG6G2jgsOHQUist7Sm0EBLDCx8sen072IwqNuzMegZNXrYnSv77cYzA8mLDZAfQYqsLIhimiP1s2HQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -726,6 +657,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -740,7 +678,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -775,13 +713,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.14.5", "@babel/plugin-syntax-typescript@^7.7.2": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" - integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-typescript@^7.17.12": version "7.17.12" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b" @@ -789,6 +720,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.17.12" +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" + integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" @@ -854,15 +792,7 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.14.5.tgz#0dc9c1d11dcdc873417903d6df4bed019ef0f85e" - integrity sha512-KhcolBKfXbvjwI3TV7r7TkYm8oNXHNBqGOy6JDVwtecFaRoKYsUUqJdS10q0YDKW1c6aZQgO+Ys3LfGkox8pXA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-flow" "^7.14.5" - -"@babel/plugin-transform-flow-strip-types@^7.17.12": +"@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.17.12": version "7.17.12" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.17.12.tgz#5e070f99a4152194bd9275de140e83a92966cab3" integrity sha512-g8cSNt+cHCpG/uunPQELdq/TeV3eg1OLJYwxypwHtAWo9+nErH3lQx9CSO2uI9lF74A0mR0t4KoMjs1snSgnTw== @@ -899,17 +829,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.4.tgz#8201101240eabb5a76c08ef61b2954f767b6b4c1" - integrity sha512-qg4DPhwG8hKp4BbVDvX1s8cohM8a6Bvptu4l6Iingq5rW+yRUAhe/YRup/YcW2zCOlrysEWVhftIcKzrEZv3sA== - dependencies: - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-simple-access" "^7.15.4" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-commonjs@^7.13.8": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e" integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ== @@ -926,13 +846,6 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" -"@babel/plugin-transform-object-assign@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.14.5.tgz#62537d54b6d85de04f4df48bfdba2eebff17b760" - integrity sha512-lvhjk4UN9xJJYB1mI5KC0/o1D5EcJXdbhVe+4fSk08D6ZN+iuAIs7LJC+71h8av9Ew4+uRq9452v9R93SFmQlQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-object-super@^7.0.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45" @@ -987,13 +900,6 @@ "@babel/plugin-syntax-jsx" "^7.14.5" "@babel/types" "^7.14.9" -"@babel/plugin-transform-regenerator@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f" - integrity sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg== - dependencies: - regenerator-transform "^0.14.2" - "@babel/plugin-transform-runtime@^7.0.0": version "7.15.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.15.8.tgz#9d15b1e94e1c7f6344f65a8d573597d93c6cd886" @@ -1035,7 +941,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-typescript@^7.17.12": +"@babel/plugin-transform-typescript@^7.17.12", "@babel/plugin-transform-typescript@^7.5.0": version "7.18.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.4.tgz#587eaf6a39edb8c06215e550dc939faeadd750bf" integrity sha512-l4vHuSLUajptpHNEOUDEGsnpl9pfRLsN1XUoDQDD/YBuXTM+v37SHGS+c6n4jdcZy96QtuUuSvZYMLSSsjH8Mw== @@ -1044,15 +950,6 @@ "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-typescript" "^7.17.12" -"@babel/plugin-transform-typescript@^7.5.0": - version "7.15.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.15.8.tgz#ff0e6a47de9b2d58652123ab5a879b2ff20665d8" - integrity sha512-ZXIkJpbaf6/EsmjeTbiJN/yMxWPFWvlr7sEG1P95Xb4S4IBcrf2n7s/fItIhsAmOf8oSh3VJPDppO6ExfAfKRQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-typescript" "^7.14.5" - "@babel/plugin-transform-unicode-regex@^7.0.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz#4cd09b6c8425dd81255c7ceb3fb1836e7414382e" @@ -1079,17 +976,6 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.17.12" -"@babel/register@^7.0.0": - version "7.15.3" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.15.3.tgz#6b40a549e06ec06c885b2ec42c3dd711f55fe752" - integrity sha512-mj4IY1ZJkorClxKTImccn4T81+UKTo4Ux0+OFSV9hME1ooqS9UV+pJ6BjD0qXPK4T3XW/KNa79XByjeEMZz+fw== - dependencies: - clone-deep "^4.0.1" - find-cache-dir "^2.0.0" - make-dir "^2.1.0" - pirates "^4.0.0" - source-map-support "^0.5.16" - "@babel/register@^7.13.16": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.17.7.tgz#5eef3e0f4afc07e25e847720e7b987ae33f08d0b" @@ -1101,123 +987,95 @@ pirates "^4.0.5" source-map-support "^0.5.16" -"@babel/runtime@^7.0.0": - version "7.18.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" - integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== dependencies: - regenerator-runtime "^0.13.4" + regenerator-runtime "^0.13.11" -"@babel/runtime@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" - integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.8.4": - version "7.17.8" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2" - integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.0.0", "@babel/template@^7.15.4", "@babel/template@^7.16.7", "@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== +"@babel/template@^7.0.0", "@babel/template@^7.15.4", "@babel/template@^7.16.7", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" -"@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.10.tgz#37ad97d1cb00efa869b91dd5d1950f8a6cf0cb08" - integrity sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g== +"@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.2", "@babel/traverse@^7.20.1", "@babel/traverse@^7.7.2": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133" + integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" + "@babel/generator" "^7.20.5" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" + "@babel/parser" "^7.20.5" + "@babel/types" "^7.20.5" debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.17.10": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.10.tgz#1ee1a5ac39f4eac844e6cf855b35520e5eb6f8b5" - integrity sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw== +"@babel/traverse@^7.20.0": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" + integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.10" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.10" - "@babel/types" "^7.17.10" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.13" + "@babel/types" "^7.20.7" debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" - integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== +"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67" + integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ== dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.9" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.9" - "@babel/types" "^7.17.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.21.3" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.3" + "@babel/types" "^7.21.3" debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.2.tgz#b77a52604b5cc836a9e1e08dca01cba67a12d2e8" - integrity sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA== +"@babel/types@^7.0.0", "@babel/types@^7.14.9", "@babel/types@^7.15.4", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.2", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" + integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.18.0" - "@babel/types" "^7.18.2" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.14.9", "@babel/types@^7.15.4", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@babel/types@^7.16.0", "@babel/types@^7.18.0", "@babel/types@^7.18.2": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354" - integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw== +"@babel/types@^7.20.0", "@babel/types@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" + integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@babel/types@^7.17.10": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.10.tgz#d35d7b4467e439fcf06d195f8100e0fea7fc82c4" - integrity sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A== +"@babel/types@^7.20.5", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" + integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== dependencies: - "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1225,14 +1083,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cnakazawa/watch@^1.0.3": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" - integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== - dependencies: - exec-sh "^0.3.2" - minimist "^1.2.0" - "@hapi/hoek@^9.0.0": version "9.2.1" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" @@ -1261,50 +1111,49 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" - integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== +"@jest/console@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" + integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^28.1.3" - jest-util "^28.1.3" + jest-message-util "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" -"@jest/core@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" - integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== +"@jest/core@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" + integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== dependencies: - "@jest/console" "^28.1.3" - "@jest/reporters" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/console" "^29.5.0" + "@jest/reporters" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^28.1.3" - jest-config "^28.1.3" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-resolve-dependencies "^28.1.3" - jest-runner "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - jest-watcher "^28.1.3" + jest-changed-files "^29.5.0" + jest-config "^29.5.0" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-resolve-dependencies "^29.5.0" + jest-runner "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + jest-watcher "^29.5.0" micromatch "^4.0.4" - pretty-format "^28.1.3" - rimraf "^3.0.0" + pretty-format "^29.5.0" slash "^3.0.0" strip-ansi "^6.0.0" @@ -1315,63 +1164,64 @@ dependencies: "@jest/types" "^27.2.4" -"@jest/environment@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" - integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== +"@jest/environment@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" + integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== dependencies: - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^28.1.3" + jest-mock "^29.5.0" -"@jest/expect-utils@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" - integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== dependencies: - jest-get-type "^28.0.2" + jest-get-type "^29.4.3" -"@jest/expect@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" - integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== +"@jest/expect@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" + integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== dependencies: - expect "^28.1.3" - jest-snapshot "^28.1.3" + expect "^29.5.0" + jest-snapshot "^29.5.0" -"@jest/fake-timers@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" - integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== +"@jest/fake-timers@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" + integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== dependencies: - "@jest/types" "^28.1.3" - "@sinonjs/fake-timers" "^9.1.2" + "@jest/types" "^29.5.0" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-util "^28.1.3" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-util "^29.5.0" -"@jest/globals@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" - integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== +"@jest/globals@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" + integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/types" "^29.5.0" + jest-mock "^29.5.0" -"@jest/reporters@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" - integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== +"@jest/reporters@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" + integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" + "@jest/console" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -1383,71 +1233,70 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - jest-worker "^28.1.3" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + jest-worker "^29.5.0" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" - terminal-link "^2.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" - integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== dependencies: - "@sinclair/typebox" "^0.24.1" + "@sinclair/typebox" "^0.25.16" -"@jest/source-map@^28.1.2": - version "28.1.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" - integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== +"@jest/source-map@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" + integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== dependencies: - "@jridgewell/trace-mapping" "^0.3.13" + "@jridgewell/trace-mapping" "^0.3.15" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" - integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== +"@jest/test-result@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" + integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== dependencies: - "@jest/console" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/console" "^29.5.0" + "@jest/types" "^29.5.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" - integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== +"@jest/test-sequencer@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" + integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== dependencies: - "@jest/test-result" "^28.1.3" + "@jest/test-result" "^29.5.0" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" + jest-haste-map "^29.5.0" slash "^3.0.0" -"@jest/transform@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" - integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== +"@jest/transform@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" + integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" + jest-haste-map "^29.5.0" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - write-file-atomic "^4.0.1" + write-file-atomic "^4.0.2" "@jest/types@^26.6.2": version "26.6.2" @@ -1460,18 +1309,7 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@jest/types@^27.2.4": - version "27.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.0.tgz#6ad04a5c5355fd9f46e5cf761850e0edb3c209dd" - integrity sha512-oDHEp7gwSgA82RZ6pzUL3ugM2njP/lVB1MsxRZNOBk+CoNvh9SpH1lQixPFc/kDlV50v59csiW4HLixWmhmgPQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - -"@jest/types@^27.5.1": +"@jest/types@^27.2.4", "@jest/types@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== @@ -1482,19 +1320,27 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.0.tgz#508327a89976cbf9bd3e1cc74641a29fd7dfd519" - integrity sha512-xmEggMPr317MIOjjDoZ4ejCSr9Lpbt/u34+dvc99t7DS8YirW5rwZEhzKPC2BMUFkUhI48qs6qLUSGw5FuL0GA== +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== dependencies: - "@jest/schemas" "^28.1.3" + "@jest/schemas" "^29.4.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.3.2": +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== @@ -1503,39 +1349,40 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" - integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== "@jridgewell/resolve-uri@^3.0.3": version "3.0.4" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.4.tgz#b876e3feefb9c8d3aa84014da28b5e52a0640d72" integrity sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg== -"@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.10" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.10.tgz#baf57b4e2a690d4f38560171f91783656b7f8186" integrity sha512-Ht8wIW5v165atIX1p+JvKR5ONzUyF4Ac8DZIQ5kZs9zrb6M8SJNXpx1zn04rn65VjBMygRoMXcyYwNK0fT7bEg== -"@jridgewell/trace-mapping@^0.3.0": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.2.tgz#e051581782a770c30ba219634f2019241c5d3cde" - integrity sha512-9KzzH4kMjA2XmBRHfqG2/Vtl7s92l6uNDd0wW7frDE+EUvQFGqNXhWp0UGJjSkt3v2AYjzOZn1QO9XaTNJIt1Q== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/trace-mapping@^0.3.12": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15": version "0.3.15" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== @@ -1543,21 +1390,13 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.13": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" - integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" - integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" "@jridgewell/trace-mapping@^0.3.9": version "0.3.13" @@ -1575,11 +1414,32 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + "@nodelib/fs.stat@^1.1.2": version "1.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@opentelemetry/api@^1.0.1", "@opentelemetry/api@^1.0.4": version "1.1.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a" @@ -1614,30 +1474,73 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.0.tgz#0d740709fd65845c9cab215d5581e9fa80c59520" integrity sha512-7lmGpLL/7EHQcLVBxxOesgQQS7JSxzF/Xqx7VNMxAQbo14dzJEX6Ks0hb4LHqEMpCrKpErWXi4JxYCGrRJgx9A== -"@react-native-community/cli-debugger-ui@^7.0.3": - version "7.0.3" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-7.0.3.tgz#3eeeacc5a43513cbcae56e5e965d77726361bcb4" - integrity sha512-G4SA6jFI0j22o+j+kYP8/7sxzbCDqSp2QiHA/X5E0lsGEd2o9qN2zbIjiFr8b8k+VVAYSUONhoC0+uKuINvmkA== +"@react-native-community/cli-clean@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-8.0.4.tgz#97e16a20e207b95de12e29b03816e8f2b2c80cc7" + integrity sha512-IwS1M1NHg6+qL8PThZYMSIMYbZ6Zbx+lIck9PLBskbosFo24M3lCOflOl++Bggjakp6mR+sRXxLMexid/GeOsQ== + dependencies: + "@react-native-community/cli-tools" "^8.0.4" + chalk "^4.1.2" + execa "^1.0.0" + prompts "^2.4.0" + +"@react-native-community/cli-config@^8.0.6": + version "8.0.6" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-8.0.6.tgz#041eee7dd8fdef595bf7a3f24228c173bf294a44" + integrity sha512-mjVpVvdh8AviiO8xtqeX+BkjqE//NMDnISwsLWSJUfNCwTAPmdR8PGbhgP5O4hWHyJ3WkepTopl0ya7Tfi3ifw== + dependencies: + "@react-native-community/cli-tools" "^8.0.4" + cosmiconfig "^5.1.0" + deepmerge "^3.2.0" + glob "^7.1.3" + joi "^17.2.1" + +"@react-native-community/cli-debugger-ui@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-8.0.0.tgz#98263dc525e65015e2d6392c940114028f87e8e9" + integrity sha512-u2jq06GZwZ9sRERzd9FIgpW6yv4YOW4zz7Ym/B8eSzviLmy3yI/8mxJtvlGW+J8lBsfMcQoqJpqI6Rl1nZy9yQ== dependencies: serve-static "^1.13.1" -"@react-native-community/cli-hermes@^6.3.0": - version "6.3.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-6.3.0.tgz#92b2f07d08626a60f6893c3e3d57c1538c8fb5a7" - integrity sha512-Uhbm9bubyZLZ12vFCIfWbE/Qi3SBTbYIN/TC08EudTLhv/KbPomCQnmFsnJ7AXQFuOZJs73mBxoEAYSbRbwyVA== +"@react-native-community/cli-doctor@^8.0.6": + version "8.0.6" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-8.0.6.tgz#954250155ab2f3a66a54821e071bc4a631d2dfff" + integrity sha512-ZQqyT9mJMVeFEVIwj8rbDYGCA2xXjJfsQjWk2iTRZ1CFHfhPSUuUiG8r6mJmTinAP9t+wYcbbIYzNgdSUKnDMw== dependencies: - "@react-native-community/cli-platform-android" "^6.3.0" - "@react-native-community/cli-tools" "^6.2.0" + "@react-native-community/cli-config" "^8.0.6" + "@react-native-community/cli-platform-ios" "^8.0.6" + "@react-native-community/cli-tools" "^8.0.4" + chalk "^4.1.2" + command-exists "^1.2.8" + envinfo "^7.7.2" + execa "^1.0.0" + hermes-profile-transformer "^0.0.6" + ip "^1.1.5" + node-stream-zip "^1.9.1" + ora "^5.4.1" + prompts "^2.4.0" + semver "^6.3.0" + strip-ansi "^5.2.0" + sudo-prompt "^9.0.0" + wcwidth "^1.0.1" + +"@react-native-community/cli-hermes@^8.0.5": + version "8.0.5" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-8.0.5.tgz#639edc6b0ce73f705e4b737e3de1cc47d42516ff" + integrity sha512-Zm0wM6SfgYAEX1kfJ1QBvTayabvh79GzmjHyuSnEROVNPbl4PeCG4WFbwy489tGwOP9Qx9fMT5tRIFCD8bp6/g== + dependencies: + "@react-native-community/cli-platform-android" "^8.0.5" + "@react-native-community/cli-tools" "^8.0.4" chalk "^4.1.2" hermes-profile-transformer "^0.0.6" ip "^1.1.5" -"@react-native-community/cli-platform-android@^6.3.0": - version "6.3.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-6.3.0.tgz#ab7d156bd69a392493323eeaba839a874c0e201f" - integrity sha512-d5ufyYcvrZoHznYm5bjBXaiHIJv552t5gYtQpnUsxBhHSQ8QlaNmlLUyeSPRDfOw4ND9b0tPHqs4ufwx6vp/fQ== +"@react-native-community/cli-platform-android@^8.0.0", "@react-native-community/cli-platform-android@^8.0.4", "@react-native-community/cli-platform-android@^8.0.5": + version "8.0.5" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-8.0.5.tgz#da11d2678adeca98e83494d68de80e50571b4af4" + integrity sha512-z1YNE4T1lG5o9acoQR1GBvf7mq6Tzayqo/za5sHVSOJAC9SZOuVN/gg/nkBa9a8n5U7qOMFXfwhTMNqA474gXA== dependencies: - "@react-native-community/cli-tools" "^6.2.0" + "@react-native-community/cli-tools" "^8.0.4" chalk "^4.1.2" execa "^1.0.0" fs-extra "^8.1.0" @@ -1646,30 +1549,13 @@ lodash "^4.17.15" logkitty "^0.7.1" slash "^3.0.0" - xmldoc "^1.1.2" -"@react-native-community/cli-platform-android@^7.0.1": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-7.0.1.tgz#d165897edf401f9bceff1f361ef446528133cb52" - integrity sha512-nOr0aMkxAymCnbtsQwXBlyoRN2Y+IzC7Qz5T+/zyWwEbTY8SKQI8uV+8+qttUvzSvuXa2PeXsTWluuliOS8KCw== +"@react-native-community/cli-platform-ios@^8.0.0", "@react-native-community/cli-platform-ios@^8.0.4", "@react-native-community/cli-platform-ios@^8.0.6": + version "8.0.6" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-8.0.6.tgz#ab80cd4eb3014b8fcfc9bd1b53ec0a9f8e5d1430" + integrity sha512-CMR6mu/LVx6JVfQRDL9uULsMirJT633bODn+IrYmrwSz250pnhON16We8eLPzxOZHyDjm7JPuSgHG3a/BPiRuQ== dependencies: - "@react-native-community/cli-tools" "^7.0.1" - chalk "^4.1.2" - execa "^1.0.0" - fs-extra "^8.1.0" - glob "^7.1.3" - jetifier "^1.6.2" - lodash "^4.17.15" - logkitty "^0.7.1" - slash "^3.0.0" - xmldoc "^1.1.2" - -"@react-native-community/cli-platform-ios@^7.0.1": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-7.0.1.tgz#1c27af85229246b7a528e97f093e38859896cc93" - integrity sha512-PLRIbzrCzSedmpjuFtQqcqUD45G8q7sEciI1lf5zUbVMXqjIBwJWS7iz8235PyWwj8J4MNHohLC+oyRueFtbGg== - dependencies: - "@react-native-community/cli-tools" "^7.0.1" + "@react-native-community/cli-tools" "^8.0.4" chalk "^4.1.2" execa "^1.0.0" glob "^7.1.3" @@ -1677,60 +1563,46 @@ lodash "^4.17.15" ora "^5.4.1" plist "^3.0.2" - xcode "^3.0.0" -"@react-native-community/cli-plugin-metro@^7.0.3": - version "7.0.3" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-7.0.3.tgz#b381ed2f68a0b126e4fa238f1956a44846e1ef8a" - integrity sha512-HJrEkFbxv9DNixsGwO+Q0zCcZMghDltyzeB9yQ//D5ZR4ZUEuAIPrRDdEp9xVw0WkBxAIZs6KXLux2/yPMwLhA== +"@react-native-community/cli-plugin-metro@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-8.0.4.tgz#a364a50a2e05fc5d0b548759e499e5b681b6e4cc" + integrity sha512-UWzY1eMcEr/6262R2+d0Is5M3L/7Y/xXSDIFMoc5Rv5Wucl3hJM/TxHXmByvHpuJf6fJAfqOskyt4bZCvbI+wQ== dependencies: - "@react-native-community/cli-server-api" "^7.0.3" - "@react-native-community/cli-tools" "^6.2.0" + "@react-native-community/cli-server-api" "^8.0.4" + "@react-native-community/cli-tools" "^8.0.4" chalk "^4.1.2" - metro "^0.67.0" - metro-config "^0.67.0" - metro-core "^0.67.0" - metro-react-native-babel-transformer "^0.67.0" - metro-resolver "^0.67.0" - metro-runtime "^0.67.0" + metro "^0.70.1" + metro-config "^0.70.1" + metro-core "^0.70.1" + metro-react-native-babel-transformer "^0.70.1" + metro-resolver "^0.70.1" + metro-runtime "^0.70.1" readline "^1.3.0" -"@react-native-community/cli-server-api@^7.0.3": - version "7.0.3" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-7.0.3.tgz#ba9695a2fdfef22750d141153efd94baf641129b" - integrity sha512-JDrLsrkBgNxbG2u3fouoVGL9tKrXUrTsaNwr+oCV+3XyMwbVe42r/OaQ681/iW/7mHXjuVkDnMcp7BMg7e2yJg== +"@react-native-community/cli-server-api@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-8.0.4.tgz#d45d895a0a6e8b960c9d677188d414a996faa4d3" + integrity sha512-Orr14njx1E70CVrUA8bFdl+mrnbuXUjf1Rhhm0RxUadFpvkHuOi5dh8Bryj2MKtf8eZrpEwZ7tuQPhJEULW16A== dependencies: - "@react-native-community/cli-debugger-ui" "^7.0.3" - "@react-native-community/cli-tools" "^6.2.0" + "@react-native-community/cli-debugger-ui" "^8.0.0" + "@react-native-community/cli-tools" "^8.0.4" compression "^1.7.1" connect "^3.6.5" errorhandler "^1.5.0" - nocache "^2.1.0" + nocache "^3.0.1" pretty-format "^26.6.2" serve-static "^1.13.1" ws "^7.5.1" -"@react-native-community/cli-tools@^6.2.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-6.2.0.tgz#8f4adc2d83ab96e5654348533c8fa602742c4fce" - integrity sha512-08ssz4GMEnRxC/1FgTTN/Ud7mExQi5xMphItPjfHiTxpZPhrFn+IMx6mya0ncFEhhxQ207wYlJMRLPRRdBZ8oA== - dependencies: - appdirsjs "^1.2.4" - chalk "^4.1.2" - lodash "^4.17.15" - mime "^2.4.1" - node-fetch "^2.6.0" - open "^6.2.0" - semver "^6.3.0" - shell-quote "1.6.1" - -"@react-native-community/cli-tools@^7.0.1": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-7.0.1.tgz#73790d6ca2825e42a70a770c1b403a6777e690d6" - integrity sha512-0xra4hKNA5PR2zYVXsDMNiXMGaDNoNRYMY6eTP2aVIxQbqIcVMDWSyCA8wMWX5iOpMWg0cZGaQ6a77f3Rlb34g== +"@react-native-community/cli-tools@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-8.0.4.tgz#994b9d56c84472491c876b71acd4356773fcbe65" + integrity sha512-ePN9lGxh6LRFiotyddEkSmuqpQhnq2iw9oiXYr4EFWpIEy0yCigTuSTiDF68+c8M9B+7bTwkRpz/rMPC4ViO5Q== dependencies: appdirsjs "^1.2.4" chalk "^4.1.2" + find-up "^5.0.0" lodash "^4.17.15" mime "^2.4.1" node-fetch "^2.6.0" @@ -1739,58 +1611,49 @@ semver "^6.3.0" shell-quote "^1.7.3" -"@react-native-community/cli-types@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-6.0.0.tgz#90269fbdc7229d5e3b8f2f3e029a94083551040d" - integrity sha512-K493Fk2DMJC0ZM8s8gnfseKxGasIhuDaCUDeLZcoCSFlrjKEuEs1BKKEJiev0CARhKEXKOyyp/uqYM9nWhisNw== +"@react-native-community/cli-types@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-8.0.0.tgz#72d24178e5ed1c2d271da43e0a4a4f59178f261a" + integrity sha512-1lZS1PEvMlFaN3Se1ksyoFWzMjk+YfKi490GgsqKJln9gvFm8tqVPdnXttI5Uf2DQf3BMse8Bk8dNH4oV6Ewow== dependencies: - ora "^3.4.0" + joi "^17.2.1" -"@react-native-community/cli@^7.0.3": - version "7.0.3" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-7.0.3.tgz#1addb462d71786fcbbd266fbceb41819b8cf7839" - integrity sha512-WyJOA829KAhU1pw2MDQt0YhOS9kyR2KqyqgJyTuQhzFVCBPX4F5aDEkZYYn4jdldaDHCPrLJ3ho3gxYTXy+x7w== +"@react-native-community/cli@^8.0.0", "@react-native-community/cli@^8.0.4": + version "8.0.6" + resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-8.0.6.tgz#7aae37843ab8e44b75c477c1de69f4c902e599ef" + integrity sha512-E36hU/if3quQCfJHGWVkpsCnwtByRCwORuAX0r6yr1ebKktpKeEO49zY9PAu/Z1gfyxCtgluXY0HfRxjKRFXTg== dependencies: - "@react-native-community/cli-debugger-ui" "^7.0.3" - "@react-native-community/cli-hermes" "^6.3.0" - "@react-native-community/cli-plugin-metro" "^7.0.3" - "@react-native-community/cli-server-api" "^7.0.3" - "@react-native-community/cli-tools" "^6.2.0" - "@react-native-community/cli-types" "^6.0.0" - appdirsjs "^1.2.4" + "@react-native-community/cli-clean" "^8.0.4" + "@react-native-community/cli-config" "^8.0.6" + "@react-native-community/cli-debugger-ui" "^8.0.0" + "@react-native-community/cli-doctor" "^8.0.6" + "@react-native-community/cli-hermes" "^8.0.5" + "@react-native-community/cli-plugin-metro" "^8.0.4" + "@react-native-community/cli-server-api" "^8.0.4" + "@react-native-community/cli-tools" "^8.0.4" + "@react-native-community/cli-types" "^8.0.0" chalk "^4.1.2" - command-exists "^1.2.8" commander "^2.19.0" - cosmiconfig "^5.1.0" - deepmerge "^3.2.0" - envinfo "^7.7.2" execa "^1.0.0" find-up "^4.1.0" fs-extra "^8.1.0" - glob "^7.1.3" graceful-fs "^4.1.3" - joi "^17.2.1" leven "^3.1.0" lodash "^4.17.15" minimist "^1.2.0" - node-stream-zip "^1.9.1" - ora "^3.4.0" - pretty-format "^26.6.2" prompts "^2.4.0" semver "^6.3.0" - serve-static "^1.13.1" - strip-ansi "^5.2.0" - sudo-prompt "^9.0.0" - wcwidth "^1.0.1" -"@react-native-windows/cli@0.68.2": - version "0.68.2" - resolved "https://registry.yarnpkg.com/@react-native-windows/cli/-/cli-0.68.2.tgz#6c47f9e5c43a4132892a9bf1df5f58ead09b851e" - integrity sha512-hbWUy4EiWapkLfP5rouMb7yRiVvltOAJex1WlwfEfbVLWPg0WAf1mtMfc10VViorFk6xNL9JjVPzbaUO4tO58A== +"@react-native-windows/cli@0.69.3": + version "0.69.3" + resolved "https://registry.yarnpkg.com/@react-native-windows/cli/-/cli-0.69.3.tgz#fc2c649985c174375efdf1a2f4f39feff4d23aed" + integrity sha512-UF8skMU/6sf2Hy1GV795SL7yYeRD87d2oTtKc4y4n2lYSppCStGuOna0AINL7I+PJQLCz8/35IXJ7kS8iwn/gw== dependencies: - "@react-native-windows/fs" "0.68.0" - "@react-native-windows/package-utils" "0.68.0" - "@react-native-windows/telemetry" "0.68.2" + "@react-native-windows/fs" "0.69.1" + "@react-native-windows/package-utils" "0.69.1" + "@react-native-windows/telemetry" "0.69.2" + "@typescript-eslint/eslint-plugin" "^5.20.0" + "@typescript-eslint/parser" "^5.20.0" "@xmldom/xmldom" "^0.7.5" chalk "^4.1.0" cli-spinners "^2.2.0" @@ -1809,49 +1672,57 @@ xml-parser "^1.2.1" xpath "^0.0.27" -"@react-native-windows/find-repo-root@0.68.0": - version "0.68.0" - resolved "https://registry.yarnpkg.com/@react-native-windows/find-repo-root/-/find-repo-root-0.68.0.tgz#efbc5af3a4952a7e7712f8d6bd73b8de858f3d7d" - integrity sha512-YuSUlrRdr6SNPapf4b77yi3U/rfTeYhetBpSwm1L/jq3UT8Wm4Z/JSBZg2UioYIeQV6xrNRjlLQHKjDBzmxOkQ== +"@react-native-windows/find-repo-root@0.69.1": + version "0.69.1" + resolved "https://registry.yarnpkg.com/@react-native-windows/find-repo-root/-/find-repo-root-0.69.1.tgz#2da73276bc7b88db152f84ed75a29db3e0884cf1" + integrity sha512-HIeCVUDYdwYB7t0MjfhDeR/48OX6M8lAjOwgz+ACrpaiEHnnGBxiAOjgB2ZcAf1dSuCPSeVJgn7Plq1Fem2kNw== dependencies: + "@react-native-windows/fs" "0.69.1" + "@typescript-eslint/eslint-plugin" "^5.20.0" + "@typescript-eslint/parser" "^5.20.0" find-up "^4.1.0" -"@react-native-windows/fs@0.68.0": - version "0.68.0" - resolved "https://registry.yarnpkg.com/@react-native-windows/fs/-/fs-0.68.0.tgz#bdf722b9c7475e66de9a580b37cdb7136af797a4" - integrity sha512-2ZnL0NVs6zJQgXypyX1Arh7Xyo8mG1yKw1ephbOX5U6J7HLlnsx3X1sANGIz+JcAjA9jEJ08r9DHbH93NvzFWA== +"@react-native-windows/fs@0.69.1": + version "0.69.1" + resolved "https://registry.yarnpkg.com/@react-native-windows/fs/-/fs-0.69.1.tgz#22ab85f042dc449d0016fd31a0f3691c1ce9e59a" + integrity sha512-Brw5BhYFHq4JFZ7wWcXTaKSbZxr+0Aqf9ff8lbJv/XdMSPihI8mHvgRKhXZH7r4K/jNWFFMUiaQv2YUSBSL7rg== dependencies: + "@typescript-eslint/eslint-plugin" "^5.20.0" + "@typescript-eslint/parser" "^5.20.0" graceful-fs "^4.2.8" -"@react-native-windows/package-utils@0.68.0": - version "0.68.0" - resolved "https://registry.yarnpkg.com/@react-native-windows/package-utils/-/package-utils-0.68.0.tgz#072b838fd106aedaff746ae6fab5173b6c61880a" - integrity sha512-IgcZib3Ydhosk8HoN/9l/im/EzeualqQPku/aRUyLehKjYEi7YksUDEM3un9bWyAwCeT9gpIjbEnEFwODQdvZg== +"@react-native-windows/package-utils@0.69.1": + version "0.69.1" + resolved "https://registry.yarnpkg.com/@react-native-windows/package-utils/-/package-utils-0.69.1.tgz#2e1ebc2e4a28a447fe7b32b91b15545c14b88f48" + integrity sha512-o6O7bOR0BWQZxvMGlbYnAXsn/HtKK+2/LkrF0EJMyi4GA8lLni1Z1Wz29Gsv2bk/i/RAVGHv2M/KON+2W3MISw== dependencies: - "@react-native-windows/find-repo-root" "0.68.0" - "@react-native-windows/fs" "0.68.0" + "@react-native-windows/find-repo-root" "0.69.1" + "@react-native-windows/fs" "0.69.1" + "@typescript-eslint/eslint-plugin" "^5.20.0" + "@typescript-eslint/parser" "^5.20.0" get-monorepo-packages "^1.2.0" lodash "^4.17.15" -"@react-native-windows/telemetry@0.68.2": - version "0.68.2" - resolved "https://registry.yarnpkg.com/@react-native-windows/telemetry/-/telemetry-0.68.2.tgz#c693f66e9e4032cce6256ef777c0033b7e5fb262" - integrity sha512-tkd7eJM3Jhs6aenL0Iz8msRbK22GGgYvaSViCLBkenlwOdF0AE/r0+NAGGXzOxHz89GIDYUhj5rtpln83NtWXA== +"@react-native-windows/telemetry@0.69.2": + version "0.69.2" + resolved "https://registry.yarnpkg.com/@react-native-windows/telemetry/-/telemetry-0.69.2.tgz#6eb76882381ced4c971fb1eff229b868ef35029d" + integrity sha512-U7ps/71vtzLcTDKHW4pYwbJK4nV01VqPErcx053N6oUZvv26HEmuw2/iH8D9ENaVOsdSwvHZJ/kq4+Ht2TD2FQ== dependencies: - "@react-native-windows/fs" "0.68.0" + "@react-native-windows/fs" "0.69.1" + "@typescript-eslint/eslint-plugin" "^5.20.0" + "@typescript-eslint/parser" "^5.20.0" "@xmldom/xmldom" "^0.7.5" applicationinsights "^2.3.1" ci-info "^3.2.0" envinfo "^7.8.1" lodash "^4.17.21" - node-machine-id "^1.1.12" os-locale "^5.0.0" xpath "^0.0.27" -"@react-native-windows/virtualized-list@0.68.0": - version "0.68.0" - resolved "https://registry.yarnpkg.com/@react-native-windows/virtualized-list/-/virtualized-list-0.68.0.tgz#06b63d21bad896c2bd37d6b5544f90f0f4fa838b" - integrity sha512-XKqwn7FWDUVanr42sNT+547je2YwCq38I3YR+iq1WcmH5TiQ5E+KF5jXvPv7oOFuWJzzXzjiRZUfEwMv2qqoMg== +"@react-native-windows/virtualized-list@0.69.1": + version "0.69.1" + resolved "https://registry.yarnpkg.com/@react-native-windows/virtualized-list/-/virtualized-list-0.69.1.tgz#5789c97b344abda4c42ea07ed337208a2ca44f12" + integrity sha512-Qz9ZlLbgnEYpOb7815x37czJOqpvOsZzkIZPuR28bVhjSJ1Xc+DEdIkObKbvJ4aIBHIcZsxB36Hh+NDXUnpKkg== dependencies: invariant "^2.2.4" @@ -1860,7 +1731,7 @@ resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== -"@react-native/normalize-color@*", "@react-native/normalize-color@2.0.0": +"@react-native/normalize-color@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.0.0.tgz#da955909432474a9a0fe1cbffc66576a0447f567" integrity sha512-Wip/xsc5lw8vsBlmY2MO/gFLp3MvuZ2baBZjDeTjjndMgM0h5sxz7AZR62RDPGgstp8Np7JzjvVqVT7tpFZqsw== @@ -1887,24 +1758,24 @@ resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== -"@sinclair/typebox@^0.24.1": - version "0.24.26" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.26.tgz#84f9e8c1d93154e734a7947609a1dc7c7a81cc22" - integrity sha512-1ZVIyyS1NXDRVT8GjWD5jULjhDyM3IsIHef2VGUMdnWOlX2tkPjyEX/7K0TGSH2S8EaPhp1ylFdjSjUGQ+gecg== +"@sinclair/typebox@^0.25.16": + version "0.25.21" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" + integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== +"@sinonjs/fake-timers@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" + integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^2.0.0" "@types/babel__core@^7.1.14": version "7.1.16" @@ -1973,6 +1844,11 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + "@types/minimatch@*": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" @@ -2001,6 +1877,11 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.1.tgz#e1303048d5389563e130f5bdd89d37a99acb75eb" integrity sha512-Fo79ojj3vdEZOHg3wR9ksAMRz4P3S5fDB5e/YWZiFnyFQI1WY2Vftu9XoXVVtJfxB7Bpce/QTqWSSntkz2Znrw== +"@types/semver@^7.3.12": + version "7.3.12" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.12.tgz#920447fdd78d76b19de0438b7f60df3c4a80bf1c" + integrity sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -2039,6 +1920,88 @@ dependencies: "@types/yargs-parser" "*" +"@typescript-eslint/eslint-plugin@^5.20.0": + version "5.41.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz#f8eeb1c6bb2549f795f3ba71aec3b38d1ab6b1e1" + integrity sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA== + dependencies: + "@typescript-eslint/scope-manager" "5.41.0" + "@typescript-eslint/type-utils" "5.41.0" + "@typescript-eslint/utils" "5.41.0" + debug "^4.3.4" + ignore "^5.2.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.20.0": + version "5.41.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.41.0.tgz#0414a6405007e463dc527b459af1f19430382d67" + integrity sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA== + dependencies: + "@typescript-eslint/scope-manager" "5.41.0" + "@typescript-eslint/types" "5.41.0" + "@typescript-eslint/typescript-estree" "5.41.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.41.0": + version "5.41.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz#28e3a41d626288d0628be14cf9de8d49fc30fadf" + integrity sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ== + dependencies: + "@typescript-eslint/types" "5.41.0" + "@typescript-eslint/visitor-keys" "5.41.0" + +"@typescript-eslint/type-utils@5.41.0": + version "5.41.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz#2371601171e9f26a4e6da918a7913f7266890cdf" + integrity sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA== + dependencies: + "@typescript-eslint/typescript-estree" "5.41.0" + "@typescript-eslint/utils" "5.41.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.41.0": + version "5.41.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.41.0.tgz#6800abebc4e6abaf24cdf220fb4ce28f4ab09a85" + integrity sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA== + +"@typescript-eslint/typescript-estree@5.41.0": + version "5.41.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz#bf5c6b3138adbdc73ba4871d060ae12c59366c61" + integrity sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg== + dependencies: + "@typescript-eslint/types" "5.41.0" + "@typescript-eslint/visitor-keys" "5.41.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.41.0": + version "5.41.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.41.0.tgz#f41ae5883994a249d00b2ce69f4188f3a23fa0f9" + integrity sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ== + dependencies: + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.41.0" + "@typescript-eslint/types" "5.41.0" + "@typescript-eslint/typescript-estree" "5.41.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.41.0": + version "5.41.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz#d3510712bc07d5540160ed3c0f8f213b73e3bcd9" + integrity sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw== + dependencies: + "@typescript-eslint/types" "5.41.0" + eslint-visitor-keys "^3.3.0" + "@xmldom/xmldom@^0.7.5": version "0.7.5" resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.5.tgz#09fa51e356d07d0be200642b0e4f91d8e6dd408d" @@ -2064,6 +2027,11 @@ accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +acorn@^8.5.0: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + anser@^1.4.9: version "1.4.10" resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.10.tgz#befa3eddf282684bd03b63dcda3927aef8c2e35b" @@ -2114,14 +2082,6 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -2172,21 +2132,6 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -array-filter@~0.0.0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" - integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= - -array-map@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" - integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= - -array-reduce@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" - integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= - array-union@^1.0.1, array-union@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -2194,6 +2139,11 @@ array-union@^1.0.1, array-union@^1.0.2: dependencies: array-uniq "^1.0.1" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" @@ -2246,12 +2196,10 @@ async-listener@^0.6.0: semver "^5.3.0" shimmer "^1.1.0" -async@^2.4.0: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" +async@^3.2.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== asynckit@^0.4.0: version "0.4.0" @@ -2268,15 +2216,15 @@ babel-core@^7.0.0-bridge.0: resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== -babel-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" - integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== +babel-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" + integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== dependencies: - "@jest/transform" "^28.1.3" + "@jest/transform" "^29.5.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^28.1.3" + babel-preset-jest "^29.5.0" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -2299,10 +2247,10 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" - integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2338,6 +2286,13 @@ babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== +babel-plugin-transform-flow-enums@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz#d1d0cc9bdc799c850ca110d0ddc9f21b9ec3ef25" + integrity sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ== + dependencies: + "@babel/plugin-syntax-flow" "^7.12.1" + babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -2389,12 +2344,12 @@ babel-preset-fbjs@^3.4.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" - integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== dependencies: - babel-plugin-jest-hoist "^28.1.3" + babel-plugin-jest-hoist "^29.5.0" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -2420,11 +2375,6 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -big-integer@1.6.x: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== - bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -2434,20 +2384,6 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -bplist-creator@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" - integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== - dependencies: - stream-buffers "2.2.x" - -bplist-parser@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.1.tgz#e1c90b2ca2a9f9474cc72f6862bbf3fee8341fd1" - integrity sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA== - dependencies: - big-integer "1.6.x" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2490,16 +2426,15 @@ browserslist@^4.17.3: node-releases "^1.1.77" picocolors "^0.2.1" -browserslist@^4.20.2: - version "4.20.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf" - integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg== +browserslist@^4.21.3: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== dependencies: - caniuse-lite "^1.0.30001332" - electron-to-chromium "^1.4.118" - escalade "^3.1.1" - node-releases "^2.0.3" - picocolors "^1.0.0" + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" + node-releases "^2.0.6" + update-browserslist-db "^1.0.9" bser@2.1.1: version "2.1.1" @@ -2593,17 +2528,10 @@ caniuse-lite@^1.0.30001264: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz#0613c9e6c922e422792e6fcefdf9a3afeee4f8c3" integrity sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw== -caniuse-lite@^1.0.30001332: - version "1.0.30001335" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz#899254a0b70579e5a957c32dced79f0727c61f2a" - integrity sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w== - -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== - dependencies: - rsvp "^4.8.4" +caniuse-lite@^1.0.30001400: + version "1.0.30001407" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001407.tgz#92281a6ee67cb90bfd8a6a1201fcc2dc19b60a15" + integrity sha512-4ydV+t4P7X3zH83fQWNDX/mQEzYomossfpViCOx9zHBSMV+rIe3LFqglHHtVyvNl1FhTNxPxs3jei82iqOW04w== chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" @@ -2701,6 +2629,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -2783,7 +2720,7 @@ command-exists@^1.2.8: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -commander@^2.19.0: +commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2846,13 +2783,18 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -2913,10 +2855,10 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^4.1.0, debug@^4.1.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== +debug@^4.1.0, debug@^4.1.1, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" @@ -2996,15 +2938,6 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -deprecated-react-native-prop-types@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz#c10c6ee75ff2b6de94bb127f142b814e6e08d9ab" - integrity sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA== - dependencies: - "@react-native/normalize-color" "*" - invariant "*" - prop-types "*" - destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" @@ -3027,10 +2960,10 @@ diagnostic-channel@1.1.0: dependencies: semver "^5.3.0" -diff-sequences@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.0.2.tgz#40f8d4ffa081acbd8902ba35c798458d0ff1af41" - integrity sha512-YtEoNynLDFCRznv/XDalsKGSZDoj0U5kLnXvY0JSq3nBboRrZXjD81+eSiwi+nzcZDwedMmcowcxNwwgFW23mQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== dir-glob@^2.0.0, dir-glob@^2.2.2: version "2.2.2" @@ -3039,6 +2972,13 @@ dir-glob@^2.0.0, dir-glob@^2.2.2: dependencies: path-type "^3.0.0" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -3049,15 +2989,10 @@ electron-to-chromium@^1.3.857: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.861.tgz#981e37a79af7a7b29bbaeed36376f4795527de13" integrity sha512-GZyflmpMnZRdZ1e2yAyvuFwz1MPSVQelwHX4TJZyXypB8NcxdPvPNwy5lOTxnlkrK13EiQzyTPugRSnj6cBgKg== -electron-to-chromium@^1.4.118: - version "1.4.131" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.131.tgz#ca42d22eac0fe545860fbc636a6f4a7190ba70a9" - integrity sha512-oi3YPmaP87hiHn0c4ePB67tXaF+ldGhxvZnT19tW9zX6/Ej+pLN0Afja5rQ6S+TND7I9EuwQTT8JYn1k7R7rrw== - -electron-to-chromium@^1.4.17: - version "1.4.24" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz#9cf8a92d5729c480ee47ff0aa5555f57467ae2fa" - integrity sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg== +electron-to-chromium@^1.4.251: + version "1.4.255" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.255.tgz#dc52d1095b876ed8acf25865db10265b02b1d6e1" + integrity sha512-H+mFNKow6gi2P5Gi2d1Fvd3TUEJlB9CF7zYaIV9T83BE3wP1xZ0mRPbNTm0KUjyd1QiVy7iKXuIcjlDtBQMiAQ== emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -3066,10 +3001,10 @@ emitter-listener@^1.0.1, emitter-listener@^1.1.1: dependencies: shimmer "^1.2.0" -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" @@ -3135,11 +3070,53 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -3150,11 +3127,6 @@ event-target-shim@^5.0.0, event-target-shim@^5.0.1: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -exec-sh@^0.3.2: - version "0.3.6" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" - integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== - execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -3216,16 +3188,16 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" - integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== +expect@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== dependencies: - "@jest/expect-utils" "^28.1.3" - jest-get-type "^28.0.2" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" + "@jest/expect-utils" "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" extend-shallow@^2.0.1: version "2.0.1" @@ -3268,11 +3240,29 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-json-stable-stringify@^2.0.0: +fast-glob@^3.2.9: + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + fb-watchman@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" @@ -3334,6 +3324,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + flow-parser@0.*: version "0.161.0" resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.161.0.tgz#9b8d892deaca8c180ffaf332c1d1eef44902397c" @@ -3409,7 +3407,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2, fsevents@^2.3.2: +fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -3483,6 +3481,13 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" @@ -3517,6 +3522,18 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + globby@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" @@ -3543,12 +3560,7 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== - -graceful-fs@^4.2.8: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.8, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -3611,22 +3623,29 @@ hermes-engine@~0.11.0: resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.11.0.tgz#bb224730d230a02a5af02c4e090d1f52d57dd3db" integrity sha512-7aMUlZja2IyLYAcZ69NBnwJAR5ZOYlSllj0oMpx08a8HzxHOys0eKCzfphrf6D0vX1JGO1QQvVsQKe6TkYherw== -hermes-estree@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.5.0.tgz#36432a2b12f01b217244da098924efdfdfc12327" - integrity sha512-1h8rvG23HhIR5K6Kt0e5C7BC72J1Ath/8MmSta49vxXp/j6wl7IMHvIRFYBQr35tWnQY97dSGR2uoAJ5pHUQkg== +hermes-estree@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.6.0.tgz#e866fddae1b80aec65fe2ae450a5f2070ad54033" + integrity sha512-2YTGzJCkhdmT6VuNprWjXnvTvw/3iPNw804oc7yknvQpNKo+vJGZmtvLLCghOZf0OwzKaNAzeIMp71zQbNl09w== -hermes-parser@0.4.7: - version "0.4.7" - resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.4.7.tgz#410f5129d57183784d205a0538e6fbdcf614c9ea" - integrity sha512-jc+zCtXbtwTiXoMAoXOHepxAaGVFIp89wwE9qcdwnMd/uGVEtPoY8FaFSsx0ThPvyKirdR2EsIIDVrpbSXz1Ag== +hermes-estree@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.8.0.tgz#530be27243ca49f008381c1f3e8b18fb26bf9ec0" + integrity sha512-W6JDAOLZ5pMPMjEiQGLCXSSV7pIBEgRR5zGkxgmzGSXHOxqV5dC/M1Zevqpbm9TZDE5tu358qZf8Vkzmsc+u7Q== -hermes-parser@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.5.0.tgz#8b678dd8b29a08b57cbaf60adba4896494c59a53" - integrity sha512-ARnJBScKAkkq8j3BHrNGBUv/4cSpZNbKDsVizEtzmsFeqC67Dopa5s4XRe+e3wN52Dh5Mj2kDB5wJvhcxwDkPg== +hermes-parser@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.6.0.tgz#00d14e91bca830b3c1457050fa4187400cb96328" + integrity sha512-Vf58jBZca2+QBLR9h7B7mdg8oFz2g5ILz1iVouZ5DOrOrAfBmPfJjdjDT8jrO0f+iJ4/hSRrQHqHIjSnTaLUDQ== dependencies: - hermes-estree "0.5.0" + hermes-estree "0.6.0" + +hermes-parser@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.8.0.tgz#116dceaba32e45b16d6aefb5c4c830eaeba2d257" + integrity sha512-yZKalg1fTYG5eOiToLUaw69rQfZq/fi+/NtEXRU7N87K/XobNRhRWorh80oSge2lWUiZfTgUvRJH+XgZWrhoqA== + dependencies: + hermes-estree "0.8.0" hermes-profile-transformer@^0.0.6: version "0.0.6" @@ -3681,11 +3700,23 @@ ignore@^4.0.3: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + image-size@^0.6.0: version "0.6.3" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== +image-size@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.2.tgz#d778b6d0ab75b2737c1556dd631652eb963bc486" + integrity sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg== + dependencies: + queue "6.0.2" + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -3725,7 +3756,7 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -invariant@*, invariant@^2.2.4: +invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -3766,13 +3797,6 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - is-core-module@^2.2.0: version "2.7.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.7.0.tgz#3c0ef7d31b4acfc574f80c58409d568a836848e3" @@ -3863,7 +3887,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -3988,155 +4012,135 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" - integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== dependencies: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" - integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== +jest-circus@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" + integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" is-generator-fn "^2.0.0" - jest-each "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" + jest-each "^29.5.0" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" p-limit "^3.1.0" - pretty-format "^28.1.3" + pretty-format "^29.5.0" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" - integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== +jest-cli@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" + integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== dependencies: - "@jest/core" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/core" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-config "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" prompts "^2.0.1" yargs "^17.3.1" -jest-config@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" - integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== +jest-config@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" + integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^28.1.3" - "@jest/types" "^28.1.3" - babel-jest "^28.1.3" + "@jest/test-sequencer" "^29.5.0" + "@jest/types" "^29.5.0" + babel-jest "^29.5.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^28.1.3" - jest-environment-node "^28.1.3" - jest-get-type "^28.0.2" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-runner "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-circus "^29.5.0" + jest-environment-node "^29.5.0" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-runner "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^28.1.3" + pretty-format "^29.5.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" - integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== dependencies: chalk "^4.0.0" - diff-sequences "^28.1.1" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-docblock@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" - integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== dependencies: detect-newline "^3.0.0" -jest-each@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" - integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== +jest-each@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" + integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.5.0" chalk "^4.0.0" - jest-get-type "^28.0.2" - jest-util "^28.1.3" - pretty-format "^28.1.3" + jest-get-type "^29.4.3" + jest-util "^29.5.0" + pretty-format "^29.5.0" -jest-environment-node@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" - integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== +jest-environment-node@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" + integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^28.1.3" - jest-util "^28.1.3" + jest-mock "^29.5.0" + jest-util "^29.5.0" jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== - -jest-haste-map@^26.5.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" - integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== - dependencies: - "@jest/types" "^26.6.2" - "@types/graceful-fs" "^4.1.2" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^26.0.0" - jest-serializer "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" - micromatch "^4.0.2" - sane "^4.0.3" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.1.2" +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== jest-haste-map@^27.3.1: version "27.5.1" @@ -4158,172 +4162,160 @@ jest-haste-map@^27.3.1: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.0.tgz#6c1ee2daf1c20a3e03dbd8e5b35c4d73d2349cf0" - integrity sha512-xyZ9sXV8PtKi6NCrJlmq53PyNVHzxmcfXNVvIRHpHmh1j/HChC4pwKgyjj7Z9us19JMw8PpQTJsFWOsIfT93Dw== +jest-haste-map@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" + integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.5.0" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - jest-worker "^28.1.3" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + jest-worker "^29.5.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" - integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== +jest-leak-detector@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" + integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== dependencies: - jest-get-type "^28.0.2" - pretty-format "^28.1.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-matcher-utils@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" - integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== dependencies: chalk "^4.0.0" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" -jest-message-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" - integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^28.1.3" + "@jest/types" "^29.5.0" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^28.1.3" + pretty-format "^29.5.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" - integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== +jest-mock@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" + integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.5.0" "@types/node" "*" + jest-util "^29.5.0" jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" - integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== - -jest-regex-util@^27.5.1: +jest-regex-util@^27.0.6, jest-regex-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-regex-util@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" - integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== -jest-resolve-dependencies@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" - integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== +jest-resolve-dependencies@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" + integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== dependencies: - jest-regex-util "^28.0.2" - jest-snapshot "^28.1.3" + jest-regex-util "^29.4.3" + jest-snapshot "^29.5.0" -jest-resolve@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" - integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== +jest-resolve@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" + integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" + jest-haste-map "^29.5.0" jest-pnp-resolver "^1.2.2" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-util "^29.5.0" + jest-validate "^29.5.0" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" - integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== +jest-runner@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" + integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== dependencies: - "@jest/console" "^28.1.3" - "@jest/environment" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/console" "^29.5.0" + "@jest/environment" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" - emittery "^0.10.2" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^28.1.1" - jest-environment-node "^28.1.3" - jest-haste-map "^28.1.3" - jest-leak-detector "^28.1.3" - jest-message-util "^28.1.3" - jest-resolve "^28.1.3" - jest-runtime "^28.1.3" - jest-util "^28.1.3" - jest-watcher "^28.1.3" - jest-worker "^28.1.3" + jest-docblock "^29.4.3" + jest-environment-node "^29.5.0" + jest-haste-map "^29.5.0" + jest-leak-detector "^29.5.0" + jest-message-util "^29.5.0" + jest-resolve "^29.5.0" + jest-runtime "^29.5.0" + jest-util "^29.5.0" + jest-watcher "^29.5.0" + jest-worker "^29.5.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" - integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== +jest-runtime@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" + integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/globals" "^28.1.3" - "@jest/source-map" "^28.1.2" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/globals" "^29.5.0" + "@jest/source-map" "^29.4.3" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" - integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.4" - jest-serializer@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" @@ -4332,48 +4324,36 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.0.tgz#4b74fa8816707dd10fe9d551c2c258e5a67b53b6" - integrity sha512-ex49M2ZrZsUyQLpLGxQtDbahvgBjlLPgklkqGM0hq/F7W/f8DyqZxVHjdy19QKBm4O93eDp+H5S23EiTbbUmHw== +jest-snapshot@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" + integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/expect-utils" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^28.1.3" + expect "^29.5.0" graceful-fs "^4.2.9" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - jest-haste-map "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" natural-compare "^1.4.0" - pretty-format "^28.1.3" + pretty-format "^29.5.0" semver "^7.3.5" -jest-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" - integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - micromatch "^4.0.2" - -jest-util@^27.5.1: +jest-util@^27.2.0, jest-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== @@ -4385,12 +4365,12 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.0.tgz#d54eb83ad77e1dd441408738c5a5043642823be5" - integrity sha512-qYdCKD77k4Hwkose2YBEqQk7PzUf/NSE+rutzceduFveQREeH6b+89Dc9+wjX9dAwHcgdx4yedGA3FQlU/qCTA== +jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" @@ -4409,42 +4389,33 @@ jest-validate@^26.5.2: leven "^3.1.0" pretty-format "^26.6.2" -jest-validate@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" - integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== +jest-validate@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" + integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.5.0" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^28.0.2" + jest-get-type "^29.4.3" leven "^3.1.0" - pretty-format "^28.1.3" + pretty-format "^29.5.0" -jest-watcher@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" - integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== +jest-watcher@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" + integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== dependencies: - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^28.1.3" + emittery "^0.13.1" + jest-util "^29.5.0" string-length "^4.0.1" -jest-worker@^26.0.0, jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest-worker@^27.5.1: +jest-worker@^27.2.0, jest-worker@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== @@ -4453,24 +4424,25 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.0.tgz#ced54757a035e87591e1208253a6e3aac1a855e5" - integrity sha512-ZHwM6mNwaWBR52Snff8ZvsCTqQsvhCxP/bT1I6T6DAnb6ygkshsyLQIMxFwHpYxht0HOoqt23JlC01viI7T03A== +jest-worker@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" + integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== dependencies: "@types/node" "*" + jest-util "^29.5.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" - integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== +jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" + integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== dependencies: - "@jest/core" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/core" "^29.5.0" + "@jest/types" "^29.5.0" import-local "^3.0.2" - jest-cli "^28.1.3" + jest-cli "^29.5.0" jetifier@^1.6.2: version "1.6.8" @@ -4551,10 +4523,10 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonfile@^2.1.0: version "2.4.0" @@ -4570,11 +4542,6 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= - kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -4653,6 +4620,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -4663,7 +4637,7 @@ lodash.throttle@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21: +lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4692,13 +4666,20 @@ logkitty@^0.7.1: dayjs "^1.8.15" yargs "^15.1.0" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -4772,257 +4753,176 @@ mem@^5.0.0: mimic-fn "^2.1.0" p-is-promise "^2.1.0" +memoize-one@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.2.3: +merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -metro-babel-register@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-babel-register/-/metro-babel-register-0.66.2.tgz#c6bbe36c7a77590687ccd74b425dc020d17d05af" - integrity sha512-3F+vsVubUPJYKfVMeol8/7pd8CC287Rw92QYzJD8LEmI980xcgwMUEVBZ0UIAUwlLgiJG/f4Mwhuji2EeBXrPg== +metro-babel-transformer@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.70.3.tgz#dca61852be273824a4b641bd1ecafff07ff3ad1f" + integrity sha512-bWhZRMn+mIOR/s3BDpFevWScz9sV8FGktVfMlF1eJBLoX24itHDbXvTktKBYi38PWIKcHedh6THSFpJogfuwNA== dependencies: "@babel/core" "^7.14.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-syntax-class-properties" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/register" "^7.0.0" - escape-string-regexp "^1.0.5" - -metro-babel-transformer@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.66.2.tgz#fce0a3e314d28a5e7141c135665e1cc9b8e7ce86" - integrity sha512-aJ/7fc/Xkofw8Fqa51OTDhBzBz26mmpIWrXAZcPdQ8MSTt883EWncxeCEjasc79NJ89BRi7sOkkaWZo2sXlKvw== - dependencies: - "@babel/core" "^7.14.0" - hermes-parser "0.4.7" - metro-source-map "0.66.2" + hermes-parser "0.6.0" + metro-source-map "0.70.3" nullthrows "^1.1.1" -metro-babel-transformer@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.67.0.tgz#42fe82af9953e5c62d9a8d7d544eb7be9020dd18" - integrity sha512-SBqc4nq/dgsPNFm+mpWcQQzJaXnh0nrfz2pSnZC4i6zMtIakrTWb8SQ78jOU1FZVEZ3nu9xCYVHS9Tbr/LoEuw== +metro-babel-transformer@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.76.4.tgz#d5ebcae4dd1ba1b15eb7de288d0347f5f52d1432" + integrity sha512-VTvCj6wnYfg5TeFrASegdGqPrdoPsfPbS0g0yd3VzZlDJpMN+qhYH5Qn0ERFK5I0LLedjHlOrSZzOhlElFEbPw== dependencies: - "@babel/core" "^7.14.0" - hermes-parser "0.5.0" - metro-source-map "0.67.0" + "@babel/core" "^7.20.0" + hermes-parser "0.8.0" + metro-source-map "0.76.4" nullthrows "^1.1.1" -metro-cache-key@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.66.2.tgz#d6463d2a53e887a38419d523962cc24ea0e780b4" - integrity sha512-WtkNmRt41qOpHh1MkNA4nLiQ/m7iGL90ysSKD+fcLqlUnOBKJptPQm0ZUv8Kfqk18ddWX2KmsSbq+Sf3I6XohQ== +metro-cache-key@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.70.3.tgz#898803db04178a8f440598afba7d82a9cf35abf7" + integrity sha512-0zpw+IcpM3hmGd5sKMdxNv3sbOIUYnMUvx1/yaM6vNRReSPmOLX0bP8fYf3CGgk8NEreZ1OHbVsuw7bdKt40Mw== -metro-cache-key@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.67.0.tgz#4df6a73cced199e1bddd0f3454bb931a27141eeb" - integrity sha512-FNJe5Rcb2uzY6G6tsqCf0RV4t2rCeX6vSHBxmP7k+4aI4NqX4evtPI0K82r221nBzm5DqNWCURZ0RYUT6jZMGA== +metro-cache-key@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.76.4.tgz#684d7ffd2b2936be824475296eaaf9a1374b9835" + integrity sha512-bh7GFqPg4+IKolI0ybalotqyMU/nbHxyXrKad7bwhUI0z6RSNv0ImXGtDWzzMQ6tCPV2MTCDzzSPLEXLeUVH8A== -metro-cache@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.66.2.tgz#e0af4e0a319898f7d42a980f7ee5da153fcfd019" - integrity sha512-5QCYJtJOHoBSbL3H4/Fpl36oA697C3oYHqsce+Hk/dh2qtODUGpS3gOBhvP1B8iB+H8jJMyR75lZq129LJEsIQ== +metro-cache@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.70.3.tgz#42cf3cdf8a7b3691f3bef9a86bed38d4c5f6201f" + integrity sha512-iCix/+z812fUqa6KlOxaTkY6LQQDoXIe/VljXkGIvpygSCmYyhjQpfQVZEVVPezFmUBYXNdabdQ6cYx6JX3yMg== dependencies: - metro-core "0.66.2" - mkdirp "^0.5.1" + metro-core "0.70.3" rimraf "^2.5.4" -metro-cache@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.67.0.tgz#928db5742542719677468c4d22ea29b71c7ec8fc" - integrity sha512-IY5dXiR76L75b2ue/mv+9vW8g5hdQJU6YEe81lj6gTSoUrhcONT0rzY+Gh5QOS2Kk6z9utZQMvd9PRKL9/635A== +metro-cache@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.76.4.tgz#cfd35f817c06148c5f53ea25b737469abce14447" + integrity sha512-pWQTKbA92XUhg9QVCwDsKwBxyZrjYPs8fYCnqB1phF+r26Vj4VYVKLAZam4lxFXC+awiEpYzpgmQkfwqODZE0g== dependencies: - metro-core "0.67.0" - mkdirp "^0.5.1" - rimraf "^2.5.4" + metro-core "0.76.4" + rimraf "^3.0.2" -metro-config@0.66.2, metro-config@^0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.66.2.tgz#e365acdb66ad0cda0182b9c9910760a97ee4293b" - integrity sha512-0C+PrKKIBNNzLZUKN/8ZDJS2U5FLMOTXDWbvBHIdqb6YXz8WplXR2+xlSlaSCCi5b+GR7cWFWUNeKA4GQS1/AQ== +metro-config@0.70.3, metro-config@^0.70.1: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.70.3.tgz#fe6f7330f679d5594e5724af7a69d4dbe1bb5bc3" + integrity sha512-SSCDjSTygoCgzoj61DdrBeJzZDRwQxUEfcgc6t6coxWSExXNR4mOngz0q4SAam49Bmjq9J2Jft6qUKnUTPrRgA== dependencies: cosmiconfig "^5.0.5" jest-validate "^26.5.2" - metro "0.66.2" - metro-cache "0.66.2" - metro-core "0.66.2" - metro-runtime "0.66.2" + metro "0.70.3" + metro-cache "0.70.3" + metro-core "0.70.3" + metro-runtime "0.70.3" -metro-config@0.67.0, metro-config@^0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.67.0.tgz#5507d3b295bd10c87bd13dbe5a3033a357418786" - integrity sha512-ThAwUmzZwTbKyyrIn2bKIcJDPDBS0LKAbqJZQioflvBGfcgA21h3fdL3IxRmvCEl6OnkEWI0Tn1Z9w2GLAjf2g== +metro-config@0.76.4, metro-config@^0.76.0: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.76.4.tgz#28753ab0671705914c75a945e85f671922ea128f" + integrity sha512-o0rK5HnQZoCI6xfVgtjkMyBVcj6UZ4c6lbr8MwRzjIH6rKyoetxdM6OCnnBcZgCmid10apYdveaIQBJ7KYCNlg== dependencies: cosmiconfig "^5.0.5" jest-validate "^26.5.2" - metro "0.67.0" - metro-cache "0.67.0" - metro-core "0.67.0" - metro-runtime "0.67.0" + metro "0.76.4" + metro-cache "0.76.4" + metro-core "0.76.4" + metro-runtime "0.76.4" -metro-core@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.66.2.tgz#ead776a17b3e5a307e6dc22259db30bf5c7e8490" - integrity sha512-JieLZkef/516yxXYvQxWnf3OWw5rcgWRy76K8JV/wr/i8LGVGulPAXlIi445/QZzXVydzRVASKAEVqyxM5F4mA== - dependencies: - jest-haste-map "^26.5.2" - lodash.throttle "^4.1.1" - metro-resolver "0.66.2" - -metro-core@0.67.0, metro-core@^0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.67.0.tgz#75066e11b4df220992abf9cd6200279dd87876c8" - integrity sha512-TOa/ShE1bUq83fGNfV6rFwyfZ288M8ydmWN3g9C2OW8emOHLhJslYD/SIU4DhDkP/99yaJluIALdZ2g0+pCrvQ== +metro-core@0.70.3, metro-core@^0.70.1: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.70.3.tgz#bf4dda15a5185f5a7931de463a1b97ac9ef680a0" + integrity sha512-NzfHB/w5R7yLaOeU1tzPTbBzCRsYSvpKJkLMP0yudszKZzIAZqNdjoEJ9GZ688Wi0ynZxcU0BxukXh4my80ZBw== dependencies: jest-haste-map "^27.3.1" lodash.throttle "^4.1.1" - metro-resolver "0.67.0" + metro-resolver "0.70.3" -metro-hermes-compiler@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.66.2.tgz#30290748f83805faa601aa487632444915795823" - integrity sha512-nCVL1g9uR6vrw5+X1wjwZruRyMkndnzGRMqjqoljf+nGEqBTD607CR7elXw4fMWn/EM+1y0Vdq5altUu9LdgCA== - -metro-hermes-compiler@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.67.0.tgz#9c1340f1882fbf535145868d0d28211ca15b0477" - integrity sha512-X5Pr1jC8/kO6d1EBDJ6yhtuc5euHX89UDNv8qdPJHAET03xfFnlojRPwOw6il2udAH20WLBv+F5M9VY+58zspQ== - -metro-inspector-proxy@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.66.2.tgz#a83c76bd2f2fd7b9240be92acf9a8b1d1404547a" - integrity sha512-gnLc9121eznwP0iiA9tCBW8qZjwIsCgwHWMF1g1Qaki9le9tzeJv3dK4/lFNGxyfSaLO7vahQEhsEYsiRnTROg== +metro-core@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.76.4.tgz#51aa3065ec324112827d44f881e39a38bdd8813a" + integrity sha512-zrWl2MLvW8Nxa4sX5wnPm1cPvAu5J3DmnUMHK5PHELgJGtb5XNV7UibxmI/6zM423OQH1KPY7Hh5l3qkn4cDRQ== dependencies: - connect "^3.6.5" - debug "^2.2.0" - ws "^1.1.5" - yargs "^15.3.1" + lodash.throttle "^4.1.1" + metro-resolver "0.76.4" -metro-inspector-proxy@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.67.0.tgz#22b360a837b07e9e2bc87a71af6154dd8fcc02a5" - integrity sha512-5Ubjk94qpNaU3OT2IZa4/dec09bauic1hzWms4czorBzDenkp4kYXG9/aWTmgQLtCk92H3Q8jKl1PQRxUSkrOQ== +metro-file-map@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.76.4.tgz#c355018f176900a21cf2cf95a46b0c9a3012eaec" + integrity sha512-k/FPwqdZVYnyaB3Ar+dlA37RVs6TNx8hBkGj28gQE/8E2bfDDRSFlCPe69M92fcSUZDVPZqC2vS7lDmUa3xxQQ== + dependencies: + anymatch "^3.0.3" + debug "^2.2.0" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + invariant "^2.2.4" + jest-regex-util "^27.0.6" + jest-util "^27.2.0" + jest-worker "^27.2.0" + micromatch "^4.0.4" + node-abort-controller "^3.1.1" + nullthrows "^1.1.1" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +metro-hermes-compiler@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.70.3.tgz#ac7ed656fbcf0a59adcd010d3639e4cfdbc76b4f" + integrity sha512-W6WttLi4E72JL/NyteQ84uxYOFMibe0PUr9aBKuJxxfCq6QRnJKOVcNY0NLW0He2tneXGk+8ZsNz8c0flEvYqg== + +metro-inspector-proxy@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.70.3.tgz#321c25b2261e76d8c4bcc39e092714adfcb50a14" + integrity sha512-qQoNdPGrmyoJSWYkxSDpTaAI8xyqVdNDVVj9KRm1PG8niSuYmrCCFGLLFsMvkVYwsCWUGHoGBx0UoAzVp14ejw== dependencies: connect "^3.6.5" debug "^2.2.0" ws "^7.5.1" yargs "^15.3.1" -metro-minify-uglify@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.66.2.tgz#6061dbee4f61e6d5bb3c100e4379ff6f2e16e42b" - integrity sha512-7TUK+L5CmB5x1PVnFbgmjzHW4CUadq9H5jgp0HfFoWT1skXAyEsx0DHkKDXwnot0khnNhBOEfl62ctQOnE110Q== +metro-inspector-proxy@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.76.4.tgz#0d609cdbb8a31ac43665fb567079adee47e7f385" + integrity sha512-gsnUagRBoNgaWjcwD9eeweJwAw6pvMxKywZNM5HBvU2FfXnJT1BrHZznaL5ABCPhJKHcO9MvnJT2fo6x09owfg== + dependencies: + connect "^3.6.5" + debug "^2.2.0" + node-fetch "^2.2.0" + ws "^7.5.1" + yargs "^17.6.2" + +metro-minify-terser@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.76.4.tgz#e02bc231a39f23c9dad513abf42ca50b2c111887" + integrity sha512-u4MqTTE30UkUV3zqj3wX1QWqXc6SS2uLRk1DQCelmB6fLzqVQR0xe00hlnzkxc/V4w8x19Bs00lPpAEOzGXNvQ== + dependencies: + terser "^5.15.0" + +metro-minify-uglify@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.70.3.tgz#2f28129ca5b8ef958f3e3fcf004c3707c7732e1e" + integrity sha512-oHyjV9WDqOlDE1FPtvs6tIjjeY/oP1PNUPYL1wqyYtqvjN+zzAOrcbsAAL1sv+WARaeiMsWkF2bwtNo+Hghoog== dependencies: uglify-es "^3.1.9" -metro-minify-uglify@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.67.0.tgz#28a77dbd78d9e558dba8c2f31c2b9c6f939df966" - integrity sha512-4CmM5b3MTAmQ/yFEfsHOhD2SuBObB2YF6PKzXZc4agUsQVVtkrrNElaiWa8w26vrTzA9emwcyurxMf4Nl3lYPQ== +metro-minify-uglify@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.76.4.tgz#62604b972caf114baf1a968155fd27e876017432" + integrity sha512-LNGEYzk12xLld63NrMEnJdmdwk6nife9cylahm0V3SOa8wN16j9004aGywrBajOJEAqoF4nbrVgzfAs2tB3nZA== dependencies: uglify-es "^3.1.9" -metro-react-native-babel-preset@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.66.2.tgz#fddebcf413ad4ea617d4f47f7c1da401052de734" - integrity sha512-H/nLBAz0MgfDloSe1FjyH4EnbokHFdncyERvLPXDACY3ROVRCeUyFNo70ywRGXW2NMbrV4H7KUyU4zkfWhC2HQ== - dependencies: - "@babel/core" "^7.14.0" - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-export-default-from" "^7.0.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.0.0" - "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.2.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-syntax-optional-chaining" "^7.0.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-async-to-generator" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.0.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-for-of" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.0.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-object-assign" "^7.0.0" - "@babel/plugin-transform-parameters" "^7.0.0" - "@babel/plugin-transform-react-display-name" "^7.0.0" - "@babel/plugin-transform-react-jsx" "^7.0.0" - "@babel/plugin-transform-react-jsx-self" "^7.0.0" - "@babel/plugin-transform-react-jsx-source" "^7.0.0" - "@babel/plugin-transform-regenerator" "^7.0.0" - "@babel/plugin-transform-runtime" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.5.0" - "@babel/plugin-transform-unicode-regex" "^7.0.0" - "@babel/template" "^7.0.0" - react-refresh "^0.4.0" - -metro-react-native-babel-preset@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.67.0.tgz#53aec093f53a09b56236a9bb534d76658efcbec7" - integrity sha512-tgTG4j0SKwLHbLRELMmgkgkjV1biYkWlGGKOmM484/fJC6bpDikdaFhfjsyE+W+qt7I5szbCPCickMTNQ+zwig== - dependencies: - "@babel/core" "^7.14.0" - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-export-default-from" "^7.0.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-optional-chaining" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.0.0" - "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.2.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" - "@babel/plugin-syntax-optional-chaining" "^7.0.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-async-to-generator" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.0.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-for-of" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.0.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-object-assign" "^7.0.0" - "@babel/plugin-transform-parameters" "^7.0.0" - "@babel/plugin-transform-react-display-name" "^7.0.0" - "@babel/plugin-transform-react-jsx" "^7.0.0" - "@babel/plugin-transform-react-jsx-self" "^7.0.0" - "@babel/plugin-transform-react-jsx-source" "^7.0.0" - "@babel/plugin-transform-regenerator" "^7.0.0" - "@babel/plugin-transform-runtime" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.5.0" - "@babel/plugin-transform-unicode-regex" "^7.0.0" - "@babel/template" "^7.0.0" - react-refresh "^0.4.0" - -metro-react-native-babel-preset@^0.70.3: +metro-react-native-babel-preset@0.70.3: version "0.70.3" resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.70.3.tgz#1c77ec4544ecd5fb6c803e70b21284d7483e4842" integrity sha512-4Nxc1zEiHEu+GTdEMEsHnRgfaBkg8f/Td3+FcQ8NTSvs+xL3LBrQy6N07idWSQZHIdGFf+tTHvRfSIWLD8u8Tg== @@ -5067,99 +4967,147 @@ metro-react-native-babel-preset@^0.70.3: "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-transformer@0.67.0, metro-react-native-babel-transformer@^0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.67.0.tgz#756d32eb3c05cab3d72fcb1700f8fd09322bb07f" - integrity sha512-P0JT09n7T01epUtgL9mH6BPat3xn4JjBakl4lWHdL61cvEGcrxuIom1eoFFKkgU/K5AVLU4aCAttHS7nSFCcEQ== +metro-react-native-babel-preset@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.4.tgz#f9422a62348c3fe9c2f733fe005e75b97345f4b1" + integrity sha512-BKyfPz7mn3ncgRjqi8Z9nYLbzuuiBWns2AAEIGctQdz9OMMAisPlZmWscv09UjhPXkQc/XtToFETVN7fmHMfug== + dependencies: + "@babel/core" "^7.20.0" + "@babel/plugin-proposal-async-generator-functions" "^7.0.0" + "@babel/plugin-proposal-class-properties" "^7.0.0" + "@babel/plugin-proposal-export-default-from" "^7.0.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-proposal-numeric-separator" "^7.0.0" + "@babel/plugin-proposal-object-rest-spread" "^7.0.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" + "@babel/plugin-proposal-optional-chaining" "^7.0.0" + "@babel/plugin-syntax-dynamic-import" "^7.0.0" + "@babel/plugin-syntax-export-default-from" "^7.0.0" + "@babel/plugin-syntax-flow" "^7.18.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-syntax-optional-chaining" "^7.0.0" + "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-async-to-generator" "^7.0.0" + "@babel/plugin-transform-block-scoping" "^7.0.0" + "@babel/plugin-transform-classes" "^7.0.0" + "@babel/plugin-transform-computed-properties" "^7.0.0" + "@babel/plugin-transform-destructuring" "^7.0.0" + "@babel/plugin-transform-flow-strip-types" "^7.0.0" + "@babel/plugin-transform-function-name" "^7.0.0" + "@babel/plugin-transform-literals" "^7.0.0" + "@babel/plugin-transform-modules-commonjs" "^7.0.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0" + "@babel/plugin-transform-parameters" "^7.0.0" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-react-jsx-self" "^7.0.0" + "@babel/plugin-transform-react-jsx-source" "^7.0.0" + "@babel/plugin-transform-runtime" "^7.0.0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0" + "@babel/plugin-transform-spread" "^7.0.0" + "@babel/plugin-transform-sticky-regex" "^7.0.0" + "@babel/plugin-transform-typescript" "^7.5.0" + "@babel/plugin-transform-unicode-regex" "^7.0.0" + "@babel/template" "^7.0.0" + babel-plugin-transform-flow-enums "^0.0.2" + react-refresh "^0.4.0" + +metro-react-native-babel-transformer@0.70.3, metro-react-native-babel-transformer@^0.70.1: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.70.3.tgz#195597c32488f820aa9e441bbca7c04fe7de7a2d" + integrity sha512-WKBU6S/G50j9cfmFM4k4oRYprd8u3qjleD4so1E2zbTNILg+gYla7ZFGCAvi2G0ZcqS2XuGCR375c2hF6VVvwg== dependencies: "@babel/core" "^7.14.0" babel-preset-fbjs "^3.4.0" - hermes-parser "0.5.0" - metro-babel-transformer "0.67.0" - metro-react-native-babel-preset "0.67.0" - metro-source-map "0.67.0" + hermes-parser "0.6.0" + metro-babel-transformer "0.70.3" + metro-react-native-babel-preset "0.70.3" + metro-source-map "0.70.3" nullthrows "^1.1.1" -metro-resolver@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.66.2.tgz#f743ddbe7a12dd137d1f7a555732cafcaea421f8" - integrity sha512-pXQAJR/xauRf4kWFj2/hN5a77B4jLl0Fom5I3PHp6Arw/KxSBp0cnguXpGLwNQ6zQC0nxKCoYGL9gQpzMnN7Hw== +metro-resolver@0.70.3, metro-resolver@^0.70.1: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.70.3.tgz#c64fdd6d0a88fa62f3f99f87e539b5f603bd47bf" + integrity sha512-5Pc5S/Gs4RlLbziuIWtvtFd9GRoILlaRC8RZDVq5JZWcWHywKy/PjNmOBNhpyvtRlzpJfy/ssIfLhu8zINt1Mw== dependencies: absolute-path "^0.0.0" -metro-resolver@0.67.0, metro-resolver@^0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.67.0.tgz#8143c716f77e468d1d42eca805243706eb349959" - integrity sha512-d2KS/zAyOA/z/q4/ff41rAp+1txF4H6qItwpsls/RHStV2j6PqgRHUzq/3ga+VIeoUJntYJ8nGW3+3qSrhFlig== +metro-resolver@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.76.4.tgz#34bf2c9f8c9bacbc71e7ecbfb6b64e998c5910ff" + integrity sha512-SXVNS+j055FLiAWx5Lems4Yw/dMxm6uo8j2ew76C+Vib++aSA/hcEoYHy1GB63jG6FmymPH3yrnyX0F6mcRnsQ== + +metro-runtime@0.70.3, metro-runtime@^0.70.1: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.70.3.tgz#09231b9d05dcbdfb5a13df0a45307273e6fe1168" + integrity sha512-22xU7UdXZacniTIDZgN2EYtmfau2pPyh97Dcs+cWrLcJYgfMKjWBtesnDcUAQy3PHekDYvBdJZkoQUeskYTM+w== dependencies: - absolute-path "^0.0.0" + "@babel/runtime" "^7.0.0" -metro-runtime@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.66.2.tgz#3409ee957b949b6c7b72ef6ed2b9af9a4f4a910e" - integrity sha512-vFhKBk2ot9FS4b+2v0OTa/guCF/QDAOJubY0CNg7PzCS5+w4y3IvZIcPX4SSS1t8pYEZBLvtdtTDarlDl81xmg== +metro-runtime@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.76.4.tgz#10cf5d9b6be7a52d4990a40f0183e994b9338201" + integrity sha512-ngNjwPTUrU3thjPZq+0zw/kwFHCtSx4WTIbxw3DNAyxzrgimgoYqtJb9KVFbE/d4Juan+JHymSDtovJstOMjmw== + dependencies: + "@babel/runtime" "^7.0.0" + react-refresh "^0.4.0" -metro-runtime@0.67.0, metro-runtime@^0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.67.0.tgz#a8888dfd06bcebbac3c99dcac7cd622510dd8ee0" - integrity sha512-IFtSL0JUt1xK3t9IoLflTDft82bjieSzdIJWLzrRzBMlesz8ox5bVmnpQbVQEwfYUpEOxbM3VOZauVbdCmXA7g== - -metro-source-map@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.66.2.tgz#b5304a282a5d55fa67b599265e9cf3217175cdd7" - integrity sha512-038tFmB7vSh73VQcDWIbr5O1m+WXWyYafDaOy+1A/2K308YP0oj33gbEgDnZsLZDwcJ+xt1x6KUEBIzlX4YGeQ== +metro-source-map@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.70.3.tgz#f5976108c18d4661eaa4d188c96713e5d67a903b" + integrity sha512-zsYtZGrwRbbGEFHtmMqqeCH9K9aTGNVPsurMOWCUeQA3VGyVGXPGtLMC+CdAM9jLpUyg6jw2xh0esxi+tYH7Uw== dependencies: "@babel/traverse" "^7.14.0" "@babel/types" "^7.0.0" invariant "^2.2.4" - metro-symbolicate "0.66.2" + metro-symbolicate "0.70.3" nullthrows "^1.1.1" - ob1 "0.66.2" + ob1 "0.70.3" source-map "^0.5.6" vlq "^1.0.0" -metro-source-map@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.67.0.tgz#e28db7253b9ca688e60d5710ebdccba60b45b2df" - integrity sha512-yxypInsRo3SfS00IgTuL6a2W2tfwLY//vA2E+GeqGBF5zTbJZAhwNGIEl8S87XXZhwzJcxf5/8LjJC1YDzabww== +metro-source-map@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.76.4.tgz#ab24b3969db4cc8d63015ed95402e1afde29a0a2" + integrity sha512-+nWoqIhzEwi6adSYHpmJN9KUfrW1Gpm17ZpE0JpkWHYvzKs9PDGvQkrd0lYmFiW5Cnfxm8D3rdVqAaH9rYincw== dependencies: - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.0.0" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" invariant "^2.2.4" - metro-symbolicate "0.67.0" + metro-symbolicate "0.76.4" nullthrows "^1.1.1" - ob1 "0.67.0" + ob1 "0.76.4" source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.66.2.tgz#addd095ce5f77e73ca21ddb5dfb396ff5d4fa041" - integrity sha512-u+DeQHyAFXVD7mVP+GST/894WHJ3i/U8oEJFnT7U3P52ZuLgX8n4tMNxhqZU12RcLR6etF8143aP0Ktx1gFLEQ== +metro-symbolicate@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.70.3.tgz#b039e5629c4ed0c999ea0496d580e1c98260f5cb" + integrity sha512-JTYkF1dpeDUssQ84juE1ycnhHki2ylJBBdJE1JHtfu5oC+z1ElDbBdPHq90Uvt8HbRov/ZAnxvv7Zy6asS+WCA== dependencies: invariant "^2.2.4" - metro-source-map "0.66.2" + metro-source-map "0.70.3" nullthrows "^1.1.1" source-map "^0.5.6" through2 "^2.0.1" vlq "^1.0.0" -metro-symbolicate@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.67.0.tgz#16729d05663d28176895244b3d932a898fca2b45" - integrity sha512-ZqVVcfa0xSz40eFzA5P8pCF3V6Tna9RU1prFzAJTa3j9dCGqwh0HTXC8AIkMtgX7hNdZrCJI1YipzUBlwkT0/A== +metro-symbolicate@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.76.4.tgz#af0f68ed4b0196091d5c0d7c72b9b7dcdf2e8d69" + integrity sha512-k2WkvGNx35PkLwucf20E1uvC2CB2AI+svoBY5T2c864gf77bh94t8cM9/Bi3up36QG5001pHtxTXS4frb59Mew== dependencies: invariant "^2.2.4" - metro-source-map "0.67.0" + metro-source-map "0.76.4" nullthrows "^1.1.1" source-map "^0.5.6" through2 "^2.0.1" vlq "^1.0.0" -metro-transform-plugins@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.66.2.tgz#39dd044a23b1343e4f2d2ec34d08128cdf255ed4" - integrity sha512-KTvqplh0ut7oDKovvDG6yzXM02R6X+9b2oVG+qYq8Zd3aCGTi51ASx4ThCNkAHyEvCuJdYg9fxXTL+j+wvhB5w== +metro-transform-plugins@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.70.3.tgz#7fe87cd0d8979b4d5d6e375751d86188fff38fd9" + integrity sha512-dQRIJoTkWZN2IVS2KzgS1hs7ZdHDX3fS3esfifPkqFAEwHiLctCf0EsPgIknp0AjMLvmGWfSLJigdRB/dc0ASw== dependencies: "@babel/core" "^7.14.0" "@babel/generator" "^7.14.0" @@ -5167,59 +5115,58 @@ metro-transform-plugins@0.66.2: "@babel/traverse" "^7.14.0" nullthrows "^1.1.1" -metro-transform-plugins@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.67.0.tgz#6122aa4e5e5f9a767cebcc5af6fd1695666683ce" - integrity sha512-DQFoSDIJdTMPDTUlKaCNJjEXiHGwFNneAF9wDSJ3luO5gigM7t7MuSaPzF4hpjmfmcfPnRhP6AEn9jcza2Sh8Q== +metro-transform-plugins@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.76.4.tgz#31d74ef1c6431dd371f90662f78a3ec034fbfea2" + integrity sha512-nrM0raOYQDWKnfvC6RQSlZtznID2Zfkg6U+RnJATeJbdKekouHbe5mmTC10nH7uxJI9hXHbKMNhyWFcImmmvjQ== dependencies: - "@babel/core" "^7.14.0" - "@babel/generator" "^7.14.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" "@babel/template" "^7.0.0" - "@babel/traverse" "^7.14.0" + "@babel/traverse" "^7.20.0" nullthrows "^1.1.1" -metro-transform-worker@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.66.2.tgz#0a8455992132c479721accd52c9bd47deb77769e" - integrity sha512-dO4PtYOMGB7Vzte8aIzX39xytODhmbJrBYPu+zYzlDjyefJZT7BkZ0LkPIThtyJi96xWcGqi9JBSo0CeRupAHw== +metro-transform-worker@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.70.3.tgz#62bfa28ebef98803531c4bcb558de5fc804c94ef" + integrity sha512-MtVVsnHhhBOp9GRLCdAb2mD1dTCsIzT4+m34KMRdBDCEbDIb90YafT5prpU8qbj5uKd0o2FOQdrJ5iy5zQilHw== dependencies: "@babel/core" "^7.14.0" "@babel/generator" "^7.14.0" "@babel/parser" "^7.14.0" "@babel/types" "^7.0.0" babel-preset-fbjs "^3.4.0" - metro "0.66.2" - metro-babel-transformer "0.66.2" - metro-cache "0.66.2" - metro-cache-key "0.66.2" - metro-hermes-compiler "0.66.2" - metro-source-map "0.66.2" - metro-transform-plugins "0.66.2" + metro "0.70.3" + metro-babel-transformer "0.70.3" + metro-cache "0.70.3" + metro-cache-key "0.70.3" + metro-hermes-compiler "0.70.3" + metro-source-map "0.70.3" + metro-transform-plugins "0.70.3" nullthrows "^1.1.1" -metro-transform-worker@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.67.0.tgz#5689553c25b0657aadefdf4ea2cd8dd06e18882a" - integrity sha512-29n+JdTb80ROiv/wDiBVlY/xRAF/nrjhp/Udv/XJl1DZb+x7JEiPxpbpthPhwwl+AYxVrostGB0W06WJ61hfiw== +metro-transform-worker@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.76.4.tgz#ee5b531e86d90f173356e93dc8465d06c36c2cb0" + integrity sha512-KvBUmgG9Z4fjbl2o6R1MZzsESIUVYw8XjfMdaBISP+yoYHoBLh841yzhCGWBc3JI8fQ86StxBMztlKCdxv0AWA== dependencies: - "@babel/core" "^7.14.0" - "@babel/generator" "^7.14.0" - "@babel/parser" "^7.14.0" - "@babel/types" "^7.0.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" + "@babel/types" "^7.20.0" babel-preset-fbjs "^3.4.0" - metro "0.67.0" - metro-babel-transformer "0.67.0" - metro-cache "0.67.0" - metro-cache-key "0.67.0" - metro-hermes-compiler "0.67.0" - metro-source-map "0.67.0" - metro-transform-plugins "0.67.0" + metro "0.76.4" + metro-babel-transformer "0.76.4" + metro-cache "0.76.4" + metro-cache-key "0.76.4" + metro-source-map "0.76.4" + metro-transform-plugins "0.76.4" nullthrows "^1.1.1" -metro@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.66.2.tgz#f21759bf00995470e7577b5b88a5277963f24492" - integrity sha512-uNsISfcQ3iKKSHoN5Q+LAh0l3jeeg7ZcNZ/4BAHGsk02erA0OP+l2m+b5qYVoPptHz9Oc3KyG5oGJoTu41pWjg== +metro@0.70.3, metro@^0.70.1: + version "0.70.3" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.70.3.tgz#4290f538ab5446c7050e718b5c5823eea292c5c2" + integrity sha512-uEWS7xg8oTetQDABYNtsyeUjdLhH3KAvLFpaFFoJqUpOk2A3iygszdqmjobFl6W4zrvKDJS+XxdMR1roYvUhTw== dependencies: "@babel/code-frame" "^7.0.0" "@babel/core" "^7.14.0" @@ -5230,7 +5177,7 @@ metro@0.66.2: "@babel/types" "^7.0.0" absolute-path "^0.0.0" accepts "^1.3.7" - async "^2.4.0" + async "^3.2.2" chalk "^4.0.0" ci-info "^2.0.0" connect "^3.6.5" @@ -5238,88 +5185,29 @@ metro@0.66.2: denodeify "^1.2.1" error-stack-parser "^2.0.6" fs-extra "^1.0.0" - graceful-fs "^4.1.3" - hermes-parser "0.4.7" - image-size "^0.6.0" - invariant "^2.2.4" - jest-haste-map "^26.5.2" - jest-worker "^26.0.0" - lodash.throttle "^4.1.1" - metro-babel-register "0.66.2" - metro-babel-transformer "0.66.2" - metro-cache "0.66.2" - metro-cache-key "0.66.2" - metro-config "0.66.2" - metro-core "0.66.2" - metro-hermes-compiler "0.66.2" - metro-inspector-proxy "0.66.2" - metro-minify-uglify "0.66.2" - metro-react-native-babel-preset "0.66.2" - metro-resolver "0.66.2" - metro-runtime "0.66.2" - metro-source-map "0.66.2" - metro-symbolicate "0.66.2" - metro-transform-plugins "0.66.2" - metro-transform-worker "0.66.2" - mime-types "^2.1.27" - mkdirp "^0.5.1" - node-fetch "^2.2.0" - nullthrows "^1.1.1" - rimraf "^2.5.4" - serialize-error "^2.1.0" - source-map "^0.5.6" - strip-ansi "^6.0.0" - temp "0.8.3" - throat "^5.0.0" - ws "^1.1.5" - yargs "^15.3.1" - -metro@0.67.0, metro@^0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.67.0.tgz#8007a041d22de1cdb05184431c67eb7989eef6e0" - integrity sha512-DwuBGAFcAivoac/swz8Lp7Y5Bcge1tzT7T6K0nf1ubqJP8YzBUtyR4pkjEYVUzVu/NZf7O54kHSPVu1ibYzOBQ== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/core" "^7.14.0" - "@babel/generator" "^7.14.0" - "@babel/parser" "^7.14.0" - "@babel/template" "^7.0.0" - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.0.0" - absolute-path "^0.0.0" - accepts "^1.3.7" - async "^2.4.0" - chalk "^4.0.0" - ci-info "^2.0.0" - connect "^3.6.5" - debug "^2.2.0" - denodeify "^1.2.1" - error-stack-parser "^2.0.6" - fs-extra "^1.0.0" - graceful-fs "^4.1.3" - hermes-parser "0.5.0" + graceful-fs "^4.2.4" + hermes-parser "0.6.0" image-size "^0.6.0" invariant "^2.2.4" jest-haste-map "^27.3.1" - jest-worker "^26.0.0" + jest-worker "^27.2.0" lodash.throttle "^4.1.1" - metro-babel-transformer "0.67.0" - metro-cache "0.67.0" - metro-cache-key "0.67.0" - metro-config "0.67.0" - metro-core "0.67.0" - metro-hermes-compiler "0.67.0" - metro-inspector-proxy "0.67.0" - metro-minify-uglify "0.67.0" - metro-react-native-babel-preset "0.67.0" - metro-resolver "0.67.0" - metro-runtime "0.67.0" - metro-source-map "0.67.0" - metro-symbolicate "0.67.0" - metro-transform-plugins "0.67.0" - metro-transform-worker "0.67.0" + metro-babel-transformer "0.70.3" + metro-cache "0.70.3" + metro-cache-key "0.70.3" + metro-config "0.70.3" + metro-core "0.70.3" + metro-hermes-compiler "0.70.3" + metro-inspector-proxy "0.70.3" + metro-minify-uglify "0.70.3" + metro-react-native-babel-preset "0.70.3" + metro-resolver "0.70.3" + metro-runtime "0.70.3" + metro-source-map "0.70.3" + metro-symbolicate "0.70.3" + metro-transform-plugins "0.70.3" + metro-transform-worker "0.70.3" mime-types "^2.1.27" - mkdirp "^0.5.1" node-fetch "^2.2.0" nullthrows "^1.1.1" rimraf "^2.5.4" @@ -5331,7 +5219,60 @@ metro@0.67.0, metro@^0.67.0: ws "^7.5.1" yargs "^15.3.1" -micromatch@^3.1.10, micromatch@^3.1.4: +metro@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.76.4.tgz#e965f17d2c20a56ee7357bce68871150fe12681d" + integrity sha512-LOnlkLt6ataFzBifhhBUjHpaQo22vqfqMEpT+LMBfF9IqESJKahe5TBpQ5OsBY4LtVp9EZxKi8FvK0B4tvTx1g== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" + "@babel/template" "^7.0.0" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" + accepts "^1.3.7" + async "^3.2.2" + chalk "^4.0.0" + ci-info "^2.0.0" + connect "^3.6.5" + debug "^2.2.0" + denodeify "^1.2.1" + error-stack-parser "^2.0.6" + graceful-fs "^4.2.4" + hermes-parser "0.8.0" + image-size "^1.0.2" + invariant "^2.2.4" + jest-worker "^27.2.0" + lodash.throttle "^4.1.1" + metro-babel-transformer "0.76.4" + metro-cache "0.76.4" + metro-cache-key "0.76.4" + metro-config "0.76.4" + metro-core "0.76.4" + metro-file-map "0.76.4" + metro-inspector-proxy "0.76.4" + metro-minify-terser "0.76.4" + metro-minify-uglify "0.76.4" + metro-react-native-babel-preset "0.76.4" + metro-resolver "0.76.4" + metro-runtime "0.76.4" + metro-source-map "0.76.4" + metro-symbolicate "0.76.4" + metro-transform-plugins "0.76.4" + metro-transform-worker "0.76.4" + mime-types "^2.1.27" + node-fetch "^2.2.0" + nullthrows "^1.1.1" + rimraf "^3.0.2" + serialize-error "^2.1.0" + source-map "^0.5.6" + strip-ansi "^6.0.0" + throat "^5.0.0" + ws "^7.5.1" + yargs "^17.6.2" + +micromatch@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -5350,7 +5291,7 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== @@ -5416,7 +5357,7 @@ minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -5513,10 +5454,15 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nocache@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" - integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +nocache@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/nocache/-/nocache-3.0.4.tgz#5b37a56ec6e09fc7d401dceaed2eab40c8bfdf79" + integrity sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw== + +node-abort-controller@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== node-dir@^0.1.17: version "0.1.17" @@ -5544,25 +5490,15 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-machine-id@^1.1.12: - version "1.1.12" - resolved "https://registry.yarnpkg.com/node-machine-id/-/node-machine-id-1.1.12.tgz#37904eee1e59b320bb9c5d6c0a59f3b469cb6267" - integrity sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ== - -node-modules-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= - node-releases@^1.1.77: version "1.1.77" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== -node-releases@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" - integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ== +node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== node-stream-zip@^1.9.1: version "1.15.0" @@ -5579,13 +5515,6 @@ normalize-package-data@^2.5.0: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -5610,15 +5539,15 @@ nullthrows@^1.1.1: resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== -ob1@0.66.2: - version "0.66.2" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.66.2.tgz#8caf548202cf2688944bae47db405a08bca17a61" - integrity sha512-RFewnL/RjE0qQBOuM+2bbY96zmJPIge/aDtsiDbLSb+MOiK8CReAhBHDgL+zrA3F1hQk00lMWpUwYcep750plA== +ob1@0.70.3: + version "0.70.3" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.70.3.tgz#f48cd5a5abf54b0c423b1b06b6d4ff4d049816cb" + integrity sha512-Vy9GGhuXgDRY01QA6kdhToPd8AkLdLpX9GjH5kpqluVqTu70mgOm7tpGoJDZGaNbr9nJlJgnipqHJQRPORixIQ== -ob1@0.67.0: - version "0.67.0" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.67.0.tgz#91f104c90641b1af8c364fc82a4b2c7d0801072d" - integrity sha512-YvZtX8HKYackQ5PwdFIuuNFVsMChRPHvnARRRT0Vk59xsBvL5t9U1Ock3M1sYrKj+Gp73+0q9xcHLAxI+xLi5g== +ob1@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.76.4.tgz#3c576b363d7efd7ef2491f433fe143aa092b1390" + integrity sha512-S6qUASzl27QBmauuvC9aDkJwNqgLjMtp7AFqrm8MjpyjVYQ4p4V8zqqyMNaHnXNYl4+qB9hvUBXqFd3QOpNKig== object-assign@^4.1.1: version "4.1.1" @@ -5710,11 +5639,6 @@ optimist@~0.3.5: dependencies: wordwrap "~0.0.2" -options@>=0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= - ora@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" @@ -5778,7 +5702,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.1.0: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -5799,6 +5723,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -5874,6 +5805,11 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + picocolors@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" @@ -5899,13 +5835,6 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pirates@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" - pirates@^4.0.4, pirates@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -5933,14 +5862,6 @@ plist@^3.0.2: base64-js "^1.5.1" xmlbuilder "^9.0.7" -plist@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" - integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA== - dependencies: - base64-js "^1.5.1" - xmlbuilder "^9.0.7" - posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -5956,13 +5877,12 @@ pretty-format@^26.5.2, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" - integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== +pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== dependencies: - "@jest/schemas" "^28.1.3" - ansi-regex "^5.0.1" + "@jest/schemas" "^29.4.3" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -5983,6 +5903,13 @@ promise@^8.0.3: dependencies: asap "~2.0.6" +promise@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.2.0.tgz#a1f6280ab67457fbfc8aad2b198c9497e9e5c806" + integrity sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg== + dependencies: + asap "~2.0.6" + prompts@^2.0.1, prompts@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" @@ -5999,15 +5926,6 @@ prompts@^2.4.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@*: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -6026,12 +5944,29 @@ punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pure-rand@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af" + integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +queue@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" + integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== + dependencies: + inherits "~2.0.3" + range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -react-devtools-core@4.24.1, react-devtools-core@^4.23.0: +react-devtools-core@4.24.0, react-devtools-core@4.24.1: version "4.24.1" resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.24.1.tgz#d274390db86898d779b6202a318fe6422eb046bb" integrity sha512-skar+cqSg5Oz89n4lQ/aBQS8RGj93FMufg2TrMJqE+RSUTO9nLEYawRMXXCs8PnDVRSfG5pPVU5Nt1OegNflyA== @@ -6044,140 +5979,111 @@ react-devtools-core@4.24.1, react-devtools-core@^4.23.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.1.0: - version "18.1.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" - integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg== +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-native-codegen@*: - version "0.69.1" - resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.69.1.tgz#3632be2f24464e6fad8dd11a25d1b6f3bc2c7d0b" - integrity sha512-TOZEqBarczcyYN3iZE3VpKkooOevaAzBq9n7lU0h9mQUvtRhLVyolc+a5K6cWI0e4v4K69I0MqzjPcPeFKo32Q== +react-native-codegen@^0.69.1, react-native-codegen@^0.69.2: + version "0.69.2" + resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.69.2.tgz#e33ac3b1486de59ddae687b731ddbfcef8af0e4e" + integrity sha512-yPcgMHD4mqLbckqnWjFBaxomDnBREfRjDi2G/WxNyPBQLD+PXUEmZTkDx6QoOXN+Bl2SkpnNOSsLE2+/RUHoPw== dependencies: "@babel/parser" "^7.14.0" flow-parser "^0.121.0" jscodeshift "^0.13.1" nullthrows "^1.1.1" -react-native-codegen@^0.0.13: - version "0.0.13" - resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.13.tgz#4cc94546fc75a5dbe9350d59c10108f2efe6bc17" - integrity sha512-rCh1P+s0Q4N6vNgS97ckafbhJRztz22+0l0VZoyQC06F07J98kI5cUByH0ATypPRIdpkMbAZc59DoPdDFc01bg== - dependencies: - "@babel/parser" "^7.14.0" - flow-parser "^0.121.0" - jscodeshift "^0.13.1" - nullthrows "^1.1.1" +react-native-flipper@^0.195.0: + version "0.195.0" + resolved "https://registry.yarnpkg.com/react-native-flipper/-/react-native-flipper-0.195.0.tgz#0769bd76a42b649c1c92b884c833fd0cda34c59e" + integrity sha512-tNP3F552S3pi+CYLZrjAeX4a2ZW5ktOi6UWo99yCMJAp6oUQKbhFxpFw1xoyFZzKtdsKVxtUxewYDNJG0MEItg== -react-native-codegen@^0.0.17: - version "0.0.17" - resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.17.tgz#83fb814d94061cbd46667f510d2ddba35ffb50ac" - integrity sha512-7GIEUmAemH9uWwB6iYXNNsPoPgH06pxzGRmdBzK98TgFBdYJZ7CBuZFPMe4jmHQTPOkQazKZ/w5O6/71JBixmw== - dependencies: - "@babel/parser" "^7.14.0" - flow-parser "^0.121.0" - jscodeshift "^0.13.1" - nullthrows "^1.1.1" +react-native-gradle-plugin@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.7.tgz#96602f909745239deab7b589443f14fce5da2056" + integrity sha512-+4JpbIx42zGTONhBTIXSyfyHICHC29VTvhkkoUOJAh/XHPEixpuBduYgf6Y4y9wsN1ARlQhBBoptTvXvAFQf5g== -react-native-flipper@^0.159.0: - version "0.159.0" - resolved "https://registry.yarnpkg.com/react-native-flipper/-/react-native-flipper-0.159.0.tgz#f730467eb25f91ae21531ae03506391dc1db789b" - integrity sha512-7lFEpbv542q8Bf8qtkPeuq3ICr725F/DUKL1p30uVphT47FIgDpCOA9l/rBZk1/ExaI+BoooikLiz/d+Wthuxg== - -react-native-gradle-plugin@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.5.tgz#1f20d437b140eda65b6e3bdf6eb102bbab1a5a10" - integrity sha512-kGupXo+pD2mB6Z+Oyowor3qlCroiS32FNGoiGQdwU19u8o+NNhEZKwoKfC5Qt03bMZSmFlcAlTyf79vrS2BZKQ== - dependencies: - react-native-codegen "*" - -react-native-gradle-plugin@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.6.tgz#b61a9234ad2f61430937911003cddd7e15c72b45" - integrity sha512-eIlgtsmDp1jLC24dRn43hB3kEcZVqx6DUQbR0N1ABXGnMEafm9I3V3dUUeD1vh+Dy5WqijSoEwLNUPLgu5zDMg== - -react-native-windows@0.68.4: - version "0.68.4" - resolved "https://registry.yarnpkg.com/react-native-windows/-/react-native-windows-0.68.4.tgz#8619b002c05d33e52c3aa0b871ad55789016d8d5" - integrity sha512-i4PMiFbbuKmCcYaMQs0YKpsxl9mn2m8Ij4BnOYtJji4eS108O3/bB/qLd3povWq5qu96D2anpw/KleVozvMBjw== +react-native-windows@^0.69.0: + version "0.69.11" + resolved "https://registry.yarnpkg.com/react-native-windows/-/react-native-windows-0.69.11.tgz#45024d1ef62b5006ea7f1e3e42ad837147c431e2" + integrity sha512-wmc4KeGl0y9vaBYgNZ52SAk6au4Z3J3q6IIxpthJjtiA+ViG2oHHEgas/huyQkoRFq4Jk0ALniO3ojWX7qYuKA== dependencies: "@babel/runtime" "^7.0.0" "@jest/create-cache-key-function" "^27.0.1" - "@react-native-community/cli" "^7.0.3" - "@react-native-community/cli-platform-android" "^7.0.1" - "@react-native-community/cli-platform-ios" "^7.0.1" - "@react-native-windows/cli" "0.68.2" - "@react-native-windows/virtualized-list" "0.68.0" + "@react-native-community/cli" "^8.0.0" + "@react-native-community/cli-platform-android" "^8.0.0" + "@react-native-community/cli-platform-ios" "^8.0.0" + "@react-native-windows/cli" "0.69.3" + "@react-native-windows/virtualized-list" "0.69.1" "@react-native/assets" "1.0.0" "@react-native/normalize-color" "2.0.0" "@react-native/polyfills" "2.0.0" abort-controller "^3.0.0" anser "^1.4.9" base64-js "^1.1.2" - deprecated-react-native-prop-types "^2.3.0" event-target-shim "^5.0.1" hermes-engine "~0.11.0" invariant "^2.2.4" jsc-android "^250230.2.1" - metro-react-native-babel-transformer "0.67.0" - metro-runtime "0.67.0" - metro-source-map "0.67.0" + memoize-one "^5.0.0" + metro-react-native-babel-transformer "0.70.3" + metro-runtime "0.70.3" + metro-source-map "0.70.3" + mkdirp "^0.5.1" nullthrows "^1.1.1" pretty-format "^26.5.2" promise "^8.0.3" - react-devtools-core "^4.23.0" - react-native-codegen "^0.0.13" - react-native-gradle-plugin "^0.0.5" + react-devtools-core "4.24.0" + react-native-codegen "^0.69.1" + react-native-gradle-plugin "^0.0.7" react-refresh "^0.4.0" react-shallow-renderer "16.14.1" regenerator-runtime "^0.13.2" - scheduler "^0.20.2" + scheduler "^0.21.0" + shelljs "^0.8.5" source-map-support "^0.5.19" stacktrace-parser "^0.1.3" - use-subscription "^1.0.0" + use-sync-external-store "^1.0.0" whatwg-fetch "^3.0.0" ws "^6.1.4" -react-native@^0.68.0: - version "0.68.2" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.2.tgz#07547cd31bb9335a7fa4135cfbdc24e067142585" - integrity sha512-qNMz+mdIirCEmlrhapAtAG+SWVx6MAiSfCbFNhfHqiqu1xw1OKXdzIrjaBEPihRC2pcORCoCHduHGQe/Pz9Yuw== +react-native@^0.69.7: + version "0.69.7" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.69.7.tgz#891ba4ed7722f1ab570099ce097c355bef8ceb05" + integrity sha512-T3z2utgRcE/+mMML3Wg4vvpnFoGWJcqWskq+8vdFS4ASM1zYg5Hab5vPlKZp9uncD8weYiGsYwkWXzrvZrsayQ== dependencies: "@jest/create-cache-key-function" "^27.0.1" - "@react-native-community/cli" "^7.0.3" - "@react-native-community/cli-platform-android" "^7.0.1" - "@react-native-community/cli-platform-ios" "^7.0.1" + "@react-native-community/cli" "^8.0.4" + "@react-native-community/cli-platform-android" "^8.0.4" + "@react-native-community/cli-platform-ios" "^8.0.4" "@react-native/assets" "1.0.0" "@react-native/normalize-color" "2.0.0" "@react-native/polyfills" "2.0.0" abort-controller "^3.0.0" anser "^1.4.9" base64-js "^1.1.2" - deprecated-react-native-prop-types "^2.3.0" event-target-shim "^5.0.1" hermes-engine "~0.11.0" invariant "^2.2.4" jsc-android "^250230.2.1" - metro-react-native-babel-transformer "0.67.0" - metro-runtime "0.67.0" - metro-source-map "0.67.0" + memoize-one "^5.0.0" + metro-react-native-babel-transformer "0.70.3" + metro-runtime "0.70.3" + metro-source-map "0.70.3" + mkdirp "^0.5.1" nullthrows "^1.1.1" pretty-format "^26.5.2" - promise "^8.0.3" - react-devtools-core "^4.23.0" - react-native-codegen "^0.0.17" - react-native-gradle-plugin "^0.0.6" + promise "^8.2.0" + react-devtools-core "4.24.0" + react-native-codegen "^0.69.2" + react-native-gradle-plugin "^0.0.7" react-refresh "^0.4.0" - react-shallow-renderer "16.14.1" + react-shallow-renderer "16.15.0" regenerator-runtime "^0.13.2" - scheduler "^0.20.2" + scheduler "^0.21.0" stacktrace-parser "^0.1.3" - use-subscription ">=1.0.0 <1.6.0" + use-sync-external-store "^1.0.0" whatwg-fetch "^3.0.0" ws "^6.1.4" @@ -6194,7 +6100,7 @@ react-shallow-renderer@16.14.1: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0" -react-shallow-renderer@^16.15.0: +react-shallow-renderer@16.15.0, react-shallow-renderer@^16.15.0: version "16.15.0" resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== @@ -6202,22 +6108,21 @@ react-shallow-renderer@^16.15.0: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0 || ^18.0.0" -react-test-renderer@18.1.0: - version "18.1.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.1.0.tgz#35b75754834cf9ab517b6813db94aee0a6b545c3" - integrity sha512-OfuueprJFW7h69GN+kr4Ywin7stcuqaYAt1g7airM5cUgP0BoF5G5CXsPGmXeDeEkncb2fqYNECO4y18sSqphg== +react-test-renderer@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e" + integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA== dependencies: - react-is "^18.1.0" + react-is "^18.2.0" react-shallow-renderer "^16.15.0" - scheduler "^0.22.0" + scheduler "^0.23.0" -react@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" read-pkg-up@^6.0.0: version "6.0.0" @@ -6301,17 +6206,10 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== - dependencies: - "@babel/runtime" "^7.8.4" +regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" @@ -6321,6 +6219,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + regexpu-core@^4.7.1: version "4.8.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" @@ -6383,11 +6286,6 @@ relative-deps@^1.0.7: yargs "^15.0.2" yarn-or-npm "^3.0.1" -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" @@ -6430,10 +6328,10 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== +resolve.exports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" + integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== resolve@^1.1.6: version "1.22.0" @@ -6473,6 +6371,11 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -6480,7 +6383,7 @@ rimraf@^2.5.4, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0: +rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -6499,10 +6402,12 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" -rsvp@^4.8.4: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" @@ -6521,38 +6426,22 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -sane@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" - integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== - dependencies: - "@cnakazawa/watch" "^1.0.3" - anymatch "^2.0.0" - capture-exit "^2.0.0" - exec-sh "^0.3.2" - execa "^1.0.0" - fb-watchman "^2.0.0" - micromatch "^3.1.4" - minimist "^1.1.1" - walker "~1.0.5" - -sax@>=0.6.0, sax@^1.2.1: +sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820" + integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" -scheduler@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8" - integrity sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" @@ -6571,10 +6460,10 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2, semver@^7.3.5: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@^7.3.2, semver@^7.3.5, semver@^7.3.7: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -6663,16 +6552,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" - integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= - dependencies: - array-filter "~0.0.0" - array-map "~0.0.0" - array-reduce "~0.0.0" - jsonify "~0.0.0" - shell-quote@^1.6.1: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" @@ -6683,7 +6562,7 @@ shell-quote@^1.7.3: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== -shelljs@^0.8.4: +shelljs@^0.8.4, shelljs@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== @@ -6707,15 +6586,6 @@ signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -simple-plist@^1.1.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.1.tgz#16e1d8f62c6c9b691b8383127663d834112fb017" - integrity sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw== - dependencies: - bplist-creator "0.1.0" - bplist-parser "0.3.1" - plist "^3.0.5" - sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -6802,7 +6672,7 @@ source-map-support@^0.5.16: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.19: +source-map-support@^0.5.19, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -6905,11 +6775,6 @@ static-extend@^0.1.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stream-buffers@2.2.x: - version "2.2.0" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" - integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -6992,7 +6857,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -7006,14 +6871,6 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -7046,13 +6903,15 @@ temp@^0.8.4: dependencies: rimraf "~2.6.2" -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== +terser@^5.15.0: + version "5.16.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.2.tgz#8f495819439e8b5c150e7530fc434a6e70ea18b2" + integrity sha512-JKuM+KvvWVqT7muHVyrwv7FVRPnmHDwF6XwoIxdbF5Witi0vu99RYpxDexpJndXt3jbZZmmWr2/mQa6HvSNdSg== dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" test-exclude@^6.0.0: version "6.0.0" @@ -7137,6 +6996,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + tslib@^2.0.1: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" @@ -7147,6 +7011,13 @@ tslib@^2.2.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + tunnel@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" @@ -7185,11 +7056,6 @@ uglify-es@^3.1.9: commander "~2.13.0" source-map "~0.6.1" -ultron@1.0.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" - integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= - unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -7241,17 +7107,23 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +update-browserslist-db@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" + integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -"use-subscription@>=1.0.0 <1.6.0", use-subscription@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" - integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== - dependencies: - object-assign "^4.1.1" +use-sync-external-store@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== use@^3.1.0: version "3.1.1" @@ -7281,20 +7153,15 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - uuid@^8.3.0: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-to-istanbul@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.0.tgz#be0dae58719fc53cb97e5c7ac1d7e6d4f5b19511" - integrity sha512-HcvgY/xaRm7isYmyx+lFKA4uQmfUbN0J4M0nNItvzTvH/iQ9kW5j/t4YSR+Ge323/lrgDAWJoF46tzGQHwBHFw== +v8-to-istanbul@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" + integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== dependencies: "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" @@ -7318,7 +7185,7 @@ vlq@^1.0.0: resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== -walker@^1.0.7, walker@~1.0.5: +walker@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= @@ -7413,22 +7280,14 @@ write-file-atomic@^2.3.0: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write-file-atomic@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" - integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" - integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== - dependencies: - options ">=0.0.5" - ultron "1.0.x" - ws@^6.1.4: version "6.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" @@ -7446,14 +7305,6 @@ ws@^7.5.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a" integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw== -xcode@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/xcode/-/xcode-3.0.1.tgz#3efb62aac641ab2c702458f9a0302696146aa53c" - integrity sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA== - dependencies: - simple-plist "^1.1.0" - uuid "^7.0.3" - xml-formatter@^2.4.0: version "2.6.1" resolved "https://registry.yarnpkg.com/xml-formatter/-/xml-formatter-2.6.1.tgz#066ef3a100bd58ee3b943f0c503be63176d3d497" @@ -7491,13 +7342,6 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== -xmldoc@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.1.2.tgz#6666e029fe25470d599cd30e23ff0d1ed50466d7" - integrity sha512-ruPC/fyPNck2BD1dpz0AZZyrEwMOrWTO5lDdIXS91rs3wtm4j+T8Rp2o+zoOYkkAxJTZRPOSnOGei1egoRmKMQ== - dependencies: - sax "^1.2.1" - xpath@^0.0.27: version "0.0.27" resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92" @@ -7518,6 +7362,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" @@ -7536,6 +7385,11 @@ yargs-parser@^21.0.0: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs@^15.0.2, yargs@^15.1.0, yargs@^15.3.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" @@ -7566,6 +7420,19 @@ yargs@^17.3.1: y18n "^5.0.5" yargs-parser "^21.0.0" +yargs@^17.6.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + 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" + yarn-or-npm@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/yarn-or-npm/-/yarn-or-npm-3.0.1.tgz#6336eea4dff7e23e226acc98c1a8ada17a1b8666" diff --git a/react-native/react-native-flipper/ios/FlipperModule.m b/react-native/react-native-flipper/ios/FlipperModule.m index fc69190ea..e56247f00 100644 --- a/react-native/react-native-flipper/ios/FlipperModule.m +++ b/react-native/react-native-flipper/ios/FlipperModule.m @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifdef DEBUG +#if defined(DEBUG) || defined(FB_SONARKIT_ENABLED) #import "FlipperModule.h" #import "FlipperReactNativeJavaScriptPluginManager.h" diff --git a/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPlugin.h b/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPlugin.h index 73fc6a1c8..d3f3fae28 100644 --- a/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPlugin.h +++ b/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPlugin.h @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#if DEBUG +#if defined(DEBUG) || defined(FB_SONARKIT_ENABLED) #import #import @@ -30,4 +30,4 @@ NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END -#endif +#endif // DEBUG || FB_SONARKIT_ENABLED diff --git a/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPlugin.m b/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPlugin.m index 153a846cb..f46d4e9d9 100644 --- a/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPlugin.m +++ b/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPlugin.m @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#if DEBUG +#if defined(DEBUG) || defined(FB_SONARKIT_ENABLED) #import "FlipperReactNativeJavaScriptPlugin.h" @@ -64,4 +64,4 @@ @end -#endif +#endif // DEBUG || FB_SONARKIT_ENABLED diff --git a/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPluginManager.h b/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPluginManager.h index a5bcd5db8..ed495fc4b 100644 --- a/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPluginManager.h +++ b/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPluginManager.h @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifdef DEBUG +#if defined(DEBUG) || defined(FB_SONARKIT_ENABLED) #import @@ -47,4 +47,4 @@ NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END -#endif +#endif // DEBUG || FB_SONARKIT_ENABLED diff --git a/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPluginManager.m b/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPluginManager.m index 5aeb56e9d..ee7847373 100644 --- a/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPluginManager.m +++ b/react-native/react-native-flipper/ios/FlipperReactNativeJavaScriptPluginManager.m @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#ifdef DEBUG +#if defined(DEBUG) || defined(FB_SONARKIT_ENABLED) #import "FlipperReactNativeJavaScriptPluginManager.h" #import @@ -150,4 +150,4 @@ static uint32_t FlipperResponderKeyGenerator = 0; @end -#endif +#endif // DEBUG || FB_SONARKIT_ENABLED diff --git a/react-native/react-native-flipper/package.json b/react-native/react-native-flipper/package.json index dd0b81f27..e38fb5494 100644 --- a/react-native/react-native-flipper/package.json +++ b/react-native/react-native-flipper/package.json @@ -1,7 +1,7 @@ { "name": "react-native-flipper", "title": "React Native Flipper Bindings", - "version": "0.162.0", + "version": "0.227.0", "description": "Flipper bindings for React Native", "main": "index.js", "types": "index.d.ts", @@ -28,7 +28,7 @@ "react-native": ">0.62.0" }, "devDependencies": { - "metro-config": "^0.72.0", + "metro-config": "^0.76.0", "react": "^17.0.2", "react-native": "^0.68.2", "react-native-windows": "0.68.2" @@ -38,7 +38,6 @@ "minimist": "1.2.3", "kind-of": "6.0.3", "node-fetch": "^2.6.7", - "xmldom": "github:xmldom/xmldom#0.7.0", "browserslist": "^4.16.5", "ws": "7.4.6" } diff --git a/react-native/react-native-flipper/react-native-flipper.podspec b/react-native/react-native-flipper/react-native-flipper.podspec index a8b591af0..f76436e22 100644 --- a/react-native/react-native-flipper/react-native-flipper.podspec +++ b/react-native/react-native-flipper/react-native-flipper.podspec @@ -25,6 +25,10 @@ Pod::Spec.new do |s| s.source_files = "ios/**/*.{h,m,swift}" s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"${PODS_ROOT}/Headers/Public/FlipperKit\"" } s.requires_arc = true - s.compiler_flags = compiler_flags + if ENV['PRODUCTION'] == '1' + Pod::UI.puts "#{s.name}: Found PRODUCTION=1, #{s.name} will be disabled for production builds" + else + s.compiler_flags = compiler_flags + end s.dependency "React-Core" end diff --git a/react-native/react-native-flipper/react-native.config.js b/react-native/react-native-flipper/react-native.config.js deleted file mode 100644 index 9b962ed1c..000000000 --- a/react-native/react-native-flipper/react-native.config.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -module.exports = { - dependency: { - platforms: { - ios: { - // This file should not be needed at all, but without it. - // cli 4.2.2 will search for an xcode project - project: '.', - }, - }, - }, -}; diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactBaseSocket.h b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactBaseSocket.h index 84c49c687..7ed525c03 100644 --- a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactBaseSocket.h +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactBaseSocket.h @@ -65,7 +65,7 @@ class FlipperReactBaseSocket { messageHandler_ = std::move(messageHandler); } - virtual bool connect(FlipperConnectionManager* manager) = 0; + virtual void connect(FlipperConnectionManager* manager) = 0; virtual void disconnect() = 0; virtual void send( diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocket.cpp b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocket.cpp index 90ce96e64..4faf1959d 100644 --- a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocket.cpp +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocket.cpp @@ -55,8 +55,8 @@ void FlipperReactSocket::setMessageHandler( socket_->setMessageHandler(messageHandler); } -bool FlipperReactSocket::connect(FlipperConnectionManager* manager) { - return socket_->connect(manager); +void FlipperReactSocket::connect(FlipperConnectionManager* manager) { + socket_->connect(manager); } void FlipperReactSocket::disconnect() { diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocket.h b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocket.h index 9f0523a03..46b3afcef 100644 --- a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocket.h +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocket.h @@ -37,7 +37,7 @@ class FlipperReactSocket : public FlipperSocket { virtual void setEventHandler(SocketEventHandler eventHandler) override; virtual void setMessageHandler(SocketMessageHandler messageHandler) override; - virtual bool connect(FlipperConnectionManager* manager) override; + virtual void connect(FlipperConnectionManager* manager) override; virtual void disconnect() override; virtual void send(const folly::dynamic& message, SocketSendHandler completion) diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocketClient.cpp b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocketClient.cpp index b0ea8c107..d5a2c768b 100644 --- a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocketClient.cpp +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocketClient.cpp @@ -134,9 +134,9 @@ FlipperReactSocketClient::getClientCertificate() { return installClientCertificate(); } -bool FlipperReactSocketClient::connect(FlipperConnectionManager* manager) { +void FlipperReactSocketClient::connect(FlipperConnectionManager* manager) { if (status_ != Status::Unconnected) { - return false; + return; } status_ = Status::Connecting; @@ -176,30 +176,30 @@ bool FlipperReactSocketClient::connect(FlipperConnectionManager* manager) { socket_.Closed({this, &FlipperReactSocketClient::OnWebSocketClosed}); try { - this->socket_.ConnectAsync(winrt::Windows::Foundation::Uri(uri)) - .wait_for(std::chrono::seconds(10)); - status_ = Status::Initializing; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::OPEN); }); - return true; + this->socket_.ConnectAsync(winrt::Windows::Foundation::Uri(uri)) + .Completed([&](auto&& asyncInfo, auto&& asyncStatus) { + if (asyncStatus == + winrt::Windows::Foundation::AsyncStatus::Completed) { + eventHandler_(SocketEvent::OPEN); + } else { + eventHandler_(SocketEvent::ERROR); + } + }); + } catch (winrt::hresult_error const& ex) { winrt::Windows::Web::WebErrorStatus webErrorStatus{ winrt::Windows::Networking::Sockets::WebSocketError::GetStatus( ex.to_abi())}; + socket_ = nullptr; + status_ = Status::Unconnected; + eventHandler_(SocketEvent::ERROR); } - - disconnect(); - - return false; } void FlipperReactSocketClient::disconnect() { status_ = Status::Closed; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); }); - // socket_.Close(); socket_ = nullptr; } @@ -269,14 +269,11 @@ void FlipperReactSocketClient::OnWebSocketMessageReceived( const std::string payload = winrt::to_string(message); if (overrideHandler_ != nullptr) { - scheduler_->schedule([payload, messageHandler = *overrideHandler_]() { - messageHandler(payload, false); - }); + auto messageHandler = *overrideHandler_; + messageHandler(payload, false); overrideHandler_ = nullptr; } else if (messageHandler_) { - scheduler_->schedule([payload, messageHandler = messageHandler_]() { - messageHandler(payload); - }); + messageHandler_(payload); } } catch (winrt::hresult_error const& ex) { // winrt::Windows::Web::WebErrorStatus webErrorStatus{ @@ -291,8 +288,7 @@ void FlipperReactSocketClient::OnWebSocketClosed( return; } status_ = Status::Closed; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); }); + eventHandler_(SocketEvent::CLOSE); } } // namespace flipper diff --git a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocketClient.h b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocketClient.h index 44187aada..65c1ee3ab 100644 --- a/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocketClient.h +++ b/react-native/react-native-flipper/windows/ReactNativeFlipper/FlipperReactSocketClient.h @@ -40,7 +40,7 @@ class FlipperReactSocketClient : public FlipperReactBaseSocket { virtual ~FlipperReactSocketClient(); - virtual bool connect(FlipperConnectionManager* manager) override; + virtual void connect(FlipperConnectionManager* manager) override; virtual void disconnect() override; virtual void send(const folly::dynamic& message, SocketSendHandler completion) @@ -61,7 +61,6 @@ class FlipperReactSocketClient : public FlipperReactBaseSocket { args); private: - std::promise connected_; winrt::Windows::Networking::Sockets::MessageWebSocket socket_; winrt::event_token messageReceivedEventToken_; winrt::event_token closedEventToken_; diff --git a/react-native/react-native-flipper/yarn.lock b/react-native/react-native-flipper/yarn.lock index d64647d2e..fb630129d 100644 --- a/react-native/react-native-flipper/yarn.lock +++ b/react-native/react-native-flipper/yarn.lock @@ -106,6 +106,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab" integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw== +"@babel/compat-data@^7.20.5": + version "7.20.14" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" + integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== + "@babel/core@^7.13.16": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.10.tgz#74ef0fbf56b7dfc3f198fc2d927f4f03e12f4b05" @@ -148,6 +153,27 @@ json5 "^2.2.1" semver "^6.3.0" +"@babel/core@^7.20.0": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" + integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helpers" "^7.20.7" + "@babel/parser" "^7.20.7" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.12" + "@babel/types" "^7.20.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + "@babel/generator@^7.10.1": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9" @@ -194,6 +220,15 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.20.0", "@babel/generator@^7.20.7": + version "7.20.14" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" + integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== + dependencies: + "@babel/types" "^7.20.7" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + "@babel/generator@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.4.tgz#db651e2840ca9aa66f327dcec1dc5f5fa9611369" @@ -277,6 +312,17 @@ browserslist "^4.20.2" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" + integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== + dependencies: + "@babel/compat-data" "^7.20.5" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.10.1": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz#7474295770f217dbcf288bf7572eb213db46ee67" @@ -398,6 +444,14 @@ "@babel/template" "^7.18.6" "@babel/types" "^7.18.9" +"@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + "@babel/helper-function-name@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e" @@ -491,6 +545,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-module-imports@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz#e5a92529f8888bf319a6376abfbd1cebc491ad91" @@ -526,6 +587,20 @@ "@babel/traverse" "^7.18.0" "@babel/types" "^7.18.0" +"@babel/helper-module-transforms@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" + integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.10" + "@babel/types" "^7.20.7" + "@babel/helper-module-transforms@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.4.tgz#8d7cdb1e1f8ea3d8c38b067345924ac4f8e0879a" @@ -653,6 +728,13 @@ dependencies: "@babel/types" "^7.17.0" +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + "@babel/helper-simple-access@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz#a169a0adb1b5f418cfc19f22586b2ebf58a9a294" @@ -703,6 +785,11 @@ dependencies: "@babel/types" "^7.7.4" +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + "@babel/helper-validator-identifier@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" @@ -723,11 +810,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== +"@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + "@babel/helper-validator-option@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + "@babel/helper-wrap-function@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.15.4.tgz#6f754b2446cfaf3d612523e6ab8d79c27c3a3de7" @@ -766,6 +863,15 @@ "@babel/traverse" "^7.18.2" "@babel/types" "^7.18.2" +"@babel/helpers@^7.20.7": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" + integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.13" + "@babel/types" "^7.20.7" + "@babel/highlight@^7.0.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" @@ -836,6 +942,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== +"@babel/parser@^7.20.0", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": + version "7.20.15" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" + integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== + "@babel/parser@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.4.tgz#75ab2d7110c2cf2fa949959afb05fa346d2231bb" @@ -891,6 +1002,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" +"@babel/plugin-proposal-numeric-separator@^7.0.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread@^7.0.0": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.4.tgz#cc57849894a5c774214178c8ab64f6334ec8af71" @@ -959,6 +1078,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-flow@^7.12.1", "@babel/plugin-syntax-flow@^7.18.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" + integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-flow@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" @@ -1001,6 +1127,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz#47cf220d19d6d0d7b154304701f468fc1cc6ff46" @@ -1391,6 +1524,15 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/template@^7.18.10", "@babel/template@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@babel/template@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" @@ -1478,6 +1620,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.20.0", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" + integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.13" + "@babel/types" "^7.20.7" + debug "^4.1.0" + globals "^11.1.0" + "@babel/traverse@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" @@ -1543,6 +1701,15 @@ "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" +"@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" + integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@hapi/hoek@^9.0.0": version "9.1.1" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.1.tgz#9daf5745156fd84b8e9889a2dc721f0c58e894aa" @@ -1636,6 +1803,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.13" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" @@ -2051,6 +2226,11 @@ accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +acorn@^8.5.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + anser@^1.4.9: version "1.4.9" resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760" @@ -2075,6 +2255,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2255,6 +2440,13 @@ babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== +babel-plugin-transform-flow-enums@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz#d1d0cc9bdc799c850ca110d0ddc9f21b9ec3ef25" + integrity sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ== + dependencies: + "@babel/plugin-syntax-flow" "^7.12.1" + babel-preset-fbjs@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" @@ -2370,7 +2562,7 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.16.5, browserslist@^4.20.2: +browserslist@^4.16.5, browserslist@^4.20.2, browserslist@^4.21.3: version "4.16.6" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== @@ -2533,6 +2725,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -2610,7 +2811,7 @@ command-exists@^1.2.8: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -commander@^2.19.0: +commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3138,7 +3339,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2, fsevents@^2.3.2: +fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -3153,7 +3354,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -3366,6 +3567,13 @@ image-size@^0.6.0: resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== +image-size@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.2.tgz#d778b6d0ab75b2737c1556dd631652eb963bc486" + integrity sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg== + dependencies: + queue "6.0.2" + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -3600,7 +3808,7 @@ jest-regex-util@^27.0.6, jest-regex-util@^27.5.1: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-serializer@^27.0.6, jest-serializer@^27.5.1: +jest-serializer@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== @@ -3729,6 +3937,11 @@ json5@^2.2.1: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -3843,6 +4056,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3917,14 +4137,14 @@ metro-babel-transformer@0.67.0: metro-source-map "0.67.0" nullthrows "^1.1.1" -metro-babel-transformer@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.72.0.tgz#18a425865fcdc817363e0982f6d1e3f6a1f8ee74" - integrity sha512-bezw+WQo7S+XmOqlIQ699TVWhTrHAEVtOZdK1JQeBTlxE4dvFu5xl8G2xYOvcmlE3UFbFZVjxD1kwKxRuFk0vQ== +metro-babel-transformer@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.76.4.tgz#d5ebcae4dd1ba1b15eb7de288d0347f5f52d1432" + integrity sha512-VTvCj6wnYfg5TeFrASegdGqPrdoPsfPbS0g0yd3VzZlDJpMN+qhYH5Qn0ERFK5I0LLedjHlOrSZzOhlElFEbPw== dependencies: - "@babel/core" "^7.14.0" + "@babel/core" "^7.20.0" hermes-parser "0.8.0" - metro-source-map "0.72.0" + metro-source-map "0.76.4" nullthrows "^1.1.1" metro-cache-key@0.67.0: @@ -3932,10 +4152,10 @@ metro-cache-key@0.67.0: resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.67.0.tgz#4df6a73cced199e1bddd0f3454bb931a27141eeb" integrity sha512-FNJe5Rcb2uzY6G6tsqCf0RV4t2rCeX6vSHBxmP7k+4aI4NqX4evtPI0K82r221nBzm5DqNWCURZ0RYUT6jZMGA== -metro-cache-key@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.72.0.tgz#347eebbe5a7b806f675e52890722ab1b88c953d9" - integrity sha512-zoZ2SXzjJeXkwGikx2V97r7D/yo6rZOdM9iARKugj+Dk+9W+VdehiGNnmIwKmbIudCWulWOpWkJOeygeUkTfIw== +metro-cache-key@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.76.4.tgz#684d7ffd2b2936be824475296eaaf9a1374b9835" + integrity sha512-bh7GFqPg4+IKolI0ybalotqyMU/nbHxyXrKad7bwhUI0z6RSNv0ImXGtDWzzMQ6tCPV2MTCDzzSPLEXLeUVH8A== metro-cache@0.67.0: version "0.67.0" @@ -3946,13 +4166,13 @@ metro-cache@0.67.0: mkdirp "^0.5.1" rimraf "^2.5.4" -metro-cache@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.72.0.tgz#423e38d9c45b815ff1ef2800a60f5a3c1bc333b0" - integrity sha512-Ui7qilKLjDMdc+IlkyhQXQ0wVAz9ji6qAyUC6q6WUkEyo9fp3j1Bv2c7MogXeGoRLkfw0YGXa3DFqZG8vBNDbg== +metro-cache@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.76.4.tgz#cfd35f817c06148c5f53ea25b737469abce14447" + integrity sha512-pWQTKbA92XUhg9QVCwDsKwBxyZrjYPs8fYCnqB1phF+r26Vj4VYVKLAZam4lxFXC+awiEpYzpgmQkfwqODZE0g== dependencies: - metro-core "0.72.0" - rimraf "^2.5.4" + metro-core "0.76.4" + rimraf "^3.0.2" metro-config@0.67.0, metro-config@^0.67.0: version "0.67.0" @@ -3966,17 +4186,17 @@ metro-config@0.67.0, metro-config@^0.67.0: metro-core "0.67.0" metro-runtime "0.67.0" -metro-config@0.72.0, metro-config@^0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.72.0.tgz#c027dc429d1750a6d44033fb2f5bf11c0cc4df4c" - integrity sha512-hdJUbASzO/zPo6Ip9s7FH+nnWhEqPc610xzQUBjP5PZlpfl4sOqTn+cpmy2fk1LdMV8x4Wr6y6lsEJI+pbVDKA== +metro-config@0.76.4, metro-config@^0.76.0: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.76.4.tgz#28753ab0671705914c75a945e85f671922ea128f" + integrity sha512-o0rK5HnQZoCI6xfVgtjkMyBVcj6UZ4c6lbr8MwRzjIH6rKyoetxdM6OCnnBcZgCmid10apYdveaIQBJ7KYCNlg== dependencies: cosmiconfig "^5.0.5" jest-validate "^26.5.2" - metro "0.72.0" - metro-cache "0.72.0" - metro-core "0.72.0" - metro-runtime "0.72.0" + metro "0.76.4" + metro-cache "0.76.4" + metro-core "0.76.4" + metro-runtime "0.76.4" metro-core@0.67.0, metro-core@^0.67.0: version "0.67.0" @@ -3987,44 +4207,39 @@ metro-core@0.67.0, metro-core@^0.67.0: lodash.throttle "^4.1.1" metro-resolver "0.67.0" -metro-core@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.72.0.tgz#1eb6cdde21293a7bd7a188c419780180004e9925" - integrity sha512-MeEdRBl1OQxUukI2VBAOFjUl4hU2Ll9+I72WGX1mz9qqCrslJ7++4tEmnTplbLaYBSPb0PSH3+zC073aL1yeRg== +metro-core@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.76.4.tgz#51aa3065ec324112827d44f881e39a38bdd8813a" + integrity sha512-zrWl2MLvW8Nxa4sX5wnPm1cPvAu5J3DmnUMHK5PHELgJGtb5XNV7UibxmI/6zM423OQH1KPY7Hh5l3qkn4cDRQ== dependencies: lodash.throttle "^4.1.1" - metro-resolver "0.72.0" + metro-resolver "0.76.4" -metro-file-map@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.72.0.tgz#943f068f0cefe70d37d9fda13dd21ec94fdecf2e" - integrity sha512-bKkwgrrGOlGtdhuYqqdaiiNH7dRKLDgNNgVps9uOBqpJMX/d8/f+4uurRAFSZeRcJGZ1bu7ljvFg9+8xYZlKig== +metro-file-map@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.76.4.tgz#c355018f176900a21cf2cf95a46b0c9a3012eaec" + integrity sha512-k/FPwqdZVYnyaB3Ar+dlA37RVs6TNx8hBkGj28gQE/8E2bfDDRSFlCPe69M92fcSUZDVPZqC2vS7lDmUa3xxQQ== dependencies: - abort-controller "^3.0.0" anymatch "^3.0.3" debug "^2.2.0" fb-watchman "^2.0.0" graceful-fs "^4.2.4" invariant "^2.2.4" jest-regex-util "^27.0.6" - jest-serializer "^27.0.6" jest-util "^27.2.0" jest-worker "^27.2.0" micromatch "^4.0.4" + node-abort-controller "^3.1.1" + nullthrows "^1.1.1" walker "^1.0.7" optionalDependencies: - fsevents "^2.1.2" + fsevents "^2.3.2" metro-hermes-compiler@0.67.0: version "0.67.0" resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.67.0.tgz#9c1340f1882fbf535145868d0d28211ca15b0477" integrity sha512-X5Pr1jC8/kO6d1EBDJ6yhtuc5euHX89UDNv8qdPJHAET03xfFnlojRPwOw6il2udAH20WLBv+F5M9VY+58zspQ== -metro-hermes-compiler@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-hermes-compiler/-/metro-hermes-compiler-0.72.0.tgz#b754df502e48c02c9523b92ef1ed5e19b1d94921" - integrity sha512-QkY8rHFKhjVlrrxB2FEj8bGzYVHNTOx/IENSlKx3PxlchFMGCG8fxVgzB8u5VyIW610hKd5d+Ux2pXldtm/vSA== - metro-inspector-proxy@0.67.0: version "0.67.0" resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.67.0.tgz#22b360a837b07e9e2bc87a71af6154dd8fcc02a5" @@ -4035,15 +4250,23 @@ metro-inspector-proxy@0.67.0: ws "^7.5.1" yargs "^15.3.1" -metro-inspector-proxy@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.72.0.tgz#a0cb144af9cce828731d2fccde143e0fd4e42286" - integrity sha512-GIToyQr9xkap/ayt1MVMCU7/ZecVsPrCqOjL58TgXfE/5L7wF87p+mPy8nqceHAqgajCukfQC4n5McwxFEp4KA== +metro-inspector-proxy@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.76.4.tgz#0d609cdbb8a31ac43665fb567079adee47e7f385" + integrity sha512-gsnUagRBoNgaWjcwD9eeweJwAw6pvMxKywZNM5HBvU2FfXnJT1BrHZznaL5ABCPhJKHcO9MvnJT2fo6x09owfg== dependencies: connect "^3.6.5" debug "^2.2.0" + node-fetch "^2.2.0" ws "^7.5.1" - yargs "^15.3.1" + yargs "^17.6.2" + +metro-minify-terser@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.76.4.tgz#e02bc231a39f23c9dad513abf42ca50b2c111887" + integrity sha512-u4MqTTE30UkUV3zqj3wX1QWqXc6SS2uLRk1DQCelmB6fLzqVQR0xe00hlnzkxc/V4w8x19Bs00lPpAEOzGXNvQ== + dependencies: + terser "^5.15.0" metro-minify-uglify@0.67.0: version "0.67.0" @@ -4052,10 +4275,10 @@ metro-minify-uglify@0.67.0: dependencies: uglify-es "^3.1.9" -metro-minify-uglify@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.72.0.tgz#1a4a0f1734c841771f60aa5664b27d922dc69ba8" - integrity sha512-3NtBGA1w9+9524bTCvsRajq+YqEw/p/MQTeZ1746Qs8QjtrvXgxGlCeRvH/6yhQpss+LnXmiPO60Ql9YvT8yYw== +metro-minify-uglify@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.76.4.tgz#62604b972caf114baf1a968155fd27e876017432" + integrity sha512-LNGEYzk12xLld63NrMEnJdmdwk6nife9cylahm0V3SOa8wN16j9004aGywrBajOJEAqoF4nbrVgzfAs2tB3nZA== dependencies: uglify-es "^3.1.9" @@ -4105,22 +4328,23 @@ metro-react-native-babel-preset@0.67.0: "@babel/template" "^7.0.0" react-refresh "^0.4.0" -metro-react-native-babel-preset@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.72.0.tgz#7cbb0fa9cc918aca372c40791291f159e7cd2d36" - integrity sha512-AcS5X/eacckVj6oX8G0/KT4Q9+PsZoUchTe5xnBimHAJ1ZjOhjnHSkdAM7+MHRQlp4L63pCSp0/bsJoDbF6X1g== +metro-react-native-babel-preset@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.4.tgz#f9422a62348c3fe9c2f733fe005e75b97345f4b1" + integrity sha512-BKyfPz7mn3ncgRjqi8Z9nYLbzuuiBWns2AAEIGctQdz9OMMAisPlZmWscv09UjhPXkQc/XtToFETVN7fmHMfug== dependencies: - "@babel/core" "^7.14.0" + "@babel/core" "^7.20.0" "@babel/plugin-proposal-async-generator-functions" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-export-default-from" "^7.0.0" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-proposal-numeric-separator" "^7.0.0" "@babel/plugin-proposal-object-rest-spread" "^7.0.0" "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" "@babel/plugin-proposal-optional-chaining" "^7.0.0" "@babel/plugin-syntax-dynamic-import" "^7.0.0" "@babel/plugin-syntax-export-default-from" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.2.0" + "@babel/plugin-syntax-flow" "^7.18.0" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" "@babel/plugin-syntax-optional-chaining" "^7.0.0" "@babel/plugin-transform-arrow-functions" "^7.0.0" @@ -4129,7 +4353,6 @@ metro-react-native-babel-preset@0.72.0: "@babel/plugin-transform-classes" "^7.0.0" "@babel/plugin-transform-computed-properties" "^7.0.0" "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator" "^7.0.0" "@babel/plugin-transform-flow-strip-types" "^7.0.0" "@babel/plugin-transform-function-name" "^7.0.0" "@babel/plugin-transform-literals" "^7.0.0" @@ -4144,10 +4367,10 @@ metro-react-native-babel-preset@0.72.0: "@babel/plugin-transform-shorthand-properties" "^7.0.0" "@babel/plugin-transform-spread" "^7.0.0" "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" "@babel/plugin-transform-typescript" "^7.5.0" "@babel/plugin-transform-unicode-regex" "^7.0.0" "@babel/template" "^7.0.0" + babel-plugin-transform-flow-enums "^0.0.2" react-refresh "^0.4.0" metro-react-native-babel-transformer@0.67.0, metro-react-native-babel-transformer@^0.67.0: @@ -4170,24 +4393,23 @@ metro-resolver@0.67.0, metro-resolver@^0.67.0: dependencies: absolute-path "^0.0.0" -metro-resolver@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.72.0.tgz#a0877267a16ab3fdd730a5bcabde08c8f437e108" - integrity sha512-aQ3ILLFs6e7lju60WVhU3lxROJ+fZluiOncqLq0o0QECCnrEbWQTRVMQmDTpq5cI/w7k6tSbNMhlUzPPLFI5Uw== - dependencies: - absolute-path "^0.0.0" +metro-resolver@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.76.4.tgz#34bf2c9f8c9bacbc71e7ecbfb6b64e998c5910ff" + integrity sha512-SXVNS+j055FLiAWx5Lems4Yw/dMxm6uo8j2ew76C+Vib++aSA/hcEoYHy1GB63jG6FmymPH3yrnyX0F6mcRnsQ== metro-runtime@0.67.0, metro-runtime@^0.67.0: version "0.67.0" resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.67.0.tgz#a8888dfd06bcebbac3c99dcac7cd622510dd8ee0" integrity sha512-IFtSL0JUt1xK3t9IoLflTDft82bjieSzdIJWLzrRzBMlesz8ox5bVmnpQbVQEwfYUpEOxbM3VOZauVbdCmXA7g== -metro-runtime@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.72.0.tgz#f72b158fc5072a83b6274d6eae1db41fb065d77e" - integrity sha512-ZY372filZa8KApndcC2MuuObUufW8A4UEYHaKv6onIT0maCX2mzNGaDIiOXs6i5gHjb801qaR0a3UqCVxhGgeQ== +metro-runtime@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.76.4.tgz#10cf5d9b6be7a52d4990a40f0183e994b9338201" + integrity sha512-ngNjwPTUrU3thjPZq+0zw/kwFHCtSx4WTIbxw3DNAyxzrgimgoYqtJb9KVFbE/d4Juan+JHymSDtovJstOMjmw== dependencies: "@babel/runtime" "^7.0.0" + react-refresh "^0.4.0" metro-source-map@0.67.0: version "0.67.0" @@ -4203,17 +4425,17 @@ metro-source-map@0.67.0: source-map "^0.5.6" vlq "^1.0.0" -metro-source-map@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.72.0.tgz#1f15e838a81060573a0d0011a726001a9a08ad29" - integrity sha512-uLeyQu2pyW9IlelWX1CJx4PfGYZTbKHKQ8NVFutv+j299UgUvcav+zUG7mvqrYZH4JX4YbnnD9ppqOxiFuq1IA== +metro-source-map@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.76.4.tgz#ab24b3969db4cc8d63015ed95402e1afde29a0a2" + integrity sha512-+nWoqIhzEwi6adSYHpmJN9KUfrW1Gpm17ZpE0JpkWHYvzKs9PDGvQkrd0lYmFiW5Cnfxm8D3rdVqAaH9rYincw== dependencies: - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.0.0" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" invariant "^2.2.4" - metro-symbolicate "0.72.0" + metro-symbolicate "0.76.4" nullthrows "^1.1.1" - ob1 "0.72.0" + ob1 "0.76.4" source-map "^0.5.6" vlq "^1.0.0" @@ -4229,13 +4451,13 @@ metro-symbolicate@0.67.0: through2 "^2.0.1" vlq "^1.0.0" -metro-symbolicate@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.72.0.tgz#ca46943d522cef8123735b71fb81b57cc867c48f" - integrity sha512-+sTL57kzRVnYdNfjaGbNCO0RLQowGN+U5bpkhOcB6Ax7Dn+9vLVwLaBMSNQIBfiua1a88Z4GFRvFiPNHR8hYKw== +metro-symbolicate@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.76.4.tgz#af0f68ed4b0196091d5c0d7c72b9b7dcdf2e8d69" + integrity sha512-k2WkvGNx35PkLwucf20E1uvC2CB2AI+svoBY5T2c864gf77bh94t8cM9/Bi3up36QG5001pHtxTXS4frb59Mew== dependencies: invariant "^2.2.4" - metro-source-map "0.72.0" + metro-source-map "0.76.4" nullthrows "^1.1.1" source-map "^0.5.6" through2 "^2.0.1" @@ -4252,15 +4474,15 @@ metro-transform-plugins@0.67.0: "@babel/traverse" "^7.14.0" nullthrows "^1.1.1" -metro-transform-plugins@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.72.0.tgz#d495c6a535da33d986430dc0ef6eee7549dbe47b" - integrity sha512-IV7znwPOKusmfRbZi/TLUrdJqQTZBaufNZJ+evW+yM7Vjy4mGHy1GDlgOkGiMjzjIqB8yFSNh1oVo3q7LJlxlA== +metro-transform-plugins@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.76.4.tgz#31d74ef1c6431dd371f90662f78a3ec034fbfea2" + integrity sha512-nrM0raOYQDWKnfvC6RQSlZtznID2Zfkg6U+RnJATeJbdKekouHbe5mmTC10nH7uxJI9hXHbKMNhyWFcImmmvjQ== dependencies: - "@babel/core" "^7.14.0" - "@babel/generator" "^7.14.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" "@babel/template" "^7.0.0" - "@babel/traverse" "^7.14.0" + "@babel/traverse" "^7.20.0" nullthrows "^1.1.1" metro-transform-worker@0.67.0: @@ -4282,23 +4504,22 @@ metro-transform-worker@0.67.0: metro-transform-plugins "0.67.0" nullthrows "^1.1.1" -metro-transform-worker@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.72.0.tgz#3b9dd519b6383b32ea82e3deeb2229de2aa25fe5" - integrity sha512-QUFXeU4R4xgLkxczZUmI63XHbu418YlEh2km1c2vUwuDpEYvKndBi15tlwBkXU5uaWWqUVA59FnJdccExWtD8Q== +metro-transform-worker@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.76.4.tgz#ee5b531e86d90f173356e93dc8465d06c36c2cb0" + integrity sha512-KvBUmgG9Z4fjbl2o6R1MZzsESIUVYw8XjfMdaBISP+yoYHoBLh841yzhCGWBc3JI8fQ86StxBMztlKCdxv0AWA== dependencies: - "@babel/core" "^7.14.0" - "@babel/generator" "^7.14.0" - "@babel/parser" "^7.14.0" - "@babel/types" "^7.0.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" + "@babel/types" "^7.20.0" babel-preset-fbjs "^3.4.0" - metro "0.72.0" - metro-babel-transformer "0.72.0" - metro-cache "0.72.0" - metro-cache-key "0.72.0" - metro-hermes-compiler "0.72.0" - metro-source-map "0.72.0" - metro-transform-plugins "0.72.0" + metro "0.76.4" + metro-babel-transformer "0.76.4" + metro-cache "0.76.4" + metro-cache-key "0.76.4" + metro-source-map "0.76.4" + metro-transform-plugins "0.76.4" nullthrows "^1.1.1" metro@0.67.0, metro@^0.67.0: @@ -4358,19 +4579,18 @@ metro@0.67.0, metro@^0.67.0: ws "^7.5.1" yargs "^15.3.1" -metro@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.72.0.tgz#6b7d536b28a36422bffa4e815fd27224df0273c6" - integrity sha512-LyZfgtAHkxtXBU99SBeN06UfnZyE1pcmEw7fkslZvFS2DXe30WeRosPvy/5uVgeD9du2y62Q3T7H73qRepJggQ== +metro@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.76.4.tgz#e965f17d2c20a56ee7357bce68871150fe12681d" + integrity sha512-LOnlkLt6ataFzBifhhBUjHpaQo22vqfqMEpT+LMBfF9IqESJKahe5TBpQ5OsBY4LtVp9EZxKi8FvK0B4tvTx1g== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/core" "^7.14.0" - "@babel/generator" "^7.14.0" - "@babel/parser" "^7.14.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" "@babel/template" "^7.0.0" - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.0.0" - absolute-path "^0.0.0" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" accepts "^1.3.7" async "^3.2.2" chalk "^4.0.0" @@ -4379,40 +4599,38 @@ metro@0.72.0: debug "^2.2.0" denodeify "^1.2.1" error-stack-parser "^2.0.6" - fs-extra "^1.0.0" graceful-fs "^4.2.4" hermes-parser "0.8.0" - image-size "^0.6.0" + image-size "^1.0.2" invariant "^2.2.4" jest-worker "^27.2.0" lodash.throttle "^4.1.1" - metro-babel-transformer "0.72.0" - metro-cache "0.72.0" - metro-cache-key "0.72.0" - metro-config "0.72.0" - metro-core "0.72.0" - metro-file-map "0.72.0" - metro-hermes-compiler "0.72.0" - metro-inspector-proxy "0.72.0" - metro-minify-uglify "0.72.0" - metro-react-native-babel-preset "0.72.0" - metro-resolver "0.72.0" - metro-runtime "0.72.0" - metro-source-map "0.72.0" - metro-symbolicate "0.72.0" - metro-transform-plugins "0.72.0" - metro-transform-worker "0.72.0" + metro-babel-transformer "0.76.4" + metro-cache "0.76.4" + metro-cache-key "0.76.4" + metro-config "0.76.4" + metro-core "0.76.4" + metro-file-map "0.76.4" + metro-inspector-proxy "0.76.4" + metro-minify-terser "0.76.4" + metro-minify-uglify "0.76.4" + metro-react-native-babel-preset "0.76.4" + metro-resolver "0.76.4" + metro-runtime "0.76.4" + metro-source-map "0.76.4" + metro-symbolicate "0.76.4" + metro-transform-plugins "0.76.4" + metro-transform-worker "0.76.4" mime-types "^2.1.27" node-fetch "^2.2.0" nullthrows "^1.1.1" - rimraf "^2.5.4" + rimraf "^3.0.2" serialize-error "^2.1.0" source-map "^0.5.6" strip-ansi "^6.0.0" - temp "0.8.3" throat "^5.0.0" ws "^7.5.1" - yargs "^15.3.1" + yargs "^17.6.2" micromatch@^3.1.10: version "3.1.10" @@ -4581,6 +4799,11 @@ nocache@^2.1.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +node-abort-controller@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + node-dir@^0.1.17: version "0.1.17" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" @@ -4644,10 +4867,10 @@ ob1@0.67.0: resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.67.0.tgz#91f104c90641b1af8c364fc82a4b2c7d0801072d" integrity sha512-YvZtX8HKYackQ5PwdFIuuNFVsMChRPHvnARRRT0Vk59xsBvL5t9U1Ock3M1sYrKj+Gp73+0q9xcHLAxI+xLi5g== -ob1@0.72.0: - version "0.72.0" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.72.0.tgz#13fa85d2fd1444d534156ac701c492fa6c17c956" - integrity sha512-QwCDyk1SvGg6KVEgpvAK9xG+7+0sEUZYuzNBT+aucFFVDkw0nHmsgfpzxNiiZwcSpWb04IxGp4CKAmFQBmLQPw== +ob1@0.76.4: + version "0.76.4" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.76.4.tgz#3c576b363d7efd7ef2491f433fe143aa092b1390" + integrity sha512-S6qUASzl27QBmauuvC9aDkJwNqgLjMtp7AFqrm8MjpyjVYQ4p4V8zqqyMNaHnXNYl4+qB9hvUBXqFd3QOpNKig== object-assign@^4.1.1: version "4.1.1" @@ -4954,10 +5177,10 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= -promise@^8.0.3: - version "8.1.0" - resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" - integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== +promise@^8.0.3, promise@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.2.0.tgz#a1f6280ab67457fbfc8aad2b198c9497e9e5c806" + integrity sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg== dependencies: asap "~2.0.6" @@ -5004,6 +5227,13 @@ punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +queue@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" + integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== + dependencies: + inherits "~2.0.3" + range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -5047,10 +5277,10 @@ react-native-codegen@^0.0.13: jscodeshift "^0.13.1" nullthrows "^1.1.1" -react-native-codegen@^0.0.17: - version "0.0.17" - resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.17.tgz#83fb814d94061cbd46667f510d2ddba35ffb50ac" - integrity sha512-7GIEUmAemH9uWwB6iYXNNsPoPgH06pxzGRmdBzK98TgFBdYJZ7CBuZFPMe4jmHQTPOkQazKZ/w5O6/71JBixmw== +react-native-codegen@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.18.tgz#99d6623d65292e8ce3fdb1d133a358caaa2145e7" + integrity sha512-XPI9aVsFy3dvgDZvyGWrFnknNiyb22kg5nHgxa0vjWTH9ENLBgVRZt9A64xHZ8BYihH+gl0p/1JNOCIEUzRPBg== dependencies: "@babel/parser" "^7.14.0" flow-parser "^0.121.0" @@ -5112,9 +5342,9 @@ react-native-windows@0.68.2: ws "^6.1.4" react-native@^0.68.2: - version "0.68.2" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.2.tgz#07547cd31bb9335a7fa4135cfbdc24e067142585" - integrity sha512-qNMz+mdIirCEmlrhapAtAG+SWVx6MAiSfCbFNhfHqiqu1xw1OKXdzIrjaBEPihRC2pcORCoCHduHGQe/Pz9Yuw== + version "0.68.4" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.4.tgz#c52c590f93e9d115010e458650957824c0b1406e" + integrity sha512-Hp5qwztQ1XNnV43QTz1kUx33iZHmJqbbe7L19V9psaWtX/h9j6SEtZ3UHBrigIPlppkIP1E5x3CDr9FdD4d6CA== dependencies: "@jest/create-cache-key-function" "^27.0.1" "@react-native-community/cli" "^7.0.3" @@ -5136,9 +5366,9 @@ react-native@^0.68.2: metro-source-map "0.67.0" nullthrows "^1.1.1" pretty-format "^26.5.2" - promise "^8.0.3" + promise "^8.2.0" react-devtools-core "^4.23.0" - react-native-codegen "^0.0.17" + react-native-codegen "^0.0.18" react-native-gradle-plugin "^0.0.6" react-refresh "^0.4.0" react-shallow-renderer "16.14.1" @@ -5238,12 +5468,7 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.2: - version "0.13.3" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== - -regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== @@ -5385,6 +5610,13 @@ rimraf@^2.5.4: dependencies: glob "^7.1.3" +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -5650,7 +5882,7 @@ source-map-support@^0.5.16: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.19: +source-map-support@^0.5.19, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -5734,6 +5966,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -5762,6 +6003,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -5830,6 +6078,16 @@ temp@^0.8.4: dependencies: rimraf "~2.6.2" +terser@^5.15.0: + version "5.15.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.1.tgz#8561af6e0fd6d839669c73b92bdd5777d870ed6c" + integrity sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + throat@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -6138,6 +6396,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -6209,10 +6476,6 @@ xmldoc@^1.1.2: dependencies: sax "^1.2.1" -"xmldom@github:xmldom/xmldom#0.7.0": - version "0.7.0" - resolved "https://codeload.github.com/xmldom/xmldom/tar.gz/c568938641cc1f121cef5b4df80fcfda1e489b6e" - xpath@^0.0.27: version "0.0.27" resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92" @@ -6228,6 +6491,16 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" @@ -6241,6 +6514,11 @@ yargs-parser@^18.1.1, yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs@^15.1.0: version "15.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" @@ -6274,3 +6552,16 @@ yargs@^15.3.1: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^18.1.2" + +yargs@^17.6.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + 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" diff --git a/react-native/yarn.lock b/react-native/yarn.lock deleted file mode 100644 index fb57ccd13..000000000 --- a/react-native/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - diff --git a/scripts/gradle-publish-keys.enc b/scripts/gradle-publish-keys.enc index fc7d1b95a..632242340 100644 Binary files a/scripts/gradle-publish-keys.enc and b/scripts/gradle-publish-keys.enc differ diff --git a/scripts/publish-android-snapshot.sh b/scripts/publish-android-snapshot.sh index 840ff7429..898675c78 100755 --- a/scripts/publish-android-snapshot.sh +++ b/scripts/publish-android-snapshot.sh @@ -16,6 +16,6 @@ elif [ "$IS_SNAPSHOT" == "" ]; then echo "Skipping build. Given build doesn't appear to be a SNAPSHOT release." exit 1 else - openssl aes-256-cbc -d -in scripts/gradle-publish-keys.enc -k "$ANDROID_PUBLISH_KEY" >> "$BASEDIR/gradle.properties" + openssl aes-256-cbc -pbkdf2 -d -in scripts/gradle-publish-keys.enc -k "$ANDROID_PUBLISH_KEY" >> "$BASEDIR/gradle.properties" "$BASEDIR"/gradlew publish fi diff --git a/settings.gradle b/settings.gradle index 8fe83c2f8..91a4f36a9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,9 +28,6 @@ project(':third-party').projectDir = file('android/third-party/') project(':noop').projectDir = file('android/no-op/') // Plugins -include ':fresco-plugin' -project(':fresco-plugin').projectDir = file('android/plugins/fresco') - include ':network-plugin' project(':network-plugin').projectDir = file('android/plugins/network') @@ -45,3 +42,6 @@ project(':leakcanary2-plugin').projectDir = file('android/plugins/leakcanary2') include ':retrofit2-protobuf' project(':retrofit2-protobuf').projectDir = file('android/plugins/retrofit2-protobuf') + +include ':jetpack-compose-plugin' +project(':jetpack-compose-plugin').projectDir = file('android/plugins/jetpack-compose') diff --git a/website/.gitignore b/website/.gitignore index ca9be16a3..57534583e 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -5,7 +5,6 @@ lib/core/MetadataBlog.js static/schemas website/translated_docs build/ -website/yarn.lock website/node_modules flipper-themes static/css/style-guide.css diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 38282a0ca..813605be1 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -205,6 +205,7 @@ const siteConfig = { // end_config_example plugins: [ './src/plugins/support-symlinks', + ...fbContent({internal: [], external: [require.resolve('docusaurus-lunr-search')]}), [ require.resolve('@docusaurus/plugin-content-pages'), { diff --git a/website/generate-plugin-docs.ts b/website/generate-plugin-docs.ts index 62a7f2358..bb1c02946 100644 --- a/website/generate-plugin-docs.ts +++ b/website/generate-plugin-docs.ts @@ -69,16 +69,16 @@ async function generatePluginDocs() { const id = name.replace('flipper-plugin-', ''); const generatedPluginResourcesPath = path.join(generatedPluginSymlinksDir, id); await fs.symlink(pluginSourceDocsDir, generatedPluginResourcesPath, 'junction'); - const setupDocPath = path.join(pluginSourceDocsDir, 'setup.mdx'); - const setupDocsExists = await fs.pathExists( - setupDocPath, - ); - const overviewDocPath = path.join(pluginSourceDocsDir, 'overview.mdx'); - const overviewDocsExists = await fs.pathExists( - overviewDocPath, - ); + const [setupDocRelativePath, overviewDocRelativePath] = await Promise.all([ + getInternalOrPublicDocRelativePathOrNull(pluginSourceDocsDir, 'setup.mdx'), + getInternalOrPublicDocRelativePathOrNull(pluginSourceDocsDir, 'overview.mdx'), + ]); + const setupDocsExists = setupDocRelativePath !== null; + const overviewDocsExists = overviewDocRelativePath !== null; if (setupDocsExists) { + const setupDocPath = path.join(pluginSourceDocsDir, setupDocRelativePath); const customEditUrl = `${repoUrl}/${path.relative(repoRoot, setupDocPath)}`; + const setupArticleImportPath = path.join(relativePluginSymlinksDir, id, setupDocRelativePath); await fs.writeFile( path.join(generatedPluginsSetupDocsDir, `${id}.mdx`), `--- @@ -87,7 +87,7 @@ title: ${title} Plugin Setup sidebar_label: ${title} custom_edit_url: ${customEditUrl} --- -import Article from '${relativePluginSymlinksDir}/${id}/setup.mdx'; +import Article from '${setupArticleImportPath}';
`, @@ -95,7 +95,9 @@ import Article from '${relativePluginSymlinksDir}/${id}/setup.mdx'; } if (overviewDocsExists) { + const overviewDocPath = path.join(pluginSourceDocsDir, overviewDocRelativePath); const customEditUrl = `${repoUrl}/${path.relative(repoRoot, overviewDocPath)}`; + const overviewArticleImportPath = path.join(relativePluginSymlinksDir, id, overviewDocRelativePath); const linkToSetup = setupDocsExists ? ` → [See setup instructions for the ${title} plugin](../../setup/plugins/${id}.mdx) @@ -110,7 +112,7 @@ title: ${title} Plugin sidebar_label: ${title} custom_edit_url: ${customEditUrl} --- -import Article from '${relativePluginSymlinksDir}/${id}/overview.mdx'; +import Article from '${overviewArticleImportPath}'; ${linkToSetup}
@@ -121,6 +123,16 @@ ${linkToSetup} } } +async function getInternalOrPublicDocRelativePathOrNull(docsDir: string, docName: string) { + if (process.env.FB_INTERNAL && await fs.pathExists(path.join(docsDir, 'fb', docName))) { + return path.join('fb', docName); + } + if (await fs.pathExists(path.join(docsDir, docName))) { + return docName; + } + return null +} + generatePluginDocs() .then(() => process.exit(0)) .catch(err => { diff --git a/website/package.json b/website/package.json index 1cdd30913..6423501d0 100644 --- a/website/package.json +++ b/website/package.json @@ -4,6 +4,9 @@ "start": "yarn copy-schema && yarn generate-plugin-docs && yarn build:style-guide && docusaurus start --port 3001", "build": "yarn copy-schema && yarn generate-plugin-docs && yarn build:style-guide && docusaurus build", "publish-gh-pages": "docusaurus deploy", + "clear": "docusaurus clear", + "clean": "docusaurus clear", + "swizzle": "docusaurus swizzle", "write-translations": "docusaurus write-translations", "version": "docusaurus version", "rename-version": "docusaurus rename-version", @@ -12,26 +15,27 @@ }, "devDependencies": { "@ant-design/icons": "^4.7.0", - "@docusaurus/core": "^2.0.0-beta.21", - "@docusaurus/plugin-client-redirects": "2.0.0-beta.21", - "@docusaurus/preset-classic": "^2.0.0-beta.21", - "@emotion/css": "^11.7.1", - "@emotion/styled": "^11.6.0", - "@types/fs-extra": "^9.0.13", - "antd": "^4.18.7", - "docusaurus-plugin-internaldocs-fb": "0.12.3", + "@docusaurus/core": "2.4.3", + "@docusaurus/plugin-client-redirects": "2.4.3", + "@docusaurus/preset-classic": "2.4.3", + "@emotion/css": "^11.10.6", + "@emotion/styled": "^11.10.6", + "@types/fs-extra": "^11.0.2", + "antd": "^4.23.4", + "docusaurus-lunr-search": "^3.0.0", + "docusaurus-plugin-internaldocs-fb": "1.16.1", "file-cli": "^1.2.0", - "flipper-plugin": "^0.131.1", - "fs-extra": "^10.0.0", - "less": "^4.1.2", + "flipper-plugin": "^0.183.0", + "fs-extra": "^11.1.1", + "less": "^4.1.3", "mermaid": "^8.13.4", "react": "^17.0.2", "react-dom": "^17.0.2", "react-element-to-jsx-string": "^14.3.4", - "ts-node": "^10.4.0", - "typescript": "^4.5.2" + "ts-node": "^10.9.1", + "typescript": "^4.9.5", + "yarn-audit-fix": "^9.3.9" }, - "dependencies": {}, "resolutions": { "kind-of": "6.0.3", "chokidar": "^3.1.5", @@ -40,8 +44,7 @@ "ws": "8.2.0", "trim": "1.0.1", "immer": "^9.0.6", - "shelljs": "^0.8.5", - "ansi-html": "0.0.8" + "shelljs": "^0.8.5" }, "license": "MIT" } diff --git a/website/sidebars.js b/website/sidebars.js index c5f14e020..782b8b59e 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -114,6 +114,7 @@ module.exports = { 'extending/debugging', ...fbInternalOnly([ 'fb/adding-analytics-0', + 'fb/logging', 'extending/fb/plugin-documentation', ]), 'extending/plugin-distribution', @@ -123,13 +124,14 @@ module.exports = { { 'Desktop Plugin APIs': [ 'extending/flipper-plugin', + 'extending/power-search', 'extending/styling-components', 'extending/style-guide', 'extending/deeplinks', 'extending/node-apis', ...fbInternalOnly([ { - 'QPL Linting': ['fb/building-a-linter', 'fb/active-linters'], + 'QPL Linting': ['fb/active-linters'], }, ]), ], @@ -143,38 +145,72 @@ module.exports = { 'extending/layout-inspector', ], }, + ], 'Under the Hood': [ 'internals/index', 'internals/contributing', - { - 'Contributing to the Documentation': ['internals/documentation-formatting', 'internals/documentation-writing-guide'], - }, + 'internals/device-identifiers', + 'internals/linters', 'extending/public-releases', 'extending/testing-rn', - 'internals/linters', - 'internals/device-identifiers', ...fbInternalOnly([ - 'fb/release-infra', - 'fb/LauncherConfig', - 'fb/hacking-on-launcher', - 'fb/arc_uiqr', - 'fb/Flipper-fbsource-Pinning', - 'fb/Flipper-Release-Cycle', - 'fb/Add-Support-Group-to-Flipper-Support-Form', - 'fb/Alerts', - 'fb/bundling', - 'fb/Electron-Upgrade', - 'fb/flipper-analytics', - 'fb/Navigation-Plugin-Development-Guide', - 'fb/Oncall-Runbook', - 'fb/sandcastle', - 'fb/Star-Ratings', - 'fb/sandcastle-overview', - 'fb/error-logging', - 'fb/scribe', - 'fb/async-testing', - 'fb/connections', + { + 'Internal': [ + { + 'Contributing to the Documentation': [ + 'internals/documentation-standards', + 'internals/documentation-writing-guide', + 'internals/documentation-formatting', + ] + }, + 'fb/vpnless-interngraph', + 'fb/arc_uiqr', + 'fb/connections', + { + 'Data Pipelines': [ + 'fb/data-pipelines', + 'fb/data-pipelines-details', + 'fb/flipper-analytics', + 'fb/scribe', + 'fb/error-logging', + ] + }, + 'fb/Electron-Upgrade', + { + 'Launcher': [ + 'fb/hacking-on-launcher', + 'fb/LauncherConfig', + ] + }, + 'fb/bundling', + 'fb/Navigation-Plugin-Development-Guide', + 'fb/jest-e2e-integration', + { + 'Releases': [ + 'fb/Flipper-Release-Cycle', + 'fb/release-infra', + 'fb/Flipper-fbsource-Pinning', + ] + }, + { + 'Sandcastle': [ + 'fb/sandcastle-overview', + 'fb/sandcastle', + ] + }, + { + 'Support': [ + 'fb/Alerts', + 'fb/Add-Support-Group-to-Flipper-Support-Form', + 'fb/Oncall-Runbook', + ] + }, + 'fb/Star-Ratings', + 'fb/async-testing', + 'fb/code-freeze', + ] + } ]), ], }, diff --git a/website/src/components/StyleGuide.js b/website/src/components/StyleGuide.js index e805b21ba..b911b9af5 100644 --- a/website/src/components/StyleGuide.js +++ b/website/src/components/StyleGuide.js @@ -7,7 +7,7 @@ * @format */ -import React, {useState, useLayoutEffect} from 'react'; +import React, {useLayoutEffect, useEffect, useRef} from 'react'; import { Typography, Button, @@ -20,729 +20,757 @@ import { Table, } from 'antd'; import {CodeOutlined} from '@ant-design/icons'; -import { - Layout, - NUX, - Panel, - theme, - Tracked, - TrackingScope, - Tabs, - Tab, -} from 'flipper-plugin'; import {css} from '@emotion/css'; import reactElementToJSXString from 'react-element-to-jsx-string'; import {IFrame} from './IFrame'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import useIsBrowser from '@docusaurus/useIsBrowser'; const {Title, Text, Link} = Typography; -const demoStyle = { - square: { - background: theme.successColor, - width: 50, - height: 50, - lineHeight: '50px', - textAlign: 'center', - }, - border: {border: `1px dotted ${theme.primaryColor}`}, -}; +export default function StyleGuide() { + // We need a browser environment to access the window object and import flipper-plugin. + const isBrowser = useIsBrowser(); + if (!isBrowser) { + return <>The Style Guide is only available in a browser environment.; + } -const largeChild = ( -
- -
-); -const aButton = ; -const aBox =
A fixed child
; -const aFixedWidthBox = ( -
- Fixed width box -
-); -const aFixedHeightBox = ( -
- Fixed height box -
-); -const aDynamicBox = ( -
- A dynamic child (flex: 1) -
-); -const someText = Some text; + const { + Layout, + NUX, + Panel, + theme, + Tracked, + TrackingScope, + Tabs, + Tab, + } = require('flipper-plugin'); -const demos = [ - { - title: 'Layout.Container', - description: `Layout.Container can be used to organize the UI in regions. It takes care of paddings and borders. Children will be arranged vertically. Use Layout.Horizontal instead for arranging children horizontally. If you need a margin on this component, try to wrap it in other Layout component instead.`, - props: [ - ['rounded', 'boolean (false)', 'Make the corners rounded'], - [ - 'padv / padh / pad', - Object.keys(theme.space).join(' | ') + ' | number | true', - 'Short-hand to set the horizontal, vertical or both paddings. The keys correspond to the theme space settings. Using `true` picks the default horizontal / vertical padding for inline elements.', - ], - [ - 'width / height', - 'number', - 'Set the width / height of this container in pixels. Use sparingly.', - ], - [ - 'bordered', - 'boolean (false)', - 'This container will use a default border on all sides', - ], - [ - 'borderTop / borderRight / borderBottom / borderLeft', - 'boolean (false)', - 'Use a standard padding on the top side', - ], - [ - 'gap', - 'true / number (0)', - 'Set the spacing between children. If just set, theme.space.small will be used.', - ], - [ - 'center', - 'boolean (false)', - 'If set, all children will use their own naturally width, and they will be centered horizontally in the Container. If not set, all children will be stretched to the width of the Container.', - ], - ], - demos: { - 'Basic container with fixed dimensions': ( - - ), - 'Basic container with fixed height': ( - - ), - 'bordered pad rounded': ( - -
child
-
- ), - 'Multiple children, gap={24}': ( - - {aButton} - {someText} - {aBox} - {aDynamicBox} - - ), - 'Multiple children icmw. pad center gap': ( - - {aButton} - {someText} - {aBox} - {aDynamicBox} - - ), + const demoStyle = { + square: { + background: theme.successColor, + width: 50, + height: 50, + lineHeight: '50px', + textAlign: 'center', }, - }, - { - title: 'Layout.Horizontal', - description: - 'Use this component to arrange multiple items horizontally. All vanilla Container props can be used as well.', - props: [ - [ - 'center', - 'boolean (false)', - 'If set, all children will use their own height, and they will be centered vertically in the layout. If not set, all children will be stretched to the height of the layout.', + border: {border: `1px dotted ${theme.primaryColor}`}, + }; + + const largeChild = ( +
+ +
+ ); + const aButton = ; + const aBox = ( +
A fixed child
+ ); + const aFixedWidthBox = ( +
+ Fixed width box +
+ ); + const aFixedHeightBox = ( +
+ Fixed height box +
+ ); + const aDynamicBox = ( +
+ A dynamic child (flex: 1) +
+ ); + const someText = Some text; + + const demos = [ + { + title: 'Layout.Container', + description: `Layout.Container can be used to organize the UI in regions. It takes care of paddings and borders. Children will be arranged vertically. Use Layout.Horizontal instead for arranging children horizontally. If you need a margin on this component, try to wrap it in other Layout component instead.`, + props: [ + ['rounded', 'boolean (false)', 'Make the corners rounded'], + [ + 'padv / padh / pad', + Object.keys(theme.space).join(' | ') + ' | number | true', + 'Short-hand to set the horizontal, vertical or both paddings. The keys correspond to the theme space settings. Using `true` picks the default horizontal / vertical padding for inline elements.', + ], + [ + 'width / height', + 'number', + 'Set the width / height of this container in pixels. Use sparingly.', + ], + [ + 'bordered', + 'boolean (false)', + 'This container will use a default border on all sides', + ], + [ + 'borderTop / borderRight / borderBottom / borderLeft', + 'boolean (false)', + 'Use a standard padding on the top side', + ], + [ + 'gap', + 'true / number (0)', + 'Set the spacing between children. If just set, theme.space.small will be used.', + ], + [ + 'center', + 'boolean (false)', + 'If set, all children will use their own naturally width, and they will be centered horizontally in the Container. If not set, all children will be stretched to the width of the Container.', + ], ], - ], - demos: { - 'Basic usage, gap="large"': ( - - {aButton} - {someText} - {aBox} - {aDynamicBox} - - ), - 'Using flags: pad center gap={8} (great for toolbars and such)': ( - - {aButton} - {someText} - {aBox} - {aDynamicBox} - - ), - }, - }, - { - title: 'Layout.ScrollContainer', - description: - 'Use this component to create an area that can be scrolled. The scrollable area will automatically consume all available space. ScrollContainer accepts all properties that Container accepts as well. Padding will be applied to the child rather than the parent.', - props: [ - [ - 'horizontal / vertical', - 'boolean', - 'specifies in which directions the container should scroll. If none is specified the container will scroll in both directions', - ], - [ - 'padv / padh / pad', - 'see Container', - 'Padding will be applied to the child', - ], - ], - demos: { - 'Basic usage': ( - - {largeChild} - - ), - 'ScrollContainer + Vertical for vertical scroll only': ( - - - - This text is truncated because it is too long and scroll is - vertical only... - - {largeChild} + demos: { + 'Basic container with fixed dimensions': ( + + ), + 'Basic container with fixed height': ( + + ), + 'bordered pad rounded': ( + +
child
-
- ), + ), + 'Multiple children, gap={24}': ( + + {aButton} + {someText} + {aBox} + {aDynamicBox} + + ), + 'Multiple children icmw. pad center gap': ( + + {aButton} + {someText} + {aBox} + {aDynamicBox} + + ), + }, }, - }, - { - title: 'Layout.Top|Left|Right|Bottom', - description: - "Divides all available space over two children. The (top|left|right|bottom)-most first child will keep it's own dimensions, and positioned (top|left|right|bottom) of the other child. All remaining space will be assigned to the remaining child. If you are using a Layout.Right at the top level of your plugin, consider using DetailSidebar component instead, which will move its children to the right sidebar of Flipper.", - props: [ - [ - 'scrollable', - 'boolean (false)', - 'If set, the area of the second child will automatically be made scrollable.', + { + title: 'Layout.Horizontal', + description: + 'Use this component to arrange multiple items horizontally. All vanilla Container props can be used as well.', + props: [ + [ + 'center', + 'boolean (false)', + 'If set, all children will use their own height, and they will be centered vertically in the layout. If not set, all children will be stretched to the height of the layout.', + ], ], - [ - 'center', - 'boolean (false)', - 'If set, all children will use their own height, and they will be centered vertically in the layout. If not set, all children will be stretched to the height of the layout.', + demos: { + 'Basic usage, gap="large"': ( + + {aButton} + {someText} + {aBox} + {aDynamicBox} + + ), + 'Using flags: pad center gap={8} (great for toolbars and such)': ( + + {aButton} + {someText} + {aBox} + {aDynamicBox} + + ), + }, + }, + { + title: 'Layout.ScrollContainer', + description: + 'Use this component to create an area that can be scrolled. The scrollable area will automatically consume all available space. ScrollContainer accepts all properties that Container accepts as well. Padding will be applied to the child rather than the parent.', + props: [ + [ + 'horizontal / vertical', + 'boolean', + 'specifies in which directions the container should scroll. If none is specified the container will scroll in both directions', + ], + [ + 'padv / padh / pad', + 'see Container', + 'Padding will be applied to the child', + ], ], - [ - 'gap', - 'true / number (0)', - 'Set the spacing between children. If just set, theme.space.small will be used.', + demos: { + 'Basic usage': ( + + {largeChild} + + ), + 'ScrollContainer + Vertical for vertical scroll only': ( + + + + This text is truncated because it is too long and scroll is + vertical only... + + {largeChild} + + + ), + }, + }, + { + title: 'Layout.Top|Left|Right|Bottom', + description: + "Divides all available space over two children. The (top|left|right|bottom)-most first child will keep it's own dimensions, and positioned (top|left|right|bottom) of the other child. All remaining space will be assigned to the remaining child. If you are using a Layout.Right at the top level of your plugin, consider using DetailSidebar component instead, which will move its children to the right sidebar of Flipper.", + props: [ + [ + 'scrollable', + 'boolean (false)', + 'If set, the area of the second child will automatically be made scrollable.', + ], + [ + 'center', + 'boolean (false)', + 'If set, all children will use their own height, and they will be centered vertically in the layout. If not set, all children will be stretched to the height of the layout.', + ], + [ + 'gap', + 'true / number (0)', + 'Set the spacing between children. If just set, theme.space.small will be used.', + ], + [ + 'resizable', + 'true / undefined', + 'If set, this split container will be resizable by the user. It is recommend to set width, maxWidth, minWidth respectively height, maxHeight, minHeight properties as well.', + ], + [ + 'width / height / minWidth / minHeight / maxWidth / maxHeight', + 'number / undefined', + 'These dimensions in pixels will be used for clamping if the layout is marked as resizable', + ], ], - [ - 'resizable', - 'true / undefined', - 'If set, this split container will be resizable by the user. It is recommend to set width, maxWidth, minWidth respectively height, maxHeight, minHeight properties as well.', - ], - [ - 'width / height / minWidth / minHeight / maxWidth / maxHeight', - 'number / undefined', - 'These dimensions in pixels will be used for clamping if the layout is marked as resizable', - ], - ], - demos: { - 'Layout.Top': ( - - {aFixedHeightBox} - {aDynamicBox} - - ), - 'Layout.Left': ( - - {aFixedWidthBox} - {aDynamicBox} - - ), - 'Layout.Right': ( - - {aDynamicBox} - {aFixedWidthBox} - - ), - 'Layout.Bottom': ( - - {aDynamicBox} - {aFixedHeightBox} - - ), - 'Layout.Top + Layout.ScrollContainer': ( - + demos: { + 'Layout.Top': ( {aFixedHeightBox} - {largeChild} + {aDynamicBox} - - ), - 'Layout.Left + Layout.ScrollContainer': ( - + ), + 'Layout.Left': ( {aFixedWidthBox} - {largeChild} + {aDynamicBox} - - ), - 'Layout.Right resizable + Layout.ScrollContainer': ( - - - {largeChild} + ), + 'Layout.Right': ( + {aDynamicBox} + {aFixedWidthBox} - - ), - 'Layout.Bottom resizable + Layout.ScrollContainer': ( - - - {largeChild} + ), + 'Layout.Bottom': ( + {aDynamicBox} + {aFixedHeightBox} - - ), + ), + 'Layout.Top + Layout.ScrollContainer': ( + + + {aFixedHeightBox} + {largeChild} + + + ), + 'Layout.Left + Layout.ScrollContainer': ( + + + {aFixedWidthBox} + {largeChild} + + + ), + 'Layout.Right resizable + Layout.ScrollContainer': ( + + + {largeChild} + {aDynamicBox} + + + ), + 'Layout.Bottom resizable + Layout.ScrollContainer': ( + + + {largeChild} + {aDynamicBox} + + + ), + }, }, - }, - { - title: 'Panel', - description: - 'A collapsible UI region. The collapsed state of the pane will automatically be persisted so that the collapsed state is restored the next time user visits the plugin again. Note that the children of a Panel should have some size, either a fixed or a natural size. Elements that grow to their parent size will become invisible.', - props: [ - ['title', 'string', 'Title of the pane'], - [ - 'collapsible', - 'boolean (true)', - "If set to false it won't be possible to collapse the panel", + { + title: 'Panel', + description: + 'A collapsible UI region. The collapsed state of the pane will automatically be persisted so that the collapsed state is restored the next time user visits the plugin again. Note that the children of a Panel should have some size, either a fixed or a natural size. Elements that grow to their parent size will become invisible.', + props: [ + ['title', 'string', 'Title of the pane'], + [ + 'collapsible', + 'boolean (true)', + "If set to false it won't be possible to collapse the panel", + ], + [ + 'collapsed', + 'boolean (false)', + 'The initial collapsed state of the panel.', + ], + [ + 'pad / gap', + 'boolean / number (false)', + 'See the pad property of Layout.Container, determines whether the pane contents will have some padding and space between the items. By default no padding / gap is applied.', + ], ], - [ - 'collapsed', - 'boolean (false)', - 'The initial collapsed state of the panel.', - ], - [ - 'pad / gap', - 'boolean / number (false)', - 'See the pad property of Layout.Container, determines whether the pane contents will have some padding and space between the items. By default no padding / gap is applied.', - ], - ], - demos: { - 'Two panels in a fixed height container': ( - - Some content - - {aFixedHeightBox} - - - {aFixedHeightBox} - {aFixedHeightBox} - - - ), - }, - }, - { - title: 'Tabs / Tab', - description: - "Tabs represents a tab control and all it's children should be Tab components. By default the Tab control uses all available space, but set grow=false to only use the minimally required space", - props: [ - [ - 'grow (Tabs)', - 'boolean (true)', - 'If true, the tab control will grow all tabs to the maximum available vertical space. If false, only the minimal required (natural) vertical space will be used', - ], - [ - 'pad / gap (Tab)', - 'boolean / number (false)', - 'See the pad property of Layout.Container, determines whether the pane contents will have some padding and space between the items. By default no padding / gap is applied.', - ], - [ - 'other props', - '', - 'This component wraps Tabs from ant design, see https://ant.design/components/tabs/ for more details', - ], - ], - demos: { - 'Two tabs': ( - - - {aDynamicBox} - + demos: { + 'Two panels in a fixed height container': ( + + Some content + + {aFixedHeightBox} + + {aFixedHeightBox} {aFixedHeightBox} - - - - ), - 'Two tabs (no grow)': ( - - - {aDynamicBox} - - {aFixedHeightBox} - {aFixedHeightBox} - - - - ), + + + ), + }, }, - }, - { - title: 'NUX', - description: - 'A component to provide a New-User-eXperience: Highlight new features to first time users. For tooltips that should stay available, use ToolTip from ANT design', - props: [ - ['title', 'string / React element', 'The tooltip contents'], - [ - 'placement', - <> - See{' '} - - docs - - , - '(optional) on which side to place the tooltip', + { + title: 'Tabs / Tab', + description: + "Tabs represents a tab control and all it's children should be Tab components. By default the Tab control uses all available space, but set grow=false to only use the minimally required space", + props: [ + [ + 'grow (Tabs)', + 'boolean (true)', + 'If true, the tab control will grow all tabs to the maximum available vertical space. If false, only the minimal required (natural) vertical space will be used', + ], + [ + 'pad / gap (Tab)', + 'boolean / number (false)', + 'See the pad property of Layout.Container, determines whether the pane contents will have some padding and space between the items. By default no padding / gap is applied.', + ], + [ + 'other props', + '', + 'This component wraps Tabs from ant design, see https://ant.design/components/tabs/ for more details', + ], ], - ], - demos: { - 'NUX example': ( - - - - ), + demos: { + 'Two tabs': ( + + + {aDynamicBox} + + {aFixedHeightBox} + {aFixedHeightBox} + + + + ), + 'Two tabs (no grow)': ( + + + {aDynamicBox} + + {aFixedHeightBox} + {aFixedHeightBox} + + + + ), + }, }, - }, - { - title: 'Tracked', - description: - 'A component that tracks component interactions. For Facebook internal builds, global stats for these interactions will be tracked. Wrap this component around another element to track its events', - props: [ - [ - 'events', - 'string | string[] (default: "onClick")', - 'The event(s) of the child component that should be tracked', + { + title: 'NUX', + description: + 'A component to provide a New-User-eXperience: Highlight new features to first time users. For tooltips that should stay available, use ToolTip from ANT design', + props: [ + ['title', 'string / React element', 'The tooltip contents'], + [ + 'placement', + <> + See{' '} + + docs + + , + '(optional) on which side to place the tooltip', + ], ], - [ - 'action', - 'string (optional)', - 'Describes the element the user interacted with. Will by default be derived from the title, key or contents of the element', - ], - ], - demos: { - 'Basic example': ( - - - - ), + demos: { + 'NUX example': ( + + + + ), + }, }, - }, - { - title: 'TrackingScope', - description: - 'Describes more precisely the place in the UI for all underlying Tracked elements. Multiple Tracking scopes are automatically nested. Use the `withTrackingScope` HoC to automatically wrap a component definition in a tracking scope', - props: [ - ['scope', 'string', 'The name of the scope. For example "Login Dialog"'], - ], - demos: { - 'Basic example': ( - + { + title: 'Tracked', + description: + 'A component that tracks component interactions. For Facebook internal builds, global stats for these interactions will be tracked. Wrap this component around another element to track its events', + props: [ + [ + 'events', + 'string | string[] (default: "onClick")', + 'The event(s) of the child component that should be tracked', + ], + [ + 'action', + 'string (optional)', + 'Describes the element the user interacted with. Will by default be derived from the title, key or contents of the element', + ], + ], + demos: { + 'Basic example': ( - - ), + ), + }, }, - }, -]; + { + title: 'TrackingScope', + description: + 'Describes more precisely the place in the UI for all underlying Tracked elements. Multiple Tracking scopes are automatically nested. Use the `withTrackingScope` HoC to automatically wrap a component definition in a tracking scope', + props: [ + [ + 'scope', + 'string', + 'The name of the scope. For example "Login Dialog"', + ], + ], + demos: { + 'Basic example': ( + + + + + + ), + }, + }, + ]; -function ComponentPreview({title, demos, description, props}) { - return ( - - - - {description} - - - - {Object.entries(demos).map(([name, children]) => ( - - -
- {children} -
-
- } key="2"> -
-
{reactElementToJSXString(children)}
-
-
-
- ))} -
-
- - - Object.assign(prop, {key: prop[0]}), - )} - columns={[ - { - title: 'Property', - dataIndex: 0, - width: 100, - }, - { - title: 'Type and default', - dataIndex: 1, - width: 200, - }, - { - title: 'Description', - dataIndex: 2, - }, - ]} - /> - - - - - + function ComponentPreview({title, demos, description, props}) { + return ( + + + + {description} + + + + {Object.entries(demos).map(([name, children]) => ( + + +
+ {children} +
+
+ } key="2"> +
+
{reactElementToJSXString(children)}
+
+
+
+ ))} +
+
+ +
+ Object.assign(prop, {key: prop[0]}), + )} + columns={[ + { + title: 'Property', + dataIndex: 0, + width: 100, + }, + { + title: 'Type and default', + dataIndex: 1, + width: 200, + }, + { + title: 'Description', + dataIndex: 2, + }, + ]} + /> + + + + + + ); + } + + const DesignComponentDemos = () => ( + + {demos.map(demo => ( + + ))} + ); -} -const DesignComponentDemos = () => ( - - {demos.map(demo => ( - - ))} - -); + function SandyDesignSystem() { + const root = useRef(null); -function SandyDesignSystem() { - const [root, setRoot] = useState(null); + // Whenever layout happens, or if the size of root changes, measure it and send a message to the parent frame. + useLayoutEffect(() => { + if (root.current) { + const sendUpdate = () => + window.postMessage( + { + name: 'setStyleGuideHeight', + value: `${root.current.scrollHeight}px`, + }, + '*', + ); + const observer = new ResizeObserver(() => { + sendUpdate(); + }); + observer.observe(root.current); - useLayoutEffect(() => { - if (root) { - const iframe = window.parent.document.getElementById('styleguide'); - iframe.style.height = `${root.scrollHeight}px`; + sendUpdate(); - const observer = new MutationObserver(() => { - iframe.style.height = `${root.scrollHeight}px`; - }); - observer.observe(root, { - subtree: true, - childList: true, - attributes: true, - characterData: true, - }); + return () => observer.disconnect(); + } + }, [root.current]); - return () => observer.disconnect(); - } - }, [root]); - - return ( - - -

- Welcome to the Flipper Design System. The Flipper design system is - based on{' '} - Ant Design - . Any component found in the ANT documentation can be used. This page - demonstrates the usage of: -

-
    -
  • Colors
  • -
  • Typography
  • -
  • Theme Variables
  • -
  • Layout components
  • -
-

- The following components from Ant should not be used: -

-
    -
  • - Layout: use Flipper's Layout.* instead. -
  • -
-

Sandy guidelines

-
    -
  • - Avoid using `margin` properties, use padding on the container - indeed, or gap in combination with{' '} - Layout.Horizontal|Vertical -
  • -
  • - Avoid using width / height: 100%, use{' '} - Layout.Container instead. -
  • -
  • - In general, components that have a grow property will - grow to use the full height of their parents if{' '} - true. In contrast, if grow is set to false{' '} - components will use their natural size, based on their{' '} - children. -
  • -
  • - The other important property here is scrollable. If an - element supports this property, setting it will imply{' '} - grow, and the element will show a scrollbar if needed. - Setting scrollabe to false causes the - element to always use its natural size, growing or shrinking based - on the contents rather than the parent. -
  • -
-
- - - - - - - - - - - - - - - - - - - + return ( + + +

+ Welcome to the Flipper Design System. The Flipper design system is + based on{' '} + + Ant Design + + . Any component found in the ANT documentation can be used. This + page demonstrates the usage of: +

+
    +
  • Colors
  • +
  • Typography
  • +
  • Theme Variables
  • +
  • Layout components
  • +
+

+ The following components from Ant should not be used: +

+
    +
  • + Layout: use Flipper's Layout.* instead. +
  • +
+

Sandy guidelines

+
    +
  • + Avoid using `margin` properties, use padding on the container + indeed, or gap in combination with{' '} + Layout.Horizontal|Vertical +
  • +
  • + Avoid using width / height: 100%, use{' '} + Layout.Container instead. +
  • +
  • + In general, components that have a grow property will + grow to use the full height of their parents if{' '} + true. In contrast, if grow is set to{' '} + false components will use their natural size, based + on their children. +
  • +
  • + The other important property here is scrollable. If an + element supports this property, setting it will imply{' '} + grow, and the element will show a scrollbar if + needed. Setting scrollabe to false{' '} + causes the element to always use its natural size, growing or + shrinking based on the contents rather than the parent. +
  • +
+
+ + + + + + + + + + + + + + + + + + + + + Common Ant components, with modifiers applied. The{' '} + Title, Text and Link{' '} + components can be found by importing the{' '} + Typography namespace from Ant. + + } + type="info" + /> + Title + Title level=2 + Title level=3 + Title level=4 + Text + Text - type=secondary + Text - type=success + Text - type=warning + Text - danger + Text - disbled + Text - strong + Text - code + Link + + Link - type=secondary + + + + + + + - Common Ant components, with modifiers applied. The{' '} - Title, Text and Link{' '} - components can be found by importing the Typography{' '} - namespace from Ant. + The following theme veriables are exposed from the Flipper{' '} + theme object. See the colors section above for a + preview of the colors. } - type="info" /> - Title - Title level=2 - Title level=3 - Title level=4 - Text - Text - type=secondary - Text - type=success - Text - type=warning - Text - danger - Text - disbled - Text - strong - Text - code - Link - - Link - type=secondary - - - - -
-
- - - The following theme veriables are exposed from the Flipper{' '} - theme object. See the colors section above for a - preview of the colors. - - } - /> -
{JSON.stringify(theme, null, 2)}
-
- - - -
- ); -} +
{JSON.stringify(theme, null, 2)}
+ + + + + + ); + } -export default function DesignSystemFramed() { + function ColorPreview({name}) { + return ( + + + } + title={`theme.${name}`} + /> + + ); + } + + const reset = css` + ol, + ul { + list-style: revert; + margin-left: ${theme.space.huge}px; + } + .ant-alert { + margin-bottom: ${theme.space.huge}px; + } + .ant-card { + background: transparent; + } + `; + + const iframe = css` + width: 100%; + `; + + const innerCss = ` + body { + overflow: hidden; + } +`; + + // We're displaying the style guide in an iframe to isolate it's styles. + // But we don't know how big it is, so don't know how high to make the iframe to avoid a double scroll bar. + // So lets get the js inside the frame measure itself and post a message to this frame, where we'll then + // adjust the size of the iframe to match. + useEffect(() => { + window.addEventListener('message', event => { + if (event.data.name === 'setStyleGuideHeight') { + document.getElementById('styleguide').style.height = event.data.value; + } + }); + }, []); return ( ); } - -function ColorPreview({name}) { - return ( - - - } - title={`theme.${name}`} - /> - - ); -} - -const reset = css` - ol, - ul { - list-style: revert; - margin-left: ${theme.space.huge}px; - } - .ant-alert { - margin-bottom: ${theme.space.huge}px; - } - .ant-card { - background: transparent; - } -`; - -const iframe = css` - width: 100%; -`; - -const innerCss = ` - body { - overflow: hidden; - } -`; diff --git a/website/src/pages/index.js b/website/src/pages/index.js index b7f0ccfb9..b19074437 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -7,190 +7,186 @@ * @format */ -import React from 'react'; -import Layout from '@theme/Layout'; -import useBaseUrl from '@docusaurus/useBaseUrl'; -import {usePluginData} from '@docusaurus/useGlobalData'; -import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + import React from 'react'; + import Layout from '@theme/Layout'; + import useBaseUrl from '@docusaurus/useBaseUrl'; + import {usePluginData} from '@docusaurus/useGlobalData'; + import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; -export default function Index() { - return ( - -
-
-
-

Extensible mobile app debugger

-

- Flipper is a platform for debugging iOS, Android and React Native - apps. Visualize, inspect, and control your apps from a simple - desktop interface. Use Flipper as is or extend it using the plugin - API. -

- - - - - - -
- - - - -
-
-
-
-
-
- -
-
-

Tools

-

Mobile development

-

- Flipper aims to be your number one companion for mobile app - development on iOS and Android. Therefore, we provide a bunch of - useful tools including a log viewer, interactive layout inspector, - and network inspector. -

- - Learn more - -
-
-
-
-

Plugins

-

Extending Flipper

-

- Flipper is built as a platform. In addition to using the tools - already included, you can create your own plugins to visualize and - debug data from your mobile apps. Flipper takes care of sending - data back and forth, calling functions, and listening for events - on the mobile app. -

- - Learn more - -
-
- -
-
-
-
- -
-
-

Open Source

-

Contributing to Flipper

-

- Both Flipper's desktop app and native mobile SDKs are open-source - and MIT licensed. This enables you to see and understand how we - are building plugins, and of course join the community and help - improve Flipper. We are excited to see what you will build on this - platform. -

- - Explore the source on GitHub - -
-
- -
- - ); -} + export default function Index() { + return ( + +
+
+
+

Extensible mobile app debugger

+

+ Flipper is a platform for debugging iOS, Android and React Native + apps. Visualize, inspect, and control your apps from a simple + desktop interface. Use Flipper as is or extend it using the plugin + API. +

+ + + + + + +
+ + + +
+
+
+
+
+
+ +
+
+

Tools

+

Mobile development

+

+ Flipper aims to be your number one companion for mobile app + development on iOS and Android. Therefore, we provide a bunch of + useful tools including a log viewer, interactive layout inspector, + and network inspector. +

+ + Learn more + +
+
+
+
+

Plugins

+

Extending Flipper

+

+ Flipper is built as a platform. In addition to using the tools + already included, you can create your own plugins to visualize and + debug data from your mobile apps. Flipper takes care of sending + data back and forth, calling functions, and listening for events + on the mobile app. +

+ + Learn more + +
+
+ +
+
+
+
+ +
+
+

Open Source

+

Contributing to Flipper

+

+ Both Flipper's desktop app and native mobile SDKs are open-source + and MIT licensed. This enables you to see and understand how we + are building plugins, and of course join the community and help + improve Flipper. We are excited to see what you will build on this + platform. +

+ + Explore the source on GitHub + +
+
+ +
+ + ); + } diff --git a/website/src/theme/EmbeddedLayout/index.js b/website/src/theme/EmbeddedLayout/index.js index b5eab042c..9155c59ed 100644 --- a/website/src/theme/EmbeddedLayout/index.js +++ b/website/src/theme/EmbeddedLayout/index.js @@ -7,9 +7,10 @@ import React from 'react'; import clsx from 'clsx'; -import LayoutProviders from '@theme/LayoutProviders'; +import LayoutProviders from '@theme/Layout/Provider'; import Head from '@docusaurus/Head'; -import {ThemeClassNames, useKeyboardNavigation} from '@docusaurus/theme-common'; +import {ThemeClassNames} from '@docusaurus/theme-common'; +import {useKeyboardNavigation} from '@docusaurus/theme-common/internal'; import './styles.css'; function EmbeddedLayout(props) { diff --git a/website/src/theme/SearchBar/DocSearch.js b/website/src/theme/SearchBar/DocSearch.js new file mode 100644 index 000000000..e98a46c2f --- /dev/null +++ b/website/src/theme/SearchBar/DocSearch.js @@ -0,0 +1,304 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import Hogan from "hogan.js"; +import LunrSearchAdapter from "./lunar-search"; +import autocomplete from "autocomplete.js"; +import templates from "./templates"; +import utils from "./utils"; +import $ from "autocomplete.js/zepto"; + +class DocSearch { + constructor({ + searchDocs, + searchIndex, + inputSelector, + debug = false, + baseUrl = '/', + queryDataCallback = null, + autocompleteOptions = { + debug: false, + hint: false, + autoselect: true + }, + transformData = false, + queryHook = false, + handleSelected = false, + enhancedSearchInput = false, + layout = "collumns" + }) { + this.input = DocSearch.getInputFromSelector(inputSelector); + this.queryDataCallback = queryDataCallback || null; + const autocompleteOptionsDebug = + autocompleteOptions && autocompleteOptions.debug + ? autocompleteOptions.debug + : false; + // eslint-disable-next-line no-param-reassign + autocompleteOptions.debug = debug || autocompleteOptionsDebug; + this.autocompleteOptions = autocompleteOptions; + this.autocompleteOptions.cssClasses = + this.autocompleteOptions.cssClasses || {}; + this.autocompleteOptions.cssClasses.prefix = + this.autocompleteOptions.cssClasses.prefix || "ds"; + const inputAriaLabel = + this.input && + typeof this.input.attr === "function" && + this.input.attr("aria-label"); + this.autocompleteOptions.ariaLabel = + this.autocompleteOptions.ariaLabel || inputAriaLabel || "search input"; + + this.isSimpleLayout = layout === "simple"; + + this.client = new LunrSearchAdapter(searchDocs, searchIndex, baseUrl); + + if (enhancedSearchInput) { + this.input = DocSearch.injectSearchBox(this.input); + } + this.autocomplete = autocomplete(this.input, autocompleteOptions, [ + { + source: this.getAutocompleteSource(transformData, queryHook), + templates: { + suggestion: DocSearch.getSuggestionTemplate(this.isSimpleLayout), + footer: templates.footer, + empty: DocSearch.getEmptyTemplate() + } + } + ]); + + const customHandleSelected = handleSelected; + this.handleSelected = customHandleSelected || this.handleSelected; + + // We prevent default link clicking if a custom handleSelected is defined + if (customHandleSelected) { + $(".algolia-autocomplete").on("click", ".ds-suggestions a", event => { + event.preventDefault(); + }); + } + + this.autocomplete.on( + "autocomplete:selected", + this.handleSelected.bind(null, this.autocomplete.autocomplete) + ); + + this.autocomplete.on( + "autocomplete:shown", + this.handleShown.bind(null, this.input) + ); + + if (enhancedSearchInput) { + DocSearch.bindSearchBoxEvent(); + } + } + + static injectSearchBox(input) { + input.before(templates.searchBox); + const newInput = input + .prev() + .prev() + .find("input"); + input.remove(); + return newInput; + } + + static bindSearchBoxEvent() { + $('.searchbox [type="reset"]').on("click", function () { + $("input#docsearch").focus(); + $(this).addClass("hide"); + autocomplete.autocomplete.setVal(""); + }); + + $("input#docsearch").on("keyup", () => { + const searchbox = document.querySelector("input#docsearch"); + const reset = document.querySelector('.searchbox [type="reset"]'); + reset.className = "searchbox__reset"; + if (searchbox.value.length === 0) { + reset.className += " hide"; + } + }); + } + + /** + * Returns the matching input from a CSS selector, null if none matches + * @function getInputFromSelector + * @param {string} selector CSS selector that matches the search + * input of the page + * @returns {void} + */ + static getInputFromSelector(selector) { + const input = $(selector).filter("input"); + return input.length ? $(input[0]) : null; + } + + /** + * Returns the `source` method to be passed to autocomplete.js. It will query + * the Algolia index and call the callbacks with the formatted hits. + * @function getAutocompleteSource + * @param {function} transformData An optional function to transform the hits + * @param {function} queryHook An optional function to transform the query + * @returns {function} Method to be passed as the `source` option of + * autocomplete + */ + getAutocompleteSource(transformData, queryHook) { + return (query, callback) => { + if (queryHook) { + // eslint-disable-next-line no-param-reassign + query = queryHook(query) || query; + } + this.client.search(query).then(hits => { + if ( + this.queryDataCallback && + typeof this.queryDataCallback == "function" + ) { + this.queryDataCallback(hits); + } + if (transformData) { + hits = transformData(hits) || hits; + } + callback(DocSearch.formatHits(hits)); + }); + }; + } + + // Given a list of hits returned by the API, will reformat them to be used in + // a Hogan template + static formatHits(receivedHits) { + const clonedHits = utils.deepClone(receivedHits); + const hits = clonedHits.map(hit => { + if (hit._highlightResult) { + // eslint-disable-next-line no-param-reassign + hit._highlightResult = utils.mergeKeyWithParent( + hit._highlightResult, + "hierarchy" + ); + } + return utils.mergeKeyWithParent(hit, "hierarchy"); + }); + + // Group hits by category / subcategory + let groupedHits = utils.groupBy(hits, "lvl0"); + $.each(groupedHits, (level, collection) => { + const groupedHitsByLvl1 = utils.groupBy(collection, "lvl1"); + const flattenedHits = utils.flattenAndFlagFirst( + groupedHitsByLvl1, + "isSubCategoryHeader" + ); + groupedHits[level] = flattenedHits; + }); + groupedHits = utils.flattenAndFlagFirst(groupedHits, "isCategoryHeader"); + + // Translate hits into smaller objects to be send to the template + return groupedHits.map(hit => { + const url = DocSearch.formatURL(hit); + const category = utils.getHighlightedValue(hit, "lvl0"); + const subcategory = utils.getHighlightedValue(hit, "lvl1") || category; + const displayTitle = utils + .compact([ + utils.getHighlightedValue(hit, "lvl2") || subcategory, + utils.getHighlightedValue(hit, "lvl3"), + utils.getHighlightedValue(hit, "lvl4"), + utils.getHighlightedValue(hit, "lvl5"), + utils.getHighlightedValue(hit, "lvl6") + ]) + .join( + '' + ); + const text = utils.getSnippetedValue(hit, "content"); + const isTextOrSubcategoryNonEmpty = + (subcategory && subcategory !== "") || + (displayTitle && displayTitle !== ""); + const isLvl1EmptyOrDuplicate = + !subcategory || subcategory === "" || subcategory === category; + const isLvl2 = + displayTitle && displayTitle !== "" && displayTitle !== subcategory; + const isLvl1 = + !isLvl2 && + (subcategory && subcategory !== "" && subcategory !== category); + const isLvl0 = !isLvl1 && !isLvl2; + + return { + isLvl0, + isLvl1, + isLvl2, + isLvl1EmptyOrDuplicate, + isCategoryHeader: hit.isCategoryHeader, + isSubCategoryHeader: hit.isSubCategoryHeader, + isTextOrSubcategoryNonEmpty, + category, + subcategory, + title: displayTitle, + text, + url + }; + }); + } + + static formatURL(hit) { + const { url, anchor } = hit; + if (url) { + const containsAnchor = url.indexOf("#") !== -1; + if (containsAnchor) return url; + else if (anchor) return `${hit.url}#${hit.anchor}`; + return url; + } else if (anchor) return `#${hit.anchor}`; + /* eslint-disable */ + console.warn("no anchor nor url for : ", JSON.stringify(hit)); + /* eslint-enable */ + return null; + } + + static getEmptyTemplate() { + return args => Hogan.compile(templates.empty).render(args); + } + + static getSuggestionTemplate(isSimpleLayout) { + const stringTemplate = isSimpleLayout + ? templates.suggestionSimple + : templates.suggestion; + const template = Hogan.compile(stringTemplate); + return suggestion => template.render(suggestion); + } + + handleSelected(input, event, suggestion, datasetNumber, context = {}) { + // Do nothing if click on the suggestion, as it's already a , the + // browser will take care of it. This allow Ctrl-Clicking on results and not + // having the main window being redirected as well + if (context.selectionMethod === "click") { + return; + } + + input.setVal(""); + window.location.assign(suggestion.url); + } + + handleShown(input) { + const middleOfInput = input.offset().left + input.width() / 2; + let middleOfWindow = $(document).width() / 2; + + if (isNaN(middleOfWindow)) { + middleOfWindow = 900; + } + + const alignClass = + middleOfInput - middleOfWindow >= 0 + ? "algolia-autocomplete-right" + : "algolia-autocomplete-left"; + const otherAlignClass = + middleOfInput - middleOfWindow < 0 + ? "algolia-autocomplete-right" + : "algolia-autocomplete-left"; + const autocompleteWrapper = $(".algolia-autocomplete"); + if (!autocompleteWrapper.hasClass(alignClass)) { + autocompleteWrapper.addClass(alignClass); + } + + if (autocompleteWrapper.hasClass(otherAlignClass)) { + autocompleteWrapper.removeClass(otherAlignClass); + } + } +} + +export default DocSearch; diff --git a/website/src/theme/SearchBar/algolia.css b/website/src/theme/SearchBar/algolia.css new file mode 100644 index 000000000..eeb7def15 --- /dev/null +++ b/website/src/theme/SearchBar/algolia.css @@ -0,0 +1,533 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* Bottom border of each suggestion */ +.algolia-docsearch-suggestion { + border-bottom-color: #3a3dd1; +} +/* Main category headers */ +.algolia-docsearch-suggestion--category-header { + background-color: #4b54de; +} +/* Highlighted search terms */ +.algolia-docsearch-suggestion--highlight { + color: #3a33d1; +} +/* Highligted search terms in the main category headers */ +.algolia-docsearch-suggestion--category-header + .algolia-docsearch-suggestion--highlight { + background-color: #4d47d5; +} +/* Currently selected suggestion */ +.aa-cursor .algolia-docsearch-suggestion--content { + color: #272296; +} +.aa-cursor .algolia-docsearch-suggestion { + background: #ebebfb; +} + +/* For bigger screens, when displaying results in two columns */ +@media (min-width: 768px) { + /* Bottom border of each suggestion */ + .algolia-docsearch-suggestion { + border-bottom-color: #7671df; + } + /* Left column, with secondary category header */ + .algolia-docsearch-suggestion--subcategory-column { + border-right-color: #7671df; + color: #4e4726; + } +} + +.searchbox { + display: inline-block; + position: relative; + width: 200px; + height: 32px !important; + white-space: nowrap; + box-sizing: border-box; + visibility: visible !important; +} + +.searchbox .algolia-autocomplete { + display: block; + width: 100%; + height: 100%; +} + +.searchbox__wrapper { + width: 100%; + height: 100%; + z-index: 999; + position: relative; +} + +.searchbox__input { + display: inline-block; + box-sizing: border-box; + -webkit-transition: box-shadow 0.4s ease, background 0.4s ease; + transition: box-shadow 0.4s ease, background 0.4s ease; + border: 0; + border-radius: 16px; + box-shadow: inset 0 0 0 1px #cccccc; + background: #ffffff !important; + padding: 0; + padding-right: 26px; + padding-left: 32px; + width: 100%; + height: 100%; + vertical-align: middle; + white-space: normal; + font-size: 12px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.searchbox__input::-webkit-search-decoration, +.searchbox__input::-webkit-search-cancel-button, +.searchbox__input::-webkit-search-results-button, +.searchbox__input::-webkit-search-results-decoration { + display: none; +} + +.searchbox__input:hover { + box-shadow: inset 0 0 0 1px #b3b3b3; +} + +.searchbox__input:focus, +.searchbox__input:active { + outline: 0; + box-shadow: inset 0 0 0 1px #aaaaaa; + background: #ffffff; +} + +.searchbox__input::-webkit-input-placeholder { + color: #aaaaaa; +} + +.searchbox__input::-moz-placeholder { + color: #aaaaaa; +} + +.searchbox__input:-ms-input-placeholder { + color: #aaaaaa; +} + +.searchbox__input::placeholder { + color: #aaaaaa; +} + +.searchbox__submit { + position: absolute; + top: 0; + margin: 0; + border: 0; + border-radius: 16px 0 0 16px; + background-color: rgba(69, 142, 225, 0); + padding: 0; + width: 32px; + height: 100%; + vertical-align: middle; + text-align: center; + font-size: inherit; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + right: inherit; + left: 0; +} + +.searchbox__submit::before { + display: inline-block; + margin-right: -4px; + height: 100%; + vertical-align: middle; + content: ''; +} + +.searchbox__submit:hover, +.searchbox__submit:active { + cursor: pointer; +} + +.searchbox__submit:focus { + outline: 0; +} + +.searchbox__submit svg { + width: 14px; + height: 14px; + vertical-align: middle; + fill: #6d7e96; +} + +.searchbox__reset { + display: block; + position: absolute; + top: 8px; + right: 8px; + margin: 0; + border: 0; + background: none; + cursor: pointer; + padding: 0; + font-size: inherit; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + fill: rgba(0, 0, 0, 0.5); +} + +.searchbox__reset.hide { + display: none; +} + +.searchbox__reset:focus { + outline: 0; +} + +.searchbox__reset svg { + display: block; + margin: 4px; + width: 8px; + height: 8px; +} + +.searchbox__input:valid ~ .searchbox__reset { + display: block; + -webkit-animation-name: sbx-reset-in; + animation-name: sbx-reset-in; + -webkit-animation-duration: 0.15s; + animation-duration: 0.15s; +} + +@-webkit-keyframes sbx-reset-in { + 0% { + -webkit-transform: translate3d(-20%, 0, 0); + transform: translate3d(-20%, 0, 0); + opacity: 0; + } + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes sbx-reset-in { + 0% { + -webkit-transform: translate3d(-20%, 0, 0); + transform: translate3d(-20%, 0, 0); + opacity: 0; + } + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.algolia-autocomplete .ds-dropdown-menu:before { + display: block; + position: absolute; + content: ''; + width: 14px; + height: 14px; + background: #373940; + z-index: 1000; + top: -7px; + border-top: 1px solid #373940; + border-right: 1px solid #373940; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + border-radius: 2px; +} + +.algolia-autocomplete .ds-dropdown-menu { + box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2), 0 2px 3px 0 rgba(0, 0, 0, 0.1); +} + +@media (min-width: 601px) { + .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu { + right: 0 !important; + left: inherit !important; + } + + .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before { + right: 48px; + } + + .algolia-autocomplete .ds-dropdown-menu { + position: relative; + top: -6px; + border-radius: 4px; + margin: 6px 0 0; + padding: 0; + text-align: left; + height: auto; + position: relative; + background: transparent; + border: none; + z-index: 999; + max-width: 600px; + min-width: 500px; + } +} + +@media (max-width: 600px) { + .algolia-autocomplete .ds-dropdown-menu { + z-index: 100; + position: fixed !important; + top: 50px !important; + left: auto !important; + right: 1rem !important; + width: 600px; + max-width: calc(100% - 2rem); + max-height: calc(100% - 5rem); + display: block; + } + + .algolia-autocomplete .ds-dropdown-menu:before { + right: 6rem; + } +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestions { + position: relative; + z-index: 1000; +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestion { + cursor: pointer; +} + +.algolia-autocomplete .ds-dropdown-menu [class^='ds-dataset-'] { + position: relative; + border-radius: 4px; + overflow: auto; + padding: 0; + background: #ffffff; +} + +.algolia-autocomplete .ds-dropdown-menu * { + box-sizing: border-box; +} + +.algolia-autocomplete .algolia-docsearch-suggestion { + display: block; + position: relative; + padding: 0; + overflow: hidden; + text-decoration: none; +} + +.algolia-autocomplete .ds-cursor .algolia-docsearch-suggestion--wrapper { + background: #f1f1f1; + box-shadow: inset -2px 0 0 #61dafb; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--highlight { + background: #ffe564; + padding: 0.1em 0.05em; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion--category-header + .algolia-docsearch-suggestion--category-header-lvl0 + .algolia-docsearch-suggestion--highlight, +.algolia-autocomplete + .algolia-docsearch-suggestion--category-header + .algolia-docsearch-suggestion--category-header-lvl1 + .algolia-docsearch-suggestion--highlight { + color: inherit; + background: inherit; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion--text + .algolia-docsearch-suggestion--highlight { + padding: 0 0 1px; + background: inherit; + box-shadow: inset 0 -2px 0 0 rgba(69, 142, 225, 0.8); + color: inherit; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--content { + display: block; + float: right; + width: 70%; + position: relative; + padding: 5.33333px 0 5.33333px 10.66667px; + cursor: pointer; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--content:before { + content: ''; + position: absolute; + display: block; + top: 0; + height: 100%; + width: 1px; + background: #ececec; + left: -1px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--category-header { + position: relative; + display: none; + font-size: 14px; + letter-spacing: 0.08em; + font-weight: 700; + background-color: #373940; + text-transform: uppercase; + color: #fff; + margin: 0; + padding: 5px 8px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--wrapper { + background-color: #fff; + width: 100%; + float: left; + padding: 8px 0 0 0; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column { + float: left; + width: 30%; + display: none; + padding-left: 0; + text-align: right; + position: relative; + padding: 5.33333px 10.66667px; + color: #777; + font-size: 0.9em; + word-wrap: break-word; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before { + content: ''; + position: absolute; + display: block; + top: 0; + height: 100%; + width: 1px; + background: #ececec; + right: 0; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main + .algolia-docsearch-suggestion--category-header, +.algolia-autocomplete + .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary { + display: block; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion--subcategory-column + .algolia-docsearch-suggestion--highlight { + background-color: inherit; + color: inherit; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline { + display: none; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--title { + margin-bottom: 4px; + color: #02060c; + font-size: 0.9em; + font-weight: bold; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--text { + display: block; + line-height: 1.2em; + font-size: 0.85em; + color: #63676d; + padding-right: 2px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--no-results { + width: 100%; + padding: 8px 0; + text-align: center; + font-size: 1.2em; + background-color: #373940; + margin-top: -8px; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion--no-results + .algolia-docsearch-suggestion--text { + color: #ffffff; + margin-top: 4px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--no-results::before { + display: none; +} + +.algolia-autocomplete .algolia-docsearch-suggestion code { + padding: 1px 5px; + font-size: 90%; + border: none; + color: #222222; + background-color: #ebebeb; + border-radius: 3px; + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion + code + .algolia-docsearch-suggestion--highlight { + background: none; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main + .algolia-docsearch-suggestion--category-header { + color: white; + display: block; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary + .algolia-docsearch-suggestion--subcategory-column { + display: block; +} + +.algolia-autocomplete .algolia-docsearch-footer { + background-color: #fff; + width: 100%; + height: 30px; + z-index: 2000; + float: right; + font-size: 0; + line-height: 0; +} + +.algolia-autocomplete .algolia-docsearch-footer--logo { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 130 18'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='-36.87%25' x2='129.43%25' y1='134.94%25' y2='-27.7%25'%3E%3Cstop stop-color='%252300AEFF' offset='0%25'/%3E%3Cstop stop-color='%25233369E7' offset='100%25'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='url(%2523a)' d='M59.4.02h13.3a2.37 2.37 0 0 1 2.38 2.37V15.6a2.37 2.37 0 0 1-2.38 2.36H59.4a2.37 2.37 0 0 1-2.38-2.36V2.38A2.37 2.37 0 0 1 59.4.02z'/%3E%3Cpath fill='%2523FFF' d='M66.26 4.56c-2.82 0-5.1 2.27-5.1 5.08 0 2.8 2.28 5.07 5.1 5.07 2.8 0 5.1-2.26 5.1-5.07 0-2.8-2.28-5.07-5.1-5.07zm0 8.65c-2 0-3.6-1.6-3.6-3.56 0-1.97 1.6-3.58 3.6-3.58 1.98 0 3.6 1.6 3.6 3.58a3.58 3.58 0 0 1-3.6 3.57zm0-6.4v2.66c0 .07.08.13.15.1l2.4-1.24c.04-.02.06-.1.03-.14a2.96 2.96 0 0 0-2.46-1.5c-.06 0-.1.05-.1.1zm-3.33-1.96l-.3-.3a.78.78 0 0 0-1.12 0l-.36.36a.77.77 0 0 0 0 1.1l.3.3c.05.05.13.04.17 0 .2-.25.4-.5.6-.7.23-.23.46-.43.7-.6.07-.04.07-.1.03-.16zm5-.8V3.4a.78.78 0 0 0-.78-.78h-1.83a.78.78 0 0 0-.78.78v.63c0 .07.06.12.14.1a5.74 5.74 0 0 1 1.58-.22c.52 0 1.04.07 1.54.2a.1.1 0 0 0 .13-.1z'/%3E%3Cpath fill='%2523182359' d='M102.16 13.76c0 1.46-.37 2.52-1.12 3.2-.75.67-1.9 1-3.44 1-.56 0-1.74-.1-2.67-.3l.34-1.7c.78.17 1.82.2 2.36.2.86 0 1.48-.16 1.84-.5.37-.36.55-.88.55-1.57v-.35a6.37 6.37 0 0 1-.84.3 4.15 4.15 0 0 1-1.2.17 4.5 4.5 0 0 1-1.6-.28 3.38 3.38 0 0 1-1.26-.82 3.74 3.74 0 0 1-.8-1.35c-.2-.54-.3-1.5-.3-2.2 0-.67.1-1.5.3-2.06a3.92 3.92 0 0 1 .9-1.43 4.12 4.12 0 0 1 1.45-.92 5.3 5.3 0 0 1 1.94-.37c.7 0 1.35.1 1.97.2a15.86 15.86 0 0 1 1.6.33v8.46zm-5.95-4.2c0 .9.2 1.88.6 2.3.4.4.9.62 1.53.62.34 0 .66-.05.96-.15a2.75 2.75 0 0 0 .73-.33V6.7a8.53 8.53 0 0 0-1.42-.17c-.76-.02-1.36.3-1.77.8-.4.5-.62 1.4-.62 2.23zm16.13 0c0 .72-.1 1.26-.32 1.85a4.4 4.4 0 0 1-.9 1.53c-.38.42-.85.75-1.4.98-.54.24-1.4.37-1.8.37-.43 0-1.27-.13-1.8-.36a4.1 4.1 0 0 1-1.4-.97 4.5 4.5 0 0 1-.92-1.52 5.04 5.04 0 0 1-.33-1.84c0-.72.1-1.4.32-2 .22-.6.53-1.1.92-1.5.4-.43.86-.75 1.4-.98a4.55 4.55 0 0 1 1.78-.34 4.7 4.7 0 0 1 1.8.34c.54.23 1 .55 1.4.97.38.42.68.92.9 1.5.23.6.35 1.3.35 2zm-2.2 0c0-.92-.2-1.7-.6-2.22-.38-.54-.94-.8-1.64-.8-.72 0-1.27.26-1.67.8-.4.54-.58 1.3-.58 2.22 0 .93.2 1.56.6 2.1.38.54.94.8 1.64.8s1.25-.26 1.65-.8c.4-.55.6-1.17.6-2.1zm6.97 4.7c-3.5.02-3.5-2.8-3.5-3.27L113.57.92l2.15-.34v10c0 .25 0 1.87 1.37 1.88v1.8zm3.77 0h-2.15v-9.2l2.15-.33v9.54zM119.8 3.74c.7 0 1.3-.58 1.3-1.3 0-.7-.58-1.3-1.3-1.3-.73 0-1.3.6-1.3 1.3 0 .72.58 1.3 1.3 1.3zm6.43 1c.7 0 1.3.1 1.78.27.5.18.88.42 1.17.73.28.3.5.74.6 1.18.13.46.2.95.2 1.5v5.47a25.24 25.24 0 0 1-1.5.25c-.67.1-1.42.15-2.25.15a6.83 6.83 0 0 1-1.52-.16 3.2 3.2 0 0 1-1.18-.5 2.46 2.46 0 0 1-.76-.9c-.18-.37-.27-.9-.27-1.44 0-.52.1-.85.3-1.2.2-.37.48-.67.83-.9a3.6 3.6 0 0 1 1.23-.5 7.07 7.07 0 0 1 2.2-.1l.83.16v-.35c0-.25-.03-.48-.1-.7a1.5 1.5 0 0 0-.3-.58c-.15-.18-.34-.3-.58-.4a2.54 2.54 0 0 0-.92-.17c-.5 0-.94.06-1.35.13-.4.08-.75.16-1 .25l-.27-1.74c.27-.1.67-.18 1.2-.28a9.34 9.34 0 0 1 1.65-.14zm.18 7.74c.66 0 1.15-.04 1.5-.1V10.2a5.1 5.1 0 0 0-2-.1c-.23.03-.45.1-.64.2a1.17 1.17 0 0 0-.47.38c-.13.17-.18.26-.18.52 0 .5.17.8.5.98.32.2.74.3 1.3.3zM84.1 4.8c.72 0 1.3.08 1.8.26.48.17.87.42 1.15.73.3.3.5.72.6 1.17.14.45.2.94.2 1.47v5.48a25.24 25.24 0 0 1-1.5.26c-.67.1-1.42.14-2.25.14a6.83 6.83 0 0 1-1.52-.16 3.2 3.2 0 0 1-1.18-.5 2.46 2.46 0 0 1-.76-.9c-.18-.38-.27-.9-.27-1.44 0-.53.1-.86.3-1.22.2-.36.5-.65.84-.88a3.6 3.6 0 0 1 1.24-.5 7.07 7.07 0 0 1 2.2-.1c.26.03.54.08.84.15v-.35c0-.24-.03-.48-.1-.7a1.5 1.5 0 0 0-.3-.58c-.15-.17-.34-.3-.58-.4a2.54 2.54 0 0 0-.9-.15c-.5 0-.96.05-1.37.12-.4.07-.75.15-1 .24l-.26-1.75c.27-.08.67-.17 1.18-.26a8.9 8.9 0 0 1 1.66-.15zm.2 7.73c.65 0 1.14-.04 1.48-.1v-2.17a5.1 5.1 0 0 0-1.98-.1c-.24.03-.46.1-.65.18a1.17 1.17 0 0 0-.47.4c-.12.17-.17.26-.17.52 0 .5.18.8.5.98.32.2.75.3 1.3.3zm8.68 1.74c-3.5 0-3.5-2.82-3.5-3.28L89.45.92 91.6.6v10c0 .25 0 1.87 1.38 1.88v1.8z'/%3E%3Cpath fill='%25231D3657' d='M5.03 11.03c0 .7-.26 1.24-.76 1.64-.5.4-1.2.6-2.1.6-.88 0-1.6-.14-2.17-.42v-1.2c.36.16.74.3 1.14.38.4.1.78.15 1.13.15.5 0 .88-.1 1.12-.3a.94.94 0 0 0 .35-.77.98.98 0 0 0-.33-.74c-.22-.2-.68-.44-1.37-.72-.72-.3-1.22-.62-1.52-1C.23 8.27.1 7.82.1 7.3c0-.65.22-1.17.7-1.55.46-.37 1.08-.56 1.86-.56.76 0 1.5.16 2.25.48l-.4 1.05c-.7-.3-1.32-.44-1.87-.44-.4 0-.73.08-.94.26a.9.9 0 0 0-.33.72c0 .2.04.38.12.52.08.15.22.3.42.4.2.14.55.3 1.06.52.58.24 1 .47 1.27.67.27.2.47.44.6.7.12.26.18.57.18.92zM9 13.27c-.92 0-1.64-.27-2.16-.8-.52-.55-.78-1.3-.78-2.24 0-.97.24-1.73.72-2.3.5-.54 1.15-.82 2-.82.78 0 1.4.25 1.85.72.46.48.7 1.14.7 1.97v.67H7.35c0 .58.17 1.02.46 1.33.3.3.7.47 1.24.47.36 0 .68-.04.98-.1a5.1 5.1 0 0 0 .98-.33v1.02a3.87 3.87 0 0 1-.94.32 5.72 5.72 0 0 1-1.08.1zm-.22-5.2c-.4 0-.73.12-.97.38s-.37.62-.42 1.1h2.7c0-.48-.13-.85-.36-1.1-.23-.26-.54-.38-.94-.38zm7.7 5.1l-.26-.84h-.05c-.28.36-.57.6-.86.74-.28.13-.65.2-1.1.2-.6 0-1.05-.16-1.38-.48-.32-.32-.5-.77-.5-1.34 0-.62.24-1.08.7-1.4.45-.3 1.14-.47 2.07-.5l1.02-.03V9.2c0-.37-.1-.65-.27-.84-.17-.2-.45-.28-.82-.28-.3 0-.6.04-.88.13a6.68 6.68 0 0 0-.8.33l-.4-.9a4.4 4.4 0 0 1 1.05-.4 4.86 4.86 0 0 1 1.08-.12c.76 0 1.33.18 1.7.5.4.33.6.85.6 1.56v4h-.9zm-1.9-.87c.47 0 .83-.13 1.1-.38.3-.26.43-.62.43-1.08v-.52l-.76.03c-.6.03-1.02.13-1.3.3s-.4.45-.4.82c0 .26.08.47.24.6.16.16.4.23.7.23zm7.57-5.2c.25 0 .46.03.62.06l-.12 1.18a2.38 2.38 0 0 0-.56-.06c-.5 0-.92.16-1.24.5-.3.32-.47.75-.47 1.27v3.1h-1.27V7.23h1l.16 1.05h.05c.2-.36.45-.64.77-.85a1.83 1.83 0 0 1 1.02-.3zm4.12 6.17c-.9 0-1.58-.27-2.05-.8-.47-.52-.7-1.27-.7-2.25 0-1 .24-1.77.73-2.3.5-.54 1.2-.8 2.12-.8.63 0 1.2.1 1.7.34l-.4 1c-.52-.2-.96-.3-1.3-.3-1.04 0-1.55.68-1.55 2.05 0 .67.13 1.17.38 1.5.26.34.64.5 1.13.5a3.23 3.23 0 0 0 1.6-.4v1.1a2.53 2.53 0 0 1-.73.28 4.36 4.36 0 0 1-.93.08zm8.28-.1h-1.27V9.5c0-.45-.1-.8-.28-1.02-.18-.23-.47-.34-.88-.34-.53 0-.9.16-1.16.48-.25.3-.38.85-.38 1.6v2.94h-1.26V4.8h1.26v2.12c0 .34-.02.7-.06 1.1h.08a1.76 1.76 0 0 1 .72-.67c.3-.16.66-.24 1.07-.24 1.43 0 2.15.74 2.15 2.2v3.86zM42.2 7.1c.74 0 1.32.28 1.73.82.4.53.62 1.3.62 2.26 0 .97-.2 1.73-.63 2.27-.42.54-1 .82-1.75.82s-1.33-.27-1.75-.8h-.08l-.23.7h-.94V4.8h1.26v2l-.02.64-.03.56h.05c.4-.6 1-.9 1.78-.9zm-.33 1.04c-.5 0-.88.15-1.1.45-.22.3-.34.8-.35 1.5v.08c0 .72.12 1.24.35 1.57.23.32.6.48 1.12.48.44 0 .78-.17 1-.53.24-.35.36-.87.36-1.53 0-1.35-.47-2.03-1.4-2.03zm3.24-.92h1.4l1.2 3.37c.18.47.3.92.36 1.34h.04l.18-.72 1.37-4H51l-2.53 6.73c-.46 1.23-1.23 1.85-2.3 1.85-.3 0-.56-.03-.83-.1v-1c.2.05.4.08.65.08.6 0 1.03-.36 1.28-1.06l.22-.56-2.4-5.94z'/%3E%3C/g%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: center; + background-size: 100%; + overflow: hidden; + text-indent: -9000px; + width: 110px; + height: 100%; + display: block; + margin-left: auto; + margin-right: 5px; +} diff --git a/website/src/theme/SearchBar/index.js b/website/src/theme/SearchBar/index.js new file mode 100644 index 000000000..bb8be607b --- /dev/null +++ b/website/src/theme/SearchBar/index.js @@ -0,0 +1,121 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { useRef, useCallback, useState } from "react"; +import classnames from "classnames"; +import { useHistory } from "@docusaurus/router"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import { usePluginData } from '@docusaurus/useGlobalData'; +import useIsBrowser from "@docusaurus/useIsBrowser"; +const Search = props => { + const initialized = useRef(false); + const searchBarRef = useRef(null); + const [indexReady, setIndexReady] = useState(false); + const history = useHistory(); + const { siteConfig = {} } = useDocusaurusContext(); + const isBrowser = useIsBrowser(); + const { baseUrl } = siteConfig; + const initAlgolia = (searchDocs, searchIndex, DocSearch) => { + new DocSearch({ + searchDocs, + searchIndex, + baseUrl, + inputSelector: "#search_input_react", + // Override algolia's default selection event, allowing us to do client-side + // navigation and avoiding a full page refresh. + handleSelected: (_input, _event, suggestion) => { + const url = suggestion.url || "/"; + // Use an anchor tag to parse the absolute url into a relative url + // Alternatively, we can use new URL(suggestion.url) but its not supported in IE + const a = document.createElement("a"); + a.href = url; + // Algolia use closest parent element id #__docusaurus when a h1 page title does not have an id + // So, we can safely remove it. See https://github.com/facebook/docusaurus/issues/1828 for more details. + + history.push(url); + } + }); + }; + + const pluginData = usePluginData('docusaurus-lunr-search'); + const getSearchDoc = () => + process.env.NODE_ENV === "production" + ? fetch(`${baseUrl}${pluginData.fileNames.searchDoc}`).then((content) => content.json()) + : Promise.resolve([]); + + const getLunrIndex = () => + process.env.NODE_ENV === "production" + ? fetch(`${baseUrl}${pluginData.fileNames.lunrIndex}`).then((content) => content.json()) + : Promise.resolve([]); + + const loadAlgolia = () => { + if (!initialized.current) { + Promise.all([ + getSearchDoc(), + getLunrIndex(), + import("./DocSearch"), + import("./algolia.css") + ]).then(([searchDocs, searchIndex, { default: DocSearch }]) => { + if (searchDocs.length === 0) { + return; + } + initAlgolia(searchDocs, searchIndex, DocSearch); + setIndexReady(true); + }); + initialized.current = true; + } + }; + + const toggleSearchIconClick = useCallback( + e => { + if (!searchBarRef.current.contains(e.target)) { + searchBarRef.current.focus(); + } + + props.handleSearchBarToggle && props.handleSearchBarToggle(!props.isSearchBarExpanded); + }, + [props.isSearchBarExpanded] + ); + + if (isBrowser) { + loadAlgolia(); + } + + return ( +
+ + +
+ ); +}; + +export default Search; diff --git a/website/src/theme/SearchBar/lunar-search.js b/website/src/theme/SearchBar/lunar-search.js new file mode 100644 index 000000000..25681f486 --- /dev/null +++ b/website/src/theme/SearchBar/lunar-search.js @@ -0,0 +1,147 @@ +import lunr from "@generated/lunr.client"; +lunr.tokenizer.separator = /[\s\-/]+/; + +class LunrSearchAdapter { + constructor(searchDocs, searchIndex, baseUrl = '/') { + this.searchDocs = searchDocs; + this.lunrIndex = lunr.Index.load(searchIndex); + this.baseUrl = baseUrl; + } + + getLunrResult(input) { + return this.lunrIndex.query(function (query) { + const tokens = lunr.tokenizer(input); + query.term(tokens, { + boost: 10 + }); + query.term(tokens, { + wildcard: lunr.Query.wildcard.TRAILING + }); + }); + } + + getHit(doc, formattedTitle, formattedContent) { + return { + hierarchy: { + lvl0: doc.pageTitle || doc.title, + lvl1: doc.type === 0 ? null : doc.title + }, + url: doc.url, + _snippetResult: formattedContent ? { + content: { + value: formattedContent, + matchLevel: "full" + } + } : null, + _highlightResult: { + hierarchy: { + lvl0: { + value: doc.type === 0 ? formattedTitle || doc.title : doc.pageTitle, + }, + lvl1: + doc.type === 0 + ? null + : { + value: formattedTitle || doc.title + } + } + } + }; + } + getTitleHit(doc, position, length) { + const start = position[0]; + const end = position[0] + length; + let formattedTitle = doc.title.substring(0, start) + '' + doc.title.substring(start, end) + '' + doc.title.substring(end, doc.title.length); + return this.getHit(doc, formattedTitle) + } + + getKeywordHit(doc, position, length) { + const start = position[0]; + const end = position[0] + length; + let formattedTitle = doc.title + '
Keywords: ' + doc.keywords.substring(0, start) + '' + doc.keywords.substring(start, end) + '' + doc.keywords.substring(end, doc.keywords.length) + '' + return this.getHit(doc, formattedTitle) + } + + getContentHit(doc, position) { + const start = position[0]; + const end = position[0] + position[1]; + let previewStart = start; + let previewEnd = end; + let ellipsesBefore = true; + let ellipsesAfter = true; + for (let k = 0; k < 3; k++) { + const nextSpace = doc.content.lastIndexOf(' ', previewStart - 2); + const nextDot = doc.content.lastIndexOf('.', previewStart - 2); + if ((nextDot > 0) && (nextDot > nextSpace)) { + previewStart = nextDot + 1; + ellipsesBefore = false; + break; + } + if (nextSpace < 0) { + previewStart = 0; + ellipsesBefore = false; + break; + } + previewStart = nextSpace + 1; + } + for (let k = 0; k < 10; k++) { + const nextSpace = doc.content.indexOf(' ', previewEnd + 1); + const nextDot = doc.content.indexOf('.', previewEnd + 1); + if ((nextDot > 0) && (nextDot < nextSpace)) { + previewEnd = nextDot; + ellipsesAfter = false; + break; + } + if (nextSpace < 0) { + previewEnd = doc.content.length; + ellipsesAfter = false; + break; + } + previewEnd = nextSpace; + } + let preview = doc.content.substring(previewStart, start); + if (ellipsesBefore) { + preview = '... ' + preview; + } + preview += '' + doc.content.substring(start, end) + ''; + preview += doc.content.substring(end, previewEnd); + if (ellipsesAfter) { + preview += ' ...'; + } + return this.getHit(doc, null, preview); + + } + search(input) { + return new Promise((resolve, rej) => { + const results = this.getLunrResult(input); + const hits = []; + results.length > 5 && (results.length = 5); + this.titleHitsRes = [] + this.contentHitsRes = [] + results.forEach(result => { + const doc = this.searchDocs[result.ref]; + const { metadata } = result.matchData; + for (let i in metadata) { + if (metadata[i].title) { + if (!this.titleHitsRes.includes(result.ref)) { + const position = metadata[i].title.position[0] + hits.push(this.getTitleHit(doc, position, input.length)); + this.titleHitsRes.push(result.ref); + } + } else if (metadata[i].content) { + const position = metadata[i].content.position[0] + hits.push(this.getContentHit(doc, position)) + } else if (metadata[i].keywords) { + const position = metadata[i].keywords.position[0] + hits.push(this.getKeywordHit(doc, position, input.length)); + this.titleHitsRes.push(result.ref); + } + } + }); + hits.length > 5 && (hits.length = 5); + resolve(hits); + }); + } +} + +export default LunrSearchAdapter; diff --git a/website/src/theme/SearchBar/styles.css b/website/src/theme/SearchBar/styles.css new file mode 100644 index 000000000..39fd06c7e --- /dev/null +++ b/website/src/theme/SearchBar/styles.css @@ -0,0 +1,40 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.search-icon { + background-image: var(--ifm-navbar-search-input-icon); + height: auto; + width: 24px; + cursor: pointer; + padding: 8px; + line-height: 32px; + background-repeat: no-repeat; + background-position: center; + display: none; +} + +.search-icon-hidden { + visibility: hidden; +} + +@media (max-width: 360px) { + .search-bar { + width: 0 !important; + background: none !important; + padding: 0 !important; + transition: none !important; + } + + .search-bar-expanded { + width: 9rem !important; + } + + .search-icon { + display: inline; + vertical-align: sub; + } +} diff --git a/website/src/theme/SearchBar/templates.js b/website/src/theme/SearchBar/templates.js new file mode 100644 index 000000000..e8112dff1 --- /dev/null +++ b/website/src/theme/SearchBar/templates.js @@ -0,0 +1,119 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const prefix = 'algolia-docsearch'; +const suggestionPrefix = `${prefix}-suggestion`; +const footerPrefix = `${prefix}-footer`; + +const templates = { + suggestion: ` +
+
+ {{{category}}} +
+
+
+ {{{subcategory}}} +
+ {{#isTextOrSubcategoryNonEmpty}} +
+
{{{subcategory}}}
+
{{{title}}}
+ {{#text}}
{{{text}}}
{{/text}} +
+ {{/isTextOrSubcategoryNonEmpty}} +
+
+ `, + suggestionSimple: ` +
+
+ {{^isLvl0}} + {{{category}}} + {{^isLvl1}} + {{^isLvl1EmptyOrDuplicate}} + + {{{subcategory}}} + + {{/isLvl1EmptyOrDuplicate}} + {{/isLvl1}} + {{/isLvl0}} +
+ {{#isLvl2}} + {{{title}}} + {{/isLvl2}} + {{#isLvl1}} + {{{subcategory}}} + {{/isLvl1}} + {{#isLvl0}} + {{{category}}} + {{/isLvl0}} +
+
+
+ {{#text}} +
+
{{{text}}}
+
+ {{/text}} +
+
+ `, + footer: ` +
+
+ `, + empty: ` +
+
+
+
+
+ No results found for query "{{query}}" +
+
+
+
+
+ `, + searchBox: ` + + + + + + `, +}; + +export default templates; diff --git a/website/src/theme/SearchBar/utils.js b/website/src/theme/SearchBar/utils.js new file mode 100644 index 000000000..1afe5c128 --- /dev/null +++ b/website/src/theme/SearchBar/utils.js @@ -0,0 +1,277 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import $ from "autocomplete.js/zepto"; + +const utils = { + /* + * Move the content of an object key one level higher. + * eg. + * { + * name: 'My name', + * hierarchy: { + * lvl0: 'Foo', + * lvl1: 'Bar' + * } + * } + * Will be converted to + * { + * name: 'My name', + * lvl0: 'Foo', + * lvl1: 'Bar' + * } + * @param {Object} object Main object + * @param {String} property Main object key to move up + * @return {Object} + * @throws Error when key is not an attribute of Object or is not an object itself + */ + mergeKeyWithParent(object, property) { + if (object[property] === undefined) { + return object; + } + if (typeof object[property] !== 'object') { + return object; + } + const newObject = $.extend({}, object, object[property]); + delete newObject[property]; + return newObject; + }, + /* + * Group all objects of a collection by the value of the specified attribute + * If the attribute is a string, use the lowercase form. + * + * eg. + * groupBy([ + * {name: 'Tim', category: 'dev'}, + * {name: 'Vincent', category: 'dev'}, + * {name: 'Ben', category: 'sales'}, + * {name: 'Jeremy', category: 'sales'}, + * {name: 'AlexS', category: 'dev'}, + * {name: 'AlexK', category: 'sales'} + * ], 'category'); + * => + * { + * 'devs': [ + * {name: 'Tim', category: 'dev'}, + * {name: 'Vincent', category: 'dev'}, + * {name: 'AlexS', category: 'dev'} + * ], + * 'sales': [ + * {name: 'Ben', category: 'sales'}, + * {name: 'Jeremy', category: 'sales'}, + * {name: 'AlexK', category: 'sales'} + * ] + * } + * @param {array} collection Array of objects to group + * @param {String} property The attribute on which apply the grouping + * @return {array} + * @throws Error when one of the element does not have the specified property + */ + groupBy(collection, property) { + const newCollection = {}; + $.each(collection, (index, item) => { + if (item[property] === undefined) { + throw new Error(`[groupBy]: Object has no key ${property}`); + } + let key = item[property]; + if (typeof key === 'string') { + key = key.toLowerCase(); + } + // fix #171 the given data type of docsearch hits might be conflict with the properties of the native Object, + // such as the constructor, so we need to do this check. + if (!Object.prototype.hasOwnProperty.call(newCollection, key)) { + newCollection[key] = []; + } + newCollection[key].push(item); + }); + return newCollection; + }, + /* + * Return an array of all the values of the specified object + * eg. + * values({ + * foo: 42, + * bar: true, + * baz: 'yep' + * }) + * => + * [42, true, yep] + * @param {object} object Object to extract values from + * @return {array} + */ + values(object) { + return Object.keys(object).map(key => object[key]); + }, + /* + * Flattens an array + * eg. + * flatten([1, 2, [3, 4], [5, 6]]) + * => + * [1, 2, 3, 4, 5, 6] + * @param {array} array Array to flatten + * @return {array} + */ + flatten(array) { + const results = []; + array.forEach(value => { + if (!Array.isArray(value)) { + results.push(value); + return; + } + value.forEach(subvalue => { + results.push(subvalue); + }); + }); + return results; + }, + /* + * Flatten all values of an object into an array, marking each first element of + * each group with a specific flag + * eg. + * flattenAndFlagFirst({ + * 'devs': [ + * {name: 'Tim', category: 'dev'}, + * {name: 'Vincent', category: 'dev'}, + * {name: 'AlexS', category: 'dev'} + * ], + * 'sales': [ + * {name: 'Ben', category: 'sales'}, + * {name: 'Jeremy', category: 'sales'}, + * {name: 'AlexK', category: 'sales'} + * ] + * , 'isTop'); + * => + * [ + * {name: 'Tim', category: 'dev', isTop: true}, + * {name: 'Vincent', category: 'dev', isTop: false}, + * {name: 'AlexS', category: 'dev', isTop: false}, + * {name: 'Ben', category: 'sales', isTop: true}, + * {name: 'Jeremy', category: 'sales', isTop: false}, + * {name: 'AlexK', category: 'sales', isTop: false} + * ] + * @param {object} object Object to flatten + * @param {string} flag Flag to set to true on first element of each group + * @return {array} + */ + flattenAndFlagFirst(object, flag) { + const values = this.values(object).map(collection => + collection.map((item, index) => { + // eslint-disable-next-line no-param-reassign + item[flag] = index === 0; + return item; + }) + ); + return this.flatten(values); + }, + /* + * Removes all empty strings, null, false and undefined elements array + * eg. + * compact([42, false, null, undefined, '', [], 'foo']); + * => + * [42, [], 'foo'] + * @param {array} array Array to compact + * @return {array} + */ + compact(array) { + const results = []; + array.forEach(value => { + if (!value) { + return; + } + results.push(value); + }); + return results; + }, + /* + * Returns the highlighted value of the specified key in the specified object. + * If no highlighted value is available, will return the key value directly + * eg. + * getHighlightedValue({ + * _highlightResult: { + * text: { + * value: 'foo' + * } + * }, + * text: 'foo' + * }, 'text'); + * => + * 'foo' + * @param {object} object Hit object returned by the Algolia API + * @param {string} property Object key to look for + * @return {string} + **/ + getHighlightedValue(object, property) { + if ( + object._highlightResult && + object._highlightResult.hierarchy_camel && + object._highlightResult.hierarchy_camel[property] && + object._highlightResult.hierarchy_camel[property].matchLevel && + object._highlightResult.hierarchy_camel[property].matchLevel !== 'none' && + object._highlightResult.hierarchy_camel[property].value + ) { + return object._highlightResult.hierarchy_camel[property].value; + } + if ( + object._highlightResult && + object._highlightResult && + object._highlightResult[property] && + object._highlightResult[property].value + ) { + return object._highlightResult[property].value; + } + return object[property]; + }, + /* + * Returns the snippeted value of the specified key in the specified object. + * If no highlighted value is available, will return the key value directly. + * Will add starting and ending ellipsis (…) if we detect that a sentence is + * incomplete + * eg. + * getSnippetedValue({ + * _snippetResult: { + * text: { + * value: 'This is an unfinished sentence' + * } + * }, + * text: 'This is an unfinished sentence' + * }, 'text'); + * => + * 'This is an unfinished sentence…' + * @param {object} object Hit object returned by the Algolia API + * @param {string} property Object key to look for + * @return {string} + **/ + getSnippetedValue(object, property) { + if ( + !object._snippetResult || + !object._snippetResult[property] || + !object._snippetResult[property].value + ) { + return object[property]; + } + let snippet = object._snippetResult[property].value; + + if (snippet[0] !== snippet[0].toUpperCase()) { + snippet = `…${snippet}`; + } + if (['.', '!', '?'].indexOf(snippet[snippet.length - 1]) === -1) { + snippet = `${snippet}…`; + } + return snippet; + }, + /* + * Deep clone an object. + * Note: This will not clone functions and dates + * @param {object} object Object to clone + * @return {object} + */ + deepClone(object) { + return JSON.parse(JSON.stringify(object)); + }, +}; + +export default utils; diff --git a/website/static/img/flipper.png b/website/static/img/flipper.png new file mode 100644 index 000000000..f7f3a7646 Binary files /dev/null and b/website/static/img/flipper.png differ diff --git a/website/static/img/flipper_jest_e2e_arch.svg b/website/static/img/flipper_jest_e2e_arch.svg new file mode 100644 index 000000000..661b04911 --- /dev/null +++ b/website/static/img/flipper_jest_e2e_arch.svg @@ -0,0 +1,17 @@ + + + + + + + + Prod networkIntern networkParagon/OneWorld machine (iOS/Android)Jest agentidb/adbOn-demandJest masterYour laptopBrowserFlipper tabSSH tunnelWebSocket connectionWebSocket tunnel that uses ProxygenmagicFlipper proxy serverFlipperserver \ No newline at end of file diff --git a/website/static/img/marketplace-discovery.png b/website/static/img/marketplace-discovery.png new file mode 100644 index 000000000..e69de29bb diff --git a/website/static/img/marketplace-settings.png b/website/static/img/marketplace-settings.png new file mode 100644 index 000000000..e69de29bb diff --git a/website/static/img/uidebugger.png b/website/static/img/uidebugger.png new file mode 100644 index 000000000..f7f3a7646 Binary files /dev/null and b/website/static/img/uidebugger.png differ diff --git a/website/yarn.lock b/website/yarn.lock index 416829390..0c2c3b261 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2,17 +2,24 @@ # yarn lockfile v1 -"@algolia/autocomplete-core@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.6.3.tgz#76832fffb6405ac2c87bac5a040b8a31a1cdef80" - integrity sha512-dqQqRt01fX3YuVFrkceHsoCnzX0bLhrrg8itJI1NM68KjrPYQPYsE+kY8EZTCM4y8VDnhqJErR73xe/ZsV+qAA== +"@algolia/autocomplete-core@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.1.tgz#025538b8a9564a9f3dd5bcf8a236d6951c76c7d1" + integrity sha512-eiZw+fxMzNQn01S8dA/hcCpoWCOCwcIIEUtHHdzN5TGB3IpzLbuhqFeTfh2OUhhgkE8Uo17+wH+QJ/wYyQmmzg== dependencies: - "@algolia/autocomplete-shared" "1.6.3" + "@algolia/autocomplete-shared" "1.7.1" -"@algolia/autocomplete-shared@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.6.3.tgz#52085ce89a755977841ed0a463aa31ce8f1dea97" - integrity sha512-UV46bnkTztyADFaETfzFC5ryIdGVb2zpAoYgu0tfcuYWjhg1KbLXveFffZIrGVoboqmAk1b+jMrl6iCja1i3lg== +"@algolia/autocomplete-preset-algolia@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.1.tgz#7dadc5607097766478014ae2e9e1c9c4b3f957c8" + integrity sha512-pJwmIxeJCymU1M6cGujnaIYcY3QPOVYZOXhFkWVM7IxKzy272BwCvMFMyc5NpG/QmiObBxjo7myd060OeTNJXg== + dependencies: + "@algolia/autocomplete-shared" "1.7.1" + +"@algolia/autocomplete-shared@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.1.tgz#95c3a0b4b78858fed730cf9c755b7d1cd0c82c74" + integrity sha512-eTmGVqY3GeyBTT8IWiB2K5EuURAqhnumfktAEoHxfDY2o7vg2rSnO16ZtIG0fMgt3py28Vwgq42/bVEuaQV7pg== "@algolia/cache-browser-local-storage@4.10.3": version "4.10.3" @@ -258,16 +265,16 @@ classnames "^2.2.6" rc-util "^5.9.4" -"@ant-design/react-slick@~0.28.1": - version "0.28.4" - resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-0.28.4.tgz#8b296b87ad7c7ae877f2a527b81b7eebd9dd29a9" - integrity sha512-j9eAHTn7GxbXUFNknJoHS2ceAsqrQi2j8XykjZE1IXCD8kJF+t28EvhBLniDpbOsBk/3kjalnhriTfZcjBHNqg== +"@ant-design/react-slick@~0.29.1": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-0.29.2.tgz#53e6a7920ea3562eebb304c15a7fc2d7e619d29c" + integrity sha512-kgjtKmkGHa19FW21lHnAfyyH9AAoh35pBdcJ53rHmQ3O+cfFHGHnUbj/HFrRNJ5vIts09FKJVAD8RpaC+RaWfA== dependencies: "@babel/runtime" "^7.10.4" classnames "^2.2.5" json2mq "^0.2.0" lodash "^4.17.21" - resize-observer-polyfill "^1.5.0" + resize-observer-polyfill "^1.5.1" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.14.5": version "7.14.5" @@ -283,6 +290,13 @@ dependencies: "@babel/highlight" "^7.16.7" +"@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + "@babel/code-frame@^7.8.3": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" @@ -300,6 +314,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.5.tgz#acac0c839e317038c73137fbb6ef71a1d6238471" integrity sha512-BxhE40PVCBxVEJsSBhB6UWyAuqJRxGsAw8BdHMJ3AKGydcwuWW4kOO3HmqBQAdcq/OP+/DlTVxLvsCzRTnZuGg== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.0.tgz#2a592fd89bacb1fcde68de31bee4f2f2dacb0e86" + integrity sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw== + "@babel/core@7.12.9": version "7.12.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" @@ -322,7 +341,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.15.5", "@babel/core@^7.18.2": +"@babel/core@^7.15.5": version "7.18.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000" integrity sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ== @@ -343,6 +362,27 @@ json5 "^2.2.1" semver "^6.3.0" +"@babel/core@^7.18.6": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.0.tgz#d2f5f4f2033c00de8096be3c9f45772563e150c3" + integrity sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.19.0" + "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-module-transforms" "^7.19.0" + "@babel/helpers" "^7.19.0" + "@babel/parser" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + "@babel/generator@^7.12.5", "@babel/generator@^7.15.0": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.0.tgz#a7d0c172e0d814974bad5aa77ace543b97917f15" @@ -370,6 +410,15 @@ "@jridgewell/gen-mapping" "^0.3.0" jsesc "^2.5.1" +"@babel/generator@^7.18.7", "@babel/generator@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a" + integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg== + dependencies: + "@babel/types" "^7.19.0" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61" @@ -391,6 +440,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz#b939b43f8c37765443a19ae74ad8b15978e0a191" @@ -399,13 +455,13 @@ "@babel/helper-explode-assignable-expression" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" - integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== dependencies: - "@babel/helper-explode-assignable-expression" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" "@babel/helper-compilation-targets@^7.13.0": version "7.15.0" @@ -427,7 +483,17 @@ browserslist "^4.16.6" semver "^6.3.0" -"@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2": +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz#537ec8339d53e806ed422f1e06c8f17d55b96bb0" + integrity sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA== + dependencies: + "@babel/compat-data" "^7.19.0" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.20.2" + semver "^6.3.0" + +"@babel/helper-compilation-targets@^7.18.2": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b" integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ== @@ -461,7 +527,7 @@ "@babel/helper-replace-supers" "^7.15.4" "@babel/helper-split-export-declaration" "^7.15.4" -"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0": +"@babel/helper-create-class-features-plugin@^7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19" integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg== @@ -474,6 +540,19 @@ "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz#bfd6904620df4e46470bae4850d66be1054c404b" + integrity sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-create-regexp-features-plugin@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4" @@ -482,13 +561,13 @@ "@babel/helper-annotate-as-pure" "^7.14.5" regexpu-core "^4.7.1" -"@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd" - integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" + integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - regexpu-core "^5.0.1" + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.1.0" "@babel/helper-define-polyfill-provider@^0.2.2": version "0.2.3" @@ -504,15 +583,13 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== +"@babel/helper-define-polyfill-provider@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" + integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" @@ -523,6 +600,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd" integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ== +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + "@babel/helper-explode-assignable-expression@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz#8aa72e708205c7bb643e45c73b4386cdf2a1f645" @@ -530,12 +612,12 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-explode-assignable-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" - integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" "@babel/helper-function-name@^7.14.5": version "7.14.5" @@ -555,7 +637,7 @@ "@babel/template" "^7.15.4" "@babel/types" "^7.15.4" -"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": +"@babel/helper-function-name@^7.17.9": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== @@ -563,6 +645,14 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.17.0" +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + "@babel/helper-get-function-arity@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" @@ -598,6 +688,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-member-expression-to-functions@^7.15.0": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.0.tgz#0ddaf5299c8179f27f37327936553e9bba60990b" @@ -619,6 +716,13 @@ dependencies: "@babel/types" "^7.17.0" +"@babel/helper-member-expression-to-functions@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" + integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== + dependencies: + "@babel/types" "^7.18.9" + "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" @@ -640,6 +744,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.14.5": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.0.tgz#679275581ea056373eddbe360e1419ef23783b08" @@ -682,6 +793,20 @@ "@babel/traverse" "^7.18.0" "@babel/types" "^7.18.0" +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" + integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" + "@babel/helper-optimise-call-expression@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" @@ -703,6 +828,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-plugin-utils@7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" @@ -723,6 +855,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== +"@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" + integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== + "@babel/helper-remap-async-to-generator@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.14.5.tgz#51439c913612958f54a987a4ffc9ee587a2045d6" @@ -741,14 +878,15 @@ "@babel/helper-wrap-function" "^7.15.4" "@babel/types" "^7.15.4" -"@babel/helper-remap-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" - integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== +"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-wrap-function" "^7.16.8" - "@babel/types" "^7.16.8" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" "@babel/helper-replace-supers@^7.14.5", "@babel/helper-replace-supers@^7.15.0": version "7.15.0" @@ -770,7 +908,7 @@ "@babel/traverse" "^7.15.4" "@babel/types" "^7.15.4" -"@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.18.2": +"@babel/helper-replace-supers@^7.16.7": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0" integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q== @@ -781,6 +919,17 @@ "@babel/traverse" "^7.18.2" "@babel/types" "^7.18.2" +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" + integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + "@babel/helper-simple-access@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz#82e1fec0644a7e775c74d305f212c39f8fe73924" @@ -795,13 +944,20 @@ dependencies: "@babel/types" "^7.15.4" -"@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2": +"@babel/helper-simple-access@^7.17.7": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9" integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ== dependencies: "@babel/types" "^7.18.2" +"@babel/helper-simple-access@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" + integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-skip-transparent-expression-wrappers@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4" @@ -816,12 +972,12 @@ dependencies: "@babel/types" "^7.15.4" -"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== +"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" + integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.18.9" "@babel/helper-split-export-declaration@^7.14.5": version "7.14.5" @@ -844,6 +1000,18 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" + integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== + "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9": version "7.14.9" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" @@ -859,6 +1027,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-validator-identifier@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" + integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== + "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" @@ -869,6 +1042,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + "@babel/helper-wrap-function@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.14.5.tgz#5919d115bf0fe328b8a5d63bcb610f51601f2bff" @@ -889,15 +1067,15 @@ "@babel/traverse" "^7.15.4" "@babel/types" "^7.15.4" -"@babel/helper-wrap-function@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" - integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== +"@babel/helper-wrap-function@^7.18.9": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz#89f18335cff1152373222f76a4b37799636ae8b1" + integrity sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg== dependencies: - "@babel/helper-function-name" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.8" - "@babel/types" "^7.16.8" + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" "@babel/helpers@^7.12.5": version "7.15.3" @@ -917,6 +1095,15 @@ "@babel/traverse" "^7.18.2" "@babel/types" "^7.18.2" +"@babel/helpers@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18" + integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg== + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" + "@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" @@ -944,6 +1131,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.12.7", "@babel/parser@^7.14.5", "@babel/parser@^7.15.0": version "7.15.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.3.tgz#3416d9bea748052cfcb63dbcc27368105b1ed862" @@ -954,17 +1150,22 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.8.tgz#7bacdcbe71bdc3ff936d510c15dcea7cf0b99016" integrity sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA== -"@babel/parser@^7.16.7", "@babel/parser@^7.18.3", "@babel/parser@^7.18.5": +"@babel/parser@^7.16.7", "@babel/parser@^7.18.5": version "7.18.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.5.tgz#337062363436a893a2d22faa60be5bb37091c83c" integrity sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz#1dca338caaefca368639c9ffb095afbd4d420b1e" - integrity sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw== +"@babel/parser@^7.18.10", "@babel/parser@^7.18.8", "@babel/parser@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c" + integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.15.4": version "7.15.4" @@ -975,14 +1176,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.15.4" "@babel/plugin-proposal-optional-chaining" "^7.14.5" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz#0d498ec8f0374b1e2eb54b9cb2c4c78714c77753" - integrity sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" + integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-proposal-optional-chaining" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" "@babel/plugin-proposal-async-generator-functions@^7.15.8": version "7.15.8" @@ -993,13 +1194,14 @@ "@babel/helper-remap-async-to-generator" "^7.15.4" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-async-generator-functions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz#094a417e31ce7e692d84bab06c8e2a607cbeef03" - integrity sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ== +"@babel/plugin-proposal-async-generator-functions@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.0.tgz#cf5740194f170467df20581712400487efc79ff1" + integrity sha512-nhEByMUTx3uZueJ/QkJuSlCfN4FGg+xy+vRsfGQGzSauq5ks2Deid2+05Q3KhfaUjvec1IGhw/Zm3cFm8JigTQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-proposal-class-properties@^7.14.5": @@ -1010,13 +1212,13 @@ "@babel/helper-create-class-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-proposal-class-properties@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4" - integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw== +"@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-class-static-block@^7.15.4": version "7.15.4" @@ -1027,13 +1229,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-class-static-block@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710" - integrity sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA== +"@babel/plugin-proposal-class-static-block@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" + integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-dynamic-import@^7.14.5": @@ -1044,12 +1246,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-proposal-dynamic-import@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" - integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-proposal-export-namespace-from@^7.14.5": @@ -1060,12 +1262,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-export-namespace-from@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz#b22864ccd662db9606edb2287ea5fd1709f05378" - integrity sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ== +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-proposal-json-strings@^7.14.5": @@ -1076,12 +1278,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz#f4642951792437233216d8c1af370bb0fbff4664" - integrity sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg== +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-proposal-logical-assignment-operators@^7.14.5": @@ -1092,12 +1294,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-logical-assignment-operators@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz#c64a1bcb2b0a6d0ed2ff674fd120f90ee4b88a23" - integrity sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q== +"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" + integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5": @@ -1108,12 +1310,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be" - integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag== +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-numeric-separator@^7.14.5": @@ -1124,12 +1326,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-numeric-separator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" - integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-proposal-object-rest-spread@7.12.1": @@ -1152,16 +1354,16 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.15.4" -"@babel/plugin-proposal-object-rest-spread@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8" - integrity sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw== +"@babel/plugin-proposal-object-rest-spread@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" + integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/compat-data" "^7.18.8" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.17.12" + "@babel/plugin-transform-parameters" "^7.18.8" "@babel/plugin-proposal-optional-catch-binding@^7.14.5": version "7.14.5" @@ -1171,12 +1373,12 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-catch-binding@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" - integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.14.5": @@ -1188,13 +1390,13 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174" - integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ== +"@babel/plugin-proposal-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" + integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.14.5": @@ -1205,13 +1407,13 @@ "@babel/helper-create-class-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-proposal-private-methods@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz#c2ca3a80beb7539289938da005ad525a038a819c" - integrity sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A== +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-private-property-in-object@^7.15.4": version "7.15.4" @@ -1223,14 +1425,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-private-property-in-object@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz#b02efb7f106d544667d91ae97405a9fd8c93952d" - integrity sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg== +"@babel/plugin-proposal-private-property-in-object@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" + integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-proposal-unicode-property-regex@^7.14.5", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": @@ -1241,13 +1443,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz#3dbd7a67bd7f94c8238b394da112d86aaf32ad4d" - integrity sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A== +"@babel/plugin-proposal-unicode-property-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -1284,12 +1486,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-import-assertions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz#58096a92b11b2e4e54b24c6a0cc0e5e607abcedd" - integrity sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw== +"@babel/plugin-syntax-import-assertions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz#cd6190500a4fa2fe31990a963ffab4b63e4505e4" + integrity sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" @@ -1319,6 +1521,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.17.12" +"@babel/plugin-syntax-jsx@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -1382,6 +1591,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.17.12" +"@babel/plugin-syntax-typescript@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" + integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-arrow-functions@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" @@ -1389,12 +1605,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-arrow-functions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45" - integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA== +"@babel/plugin-transform-arrow-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" + integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-async-to-generator@^7.14.5": version "7.14.5" @@ -1405,14 +1621,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-remap-async-to-generator" "^7.14.5" -"@babel/plugin-transform-async-to-generator@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz#dbe5511e6b01eee1496c944e35cdfe3f58050832" - integrity sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ== +"@babel/plugin-transform-async-to-generator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" + integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" "@babel/plugin-transform-block-scoped-functions@^7.14.5": version "7.14.5" @@ -1421,12 +1637,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-block-scoped-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" - integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.15.3": version "7.15.3" @@ -1435,12 +1651,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-block-scoping@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.4.tgz#7988627b3e9186a13e4d7735dc9c34a056613fb9" - integrity sha512-+Hq10ye+jlvLEogSOtq4mKvtk7qwcUQ1f0Mrueai866C82f844Yom2cttfJdMdqRLTxWpsbfbkIkOIfovyUQXw== +"@babel/plugin-transform-block-scoping@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" + integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-classes@^7.15.4": version "7.15.4" @@ -1455,18 +1671,19 @@ "@babel/helper-split-export-declaration" "^7.15.4" globals "^11.1.0" -"@babel/plugin-transform-classes@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.4.tgz#51310b812a090b846c784e47087fa6457baef814" - integrity sha512-e42NSG2mlKWgxKUAD9EJJSkZxR67+wZqzNxLSpc51T8tRU5SLFHsPmgYR5yr7sdgX4u+iHA1C5VafJ6AyImV3A== +"@babel/plugin-transform-classes@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20" + integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-replace-supers" "^7.18.2" - "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.14.5": @@ -1476,12 +1693,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-computed-properties@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f" - integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ== +"@babel/plugin-transform-computed-properties@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" + integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-destructuring@^7.14.7": version "7.14.7" @@ -1490,12 +1707,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-destructuring@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858" - integrity sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw== +"@babel/plugin-transform-destructuring@^7.18.13": + version "7.18.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5" + integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-dotall-regex@^7.14.5", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.14.5" @@ -1505,13 +1722,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-dotall-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" - integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== +"@babel/plugin-transform-dotall-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-duplicate-keys@^7.14.5": version "7.14.5" @@ -1520,12 +1737,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-duplicate-keys@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz#a09aa709a3310013f8e48e0e23bc7ace0f21477c" - integrity sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw== +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-exponentiation-operator@^7.14.5": version "7.14.5" @@ -1535,13 +1752,13 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-exponentiation-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" - integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-for-of@^7.15.4": version "7.15.4" @@ -1550,12 +1767,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-for-of@^7.18.1": - version "7.18.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036" - integrity sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg== +"@babel/plugin-transform-for-of@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" + integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-function-name@^7.14.5": version "7.14.5" @@ -1565,14 +1782,14 @@ "@babel/helper-function-name" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" - integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== dependencies: - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-literals@^7.14.5": version "7.14.5" @@ -1581,12 +1798,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-literals@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae" - integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ== +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-member-expression-literals@^7.14.5": version "7.14.5" @@ -1595,12 +1812,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-member-expression-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" - integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-amd@^7.14.5": version "7.14.5" @@ -1611,13 +1828,13 @@ "@babel/helper-plugin-utils" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-amd@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed" - integrity sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA== +"@babel/plugin-transform-modules-amd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" + integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-commonjs@^7.15.4": @@ -1630,14 +1847,14 @@ "@babel/helper-simple-access" "^7.15.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e" - integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ== +"@babel/plugin-transform-modules-commonjs@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" + integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-simple-access" "^7.18.2" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-systemjs@^7.15.4": @@ -1651,15 +1868,15 @@ "@babel/helper-validator-identifier" "^7.14.9" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.18.0": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.5.tgz#87f11c44fbfd3657be000d4897e192d9cb535996" - integrity sha512-SEewrhPpcqMF1V7DhnEbhVJLrC+nnYfe1E0piZMZXBpxi9WvZqWGwpsk7JYP7wPWeqaBh4gyKlBhHJu3uz5g4Q== +"@babel/plugin-transform-modules-systemjs@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz#5f20b471284430f02d9c5059d9b9a16d4b085a1f" + integrity sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A== dependencies: - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.19.0" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-validator-identifier" "^7.18.6" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-umd@^7.14.5": @@ -1670,13 +1887,13 @@ "@babel/helper-module-transforms" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-modules-umd@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f" - integrity sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA== +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-named-capturing-groups-regex@^7.14.9": version "7.14.9" @@ -1685,13 +1902,13 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.14.5" -"@babel/plugin-transform-named-capturing-groups-regex@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz#9c4a5a5966e0434d515f2675c227fd8cc8606931" - integrity sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA== +"@babel/plugin-transform-named-capturing-groups-regex@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.0.tgz#58c52422e4f91a381727faed7d513c89d7f41ada" + integrity sha512-HDSuqOQzkU//kfGdiHBt71/hkDTApw4U/cMVgKgX7PqfB3LOaK+2GtCEsBu1dL9CkswDm0Gwehht1dCr421ULQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-create-regexp-features-plugin" "^7.19.0" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-new-target@^7.14.5": version "7.14.5" @@ -1700,12 +1917,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-new-target@^7.17.12": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.5.tgz#8c228c4a07501dd12c95c5f23d1622131cc23931" - integrity sha512-TuRL5uGW4KXU6OsRj+mLp9BM7pO8e7SGNTEokQRRxHFkXYMFiy2jlKSZPFtI/mKORDzciH+hneskcSOp0gU8hg== +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-object-super@^7.14.5": version "7.14.5" @@ -1715,13 +1932,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-replace-supers" "^7.14.5" -"@babel/plugin-transform-object-super@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" - integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" "@babel/plugin-transform-parameters@^7.12.1": version "7.14.5" @@ -1737,12 +1954,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-parameters@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766" - integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA== +"@babel/plugin-transform-parameters@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" + integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-property-literals@^7.14.5": version "7.14.5" @@ -1751,12 +1968,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-property-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" - integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-constant-elements@^7.14.5": version "7.17.12" @@ -1772,6 +1989,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-react-display-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-react-jsx-development@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8" @@ -1779,6 +2003,13 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.16.7" +"@babel/plugin-transform-react-jsx-development@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" + integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx@^7.16.7", "@babel/plugin-transform-react-jsx@^7.17.12": version "7.17.12" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz#2aa20022709cd6a3f40b45d60603d5f269586dba" @@ -1790,6 +2021,17 @@ "@babel/plugin-syntax-jsx" "^7.17.12" "@babel/types" "^7.17.12" +"@babel/plugin-transform-react-jsx@^7.18.6": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" + integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.19.0" + "@babel/plugin-transform-react-pure-annotations@^7.16.7": version "7.18.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.0.tgz#ef82c8e310913f3522462c9ac967d395092f1954" @@ -1798,6 +2040,14 @@ "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-plugin-utils" "^7.17.12" +"@babel/plugin-transform-react-pure-annotations@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" + integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-regenerator@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f" @@ -1805,12 +2055,12 @@ dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-regenerator@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz#44274d655eb3f1af3f3a574ba819d3f48caf99d5" - integrity sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw== +"@babel/plugin-transform-regenerator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" + integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" regenerator-transform "^0.15.0" "@babel/plugin-transform-reserved-words@^7.14.5": @@ -1820,23 +2070,23 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-reserved-words@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz#7dbd349f3cdffba751e817cf40ca1386732f652f" - integrity sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA== +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-runtime@^7.18.2": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.5.tgz#f4d3188ba6a8815793993c71c2c225d0ee1d7743" - integrity sha512-Q17hHxXr2fplrE+5BSC1j1Fo5cOA8YeP8XW3/1paI8MzF/faZGh0MaH1KC4jLAvqLPamQWHB5/B7KqSLY1kuHA== +"@babel/plugin-transform-runtime@^7.18.6": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz#37d14d1fa810a368fd635d4d1476c0154144a96f" + integrity sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ== dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" + babel-plugin-polyfill-corejs2 "^0.3.2" + babel-plugin-polyfill-corejs3 "^0.5.3" + babel-plugin-polyfill-regenerator "^0.4.0" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.14.5": @@ -1846,12 +2096,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-shorthand-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" - integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== +"@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-spread@^7.15.8": version "7.15.8" @@ -1861,13 +2111,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.15.4" -"@babel/plugin-transform-spread@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5" - integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg== +"@babel/plugin-transform-spread@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" + integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-transform-sticky-regex@^7.14.5": version "7.14.5" @@ -1876,12 +2126,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-sticky-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" - integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-template-literals@^7.14.5": version "7.14.5" @@ -1890,12 +2140,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-template-literals@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28" - integrity sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g== +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typeof-symbol@^7.14.5": version "7.14.5" @@ -1904,12 +2154,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-typeof-symbol@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz#0f12f57ac35e98b35b4ed34829948d42bd0e6889" - integrity sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw== +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.17.12": version "7.18.4" @@ -1920,6 +2170,15 @@ "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-typescript" "^7.17.12" +"@babel/plugin-transform-typescript@^7.18.6": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.0.tgz#50c3a68ec8efd5e040bde2cd764e8e16bc0cbeaf" + integrity sha512-DOOIywxPpkQHXijXv+s9MDAyZcLp12oYRl3CMWZ6u7TjSoCBq/KqHR/nNFR3+i2xqheZxoF0H2XyL7B6xeSRuA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.19.0" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-syntax-typescript" "^7.18.6" + "@babel/plugin-transform-unicode-escapes@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz#9d4bd2a681e3c5d7acf4f57fa9e51175d91d0c6b" @@ -1927,12 +2186,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-unicode-escapes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" - integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== +"@babel/plugin-transform-unicode-escapes@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-unicode-regex@^7.14.5": version "7.14.5" @@ -1942,13 +2201,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.14.5" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-unicode-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" - integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-env@^7.15.6": version "7.15.8" @@ -2029,38 +2288,38 @@ core-js-compat "^3.16.0" semver "^6.3.0" -"@babel/preset-env@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a" - integrity sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q== +"@babel/preset-env@^7.18.6": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.0.tgz#fd18caf499a67d6411b9ded68dc70d01ed1e5da7" + integrity sha512-1YUju1TAFuzjIQqNM9WsF4U6VbD/8t3wEAlw3LFYuuEr+ywqLRcSXxFKz4DCEj+sN94l/XTDiUXYRrsvMpz9WQ== dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-async-generator-functions" "^7.17.12" - "@babel/plugin-proposal-class-properties" "^7.17.12" - "@babel/plugin-proposal-class-static-block" "^7.18.0" - "@babel/plugin-proposal-dynamic-import" "^7.16.7" - "@babel/plugin-proposal-export-namespace-from" "^7.17.12" - "@babel/plugin-proposal-json-strings" "^7.17.12" - "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12" - "@babel/plugin-proposal-numeric-separator" "^7.16.7" - "@babel/plugin-proposal-object-rest-spread" "^7.18.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" - "@babel/plugin-proposal-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-private-methods" "^7.17.12" - "@babel/plugin-proposal-private-property-in-object" "^7.17.12" - "@babel/plugin-proposal-unicode-property-regex" "^7.17.12" + "@babel/compat-data" "^7.19.0" + "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-async-generator-functions" "^7.19.0" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.18.9" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.17.12" + "@babel/plugin-syntax-import-assertions" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -2070,43 +2329,43 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.17.12" - "@babel/plugin-transform-async-to-generator" "^7.17.12" - "@babel/plugin-transform-block-scoped-functions" "^7.16.7" - "@babel/plugin-transform-block-scoping" "^7.17.12" - "@babel/plugin-transform-classes" "^7.17.12" - "@babel/plugin-transform-computed-properties" "^7.17.12" - "@babel/plugin-transform-destructuring" "^7.18.0" - "@babel/plugin-transform-dotall-regex" "^7.16.7" - "@babel/plugin-transform-duplicate-keys" "^7.17.12" - "@babel/plugin-transform-exponentiation-operator" "^7.16.7" - "@babel/plugin-transform-for-of" "^7.18.1" - "@babel/plugin-transform-function-name" "^7.16.7" - "@babel/plugin-transform-literals" "^7.17.12" - "@babel/plugin-transform-member-expression-literals" "^7.16.7" - "@babel/plugin-transform-modules-amd" "^7.18.0" - "@babel/plugin-transform-modules-commonjs" "^7.18.2" - "@babel/plugin-transform-modules-systemjs" "^7.18.0" - "@babel/plugin-transform-modules-umd" "^7.18.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12" - "@babel/plugin-transform-new-target" "^7.17.12" - "@babel/plugin-transform-object-super" "^7.16.7" - "@babel/plugin-transform-parameters" "^7.17.12" - "@babel/plugin-transform-property-literals" "^7.16.7" - "@babel/plugin-transform-regenerator" "^7.18.0" - "@babel/plugin-transform-reserved-words" "^7.17.12" - "@babel/plugin-transform-shorthand-properties" "^7.16.7" - "@babel/plugin-transform-spread" "^7.17.12" - "@babel/plugin-transform-sticky-regex" "^7.16.7" - "@babel/plugin-transform-template-literals" "^7.18.2" - "@babel/plugin-transform-typeof-symbol" "^7.17.12" - "@babel/plugin-transform-unicode-escapes" "^7.16.7" - "@babel/plugin-transform-unicode-regex" "^7.16.7" + "@babel/plugin-transform-arrow-functions" "^7.18.6" + "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.18.9" + "@babel/plugin-transform-classes" "^7.19.0" + "@babel/plugin-transform-computed-properties" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.18.13" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.18.6" + "@babel/plugin-transform-modules-commonjs" "^7.18.6" + "@babel/plugin-transform-modules-systemjs" "^7.19.0" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.0" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.19.0" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.18.2" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" + "@babel/types" "^7.19.0" + babel-plugin-polyfill-corejs2 "^0.3.2" + babel-plugin-polyfill-corejs3 "^0.5.3" + babel-plugin-polyfill-regenerator "^0.4.0" core-js-compat "^3.22.1" semver "^6.3.0" @@ -2132,7 +2391,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.14.5", "@babel/preset-react@^7.17.12": +"@babel/preset-react@^7.14.5": version "7.17.12" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.17.12.tgz#62adbd2d1870c0de3893095757ed5b00b492ab3d" integrity sha512-h5U+rwreXtZaRBEQhW1hOJLMq8XNJBQ/9oymXiCXTuT/0uOwpbT0gUt+sXeOqoXBgNuUKI7TaObVwoEyWkpFgA== @@ -2144,7 +2403,19 @@ "@babel/plugin-transform-react-jsx-development" "^7.16.7" "@babel/plugin-transform-react-pure-annotations" "^7.16.7" -"@babel/preset-typescript@^7.15.0", "@babel/preset-typescript@^7.17.12": +"@babel/preset-react@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" + integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-react-display-name" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-pure-annotations" "^7.18.6" + +"@babel/preset-typescript@^7.15.0": version "7.17.12" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.17.12.tgz#40269e0a0084d56fc5731b6c40febe1c9a4a3e8c" integrity sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg== @@ -2153,10 +2424,19 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.17.12" -"@babel/runtime-corejs3@^7.18.3": - version "7.18.3" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.3.tgz#52f0241a31e0ec61a6187530af6227c2846bd60c" - integrity sha512-l4ddFwrc9rnR+EJsHsh+TJ4A35YqQz/UqcjtlX2ov53hlJYG5CxtQmNZxyajwDVmCxwy++rtvGU5HazCK4W41Q== +"@babel/preset-typescript@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" + integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-typescript" "^7.18.6" + +"@babel/runtime-corejs3@^7.18.6": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.19.0.tgz#0df75cb8e5ecba3ca9e658898694e5326d52397f" + integrity sha512-JyXXoCu1N8GLuKc2ii8y5RGma5FMpFeO2nAQIe0Yzrbq+rQnN+sFj47auLblR5ka6aHNGPDgv8G/iI2Grb0ldQ== dependencies: core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" @@ -2175,13 +2455,27 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.12.13", "@babel/runtime@^7.18.3": +"@babel/runtime@^7.12.13": version "7.18.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" + integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.18.6": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" + integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.12.7", "@babel/template@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" @@ -2209,6 +2503,15 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/template@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.15.0": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.0.tgz#4cca838fd1b2a03283c1f38e141f639d60b3fc98" @@ -2239,7 +2542,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5": +"@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5": version "7.18.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd" integrity sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA== @@ -2255,6 +2558,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.18.8", "@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.0.tgz#eb9c561c7360005c592cc645abafe0c3c4548eed" + integrity sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.19.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.19.0" + "@babel/types" "^7.19.0" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.12.7", "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.15.0", "@babel/types@^7.4.4": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.0.tgz#61af11f2286c4e9c69ca8deb5f4375a73c72dcbd" @@ -2271,7 +2590,7 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" -"@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.17.12", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.18.4": +"@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.17.12", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.18.4": version "7.18.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354" integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw== @@ -2279,6 +2598,15 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" + integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== + dependencies: + "@babel/helper-string-parser" "^7.18.10" + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" @@ -2290,69 +2618,65 @@ integrity sha512-GcIY79elgB+azP74j8vqkiXz8xLFfIzbQJdlwOPisgbKT00tviJQuEghOXSMVxJ00HoYJbGswr4kcllUc4xCcg== "@braintree/sanitize-url@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.0.tgz#fe364f025ba74f6de6c837a84ef44bdb1d61e68f" - integrity sha512-mgmE7XBYY/21erpzhexk4Cj1cyTQ9LzvnTxtzM17BJ7ERMNE6W72mQRo0I1Ud8eFJ+RVVIcBNhLFZ3GX4XFz5w== + version "6.0.2" + resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz#6110f918d273fe2af8ea1c4398a88774bb9fc12f" + integrity sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg== "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@cspotcode/source-map-consumer@0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" - integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== - -"@cspotcode/source-map-support@0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" - integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: - "@cspotcode/source-map-consumer" "0.8.0" + "@jridgewell/trace-mapping" "0.3.9" "@ctrl/tinycolor@^3.4.0": version "3.4.0" resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f" integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ== -"@docsearch/css@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.1.0.tgz#6781cad43fc2e034d012ee44beddf8f93ba21f19" - integrity sha512-bh5IskwkkodbvC0FzSg1AxMykfDl95hebEKwxNoq4e5QaGzOXSBgW8+jnMFZ7JU4sTBiB04vZWoUSzNrPboLZA== +"@docsearch/css@3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.2.1.tgz#c05d7818b0e43b42f9efa2d82a11c36606b37b27" + integrity sha512-gaP6TxxwQC+K8D6TRx5WULUWKrcbzECOPA2KCVMuI+6C7dNiGUk5yXXzVhc5sld79XKYLnO9DRTI4mjXDYkh+g== -"@docsearch/react@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.1.0.tgz#da943a64c01ee82b04e53b691806469272f943f7" - integrity sha512-bjB6ExnZzf++5B7Tfoi6UXgNwoUnNOfZ1NyvnvPhWgCMy5V/biAtLL4o7owmZSYdAKeFSvZ5Lxm0is4su/dBWg== +"@docsearch/react@^3.1.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.2.1.tgz#112ad88db07367fa6fd933d67d58421d8d8289aa" + integrity sha512-EzTQ/y82s14IQC5XVestiK/kFFMe2aagoYFuTAIfIb/e+4FU7kSMKonRtLwsCiLQHmjvNQq+HO+33giJ5YVtaQ== dependencies: - "@algolia/autocomplete-core" "1.6.3" - "@docsearch/css" "3.1.0" + "@algolia/autocomplete-core" "1.7.1" + "@algolia/autocomplete-preset-algolia" "1.7.1" + "@docsearch/css" "3.2.1" algoliasearch "^4.0.0" -"@docusaurus/core@2.0.0-beta.21", "@docusaurus/core@^2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.0.0-beta.21.tgz#50897317b22dbd94b1bf91bb30c2a0fddd15a806" - integrity sha512-qysDMVp1M5UozK3u/qOxsEZsHF7jeBvJDS+5ItMPYmNKvMbNKeYZGA0g6S7F9hRDwjIlEbvo7BaX0UMDcmTAWA== +"@docusaurus/core@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.4.3.tgz#d86624901386fd8164ce4bff9cc7f16fde57f523" + integrity sha512-dWH5P7cgeNSIg9ufReX6gaCl/TmrGKD38Orbwuz05WPhAQtFXHd5B8Qym1TiXfvUNvwoYKkAJOJuGe8ou0Z7PA== dependencies: - "@babel/core" "^7.18.2" - "@babel/generator" "^7.18.2" + "@babel/core" "^7.18.6" + "@babel/generator" "^7.18.7" "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-transform-runtime" "^7.18.2" - "@babel/preset-env" "^7.18.2" - "@babel/preset-react" "^7.17.12" - "@babel/preset-typescript" "^7.17.12" - "@babel/runtime" "^7.18.3" - "@babel/runtime-corejs3" "^7.18.3" - "@babel/traverse" "^7.18.2" - "@docusaurus/cssnano-preset" "2.0.0-beta.21" - "@docusaurus/logger" "2.0.0-beta.21" - "@docusaurus/mdx-loader" "2.0.0-beta.21" + "@babel/plugin-transform-runtime" "^7.18.6" + "@babel/preset-env" "^7.18.6" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@babel/runtime" "^7.18.6" + "@babel/runtime-corejs3" "^7.18.6" + "@babel/traverse" "^7.18.8" + "@docusaurus/cssnano-preset" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/utils" "2.0.0-beta.21" - "@docusaurus/utils-common" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" - "@slorber/static-site-generator-webpack-plugin" "^4.0.4" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" + "@slorber/static-site-generator-webpack-plugin" "^4.0.7" "@svgr/webpack" "^6.2.1" autoprefixer "^10.4.7" babel-loader "^8.2.5" @@ -2365,14 +2689,14 @@ combine-promises "^1.1.0" commander "^5.1.0" copy-webpack-plugin "^11.0.0" - core-js "^3.22.7" + core-js "^3.23.3" css-loader "^6.7.1" css-minimizer-webpack-plugin "^4.0.0" - cssnano "^5.1.9" + cssnano "^5.1.12" del "^6.1.1" detect-port "^1.3.0" escape-html "^1.0.3" - eta "^1.12.3" + eta "^2.0.0" file-loader "^6.2.0" fs-extra "^10.1.0" html-minifier-terser "^6.1.0" @@ -2381,7 +2705,7 @@ import-fresh "^3.3.0" leven "^3.1.0" lodash "^4.17.21" - mini-css-extract-plugin "^2.6.0" + mini-css-extract-plugin "^2.6.1" postcss "^8.4.14" postcss-loader "^7.0.0" prompts "^2.4.2" @@ -2392,49 +2716,48 @@ react-router "^5.3.3" react-router-config "^5.1.1" react-router-dom "^5.3.3" - remark-admonitions "^1.2.1" rtl-detect "^1.0.4" semver "^7.3.7" serve-handler "^6.1.3" shelljs "^0.8.5" - terser-webpack-plugin "^5.3.1" + terser-webpack-plugin "^5.3.3" tslib "^2.4.0" update-notifier "^5.1.0" url-loader "^4.1.1" wait-on "^6.0.1" - webpack "^5.72.1" + webpack "^5.73.0" webpack-bundle-analyzer "^4.5.0" - webpack-dev-server "^4.9.0" + webpack-dev-server "^4.9.3" webpack-merge "^5.8.0" webpackbar "^5.0.2" -"@docusaurus/cssnano-preset@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.21.tgz#38113877a5857c3f9d493522085d20909dcec474" - integrity sha512-fhTZrg1vc6zYYZIIMXpe1TnEVGEjqscBo0s1uomSwKjjtMgu7wkzc1KKJYY7BndsSA+fVVkZ+OmL/kAsmK7xxw== +"@docusaurus/cssnano-preset@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.3.tgz#1d7e833c41ce240fcc2812a2ac27f7b862f32de0" + integrity sha512-ZvGSRCi7z9wLnZrXNPG6DmVPHdKGd8dIn9pYbEOFiYihfv4uDR3UtxogmKf+rT8ZlKFf5Lqne8E8nt08zNM8CA== dependencies: - cssnano-preset-advanced "^5.3.5" + cssnano-preset-advanced "^5.3.8" postcss "^8.4.14" postcss-sort-media-queries "^4.2.1" tslib "^2.4.0" -"@docusaurus/logger@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.0.0-beta.21.tgz#f6ab4133917965349ae03fd9111a940b24d4fd12" - integrity sha512-HTFp8FsSMrAj7Uxl5p72U+P7rjYU/LRRBazEoJbs9RaqoKEdtZuhv8MYPOCh46K9TekaoquRYqag2o23Qt4ggA== +"@docusaurus/logger@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.4.3.tgz#518bbc965fb4ebe8f1d0b14e5f4161607552d34c" + integrity sha512-Zxws7r3yLufk9xM1zq9ged0YHs65mlRmtsobnFkdZTxWXdTYlWWLWdKyNKAsVC+D7zg+pv2fGbyabdOnyZOM3w== dependencies: chalk "^4.1.2" tslib "^2.4.0" -"@docusaurus/mdx-loader@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.21.tgz#52af341e21f22be882d2155a7349bea10f5d77a3" - integrity sha512-AI+4obJnpOaBOAYV6df2ux5Y1YJCBS+MhXFf0yhED12sVLJi2vffZgdamYd/d/FwvWDw6QLs/VD2jebd7P50yQ== +"@docusaurus/mdx-loader@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.4.3.tgz#e8ff37f30a060eaa97b8121c135f74cb531a4a3e" + integrity sha512-b1+fDnWtl3GiqkL0BRjYtc94FZrcDDBV1j8446+4tptB9BAOlePwG2p/pK6vGvfL53lkOsszXMghr2g67M0vCw== dependencies: - "@babel/parser" "^7.18.3" - "@babel/traverse" "^7.18.2" - "@docusaurus/logger" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" + "@babel/parser" "^7.18.8" + "@babel/traverse" "^7.18.8" + "@docusaurus/logger" "2.4.3" + "@docusaurus/utils" "2.4.3" "@mdx-js/mdx" "^1.6.22" escape-html "^1.0.3" file-loader "^6.2.0" @@ -2444,151 +2767,173 @@ remark-emoji "^2.2.0" stringify-object "^3.3.0" tslib "^2.4.0" + unified "^9.2.2" unist-util-visit "^2.0.3" url-loader "^4.1.1" - webpack "^5.72.1" + webpack "^5.73.0" -"@docusaurus/module-type-aliases@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.0.0-beta.21.tgz#345f1c1a99407775d1d3ffc1a90c2df93d50a9b8" - integrity sha512-gRkWICgQZiqSJgrwRKWjXm5gAB+9IcfYdUbCG0PRPP/G8sNs9zBIOY4uT4Z5ox2CWFEm44U3RTTxj7BiLVMBXw== +"@docusaurus/module-type-aliases@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.3.tgz#d08ef67e4151e02f352a2836bcf9ecde3b9c56ac" + integrity sha512-cwkBkt1UCiduuvEAo7XZY01dJfRn7UR/75mBgOdb1hKknhrabJZ8YH+7savd/y9kLExPyrhe0QwdS9GuzsRRIA== dependencies: - "@docusaurus/types" "2.0.0-beta.21" + "@docusaurus/react-loadable" "5.5.2" + "@docusaurus/types" "2.4.3" + "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" "@types/react-router-dom" "*" react-helmet-async "*" + react-loadable "npm:@docusaurus/react-loadable@5.5.2" -"@docusaurus/plugin-client-redirects@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.0.0-beta.21.tgz#bf0c3e94d89c7bd15933dacdfefc17fec22c5d76" - integrity sha512-4xzrti0au7SaQT/cxr+FM9b+R5gfOSFODwQJ2QeTXbkdiz1+9DV3bp8nB/2CmzZ9ApY5lsueXNpa4n7+UAngrA== +"@docusaurus/plugin-client-redirects@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.4.3.tgz#0da7e6facadbca3bd7cb8d0453f21bea7f4f1721" + integrity sha512-iCwc/zH8X6eNtLYdyUJFY6+GbsbRgMgvAC/TmSmCYTmwnoN5Y1Bc5OwUkdtoch0XKizotJMRAmGIAhP8sAetdQ== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/logger" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" - "@docusaurus/utils-common" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" - eta "^1.12.3" + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" + eta "^2.0.0" fs-extra "^10.1.0" lodash "^4.17.21" tslib "^2.4.0" -"@docusaurus/plugin-content-blog@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.21.tgz#86211deeea901ddcd77ca387778e121e93ee8d01" - integrity sha512-IP21yJViP3oBmgsWBU5LhrG1MZXV4mYCQSoCAboimESmy1Z11RCNP2tXaqizE3iTmXOwZZL+SNBk06ajKCEzWg== +"@docusaurus/plugin-content-blog@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.3.tgz#6473b974acab98e967414d8bbb0d37e0cedcea14" + integrity sha512-PVhypqaA0t98zVDpOeTqWUTvRqCEjJubtfFUQ7zJNYdbYTbS/E/ytq6zbLVsN/dImvemtO/5JQgjLxsh8XLo8Q== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/logger" "2.0.0-beta.21" - "@docusaurus/mdx-loader" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" - "@docusaurus/utils-common" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" - cheerio "^1.0.0-rc.11" + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" + cheerio "^1.0.0-rc.12" feed "^4.2.2" fs-extra "^10.1.0" lodash "^4.17.21" reading-time "^1.5.0" - remark-admonitions "^1.2.1" tslib "^2.4.0" unist-util-visit "^2.0.3" utility-types "^3.10.0" - webpack "^5.72.1" + webpack "^5.73.0" -"@docusaurus/plugin-content-docs@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.21.tgz#b3171fa9aed99e367b6eb7111187bd0e3dcf2949" - integrity sha512-aa4vrzJy4xRy81wNskyhE3wzRf3AgcESZ1nfKh8xgHUkT7fDTZ1UWlg50Jb3LBCQFFyQG2XQB9N6llskI/KUnw== +"@docusaurus/plugin-content-docs@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.3.tgz#aa224c0512351e81807adf778ca59fd9cd136973" + integrity sha512-N7Po2LSH6UejQhzTCsvuX5NOzlC+HiXOVvofnEPj0WhMu1etpLEXE6a4aTxrtg95lQ5kf0xUIdjX9sh3d3G76A== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/logger" "2.0.0-beta.21" - "@docusaurus/mdx-loader" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/module-type-aliases" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" + "@types/react-router-config" "^5.0.6" combine-promises "^1.1.0" fs-extra "^10.1.0" import-fresh "^3.3.0" js-yaml "^4.1.0" lodash "^4.17.21" - remark-admonitions "^1.2.1" tslib "^2.4.0" utility-types "^3.10.0" - webpack "^5.72.1" + webpack "^5.73.0" -"@docusaurus/plugin-content-pages@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.21.tgz#df6b4c5c4cde8a0ea491a30002e84941ca7bf0cf" - integrity sha512-DmXOXjqNI+7X5hISzCvt54QIK6XBugu2MOxjxzuqI7q92Lk/EVdraEj5mthlH8IaEH/VlpWYJ1O9TzLqX5vH2g== +"@docusaurus/plugin-content-pages@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.3.tgz#7f285e718b53da8c8d0101e70840c75b9c0a1ac0" + integrity sha512-txtDVz7y3zGk67q0HjG0gRttVPodkHqE0bpJ+7dOaTH40CQFLSh7+aBeGnPOTl+oCPG+hxkim4SndqPqXjQ8Bg== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/mdx-loader" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" + "@docusaurus/core" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" fs-extra "^10.1.0" - remark-admonitions "^1.2.1" tslib "^2.4.0" - webpack "^5.72.1" + webpack "^5.73.0" -"@docusaurus/plugin-debug@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.21.tgz#dfa212fd90fe2f54439aacdc8c143e8ce96b0d27" - integrity sha512-P54J4q4ecsyWW0Jy4zbimSIHna999AfbxpXGmF1IjyHrjoA3PtuakV1Ai51XrGEAaIq9q6qMQkEhbUd3CffGAw== +"@docusaurus/plugin-debug@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.4.3.tgz#2f90eb0c9286a9f225444e3a88315676fe02c245" + integrity sha512-LkUbuq3zCmINlFb+gAd4ZvYr+bPAzMC0hwND4F7V9bZ852dCX8YoWyovVUBKq4er1XsOwSQaHmNGtObtn8Av8Q== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" + "@docusaurus/core" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" fs-extra "^10.1.0" react-json-view "^1.21.3" tslib "^2.4.0" -"@docusaurus/plugin-google-analytics@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.21.tgz#5475c58fb23603badf41d84298569f6c46b4e6b2" - integrity sha512-+5MS0PeGaJRgPuNZlbd/WMdQSpOACaxEz7A81HAxm6kE+tIASTW3l8jgj1eWFy/PGPzaLnQrEjxI1McAfnYmQw== +"@docusaurus/plugin-google-analytics@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.3.tgz#0d19993136ade6f7a7741251b4f617400d92ab45" + integrity sha512-KzBV3k8lDkWOhg/oYGxlK5o9bOwX7KpPc/FTWoB+SfKhlHfhq7qcQdMi1elAaVEIop8tgK6gD1E58Q+XC6otSQ== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" + "@docusaurus/core" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" tslib "^2.4.0" -"@docusaurus/plugin-google-gtag@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.21.tgz#a4a101089994a7103c1cc7cddb15170427b185d6" - integrity sha512-4zxKZOnf0rfh6myXLG7a6YZfQcxYDMBsWqANEjCX77H5gPdK+GHZuDrxK6sjFvRBv4liYCrNjo7HJ4DpPoT0zA== +"@docusaurus/plugin-google-gtag@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.3.tgz#e1a80b0696771b488562e5b60eff21c9932d9e1c" + integrity sha512-5FMg0rT7sDy4i9AGsvJC71MQrqQZwgLNdDetLEGDHLfSHLvJhQbTCUGbGXknUgWXQJckcV/AILYeJy+HhxeIFA== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" + "@docusaurus/core" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" tslib "^2.4.0" -"@docusaurus/plugin-sitemap@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.21.tgz#8bfa695eada2ec95c9376a884641237ffca5dd3d" - integrity sha512-/ynWbcXZXcYZ6sT2X6vAJbnfqcPxwdGEybd0rcRZi4gBHq6adMofYI25AqELmnbBDxt0If+vlAeUHFRG5ueP7Q== +"@docusaurus/plugin-google-tag-manager@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.3.tgz#e41fbf79b0ffc2de1cc4013eb77798cff0ad98e3" + integrity sha512-1jTzp71yDGuQiX9Bi0pVp3alArV0LSnHXempvQTxwCGAEzUWWaBg4d8pocAlTpbP9aULQQqhgzrs8hgTRPOM0A== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/logger" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" - "@docusaurus/utils-common" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" + "@docusaurus/core" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" + tslib "^2.4.0" + +"@docusaurus/plugin-sitemap@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.3.tgz#1b3930900a8f89670ce7e8f83fb4730cd3298c32" + integrity sha512-LRQYrK1oH1rNfr4YvWBmRzTL0LN9UAPxBbghgeFRBm5yloF6P+zv1tm2pe2hQTX/QP5bSKdnajCvfnScgKXMZQ== + dependencies: + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" fs-extra "^10.1.0" sitemap "^7.1.1" tslib "^2.4.0" -"@docusaurus/preset-classic@^2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.21.tgz#1362d8650ebed22633db411caaba80075f7c86ce" - integrity sha512-KvBnIUu7y69pNTJ9UhX6SdNlK6prR//J3L4rhN897tb8xx04xHHILlPXko2Il+C3Xzgh3OCgyvkoz9K6YlFTDw== +"@docusaurus/preset-classic@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.4.3.tgz#074c57ebf29fa43d23bd1c8ce691226f542bc262" + integrity sha512-tRyMliepY11Ym6hB1rAFSNGwQDpmszvWYJvlK1E+md4SW8i6ylNHtpZjaYFff9Mdk3i/Pg8ItQq9P0daOJAvQw== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/plugin-content-blog" "2.0.0-beta.21" - "@docusaurus/plugin-content-docs" "2.0.0-beta.21" - "@docusaurus/plugin-content-pages" "2.0.0-beta.21" - "@docusaurus/plugin-debug" "2.0.0-beta.21" - "@docusaurus/plugin-google-analytics" "2.0.0-beta.21" - "@docusaurus/plugin-google-gtag" "2.0.0-beta.21" - "@docusaurus/plugin-sitemap" "2.0.0-beta.21" - "@docusaurus/theme-classic" "2.0.0-beta.21" - "@docusaurus/theme-common" "2.0.0-beta.21" - "@docusaurus/theme-search-algolia" "2.0.0-beta.21" + "@docusaurus/core" "2.4.3" + "@docusaurus/plugin-content-blog" "2.4.3" + "@docusaurus/plugin-content-docs" "2.4.3" + "@docusaurus/plugin-content-pages" "2.4.3" + "@docusaurus/plugin-debug" "2.4.3" + "@docusaurus/plugin-google-analytics" "2.4.3" + "@docusaurus/plugin-google-gtag" "2.4.3" + "@docusaurus/plugin-google-tag-manager" "2.4.3" + "@docusaurus/plugin-sitemap" "2.4.3" + "@docusaurus/theme-classic" "2.4.3" + "@docusaurus/theme-common" "2.4.3" + "@docusaurus/theme-search-algolia" "2.4.3" + "@docusaurus/types" "2.4.3" "@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": version "5.5.2" @@ -2598,116 +2943,129 @@ "@types/react" "*" prop-types "^15.6.2" -"@docusaurus/theme-classic@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.21.tgz#6df5b9ea2d389dafb6f59badeabb3eda060b5017" - integrity sha512-Ge0WNdTefD0VDQfaIMRRWa8tWMG9+8/OlBRd5MK88/TZfqdBq7b/gnCSaalQlvZwwkj6notkKhHx72+MKwWUJA== +"@docusaurus/theme-classic@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.4.3.tgz#29360f2eb03a0e1686eb19668633ef313970ee8f" + integrity sha512-QKRAJPSGPfDY2yCiPMIVyr+MqwZCIV2lxNzqbyUW0YkrlmdzzP3WuQJPMGLCjWgQp/5c9kpWMvMxjhpZx1R32Q== dependencies: - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/plugin-content-blog" "2.0.0-beta.21" - "@docusaurus/plugin-content-docs" "2.0.0-beta.21" - "@docusaurus/plugin-content-pages" "2.0.0-beta.21" - "@docusaurus/theme-common" "2.0.0-beta.21" - "@docusaurus/theme-translations" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" - "@docusaurus/utils-common" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" + "@docusaurus/core" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/module-type-aliases" "2.4.3" + "@docusaurus/plugin-content-blog" "2.4.3" + "@docusaurus/plugin-content-docs" "2.4.3" + "@docusaurus/plugin-content-pages" "2.4.3" + "@docusaurus/theme-common" "2.4.3" + "@docusaurus/theme-translations" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" "@mdx-js/react" "^1.6.22" - clsx "^1.1.1" + clsx "^1.2.1" copy-text-to-clipboard "^3.0.1" - infima "0.2.0-alpha.39" + infima "0.2.0-alpha.43" lodash "^4.17.21" nprogress "^0.2.0" postcss "^8.4.14" - prism-react-renderer "^1.3.3" + prism-react-renderer "^1.3.5" prismjs "^1.28.0" react-router-dom "^5.3.3" rtlcss "^3.5.0" tslib "^2.4.0" - -"@docusaurus/theme-common@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.0.0-beta.21.tgz#508478251982d01655ef505ccb2420db38623db8" - integrity sha512-fTKoTLRfjuFG6c3iwnVjIIOensxWMgdBKLfyE5iih3Lq7tQgkE7NyTGG9BKLrnTJ7cAD2UXdXM9xbB7tBf1qzg== - dependencies: - "@docusaurus/module-type-aliases" "2.0.0-beta.21" - "@docusaurus/plugin-content-blog" "2.0.0-beta.21" - "@docusaurus/plugin-content-docs" "2.0.0-beta.21" - "@docusaurus/plugin-content-pages" "2.0.0-beta.21" - clsx "^1.1.1" - parse-numeric-range "^1.3.0" - prism-react-renderer "^1.3.3" - tslib "^2.4.0" utility-types "^3.10.0" -"@docusaurus/theme-search-algolia@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.21.tgz#2891f11372e2542e4e1426c3100b72c2d30d4d68" - integrity sha512-T1jKT8MVSSfnztSqeebUOpWHPoHKtwDXtKYE0xC99JWoZ+mMfv8AFhVSoSddn54jLJjV36mxg841eHQIySMCpQ== +"@docusaurus/theme-common@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.4.3.tgz#bb31d70b6b67d0bdef9baa343192dcec49946a2e" + integrity sha512-7KaDJBXKBVGXw5WOVt84FtN8czGWhM0lbyWEZXGp8AFfL6sZQfRTluFp4QriR97qwzSyOfQb+nzcDZZU4tezUw== dependencies: - "@docsearch/react" "^3.1.0" - "@docusaurus/core" "2.0.0-beta.21" - "@docusaurus/logger" "2.0.0-beta.21" - "@docusaurus/plugin-content-docs" "2.0.0-beta.21" - "@docusaurus/theme-common" "2.0.0-beta.21" - "@docusaurus/theme-translations" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" - "@docusaurus/utils-validation" "2.0.0-beta.21" + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/module-type-aliases" "2.4.3" + "@docusaurus/plugin-content-blog" "2.4.3" + "@docusaurus/plugin-content-docs" "2.4.3" + "@docusaurus/plugin-content-pages" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router-config" "*" + clsx "^1.2.1" + parse-numeric-range "^1.3.0" + prism-react-renderer "^1.3.5" + tslib "^2.4.0" + use-sync-external-store "^1.2.0" + utility-types "^3.10.0" + +"@docusaurus/theme-search-algolia@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.3.tgz#32d4cbefc3deba4112068fbdb0bde11ac51ece53" + integrity sha512-jziq4f6YVUB5hZOB85ELATwnxBz/RmSLD3ksGQOLDPKVzat4pmI8tddNWtriPpxR04BNT+ZfpPUMFkNFetSW1Q== + dependencies: + "@docsearch/react" "^3.1.1" + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/plugin-content-docs" "2.4.3" + "@docusaurus/theme-common" "2.4.3" + "@docusaurus/theme-translations" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" algoliasearch "^4.13.1" - algoliasearch-helper "^3.8.2" - clsx "^1.1.1" - eta "^1.12.3" + algoliasearch-helper "^3.10.0" + clsx "^1.2.1" + eta "^2.0.0" fs-extra "^10.1.0" lodash "^4.17.21" tslib "^2.4.0" utility-types "^3.10.0" -"@docusaurus/theme-translations@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.0.0-beta.21.tgz#5da60ffc58de256b96316c5e0fe2733c1e83f22c" - integrity sha512-dLVT9OIIBs6MpzMb1bAy+C0DPJK3e3DNctG+ES0EP45gzEqQxzs4IsghpT+QDaOsuhNnAlosgJpFWX3rqxF9xA== +"@docusaurus/theme-translations@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.4.3.tgz#91ac73fc49b8c652b7a54e88b679af57d6ac6102" + integrity sha512-H4D+lbZbjbKNS/Zw1Lel64PioUAIT3cLYYJLUf3KkuO/oc9e0QCVhIYVtUI2SfBCF2NNdlyhBDQEEMygsCedIg== dependencies: fs-extra "^10.1.0" tslib "^2.4.0" -"@docusaurus/types@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.0.0-beta.21.tgz#36659c6c012663040dcd4cbc97b5d7a555dae229" - integrity sha512-/GH6Npmq81eQfMC/ikS00QSv9jNyO1RXEpNSx5GLA3sFX8Iib26g2YI2zqNplM8nyxzZ2jVBuvUoeODTIbTchQ== +"@docusaurus/types@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.4.3.tgz#4aead281ca09f721b3c0a9b926818450cfa3db31" + integrity sha512-W6zNLGQqfrp/EoPD0bhb9n7OobP+RHpmvVzpA+Z/IuU3Q63njJM24hmT0GYboovWcDtFmnIJC9wcyx4RVPQscw== dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" commander "^5.1.0" - history "^4.9.0" joi "^17.6.0" react-helmet-async "^1.3.0" utility-types "^3.10.0" - webpack "^5.72.1" + webpack "^5.73.0" webpack-merge "^5.8.0" -"@docusaurus/utils-common@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.0.0-beta.21.tgz#81e86ed04ad62b75e9ba6a5e7689dc23d5f36a0a" - integrity sha512-5w+6KQuJb6pUR2M8xyVuTMvO5NFQm/p8TOTDFTx60wt3p0P1rRX00v6FYsD4PK6pgmuoKjt2+Ls8dtSXc4qFpQ== +"@docusaurus/utils-common@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.4.3.tgz#30656c39ef1ce7e002af7ba39ea08330f58efcfb" + integrity sha512-/jascp4GbLQCPVmcGkPzEQjNaAk3ADVfMtudk49Ggb+131B1WDD6HqlSmDf8MxGdy7Dja2gc+StHf01kiWoTDQ== dependencies: tslib "^2.4.0" -"@docusaurus/utils-validation@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.21.tgz#10169661be5f8a233f4c12202ee5802ccb77400f" - integrity sha512-6NG1FHTRjv1MFzqW//292z7uCs77vntpWEbZBHk3n67aB1HoMn5SOwjLPtRDjbCgn6HCHFmdiJr6euCbjhYolg== +"@docusaurus/utils-validation@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.4.3.tgz#8122c394feef3e96c73f6433987837ec206a63fb" + integrity sha512-G2+Vt3WR5E/9drAobP+hhZQMaswRwDlp6qOMi7o7ZypB+VO7N//DZWhZEwhcRGepMDJGQEwtPv7UxtYwPL9PBw== dependencies: - "@docusaurus/logger" "2.0.0-beta.21" - "@docusaurus/utils" "2.0.0-beta.21" + "@docusaurus/logger" "2.4.3" + "@docusaurus/utils" "2.4.3" joi "^17.6.0" js-yaml "^4.1.0" tslib "^2.4.0" -"@docusaurus/utils@2.0.0-beta.21": - version "2.0.0-beta.21" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.0.0-beta.21.tgz#8fc4499c4cfedd29805025d930f8008cad255044" - integrity sha512-M/BrVCDmmUPZLxtiStBgzpQ4I5hqkggcpnQmEN+LbvbohjbtVnnnZQ0vptIziv1w8jry/woY+ePsyOO7O/yeLQ== +"@docusaurus/utils@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.4.3.tgz#52b000d989380a2125831b84e3a7327bef471e89" + integrity sha512-fKcXsjrD86Smxv8Pt0TBFqYieZZCPh4cbf9oszUq/AMhZn3ujwpKaVYZACPX8mmjtYx0JOgNx52CREBfiGQB4A== dependencies: - "@docusaurus/logger" "2.0.0-beta.21" + "@docusaurus/logger" "2.4.3" "@svgr/webpack" "^6.2.1" + escape-string-regexp "^4.0.0" file-loader "^6.2.0" fs-extra "^10.1.0" github-slugger "^1.4.0" @@ -2720,9 +3078,26 @@ shelljs "^0.8.5" tslib "^2.4.0" url-loader "^4.1.1" - webpack "^5.72.1" + webpack "^5.73.0" -"@emotion/babel-plugin@^11.3.0", "@emotion/babel-plugin@^11.7.1": +"@emotion/babel-plugin@^11.10.6": + version "11.10.6" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz#a68ee4b019d661d6f37dec4b8903255766925ead" + integrity sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.0" + "@emotion/memoize" "^0.8.0" + "@emotion/serialize" "^1.1.1" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.1.3" + +"@emotion/babel-plugin@^11.7.1": version "11.7.2" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz#fec75f38a6ab5b304b0601c74e2a5e77c95e5fa0" integrity sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ== @@ -2740,6 +3115,17 @@ source-map "^0.5.7" stylis "4.0.13" +"@emotion/cache@^11.10.5": + version "11.10.5" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12" + integrity sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA== + dependencies: + "@emotion/memoize" "^0.8.0" + "@emotion/sheet" "^1.2.1" + "@emotion/utils" "^1.2.0" + "@emotion/weak-memoize" "^0.3.0" + stylis "4.1.3" + "@emotion/cache@^11.7.1": version "11.7.1" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.7.1.tgz#08d080e396a42e0037848214e8aa7bf879065539" @@ -2751,7 +3137,18 @@ "@emotion/weak-memoize" "^0.2.5" stylis "4.0.13" -"@emotion/css@^11.5.0", "@emotion/css@^11.7.1": +"@emotion/css@^11.10.6": + version "11.10.6" + resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.10.6.tgz#5d226fdd8ef2a46d28e4eb09f66dc01a3bda5a04" + integrity sha512-88Sr+3heKAKpj9PCqq5A1hAmAkoSIvwEq1O2TwDij7fUtsJpdkV4jMTISSTouFeRvsGvXIpuSuDQ4C1YdfNGXw== + dependencies: + "@emotion/babel-plugin" "^11.10.6" + "@emotion/cache" "^11.10.5" + "@emotion/serialize" "^1.1.1" + "@emotion/sheet" "^1.2.1" + "@emotion/utils" "^1.2.0" + +"@emotion/css@^11.7.1": version "11.7.1" resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.7.1.tgz#516b717340d36b0bbd2304ba7e1a090e866f8acc" integrity sha512-RUUgPlMZunlc7SE5A6Hg+VWRzb2cU6O9xlV78KCFgcnl25s7Qz/20oQg71iKudpLqk7xj0vhbJlwcJJMT0BOZg== @@ -2767,29 +3164,40 @@ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== -"@emotion/is-prop-valid@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.1.1.tgz#cbd843d409dfaad90f9404e7c0404c55eae8c134" - integrity sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw== +"@emotion/hash@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7" + integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ== + +"@emotion/is-prop-valid@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83" + integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg== dependencies: - "@emotion/memoize" "^0.7.4" + "@emotion/memoize" "^0.8.0" "@emotion/memoize@^0.7.4", "@emotion/memoize@^0.7.5": version "0.7.5" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50" integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ== -"@emotion/react@^11.6.0": - version "11.7.1" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.7.1.tgz#3f800ce9b20317c13e77b8489ac4a0b922b2fe07" - integrity sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw== +"@emotion/memoize@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" + integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== + +"@emotion/react@^11.8.2": + version "11.10.6" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.6.tgz#dbe5e650ab0f3b1d2e592e6ab1e006e75fd9ac11" + integrity sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw== dependencies: - "@babel/runtime" "^7.13.10" - "@emotion/cache" "^11.7.1" - "@emotion/serialize" "^1.0.2" - "@emotion/sheet" "^1.1.0" - "@emotion/utils" "^1.0.0" - "@emotion/weak-memoize" "^0.2.5" + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.10.6" + "@emotion/cache" "^11.10.5" + "@emotion/serialize" "^1.1.1" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" + "@emotion/utils" "^1.2.0" + "@emotion/weak-memoize" "^0.3.0" hoist-non-react-statics "^3.3.1" "@emotion/serialize@^1.0.0", "@emotion/serialize@^1.0.2": @@ -2803,37 +3211,74 @@ "@emotion/utils" "^1.0.0" csstype "^3.0.2" +"@emotion/serialize@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0" + integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA== + dependencies: + "@emotion/hash" "^0.9.0" + "@emotion/memoize" "^0.8.0" + "@emotion/unitless" "^0.8.0" + "@emotion/utils" "^1.2.0" + csstype "^3.0.2" + "@emotion/sheet@^1.0.3", "@emotion/sheet@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.0.tgz#56d99c41f0a1cda2726a05aa6a20afd4c63e58d2" integrity sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g== -"@emotion/styled@^11.6.0": - version "11.6.0" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.6.0.tgz#9230d1a7bcb2ebf83c6a579f4c80e0664132d81d" - integrity sha512-mxVtVyIOTmCAkFbwIp+nCjTXJNgcz4VWkOYQro87jE2QBTydnkiYusMrRGFtzuruiGK4dDaNORk4gH049iiQuw== +"@emotion/sheet@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c" + integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA== + +"@emotion/styled@^11.10.6": + version "11.10.6" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.6.tgz#d886afdc51ef4d66c787ebde848f3cc8b117ebba" + integrity sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og== dependencies: - "@babel/runtime" "^7.13.10" - "@emotion/babel-plugin" "^11.3.0" - "@emotion/is-prop-valid" "^1.1.1" - "@emotion/serialize" "^1.0.2" - "@emotion/utils" "^1.0.0" + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.10.6" + "@emotion/is-prop-valid" "^1.2.0" + "@emotion/serialize" "^1.1.1" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" + "@emotion/utils" "^1.2.0" "@emotion/unitless@^0.7.5": version "0.7.5" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== +"@emotion/unitless@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db" + integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw== + +"@emotion/use-insertion-effect-with-fallbacks@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df" + integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A== + "@emotion/utils@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af" integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA== +"@emotion/utils@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561" + integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw== + "@emotion/weak-memoize@^0.2.5": version "0.2.5" resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== +"@emotion/weak-memoize@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb" + integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== + "@hapi/hoek@^9.0.0": version "9.2.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" @@ -2868,6 +3313,15 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri@^3.0.3": version "3.0.7" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" @@ -2878,6 +3332,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + "@jridgewell/source-map@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" @@ -2891,7 +3350,23 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== -"@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.14": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" + integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.9": version "0.3.13" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== @@ -3001,9 +3476,9 @@ "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" - integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== "@sideway/pinpoint@^2.0.0": version "2.0.0" @@ -3015,7 +3490,7 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@slorber/static-site-generator-webpack-plugin@^4.0.4": +"@slorber/static-site-generator-webpack-plugin@^4.0.7": version "4.0.7" resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz#fc1678bddefab014e2145cbe25b3ce4e1cfc36f3" integrity sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA== @@ -3268,6 +3743,19 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/find-cache-dir@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz#7b959a4b9643a1e6a1a5fe49032693cc36773501" + integrity sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw== + +"@types/fs-extra@^11.0.2": + version "11.0.2" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.2.tgz#23dc1ed7b2eba8ccd75568ac34e7a4e48aa2d087" + integrity sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + "@types/fs-extra@^9.0.13": version "9.0.13" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" @@ -3304,6 +3792,20 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== +"@types/jsonfile@*": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.1.tgz#ac84e9aefa74a2425a0fb3012bdea44f58970f1b" + integrity sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png== + dependencies: + "@types/node" "*" + +"@types/lodash-es@^4.17.6": + version "4.17.6" + resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.6.tgz#c2ed4c8320ffa6f11b43eb89e9eaeec65966a0a0" + integrity sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg== + dependencies: + "@types/lodash" "*" + "@types/lodash.debounce@4.0.7": version "4.0.7" resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz#0285879defb7cdb156ae633cecd62d5680eded9f" @@ -3391,12 +3893,12 @@ "@types/react" "*" "@types/reactcss" "*" -"@types/react-dom@^17.0.11": - version "17.0.11" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" - integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== +"@types/react-dom@^17.0.13": + version "17.0.19" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.19.tgz#36feef3aa35d045cacd5ed60fe0eef5272f19492" + integrity sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ== dependencies: - "@types/react" "*" + "@types/react" "^17" "@types/react-modal@3.13.1": version "3.13.1" @@ -3405,7 +3907,7 @@ dependencies: "@types/react" "*" -"@types/react-router-config@*": +"@types/react-router-config@*", "@types/react-router-config@^5.0.6": version "5.0.6" resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.6.tgz#87c5c57e72d241db900d9734512c50ccec062451" integrity sha512-db1mx37a1EJDf1XeX8jJN7R3PZABmJQXR8r28yUjVMFSjkmnQo6X6pOEEmNl+Tp2gYQOGPdYbFIipBtdElZ3Yg== @@ -3440,10 +3942,19 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@17.0.34": - version "17.0.34" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102" - integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg== +"@types/react@17.0.39": + version "17.0.39" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce" + integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/react@^17": + version "17.0.53" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" + integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -3473,6 +3984,11 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== +"@types/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" @@ -3507,6 +4023,11 @@ dependencies: "@types/node" "*" +"@types/yarnpkg__lockfile@^1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@types/yarnpkg__lockfile/-/yarnpkg__lockfile-1.1.5.tgz#9639020e1fb65120a2f4387db8f1e8b63efdf229" + integrity sha512-8NYnGOctzsI4W0ApsP/BIHD/LnxpJ6XaGf2AZmz4EyDYJMxtprN4279dLNI1CPZcwC9H18qYcaFv4bXi0wmokg== + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -3638,6 +4159,16 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + accepts@~1.3.4, accepts@~1.3.5: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -3679,6 +4210,11 @@ acorn@^8.5.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.7.1: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + address@^1.0.1, address@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -3731,10 +4267,10 @@ ajv@^8.0.0, ajv@^8.8.0: require-from-string "^2.0.2" uri-js "^4.2.2" -algoliasearch-helper@^3.8.2: - version "3.9.0" - resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.9.0.tgz#1e99d351ecdcff48449644157a8d250c7c592828" - integrity sha512-siWWl8QYJ3sh1yzJf9h/cHHpZC8wuPoPdVx5OtQ8X62ruUembTwvsLYoicrL7pF7fsYxdyvJfV9Yb2/nrVGrfg== +algoliasearch-helper@^3.10.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.11.0.tgz#c4355056d97748a92f6ff0d4fce153b96b561ddb" + integrity sha512-TLl/MSjtQ98mgkd8hngWkzSjE+dAWldZ1NpJtv2mT+ZoFJ2P2zDE85oF9WafJOXWN9FbVRmyxpO5H+qXcNaFng== dependencies: "@algolia/events" "^4.0.1" @@ -3797,22 +4333,12 @@ ansi-html-community@^0.0.8: resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== -ansi-html@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.8.tgz#e969db193b12bcdfa6727b29ffd8882dc13cc501" - integrity sha512-QROYz1I1Kj+8bTYgx0IlMBpRSCIU+7GjbE0oH+KF7QKc+qSF8YAlIutN59Db17tXN70Ono9upT9Ht0iG93W7ug== - ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-regex@^5.0.1: +ansi-regex@^5.0.0, ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -3841,52 +4367,54 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ== -antd@^4.18.7: - version "4.18.7" - resolved "https://registry.yarnpkg.com/antd/-/antd-4.18.7.tgz#7355953a6c948b9353fe0f24681d0e1e2ca92781" - integrity sha512-OJsrZOPy4+fEbIVoUFLXQ9quLthkOjQD+AGwIey3nC5+4hebloImbGqqwQ1/ypSFDxou8NtyZ2HCTfPP5WaO4g== +antd@^4.23.4: + version "4.23.5" + resolved "https://registry.yarnpkg.com/antd/-/antd-4.23.5.tgz#4946191ac44d762590b2dbb84a4b8cd1101ab0cf" + integrity sha512-AMea5NYoMeGvRxZ/rslGvRqaiuzBgQMpOdlQfNjOfMd+0ZGi+E4AiwXilR99muFOttPcr3ebeIsKiUS5p/cnig== dependencies: "@ant-design/colors" "^6.0.0" "@ant-design/icons" "^4.7.0" - "@ant-design/react-slick" "~0.28.1" - "@babel/runtime" "^7.12.5" + "@ant-design/react-slick" "~0.29.1" + "@babel/runtime" "^7.18.3" "@ctrl/tinycolor" "^3.4.0" classnames "^2.2.6" copy-to-clipboard "^3.2.0" lodash "^4.17.21" memoize-one "^6.0.0" - moment "^2.25.3" - rc-cascader "~3.2.1" + moment "^2.29.2" + rc-cascader "~3.7.0" rc-checkbox "~2.3.0" - rc-collapse "~3.1.0" - rc-dialog "~8.6.0" - rc-drawer "~4.4.2" - rc-dropdown "~3.2.5" - rc-field-form "~1.22.0-2" - rc-image "~5.2.5" - rc-input-number "~7.3.0" - rc-mentions "~1.6.1" - rc-menu "~9.2.1" - rc-motion "^2.4.4" - rc-notification "~4.5.7" - rc-pagination "~3.1.9" - rc-picker "~2.5.17" - rc-progress "~3.2.1" + rc-collapse "~3.3.0" + rc-dialog "~8.9.0" + rc-drawer "~5.1.0" + rc-dropdown "~4.0.0" + rc-field-form "~1.27.0" + rc-image "~5.7.0" + rc-input "~0.1.2" + rc-input-number "~7.3.9" + rc-mentions "~1.10.0" + rc-menu "~9.6.3" + rc-motion "^2.6.1" + rc-notification "~4.6.0" + rc-pagination "~3.1.17" + rc-picker "~2.6.10" + rc-progress "~3.3.2" rc-rate "~2.9.0" rc-resize-observer "^1.2.0" - rc-select "~14.0.0-alpha.15" - rc-slider "~9.7.4" + rc-segmented "~2.1.0" + rc-select "~14.1.13" + rc-slider "~10.0.0" rc-steps "~4.1.0" rc-switch "~3.2.0" - rc-table "~7.22.2" - rc-tabs "~11.10.0" - rc-textarea "~0.3.0" - rc-tooltip "~5.1.1" - rc-tree "~5.4.3" - rc-tree-select "~5.1.1" + rc-table "~7.26.0" + rc-tabs "~12.1.0-alpha.1" + rc-textarea "~0.4.3" + rc-tooltip "~5.2.0" + rc-tree "~5.7.0" + rc-tree-select "~5.5.0" rc-trigger "^5.2.10" rc-upload "~4.3.0" - rc-util "^5.14.0" + rc-util "^5.22.5" scroll-into-view-if-needed "^2.2.25" anymatch@~3.1.2: @@ -3897,6 +4425,11 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -3919,6 +4452,11 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -3959,16 +4497,23 @@ astring@^1.6.0: resolved "https://registry.yarnpkg.com/astring/-/astring-1.8.1.tgz#a91c4afd4af3523e11f31242a3d5d9af62bb6cc6" integrity sha512-Aj3mbwVzj7Vve4I/v2JYOPFkCGM2YS7OqQTNSxmUR+LECRpokuPgAYghePgr6SALDo5bD5DlfbSaYjOzGJZOLQ== -async-validator@^4.0.2: - version "4.0.7" - resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.0.7.tgz#034a0fd2103a6b2ebf010da75183bec299247afe" - integrity sha512-Pj2IR7u8hmUEDOwB++su6baaRi+QvsgajuFB9j95foM1N2gy5HM4z60hfusIO0fBPG5uLAEl6yCJr1jNSVugEQ== +async-validator@^4.1.0: + version "4.2.5" + resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339" + integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg== at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +autocomplete.js@^0.37.0: + version "0.37.1" + resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.37.1.tgz#a29a048d827e7d2bf8f7df8b831766e5cc97df01" + integrity sha512-PgSe9fHYhZEsm/9jggbjtVsGXJkPLvd+9mC7gZJ662vVL5CRWEtm/mIrrzCx0MrNxHVwxD5d00UOn6NsmL2LUQ== + dependencies: + immediate "^3.2.3" + autoprefixer@^10.3.7, autoprefixer@^10.4.7: version "10.4.7" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.7.tgz#1db8d195f41a52ca5069b7593be167618edbbedf" @@ -4034,6 +4579,15 @@ babel-plugin-macros@^2.6.1: cosmiconfig "^6.0.0" resolve "^1.12.0" +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + babel-plugin-polyfill-corejs2@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" @@ -4043,13 +4597,13 @@ babel-plugin-polyfill-corejs2@^0.2.2: "@babel/helper-define-polyfill-provider" "^0.2.2" semver "^6.1.1" -babel-plugin-polyfill-corejs2@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== +babel-plugin-polyfill-corejs2@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" + integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.2" semver "^6.1.1" babel-plugin-polyfill-corejs3@^0.2.5: @@ -4060,12 +4614,12 @@ babel-plugin-polyfill-corejs3@^0.2.5: "@babel/helper-define-polyfill-provider" "^0.2.2" core-js-compat "^3.16.2" -babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== +babel-plugin-polyfill-corejs3@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" + integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.2" core-js-compat "^3.21.0" babel-plugin-polyfill-regenerator@^0.2.2: @@ -4075,12 +4629,12 @@ babel-plugin-polyfill-regenerator@^0.2.2: dependencies: "@babel/helper-define-polyfill-provider" "^0.2.2" -babel-plugin-polyfill-regenerator@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== +babel-plugin-polyfill-regenerator@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz#8f51809b6d5883e07e71548d75966ff7635527fe" + integrity sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.2" bail@^1.0.0: version "1.0.5" @@ -4107,11 +4661,37 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bash-glob@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bash-glob/-/bash-glob-2.0.0.tgz#a8ef19450783403ed93fccca2dbe09f2cf6320dc" + integrity sha512-53/NJ+t2UAkEYgQPO6aFjbx1Ue8vNNXCYaA4EljNKP1SR8A9dSQQoBmYWR8BLXO0/NDRJEMSJ4BxWihi//m3Kw== + dependencies: + bash-path "^1.0.1" + component-emitter "^1.2.1" + cross-spawn "^5.1.0" + each-parallel-async "^1.0.0" + extend-shallow "^2.0.1" + is-extglob "^2.1.1" + is-glob "^4.0.0" + +bash-path@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/bash-path/-/bash-path-1.0.3.tgz#dbc9efbdf18b1c11413dcb59b960e6aa56c84258" + integrity sha512-mGrYvOa6yTY/qNCiZkPFJqWmODK68y6kmVRAJ1NNbWlNoJrUrsFxu7FU2EKg7gbrer6ttrKkF2s/E/lhRy7/OA== + dependencies: + arr-union "^3.1.0" + is-windows "^1.0.1" + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= +bcp-47-match@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/bcp-47-match/-/bcp-47-match-1.0.3.tgz#cb8d03071389a10aff2062b862d6575ffd7cd7ef" + integrity sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -4313,7 +4893,7 @@ caniuse-lite@^1.0.30001335: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001358.tgz#473d35dabf5e448b463095cab7924e96ccfb8c00" integrity sha512-hvp8PSRymk85R20bsDra7ZTCpSVGN/PAz9pSAjPSjKC+rNmnUk5vCRgJwiTT/O4feQ/yu/drvZYpKxxhbFuChw== -ccount@^1.0.0, ccount@^1.0.3: +ccount@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== @@ -4340,6 +4920,11 @@ chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.0.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" + integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== + character-entities-html4@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" @@ -4387,10 +4972,10 @@ cheerio-select@^2.1.0: domhandler "^5.0.3" domutils "^3.0.1" -cheerio@^1.0.0-rc.11: - version "1.0.0-rc.11" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.11.tgz#1be84be1a126958366bcc57a11648cd9b30a60c2" - integrity sha512-bQwNaDIBKID5ts/DsdhxrjqFXYfLw4ste+wMKqWA8DyKcS4qwsPP4Bk8ZNaTJjvpiX/qW3BT4sU7d6Bh5i+dag== +cheerio@^1.0.0-rc.12: + version "1.0.0-rc.12" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" + integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== dependencies: cheerio-select "^2.1.0" dom-serializer "^2.0.0" @@ -4399,7 +4984,6 @@ cheerio@^1.0.0-rc.11: htmlparser2 "^8.0.1" parse5 "^7.0.0" parse5-htmlparser2-tree-adapter "^7.0.0" - tslib "^2.4.0" chokidar@^3.1.5, chokidar@^3.4.2, chokidar@^3.5.3: version "3.5.2" @@ -4494,11 +5078,6 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clsx@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" - integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== - clsx@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" @@ -4533,6 +5112,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + colord@^2.9.1: version "2.9.2" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" @@ -4548,6 +5132,11 @@ colorette@^2.0.10: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== +colors@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + combine-promises@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/combine-promises/-/combine-promises-1.1.0.tgz#72db90743c0ca7aab7d0d8d2052fd7b0f674de71" @@ -4563,7 +5152,7 @@ comma-separated-tokens@^2.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.2.tgz#d4c25abb679b7751c880be623c1179780fe1dd98" integrity sha512-G5yTt3KQN4Yn7Yk4ed73hlZ1evrFKXeUW3086p3PRFNp7m2vIjI6Pg+Kgb+oyzhd9F2qdcoj67+y3SdxL5XWsg== -commander@2, commander@^2.20.0: +commander@2, commander@^2.11.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -4573,6 +5162,11 @@ commander@7, commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1" + integrity sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA== + commander@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" @@ -4583,11 +5177,21 @@ commander@^8.1.0, commander@^8.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +common-path-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" + integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + component-props@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/component-props/-/component-props-1.1.1.tgz#f9b7df9b9927b6e6d97c9bd272aa867670f34944" @@ -4640,16 +5244,21 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" -connect-history-api-fallback@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" - integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== consola@^2.15.3: version "2.15.3" resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== +console-control-strings@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -4749,10 +5358,10 @@ core-js@^3.14.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.16.1.tgz#f4485ce5c9f3c6a7cb18fa80488e08d362097249" integrity sha512-AAkP8i35EbefU+JddyWi12AWE9f2N/qr/pwnDtWz4nyUIBGMJPX99ANFFRSw6FefM374lDujdtLDyhN2A/btHw== -core-js@^3.22.7: - version "3.23.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.2.tgz#e07a60ca8b14dd129cabdc3d2551baf5a01c76f0" - integrity sha512-ELJOWxNrJfOH/WK4VJ3Qd+fOqZuOuDNDJz0xG6Bt4mGg2eO/UT9CljCrbqDGovjLKUrGajEEBcoTOc0w+yBYeQ== +core-js@^3.23.3: + version "3.25.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.25.0.tgz#be71d9e0dd648ffd70c44a7ec2319d039357eceb" + integrity sha512-CVU1xvJEfJGhyCpBrzzzU1kjCfgsGUxhEvwUV2e/cOedYWHdmluamx+knDnmhqALddMG16fZvIqvs9aijsHHaA== core-util-is@~1.0.0: version "1.0.2" @@ -4798,11 +5407,20 @@ create-require@^1.1.0: integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.0.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" - integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== dependencies: - node-fetch "2.6.1" + node-fetch "2.6.7" + +cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" cross-spawn@^7.0.3: version "7.0.3" @@ -4871,6 +5489,11 @@ css-select@^5.1.0: domutils "^3.0.1" nth-check "^2.0.1" +css-selector-parser@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.4.1.tgz#03f9cb8a81c3e5ab2c51684557d5aaf6d2569759" + integrity sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g== + css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" @@ -4894,7 +5517,7 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssnano-preset-advanced@^5.3.5: +cssnano-preset-advanced@^5.3.8: version "5.3.8" resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.8.tgz#027b1d05ef896d908178c483f0ec4190cb50ef9a" integrity sha512-xUlLLnEB1LjpEik+zgRNlk8Y/koBPPtONZjp7JKbXigeAmCrFvq9H0pXW5jJV45bQWAlmJ0sKy+IMr0XxLYQZg== @@ -4946,7 +5569,16 @@ cssnano-utils@^3.1.0: resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== -cssnano@^5.1.8, cssnano@^5.1.9: +cssnano@^5.1.12: + version "5.1.13" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.13.tgz#83d0926e72955332dc4802a7070296e6258efc0a" + integrity sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ== + dependencies: + cssnano-preset-default "^5.2.12" + lilconfig "^2.0.3" + yaml "^1.10.2" + +cssnano@^5.1.8: version "5.1.12" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.12.tgz#bcd0b64d6be8692de79332c501daa7ece969816c" integrity sha512-TgvArbEZu0lk/dvg2ja+B7kYoD7BBCmn3+k58xD0qjrGHsFzXY/wKTo9M5egcUCabPol05e/PVoIu79s2JN4WQ== @@ -5037,9 +5669,9 @@ d3-color@1: integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== "d3-color@1 - 3", d3-color@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.0.1.tgz#03316e595955d1fcd39d9f3610ad41bb90194d0a" - integrity sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== d3-contour@1: version "1.3.2" @@ -5647,22 +6279,68 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +direction@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/direction/-/direction-1.0.4.tgz#2b86fb686967e987088caf8b89059370d4837442" + integrity sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ== + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= dns-packet@^5.2.2: - version "5.3.0" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.3.0.tgz#9a0f66118d3be176b828b911a842b0b1a4bdfd4f" - integrity sha512-Nce7YLu6YCgWRvOmDBsJMo9M5/jV3lEZ5vUWnWXYmwURvPylHvq7nkDWhNmk1ZQoZZOP7oQh/S0lSxbisKOfHg== + version "5.4.0" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.4.0.tgz#1f88477cf9f27e78a213fb6d118ae38e759a879b" + integrity sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g== dependencies: "@leichtgewicht/ip-codec" "^2.0.1" -docusaurus-plugin-internaldocs-fb@0.12.3: - version "0.12.3" - resolved "https://registry.yarnpkg.com/docusaurus-plugin-internaldocs-fb/-/docusaurus-plugin-internaldocs-fb-0.12.3.tgz#803197faa9d23cc590d44c3c63a32f250e555e87" - integrity sha512-zttBKv8hBXzOTXifzWIeA2rLXpZO/Jz/iSnu2gp0elnRU08uPEHojiPcn3wPi8kyVWjuoXysKJUP+X81Du3nsA== +docusaurus-lunr-search@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/docusaurus-lunr-search/-/docusaurus-lunr-search-2.3.2.tgz#9991ef51addb0bf09ac80a06cb5729f28939332d" + integrity sha512-Ngvm2kXwliWThqAThXI1912rOKHlFL7BjIc+OVNUfzkjpk5ar4TFEh+EUaaMOLw4V0BBko3CW0Ym7prqqm3jLQ== + dependencies: + autocomplete.js "^0.37.0" + classnames "^2.2.6" + gauge "^3.0.0" + hast-util-select "^4.0.0" + hast-util-to-text "^2.0.0" + hogan.js "^3.0.2" + lunr "^2.3.8" + lunr-languages "^1.4.0" + minimatch "^3.0.4" + object-assign "^4.1.1" + rehype-parse "^7.0.1" + to-vfile "^6.1.0" + unified "^9.0.0" + unist-util-is "^4.0.2" + +docusaurus-lunr-search@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/docusaurus-lunr-search/-/docusaurus-lunr-search-3.0.0.tgz#8e04efd625e006eeb22213f29b023fedc5c4c60e" + integrity sha512-u4hI4neG40304U+sFoPK9Vs9l8i47TZnysU8BjcV3sHA/Pr/0ztNsRAkMY0ZlMS5V9Rn0HEQ41QKbdWKNGgI5w== + dependencies: + autocomplete.js "^0.37.0" + clsx "^1.2.1" + gauge "^3.0.0" + hast-util-select "^4.0.0" + hast-util-to-text "^2.0.0" + hogan.js "^3.0.2" + lunr "^2.3.8" + lunr-languages "^1.4.0" + mark.js "^8.11.1" + minimatch "^3.0.4" + object-assign "^4.1.1" + rehype-parse "^7.0.1" + to-vfile "^6.1.0" + unified "^9.0.0" + unist-util-is "^4.0.2" + +docusaurus-plugin-internaldocs-fb@1.16.1: + version "1.16.1" + resolved "https://registry.yarnpkg.com/docusaurus-plugin-internaldocs-fb/-/docusaurus-plugin-internaldocs-fb-1.16.1.tgz#ebee630f332843fb8010065129ed8c4239c9fdd7" + integrity sha512-dV5fopgoiV1sid9L4hTtM8ed2lnBKGUeZ0Hz0kptVOYj6TFmxJUzJBtOzJtPB2Fc7tvkNoWodUpVpb7wJLsL4w== dependencies: "@mdx-js/mdx" "^2.1.1" "@mdx-js/react" "^1.6.22" @@ -5672,6 +6350,7 @@ docusaurus-plugin-internaldocs-fb@0.12.3: assert "^2.0.0" buffer "^6.0.3" clsx "^1.2.1" + docusaurus-lunr-search "^2.3.2" fs-extra "^10.1.0" lodash.debounce "^4.0.8" mermaid "^9.1.3" @@ -5680,7 +6359,7 @@ docusaurus-plugin-internaldocs-fb@0.12.3: react-live "^2.2.3" react-modal "3.15.1" remark-gfm "^3.0.1" - remark-mdx-filter-imports "^0.1.2" + remark-mdx-filter-imports "^0.1.3" unified "^9.2.1" unist-util-remove "^3.1.0" unist-util-visit "^2.0.1" @@ -5753,10 +6432,10 @@ dompurify@2.3.10: resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.10.tgz#901f7390ffe16a91a5a556b94043314cd4850385" integrity sha512-o7Fg/AgC7p/XpKjf/+RC3Ok6k4St5F7Q6q6+Nnm3p2zGWioAY6dh0CbbuwOhH2UcSzKsdniE/YnE2/92JcsA+g== -dompurify@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c" - integrity sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg== +dompurify@2.3.5: + version "2.3.5" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.5.tgz#c83ed5a3ae5ce23e52efe654ea052ffb358dd7e3" + integrity sha512-kD+f8qEaa42+mjdOpKeztu9Mfx5bv9gVLO6K9jRx4uGvh6Wv06Srn4jr1wPNY2OOUGGSKHNFN+A8MA3v0E0QAQ== domutils@^2.5.2, domutils@^2.6.0: version "2.7.0" @@ -5801,6 +6480,11 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +each-parallel-async@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/each-parallel-async/-/each-parallel-async-1.0.0.tgz#91783e190000c7dd588336b2d468ebaf71980f7b" + integrity sha512-P/9kLQiQj0vZNzphvKKTgRgMnlqs5cJsxeAiuog1jrUnwv0Z3hVUwJDQiP7MnLb2I9S15nR9SRUceFT9IxtqRg== + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -5853,10 +6537,10 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^5.9.3: - version "5.9.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88" - integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow== +enhanced-resolve@^5.10.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" + integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -5876,6 +6560,11 @@ entities@^4.2.0, entities@^4.3.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.0.tgz#62915f08d67353bb4eb67e3d62641a4059aec656" integrity sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg== +eol@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd" + integrity sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg== + errno@^0.1.1: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" @@ -6031,10 +6720,10 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -eta@^1.12.3: - version "1.12.3" - resolved "https://registry.yarnpkg.com/eta/-/eta-1.12.3.tgz#2982d08adfbef39f9fa50e2fbd42d7337e7338b1" - integrity sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg== +eta@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/eta/-/eta-2.0.1.tgz#199e675359cb6e19d38f29e1f405e1ba0e79a6df" + integrity sha512-46E2qDPDm7QA+usjffUWz9KfXsxVZclPOuKsXs4ZWZdI/X1wpDF7AO424pt7fdYohCzWsIkXAhNGXSlwo5naAg== etag@~1.8.1: version "1.8.1" @@ -6263,6 +6952,14 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" +find-cache-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz#a30ee0448f81a3990708f6453633c733e2f6eec2" + integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== + dependencies: + common-path-prefix "^3.0.0" + pkg-dir "^7.0.0" + find-root@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -6291,33 +6988,55 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flipper-common@0.131.1: - version "0.131.1" - resolved "https://registry.yarnpkg.com/flipper-common/-/flipper-common-0.131.1.tgz#3431a4a1270ea61a115995ae8bfcb0e734270762" - integrity sha512-6jpfLb8qsHRRrgsVTLvd462dWqQQaZpLOajmsk+BS0vDWk1DUKcLnuR6jK9sKvv6Htkx7+lsluVZCxNTdaErCw== +find-up@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" + integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== + dependencies: + locate-path "^7.1.0" + path-exists "^5.0.0" -flipper-plugin@^0.131.1: - version "0.131.1" - resolved "https://registry.yarnpkg.com/flipper-plugin/-/flipper-plugin-0.131.1.tgz#bde9e6cd3f45824ef12417a43b5a79ba44748ff6" - integrity sha512-BmmDXFAqAd63nmW+V35bcp0f1rllfKMqv/rNKJe34Rwvrv8xcepjYfVg0BHCUptpJ8NPFCD3wKCdiI/eAA+COA== +flipper-common@0.183.0: + version "0.183.0" + resolved "https://registry.yarnpkg.com/flipper-common/-/flipper-common-0.183.0.tgz#018c99a7d63403dcce311a6140aee3a9462ab64a" + integrity sha512-ducaS//nkWxOW5OHvppiBkNxFIWjuuBLU3KyPLIACzrQpzxCe0q/XuxYwRIthotcnOZs7QQib9IfJ4F2uHRFrQ== + dependencies: + uuid "^8.3.2" + +flipper-plugin-core@0.183.0: + version "0.183.0" + resolved "https://registry.yarnpkg.com/flipper-plugin-core/-/flipper-plugin-core-0.183.0.tgz#14462761bb975f3f81cf8c757bd12c82c0931d54" + integrity sha512-SYT8l7+AWjLdSmiMuNJ5AMicYNiCBtSlV30WGLBYgRqdwe/YaCfIWIwIM1Z0yD8qFZ7oVldx9pxPLI4BN60ruw== + dependencies: + eventemitter3 "^4.0.7" + flipper-common "0.183.0" + immer "^9.0.18" + js-base64 "^3.7.5" + lodash "^4.17.21" + string-natural-compare "^3.0.0" + +flipper-plugin@^0.183.0: + version "0.183.0" + resolved "https://registry.yarnpkg.com/flipper-plugin/-/flipper-plugin-0.183.0.tgz#d9bda280166873aba59f72ed3a93f572d76a65f7" + integrity sha512-sfirI9T0cBN6SHgQxoF12vvYhMrSiKFk7+IWPhgVtKADnSjfYkCerR7dwTl+YH8hUiyd59QfNsi+p5cVpzb82Q== dependencies: "@ant-design/colors" "^6.0.0" - "@emotion/css" "^11.5.0" - "@emotion/react" "^11.6.0" + "@emotion/css" "^11.7.1" + "@emotion/react" "^11.8.2" "@reach/observe-rect" "^1.2.0" - "@types/react" "17.0.34" + "@types/react" "17.0.39" "@types/react-color" "^3.0.6" - "@types/react-dom" "^17.0.11" + "@types/react-dom" "^17.0.13" eventemitter3 "^4.0.7" - flipper-common "0.131.1" - immer "^9.0.12" - js-base64 "^3.7.2" + flipper-common "0.183.0" + flipper-plugin-core "0.183.0" + immer "^9.0.18" + js-base64 "^3.7.5" lodash "^4.17.21" react-color "^2.19.3" react-element-to-jsx-string "^14.3.4" react-virtual "^2.10.4" string-natural-compare "^3.0.0" - uuid "^8.3.2" flux@^4.0.1: version "4.0.1" @@ -6328,9 +7047,9 @@ flux@^4.0.1: fbjs "^3.0.0" follow-redirects@^1.0.0: - version "1.14.7" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" - integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== follow-redirects@^1.14.7: version "1.15.1" @@ -6376,19 +7095,19 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fs-extra@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" - integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== +fs-extra@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -6433,6 +7152,21 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -6587,6 +7321,17 @@ globby@^13.1.1: merge2 "^1.4.1" slash "^4.0.0" +globby@^13.1.2: + version "13.1.3" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.3.tgz#f62baf5720bcb2c1330c8d4ef222ee12318563ff" + integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -6670,6 +7415,11 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -6695,17 +7445,6 @@ hast-to-hyperscript@^9.0.0: unist-util-is "^4.0.0" web-namespaces "^1.0.0" -hast-util-from-parse5@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz#3089dc0ee2ccf6ec8bc416919b51a54a589e097c" - integrity sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA== - dependencies: - ccount "^1.0.3" - hastscript "^5.0.0" - property-information "^5.0.0" - web-namespaces "^1.1.2" - xtend "^4.0.1" - hast-util-from-parse5@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" @@ -6718,6 +7457,16 @@ hast-util-from-parse5@^6.0.0: vfile-location "^3.2.0" web-namespaces "^1.0.0" +hast-util-has-property@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz#9f137565fad6082524b382c1e7d7d33ca5059f36" + integrity sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg== + +hast-util-is-element@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz#3b3ed5159a2707c6137b48637fbfe068e175a425" + integrity sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ== + hast-util-parse-selector@^2.0.0: version "2.2.5" resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" @@ -6739,6 +7488,26 @@ hast-util-raw@6.0.1: xtend "^4.0.0" zwitch "^1.0.0" +hast-util-select@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/hast-util-select/-/hast-util-select-4.0.2.tgz#ae3ef2860e02cda2ad3a2e72b47c1f5e8f44e9e7" + integrity sha512-8EEG2//bN5rrzboPWD2HdS3ugLijNioS1pqOTIolXNf67xxShYw4SQEmVXd3imiBG+U2bC2nVTySr/iRAA7Cjg== + dependencies: + bcp-47-match "^1.0.0" + comma-separated-tokens "^1.0.0" + css-selector-parser "^1.0.0" + direction "^1.0.0" + hast-util-has-property "^1.0.0" + hast-util-is-element "^1.0.0" + hast-util-to-string "^1.0.0" + hast-util-whitespace "^1.0.0" + not "^0.1.0" + nth-check "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + unist-util-visit "^2.0.0" + zwitch "^1.0.0" + hast-util-to-estree@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/hast-util-to-estree/-/hast-util-to-estree-2.0.2.tgz#79c5bf588915610b3f0d47ca83a74dc0269c7dc2" @@ -6770,21 +7539,30 @@ hast-util-to-parse5@^6.0.0: xtend "^4.0.0" zwitch "^1.0.0" +hast-util-to-string@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-1.0.4.tgz#9b24c114866bdb9478927d7e9c36a485ac728378" + integrity sha512-eK0MxRX47AV2eZ+Lyr18DCpQgodvaS3fAQO2+b9Two9F5HEoRPhiUMNzoXArMJfZi2yieFzUBMRl3HNJ3Jus3w== + +hast-util-to-text@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz#04f2e065642a0edb08341976084aa217624a0f8b" + integrity sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ== + dependencies: + hast-util-is-element "^1.0.0" + repeat-string "^1.0.0" + unist-util-find-after "^3.0.0" + +hast-util-whitespace@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz#e4fe77c4a9ae1cb2e6c25e02df0043d0164f6e41" + integrity sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A== + hast-util-whitespace@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.0.tgz#4fc1086467cc1ef5ba20673cb6b03cec3a970f1c" integrity sha512-Pkw+xBHuV6xFeJprJe2BBEoDV+AvQySaz3pPDRUs5PNZEMQjpXJJueqrpcHIXxnWTcAGi/UOCgVShlkY6kLoqg== -hastscript@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.1.2.tgz#bde2c2e56d04c62dd24e8c5df288d050a355fb8a" - integrity sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ== - dependencies: - comma-separated-tokens "^1.0.0" - hast-util-parse-selector "^2.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" - hastscript@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" @@ -6813,6 +7591,14 @@ history@^4.9.0: tiny-warning "^1.0.0" value-equal "^1.0.1" +hogan.js@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" + integrity sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg== + dependencies: + mkdirp "0.3.0" + nopt "1.0.10" + hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -6903,9 +7689,9 @@ htmlparser2@^8.0.1: entities "^4.3.0" http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-deceiver@^1.2.7: version "1.2.7" @@ -6963,14 +7749,14 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.4: +iconv-lite@0.4, iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6: +iconv-lite@0.6, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -7009,7 +7795,12 @@ image-size@~0.5.0: resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w= -immer@^9.0.12, immer@^9.0.6, immer@^9.0.7: +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +immer@^9.0.18, immer@^9.0.6, immer@^9.0.7: version "9.0.6" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.6.tgz#7a96bf2674d06c8143e327cbf73539388ddf1a73" integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ== @@ -7037,10 +7828,10 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -infima@0.2.0-alpha.39: - version "0.2.0-alpha.39" - resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.39.tgz#054b13ac44f3e9a42bc083988f1a1586add2f59c" - integrity sha512-UyYiwD3nwHakGhuOUfpe3baJ8gkiPpRVx4a4sE/Ag+932+Y6swtLsdPoRR8ezhwqGnduzxmFkjumV9roz6QoLw== +infima@0.2.0-alpha.43: + version "0.2.0-alpha.43" + resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.43.tgz#f7aa1d7b30b6c08afef441c726bac6150228cbe0" + integrity sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ== inflight@^1.0.4: version "1.0.6" @@ -7203,6 +7994,13 @@ is-core-module@^2.8.1: dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -7252,6 +8050,13 @@ is-generator-function@^1.0.7: dependencies: has-tostringtag "^1.0.0" +is-glob@^4.0.0, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -7259,13 +8064,6 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-glob@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - is-hexadecimal@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" @@ -7431,6 +8229,11 @@ is-whitespace-character@^1.0.0: resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== +is-windows@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + is-word-character@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" @@ -7497,10 +8300,10 @@ joi@^17.6.0: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -js-base64@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.2.tgz#816d11d81a8aff241603d19ce5761e13e41d7745" - integrity sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ== +js-base64@^3.7.5: + version "3.7.5" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.5.tgz#21e24cf6b886f76d6f5f165bfcd69cc55b9e3fca" + integrity sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -7559,17 +8362,10 @@ json2mq@^0.2.0: dependencies: string-convert "^0.2.0" -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - -json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@^2.1.2, json5@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonfile@^4.0.0: version "4.0.0" @@ -7631,10 +8427,10 @@ latest-version@^5.1.0: dependencies: package-json "^6.3.0" -less@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/less/-/less-4.1.2.tgz#6099ee584999750c2624b65f80145f8674e4b4b0" - integrity sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA== +less@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/less/-/less-4.1.3.tgz#175be9ddcbf9b250173e0a00b4d6920a5b770246" + integrity sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA== dependencies: copy-anything "^2.0.1" parse-node-version "^1.0.1" @@ -7645,7 +8441,7 @@ less@^4.1.2: image-size "~0.5.0" make-dir "^2.1.0" mime "^1.4.1" - needle "^2.5.2" + needle "^3.1.0" source-map "~0.6.0" leven@^3.1.0: @@ -7669,9 +8465,9 @@ loader-runner@^4.2.0: integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== loader-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" - integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" @@ -7704,7 +8500,14 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash-es@^4.17.15: +locate-path@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" + integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== + dependencies: + p-locate "^6.0.0" + +lodash-es@^4.17.15, lodash-es@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== @@ -7734,7 +8537,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.0.1, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: +lodash@4.17.21, lodash@^4.0.1, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7768,6 +8571,14 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -7775,6 +8586,16 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lunr-languages@^1.4.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/lunr-languages/-/lunr-languages-1.12.0.tgz#c44d8f2127054a55c62e1aebe0b1eaea6dfc1d91" + integrity sha512-C2z02jt74ymrDocBwxYB4Cr1LNZj9rHGLTH/00+JuoT6eJOSSuPBzeqQG8kjnlPUQe+/PAWv1/KHbDT+YYYRnA== + +lunr@^2.3.8: + version "2.3.9" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + magic-string@^0.25.0, magic-string@^0.25.1: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" @@ -7802,6 +8623,11 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +mark.js@^8.11.1: + version "8.11.1" + resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5" + integrity sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ== + markdown-escapes@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" @@ -8076,15 +8902,15 @@ merge2@^1.3.0, merge2@^1.4.1: integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== mermaid@^8.13.4: - version "8.13.4" - resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.13.4.tgz#924cb85f39380285e0a99f245c66cfa61014a2e1" - integrity sha512-zdWtsXabVy1PEAE25Jkm4zbTDlQe8rqNlTMq2B3j+D+NxDskJEY5OsgalarvNLsw+b5xFa1a8D1xcm/PijrDow== + version "8.14.0" + resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.14.0.tgz#ef589b0537f56d6340069070edb51719a4faba00" + integrity sha512-ITSHjwVaby1Li738sxhF48sLTxcNyUAoWfoqyztL1f7J6JOLpHOuQPNLBb6lxGPUA0u7xP9IRULgvod0dKu35A== dependencies: "@braintree/sanitize-url" "^3.1.0" d3 "^7.0.0" dagre "^0.8.5" dagre-d3 "^0.6.4" - dompurify "2.3.3" + dompurify "2.3.5" graphlib "^2.1.8" khroma "^1.4.1" moment-mini "^2.24.0" @@ -8565,7 +9391,7 @@ mini-create-react-context@^0.4.0: "@babel/runtime" "^7.12.1" tiny-warning "^1.0.3" -mini-css-extract-plugin@^2.6.0: +mini-css-extract-plugin@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz#9a1251d15f2035c342d99a468ab9da7a0451b71e" integrity sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg== @@ -8577,27 +9403,39 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@3.0.4, minimatch@^3.0.4: +minimatch@3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew== moment-mini@2.24.0, moment-mini@^2.24.0: version "2.24.0" resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.24.0.tgz#fa68d98f7fe93ae65bf1262f6abb5fb6983d8d18" integrity sha512-9ARkWHBs+6YJIvrIp0Ik5tyTTtP9PoV0Ssu2Ocq5y9v8+NOOpWiRshAp8c4rZVWTOe+157on/5G+zj5pwIQFEQ== -moment@^2.24.0, moment@^2.25.3: - version "2.29.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" - integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== +moment@^2.24.0, moment@^2.29.2: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== mri@^1.1.0: version "1.2.0" @@ -8632,13 +9470,13 @@ nanoid@^3.3.4: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== -needle@^2.5.2: - version "2.9.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" - integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== +needle@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-3.2.0.tgz#07d240ebcabfd65c76c03afae7f6defe6469df44" + integrity sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ== dependencies: debug "^3.2.6" - iconv-lite "^0.4.4" + iconv-lite "^0.6.3" sax "^1.2.4" negotiator@0.6.2: @@ -8656,6 +9494,13 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +nmtree@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/nmtree/-/nmtree-1.0.6.tgz#953e057ad545e9e627f1275bd25fea4e92c1cf63" + integrity sha512-SUPCoyX5w/lOT6wD/PZEymR+J899984tYEOYjuDqQlIOeX5NSb1MEsCcT0az+dhZD0MLAj5hGBZEpKQxuDdniA== + dependencies: + commander "^2.11.0" + no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -8671,11 +9516,6 @@ node-emoji@^1.10.0: dependencies: lodash "^4.17.21" -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -8693,6 +9533,13 @@ node-releases@^1.1.75: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.75.tgz#6dd8c876b9897a1b8e5a02de26afa79bb54ebbfe" integrity sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw== +nopt@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== + dependencies: + abbrev "1" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -8713,6 +9560,11 @@ normalize-url@^6.0.1: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +not@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/not/-/not-0.1.0.tgz#c9691c1746c55dcfbe54cbd8bd4ff041bc2b519d" + integrity sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA== + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -8725,14 +9577,7 @@ nprogress@^0.2.0: resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= -nth-check@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" - integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== - dependencies: - boolbase "^1.0.0" - -nth-check@^2.0.1: +nth-check@^2.0.0, nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== @@ -8841,6 +9686,13 @@ p-limit@^3.0.2, p-limit@^3.1.0: dependencies: yocto-queue "^0.1.0" +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -8862,6 +9714,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-locate@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" + integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== + dependencies: + p-limit "^4.0.0" + p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -8961,11 +9820,6 @@ parse5-htmlparser2-tree-adapter@^7.0.0: domhandler "^5.0.2" parse5 "^7.0.0" -parse5@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - parse5@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" @@ -9006,6 +9860,11 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -9095,6 +9954,13 @@ pkg-dir@^4.1.0: dependencies: find-up "^4.0.0" +pkg-dir@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-7.0.0.tgz#8f0c08d6df4476756c5ff29b3282d0bab7517d11" + integrity sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA== + dependencies: + find-up "^6.3.0" + pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -9428,10 +10294,10 @@ prism-react-renderer@^1.2.1: resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.2.1.tgz#392460acf63540960e5e3caa699d851264e99b89" integrity sha512-w23ch4f75V1Tnz8DajsYKvY5lF7H1+WvzvLUcF0paFxkTHSp42RS0H5CttdN2Q8RR3DRGZ9v5xD/h3n8C8kGmg== -prism-react-renderer@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.3.tgz#9b5a4211a6756eee3c96fee9a05733abc0b0805c" - integrity sha512-Viur/7tBTCH2HmYzwCHmt2rEFn+rdIWNIINXyg0StiISbDiIhHKhrFuEK8eMkKgvsIYSjgGqy/hNyucHp6FpoQ== +prism-react-renderer@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz#786bb69aa6f73c32ba1ee813fbe17a0115435085" + integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg== prismjs@^1.28.0: version "1.28.0" @@ -9501,6 +10367,11 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -9589,16 +10460,16 @@ rc-align@^4.0.0: rc-util "^5.3.0" resize-observer-polyfill "^1.5.1" -rc-cascader@~3.2.1: - version "3.2.6" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.2.6.tgz#499cf7f65625569eff6dc3854612298de4f24093" - integrity sha512-3CmlJP7jPVlP5jT+O3PrP8E9yxees48Na7Hiir84ktcw11pUUU5YawAhuRoSc09SGVvRcP70a9gCu94Hqp3ZwA== +rc-cascader@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.7.0.tgz#98134df578ce1cca22be8fb4319b04df4f3dca36" + integrity sha512-SFtGpwmYN7RaWEAGTS4Rkc62ZV/qmQGg/tajr/7mfIkleuu8ro9Hlk6J+aA0x1YS4zlaZBtTcSaXM01QMiEV/A== dependencies: "@babel/runtime" "^7.12.5" array-tree-filter "^2.1.0" classnames "^2.3.1" - rc-select "~14.0.0-alpha.23" - rc-tree "~5.4.3" + rc-select "~14.1.0" + rc-tree "~5.7.0" rc-util "^5.6.1" rc-checkbox@~2.3.0: @@ -9609,10 +10480,10 @@ rc-checkbox@~2.3.0: "@babel/runtime" "^7.10.1" classnames "^2.2.1" -rc-collapse@~3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.1.2.tgz#76028a811b845d03d9460ccc409c7ea8ad09db14" - integrity sha512-HujcKq7mghk/gVKeI6EjzTbb8e19XUZpakrYazu1MblEZ3Hu3WBMSN4A3QmvbF6n1g7x6lUlZvsHZ5shABWYOQ== +rc-collapse@~3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.3.1.tgz#fc66d4c9cfeaf41e932b2de6da2d454874aee55a" + integrity sha512-cOJfcSe3R8vocrF8T+PgaHDrgeA1tX+lwfhwSj60NX9QVRidsILIbRNDLD6nAzmcvVC5PWiIRiR4S1OobxdhCg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -9620,100 +10491,89 @@ rc-collapse@~3.1.0: rc-util "^5.2.1" shallowequal "^1.1.0" -rc-dialog@~8.6.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-8.6.0.tgz#3b228dac085de5eed8c6237f31162104687442e7" - integrity sha512-GSbkfqjqxpZC5/zc+8H332+q5l/DKUhpQr0vdX2uDsxo5K0PhvaMEVjyoJUTkZ3+JstEADQji1PVLVb/2bJeOQ== +rc-dialog@~8.9.0: + version "8.9.0" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-8.9.0.tgz#04dc39522f0321ed2e06018d4a7e02a4c32bd3ea" + integrity sha512-Cp0tbJnrvPchJfnwIvOMWmJ4yjX3HWFatO6oBFD1jx8QkgsQCR0p8nUWAKdd3seLJhEC39/v56kZaEjwp9muoQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" rc-motion "^2.3.0" - rc-util "^5.6.1" + rc-util "^5.21.0" -rc-drawer@~4.4.2: - version "4.4.3" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-4.4.3.tgz#2094937a844e55dc9644236a2d9fba79c344e321" - integrity sha512-FYztwRs3uXnFOIf1hLvFxIQP9MiZJA+0w+Os8dfDh/90X7z/HqP/Yg+noLCIeHEbKln1Tqelv8ymCAN24zPcfQ== +rc-drawer@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-5.1.0.tgz#c1b8a46e5c064ba46a16233fbcfb1ccec6a73c10" + integrity sha512-pU3Tsn99pxGdYowXehzZbdDVE+4lDXSGb7p8vA9mSmr569oc2Izh4Zw5vLKSe/Xxn2p5MSNbLVqD4tz+pK6SOw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" - rc-util "^5.7.0" + rc-motion "^2.6.1" + rc-util "^5.21.2" -rc-dropdown@^3.2.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.3.2.tgz#097c2ec1b6d55c10eeb94dcf6120ba034c7a58e0" - integrity sha512-49GOz42oNvLtYGoJ2X5UWXJFp7aUiSZkj9OcgTV1UpxFZqHQMw+xijkaL5k3XDkMbb92XsuFnFt7IGG3/C0DKw== +rc-dropdown@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.0.1.tgz#f65d9d3d89750241057db59d5a75e43cd4576b68" + integrity sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.18.3" classnames "^2.2.6" - rc-trigger "^5.0.4" + rc-trigger "^5.3.1" + rc-util "^5.17.0" -rc-dropdown@~3.2.5: - version "3.2.5" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.2.5.tgz#c211e571d29d15e7f725b5a75fc8c7f371fc3348" - integrity sha512-dVO2eulOSbEf+F4OyhCY5iGiMVhUYY/qeXxL7Ex2jDBt/xc89jU07mNoowV6aWxwVOc70pxEINff0oM2ogjluA== +rc-field-form@~1.27.0: + version "1.27.2" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.27.2.tgz#5123282cf1561ba34f3197c37b8b9a8b710dd7dd" + integrity sha512-NaTjkSB8JsHRgm52wkDorsDzFf2HH6GmCQ2AqkwO8zo+zIqybw8K1lkzDBMDJI8nw1pFuD46U5QsYZv4blYvdw== dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.6" - rc-trigger "^5.0.4" - -rc-field-form@~1.22.0-2: - version "1.22.1" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.22.1.tgz#0bd2f4e730ff2f071529d00bef28e062362890f5" - integrity sha512-LweU7nBeqmC5r3HDUjRprcOXXobHXp/TGIxD7ppBq5FX6Iptt3ibdpRVg4RSyNulBNGHOuknHlRcguuIpvVMVg== - dependencies: - "@babel/runtime" "^7.8.4" - async-validator "^4.0.2" + "@babel/runtime" "^7.18.0" + async-validator "^4.1.0" rc-util "^5.8.0" -rc-image@~5.2.5: - version "5.2.5" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.2.5.tgz#44e6ffc842626827960e7ab72e1c0d6f3a8ce440" - integrity sha512-qUfZjYIODxO0c8a8P5GeuclYXZjzW4hV/5hyo27XqSFo1DmTCs2HkVeQObkcIk5kNsJtgsj1KoPThVsSc/PXOw== +rc-image@~5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.7.1.tgz#678dc014845954c30237808c00c7b12e5f2a0b07" + integrity sha512-QyMfdhoUfb5W14plqXSisaYwpdstcLYnB0MjX5ccIK2rydQM9sDPuekQWu500DDGR2dBaIF5vx9XbWkNFK17Fg== dependencies: "@babel/runtime" "^7.11.2" classnames "^2.2.6" - rc-dialog "~8.6.0" + rc-dialog "~8.9.0" rc-util "^5.0.6" -rc-input-number@~7.3.0: - version "7.3.4" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-7.3.4.tgz#674aea98260250287d36e330a7e065b174486e9d" - integrity sha512-W9uqSzuvJUnz8H8vsVY4kx+yK51SsAxNTwr8SNH4G3XqQNocLVmKIibKFRjocnYX1RDHMND9FFbgj2h7E7nvGA== +rc-input-number@~7.3.9: + version "7.3.9" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-7.3.9.tgz#bc6560376ea595e3bf8fbd3137711cbc158800b5" + integrity sha512-u0+miS+SATdb6DtssYei2JJ1WuZME+nXaG6XGtR8maNyW5uGDytfDu60OTWLQEb0Anv/AcCzehldV8CKmKyQfA== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" - rc-util "^5.9.8" + rc-util "^5.23.0" -rc-mentions@~1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.6.2.tgz#62ed7cdd8fa86d857c3ce3f9e73438022130815e" - integrity sha512-cntfJkNMq8B910rXuvnsnOV88DfmoUidnQnSIeXzWiYiUX4RL5oWUfSZzs+HAXYRU4SL1l8Mwjx95wHETiZ/fQ== +rc-input@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-0.1.2.tgz#7d6a0858a5f1fd89f78020cf6f13d672778481b1" + integrity sha512-ZPmwcFspgfYpUfbSx3KnLk9gImBcLOrlQCr4oTJ4jBoIXgJLTfm26yelzRgBJewhkvD8uJbgX0sQ/yOzuOHnJg== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.18.1" + +rc-mentions@~1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.10.0.tgz#f2e4055b535d042d408e94b853b709dbd966f546" + integrity sha512-oMlYWnwXSxP2NQVlgxOTzuG/u9BUc3ySY78K3/t7MNhJWpZzXTao+/Bic6tyZLuNCO89//hVQJBdaR2rnFQl6Q== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" - rc-menu "^9.0.0" - rc-textarea "^0.3.0" + rc-menu "~9.6.0" + rc-textarea "^0.4.0" rc-trigger "^5.0.4" - rc-util "^5.0.1" + rc-util "^5.22.5" -rc-menu@^9.0.0: - version "9.3.2" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.3.2.tgz#bb842d37ebf71da912bea201cf7ef0a27267ad49" - integrity sha512-h3m45oY1INZyqphGELkdT0uiPnFzxkML8m0VMhJnk2fowtqfiT7F5tJLT3znEVaPIY80vMy1bClCkgq8U91CzQ== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.4.3" - rc-overflow "^1.2.0" - rc-trigger "^5.1.2" - rc-util "^5.12.0" - shallowequal "^1.1.0" - -rc-menu@~9.2.1: - version "9.2.1" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.2.1.tgz#6fbe47f4846363bb81a5a21f0960026c3ada497a" - integrity sha512-UbEtn3rflJ8zS+etYGTVQuzy7Fm+yWXR5c0Rl6ecNTS/dPknRyWAyhJcbeR0Hu1+RdQT+0VCqrUPrgKnm4iY+w== +rc-menu@~9.6.0, rc-menu@~9.6.3: + version "9.6.4" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.6.4.tgz#033e7b8848c17a09a81b68b8d4c3fa457605f4f6" + integrity sha512-6DiNAjxjVIPLZXHffXxxcyE15d4isRL7iQ1ru4MqYDH2Cqc5bW96wZOdMydFtGLyDdnmEQ9jVvdCE9yliGvzkw== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -9732,15 +10592,24 @@ rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motio classnames "^2.2.1" rc-util "^5.18.1" -rc-notification@~4.5.7: - version "4.5.7" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-4.5.7.tgz#265e6e6a0c1a0fac63d6abd4d832eb8ff31522f1" - integrity sha512-zhTGUjBIItbx96SiRu3KVURcLOydLUHZCPpYEn1zvh+re//Tnq/wSxN4FKgp38n4HOgHSVxcLEeSxBMTeBBDdw== +rc-motion@^2.6.1, rc-motion@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.6.2.tgz#3d31f97e41fb8e4f91a4a4189b6a98ac63342869" + integrity sha512-4w1FaX3dtV749P8GwfS4fYnFG4Rb9pxvCYPc/b2fw1cmlHJWNNgOFIz7ysiD+eOrzJSvnLJWlNQQncpNMXwwpg== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.21.0" + +rc-notification@~4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-4.6.0.tgz#4e76fc2d0568f03cc93ac18c9e20763ebe29fa46" + integrity sha512-xF3MKgIoynzjQAO4lqsoraiFo3UXNYlBfpHs0VWvwF+4pimen9/H1DYLN2mfRWhHovW6gRpla73m2nmyIqAMZQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.2.0" - rc-util "^5.0.1" + rc-util "^5.20.1" rc-overflow@^1.0.0, rc-overflow@^1.2.0: version "1.2.3" @@ -9752,18 +10621,18 @@ rc-overflow@^1.0.0, rc-overflow@^1.2.0: rc-resize-observer "^1.0.0" rc-util "^5.15.0" -rc-pagination@~3.1.9: - version "3.1.15" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.1.15.tgz#e05eddf4c15717a5858290bed0857e27e2f957ff" - integrity sha512-4L3fot8g4E+PjWEgoVGX0noFCg+8ZFZmeLH4vsnZpB3O2T2zThtakjNxG+YvSaYtyMVT4B+GLayjKrKbXQpdAg== +rc-pagination@~3.1.17: + version "3.1.17" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.1.17.tgz#91e690aa894806e344cea88ea4a16d244194a1bd" + integrity sha512-/BQ5UxcBnW28vFAcP2hfh+Xg15W0QZn8TWYwdCApchMH1H0CxiaUUcULP8uXcFM1TygcdKWdt3JqsL9cTAfdkQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" -rc-picker@~2.5.17: - version "2.5.19" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-2.5.19.tgz#73d07546fac3992f0bfabf2789654acada39e46f" - integrity sha512-u6myoCu/qiQ0vLbNzSzNrzTQhs7mldArCpPHrEI6OUiifs+IPXmbesqSm0zilJjfzrZJLgYeyyOMSznSlh0GKA== +rc-picker@~2.6.10: + version "2.6.10" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-2.6.10.tgz#8d0a473c079388bdb2d7358a2a54c7d5095893b4" + integrity sha512-9wYtw0DFWs9FO92Qh2D76P0iojUr8ZhLOtScUeOit6ks/F+TBLrOC1uze3IOu+u9gbDAjmosNWLKbBzx/Yuv2w== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" @@ -9774,10 +10643,10 @@ rc-picker@~2.5.17: rc-util "^5.4.0" shallowequal "^1.1.0" -rc-progress@~3.2.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.2.4.tgz#4036acdae2566438545bc4df2203248babaf7549" - integrity sha512-M9WWutRaoVkPUPIrTpRIDpX0SPSrVHzxHdCRCbeoBFrd9UFWTYNWRlHsruJM5FH1AZI+BwB4wOJUNNylg/uFSw== +rc-progress@~3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.3.3.tgz#eb9bffbacab1534f2542f9f6861ce772254362b1" + integrity sha512-MDVNVHzGanYtRy2KKraEaWeZLri2ZHWIRyaE1a9MQ2MuJ09m+Wxj5cfcaoaR6z5iRpHpA59YeUxAlpML8N4PJw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" @@ -9802,10 +10671,20 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0: rc-util "^5.15.0" resize-observer-polyfill "^1.5.1" -rc-select@~14.0.0-alpha.15, rc-select@~14.0.0-alpha.23, rc-select@~14.0.0-alpha.8: - version "14.0.0-alpha.26" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.0.0-alpha.26.tgz#51ae0aee882d3a729648f86fe99fe7d0006d9bdb" - integrity sha512-5+vpP+qkYg9TiQb06BIVMTdnKwjXW/4ud8NWaCtnLGsyeqN6Hg7HGTUwlTTOyNOU5zMjbKHrAIvMk8NipGKqtA== +rc-segmented@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.1.0.tgz#0e0afe646c1a0e44a0e18785f518c42633ec8efc" + integrity sha512-hUlonro+pYoZcwrH6Vm56B2ftLfQh046hrwif/VwLIw1j3zGt52p5mREBwmeVzXnSwgnagpOpfafspzs1asjGw== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-motion "^2.4.4" + rc-util "^5.17.0" + +rc-select@~14.1.0, rc-select@~14.1.13: + version "14.1.13" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.1.13.tgz#7eb53d00be82fb8e5050de3094e72edcf27ce6f6" + integrity sha512-WMEsC3gTwA1dbzWOdVIXDmWyidYNLq68AwvvUlRROw790uGUly0/vmqDozXrIr0QvN/A3CEULx12o+WtLCAefg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -9815,15 +10694,14 @@ rc-select@~14.0.0-alpha.15, rc-select@~14.0.0-alpha.23, rc-select@~14.0.0-alpha. rc-util "^5.16.1" rc-virtual-list "^3.2.0" -rc-slider@~9.7.4: - version "9.7.5" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-9.7.5.tgz#193141c68e99b1dc3b746daeb6bf852946f5b7f4" - integrity sha512-LV/MWcXFjco1epPbdw1JlLXlTgmWpB9/Y/P2yinf8Pg3wElHxA9uajN21lJiWtZjf5SCUekfSP6QMJfDo4t1hg== +rc-slider@~10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.0.1.tgz#7058c68ff1e1aa4e7c3536e5e10128bdbccb87f9" + integrity sha512-igTKF3zBet7oS/3yNiIlmU8KnZ45npmrmHlUUio8PNbIhzMcsh+oE/r2UD42Y6YD2D/s+kzCQkzQrPD6RY435Q== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" - rc-tooltip "^5.0.1" - rc-util "^5.16.1" + rc-util "^5.18.1" shallowequal "^1.1.0" rc-steps@~4.1.0: @@ -9844,33 +10722,34 @@ rc-switch@~3.2.0: classnames "^2.2.1" rc-util "^5.0.1" -rc-table@~7.22.2: - version "7.22.2" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.22.2.tgz#218f3f53bc91660560a344c8290a91a841a60b0a" - integrity sha512-Ng2gNkGi6ybl6dzneRn2H4Gp8XhIbRa5rXQ7ZhZcgWVmfVMok70UHGPXcf68tXW6O0/qckTf/eOVsoviSvK4sw== +rc-table@~7.26.0: + version "7.26.0" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.26.0.tgz#9d517e7fa512e7571fdcc453eb1bf19edfac6fbc" + integrity sha512-0cD8e6S+DTGAt5nBZQIPFYEaIukn17sfa5uFL98faHlH/whZzD8ii3dbFL4wmUDEL4BLybhYop+QUfZJ4CPvNQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" rc-resize-observer "^1.1.0" - rc-util "^5.14.0" + rc-util "^5.22.5" shallowequal "^1.1.0" -rc-tabs@~11.10.0: - version "11.10.5" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.10.5.tgz#53bbb642d04b307f8ce86e318ab99d519507b29b" - integrity sha512-DDuUdV6b9zGRYLtjI5hyejWLKoz1QiLWNgMeBzc3aMeQylZFhTYnFGdDc6HRqj5IYearNTsFPVSA+6VIT8g5cg== +rc-tabs@~12.1.0-alpha.1: + version "12.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-12.1.0-alpha.1.tgz#00f45b9dffa9bc6aff8ce2aff4a1a0764caada54" + integrity sha512-M+B88WEnGSuE+mR54fpgPbZLAakzxa/H6FmEetLBl5WG4I3AcwSk9amuIPC/tu0KXBl+H6Bg5ZwrrEUOBUvgzg== dependencies: "@babel/runtime" "^7.11.2" classnames "2.x" - rc-dropdown "^3.2.0" - rc-menu "^9.0.0" + rc-dropdown "~4.0.0" + rc-menu "~9.6.0" + rc-motion "^2.6.2" rc-resize-observer "^1.0.0" rc-util "^5.5.0" -rc-textarea@^0.3.0, rc-textarea@~0.3.0: - version "0.3.7" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-0.3.7.tgz#987142891efdedb774883c07e2f51b318fde5a11" - integrity sha512-yCdZ6binKmAQB13hc/oehh0E/QRwoPP1pjF21aHBxlgXO3RzPF6dUu4LG2R4FZ1zx/fQd2L1faktulrXOM/2rw== +rc-textarea@^0.4.0, rc-textarea@~0.4.3: + version "0.4.5" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-0.4.5.tgz#8e1167f02676301bd0817c30b326fbfdd0de6f96" + integrity sha512-WHeJRgUlloNyVgTsItMrIXwMhU6P3NmrUDkxX+JRwEpJjECsKtZNlNcXe9pHNLCaYQ3Z1cVCfsClhgDDgJ2kFQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" @@ -9878,35 +10757,36 @@ rc-textarea@^0.3.0, rc-textarea@~0.3.0: rc-util "^5.7.0" shallowequal "^1.1.0" -rc-tooltip@^5.0.1, rc-tooltip@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-5.1.1.tgz#94178ed162d0252bc4993b725f5dc2ac0fccf154" - integrity sha512-alt8eGMJulio6+4/uDm7nvV+rJq9bsfxFDCI0ljPdbuoygUscbsMYb6EQgwib/uqsXQUvzk+S7A59uYHmEgmDA== +rc-tooltip@~5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-5.2.2.tgz#e5cafa8ecebf78108936a0bcb93c150fa81ac93b" + integrity sha512-jtQzU/18S6EI3lhSGoDYhPqNpWajMtS5VV/ld1LwyfrDByQpYmw/LW6U7oFXXLukjfDHQ7Ju705A82PRNFWYhg== dependencies: "@babel/runtime" "^7.11.2" + classnames "^2.3.1" rc-trigger "^5.0.0" -rc-tree-select@~5.1.1: - version "5.1.3" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.1.3.tgz#492f76adec6b4f69beedb0ad59595cd79f671d62" - integrity sha512-nfOhsUM3SiEo/Kt+LhinC3LI3VJGCU4+TCRBAmdt0frV3Ix9GAoC3aIaHIUs2tkDf3X0qOmf6qYcyUn/RaIuoQ== +rc-tree-select@~5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.5.0.tgz#96d935ec994a0f6b0fecc4c18f22f472fe8530a3" + integrity sha512-XS0Jvw4OjFz/Xvb2byEkBWv55JFKFz0HVvTBa/cPOHJaQh/3EaYwymEMnCCvGEzS1+5CfDVwMtA8j/v4rt1DHw== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-select "~14.0.0-alpha.8" - rc-tree "~5.4.3" + rc-select "~14.1.0" + rc-tree "~5.7.0" rc-util "^5.16.1" -rc-tree@~5.4.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.4.3.tgz#8674644964e17e5ab9b111c5aa18676f673e7bd0" - integrity sha512-WAHV8FkBerulj9J/+61+Qn0TD/Zo37PrDG8/45WomzGTYavxFMur9YguKjQj/J+NxjVJzrJL3lvdSZsumfdbiA== +rc-tree@~5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.7.0.tgz#d0e316eeeac2ba4a1c36b2b2201d84884f1c76a1" + integrity sha512-F+Ewkv/UcutshnVBMISP+lPdHDlcsL+YH/MQDVWbk+QdkfID7vXiwrHMEZn31+2Rbbm21z/HPceGS8PXGMmnQg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.0.1" rc-util "^5.16.1" - rc-virtual-list "^3.4.1" + rc-virtual-list "^3.4.8" rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10: version "5.2.10" @@ -9919,6 +10799,17 @@ rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10: rc-motion "^2.0.0" rc-util "^5.5.0" +rc-trigger@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.3.1.tgz#acafadf3eaf384e7f466c303bfa0f34c8137d7b8" + integrity sha512-5gaFbDkYSefZ14j2AdzucXzlWgU2ri5uEjkHvsf1ynRhdJbKxNOnw4PBZ9+FVULNGFiDzzlVF8RJnR9P/xrnKQ== + dependencies: + "@babel/runtime" "^7.18.3" + classnames "^2.2.6" + rc-align "^4.0.0" + rc-motion "^2.0.0" + rc-util "^5.19.2" + rc-upload@~4.3.0: version "4.3.3" resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.3.3.tgz#e237aa525e5313fa16f4d04d27f53c2f0e157bb8" @@ -9928,7 +10819,7 @@ rc-upload@~4.3.0: classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.0.7, rc-util@^5.12.0, rc-util@^5.14.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4, rc-util@^5.9.8: +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.0.7, rc-util@^5.12.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4: version "5.18.1" resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.18.1.tgz#80bd1450b5254655d2fbea63e3d34f6871e9be79" integrity sha512-24xaSrMZUEKh1+suDOtJWfPe9E6YrwryViZcoPO0miJTKzP4qhUlV5AAlKQ82AJilz/AOHfi3l6HoX8qa1ye8w== @@ -9937,7 +10828,16 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.0.7, rc-util@^5.12.0, rc-util@^5.14.0 react-is "^16.12.0" shallowequal "^1.1.0" -rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.1: +rc-util@^5.17.0, rc-util@^5.19.2, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.23.0: + version "5.24.4" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.24.4.tgz#a4126f01358c86f17c1bf380a1d83d6c9155ae65" + integrity sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^16.12.0" + shallowequal "^1.1.0" + +rc-virtual-list@^3.2.0: version "3.4.2" resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.2.tgz#1078327aa7230b5e456d679ed2ce99f3c036ebd1" integrity sha512-OyVrrPvvFcHvV0ssz5EDZ+7Rf5qLat/+mmujjchNw5FfbJWNDwkpQ99EcVE6+FtNRmX9wFa1LGNpZLUTvp/4GQ== @@ -9946,6 +10846,15 @@ rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.1: rc-resize-observer "^1.0.0" rc-util "^5.0.7" +rc-virtual-list@^3.4.8: + version "3.4.8" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.8.tgz#c24c10c6940546b7e2a5e9809402c6716adfd26c" + integrity sha512-qSN+Rv4i/E7RCTvTMr1uZo7f3crJJg/5DekoCagydo9zsXrxj07zsFSxqizqW+ldGA16lwa8So/bIbV9Ofjddg== + dependencies: + classnames "^2.2.6" + rc-resize-observer "^1.0.0" + rc-util "^5.15.0" + rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -10274,10 +11183,10 @@ regexpu-core@^4.2.0, regexpu-core@^4.7.1: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.2.0" -regexpu-core@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" - integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== +regexpu-core@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" + integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== dependencies: regenerate "^1.4.2" regenerate-unicode-properties "^10.0.1" @@ -10324,29 +11233,19 @@ regjsparser@^0.8.2: dependencies: jsesc "~0.5.0" -rehype-parse@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.2.tgz#aeb3fdd68085f9f796f1d3137ae2b85a98406964" - integrity sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug== +rehype-parse@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-7.0.1.tgz#58900f6702b56767814afc2a9efa2d42b1c90c57" + integrity sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw== dependencies: - hast-util-from-parse5 "^5.0.0" - parse5 "^5.0.0" - xtend "^4.0.0" + hast-util-from-parse5 "^6.0.0" + parse5 "^6.0.0" relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= -remark-admonitions@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/remark-admonitions/-/remark-admonitions-1.2.1.tgz#87caa1a442aa7b4c0cafa04798ed58a342307870" - integrity sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow== - dependencies: - rehype-parse "^6.0.2" - unified "^8.4.2" - unist-util-visit "^2.0.1" - remark-emoji@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-2.2.0.tgz#1c702090a1525da5b80e15a8f963ef2c8236cac7" @@ -10371,10 +11270,14 @@ remark-gfm@^3.0.1: micromark-extension-gfm "^2.0.0" unified "^10.0.0" -remark-mdx-filter-imports@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/remark-mdx-filter-imports/-/remark-mdx-filter-imports-0.1.2.tgz#fa521585a14822a8e177f1b1d353b695e278cc96" - integrity sha512-8MAgusHtNjbNXKkBc/ckjh2U25N/D0aCfN2KDdnBQaWknLupU60pQzTEBpsZLZN67MsC6rVt6ZQgEzthOmQAAw== +remark-mdx-filter-imports@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/remark-mdx-filter-imports/-/remark-mdx-filter-imports-0.1.3.tgz#52aeeb235a1ac43d22d40359ae7df2632596b00c" + integrity sha512-tReGHtQ9sFMqrp95pwsIaEB6qVtGCN5urnVnkVauYHk4OLsYL5EcCVw0CnqvvjPCS4IX1I6ikK8LKWraTj+JjQ== + dependencies: + "@babel/generator" "^7.12.5" + "@babel/parser" "^7.12.7" + unist-util-visit "^2.0.3" remark-mdx@1.6.22: version "1.6.22" @@ -10457,7 +11360,7 @@ renderkid@^3.0.0: lodash "^4.17.21" strip-ansi "^6.0.1" -repeat-string@^1.5.4: +repeat-string@^1.0.0, repeat-string@^1.5.4: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -10487,7 +11390,7 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= -resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1: +resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== @@ -10526,6 +11429,15 @@ resolve@^1.12.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.19.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -10819,6 +11731,13 @@ shallowequal@^1.1.0: resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -10826,6 +11745,11 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" @@ -10854,6 +11778,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +signal-exit@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -10907,19 +11836,16 @@ sort-css-media-queries@2.0.4: resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.0.4.tgz#b2badfa519cb4a938acbc6d3aaa913d4949dc908" integrity sha512-PAIsEK/XupCQwitjv7XxoMvYhT7EAfyzI3hsy/MyDgTvc+Ft55ctdkctJLOy6cQejaIC+zjpUL4djFVm2ivOOw== +sort-object-keys@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" + integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== + source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-support@~0.5.19: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@~0.5.20: version "0.5.20" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" @@ -10938,11 +11864,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@~0.7.2: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - sourcemap-codec@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" @@ -11021,6 +11942,15 @@ string-natural-compare@^3.0.0: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -11163,6 +12093,11 @@ stylis@4.0.13: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== +stylis@4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7" + integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA== + stylis@^4.0.10: version "4.0.10" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240" @@ -11212,6 +12147,21 @@ svgo@^2.5.0, svgo@^2.7.0: picocolors "^1.0.0" stable "^0.1.8" +synp@^1.9.10: + version "1.9.10" + resolved "https://registry.yarnpkg.com/synp/-/synp-1.9.10.tgz#53163321a600418c9b06af0db499939ffce12907" + integrity sha512-G9Z/TXTaBG1xNslUf3dHFidz/8tvvRaR560WWyOwyI7XrGGEGBTEIIg4hdRh1qFtz8mPYynAUYwWXUg/Zh0Pzw== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + bash-glob "^2.0.0" + colors "1.4.0" + commander "^7.2.0" + eol "^0.9.1" + lodash "4.17.21" + nmtree "^1.0.6" + semver "^7.3.5" + sort-object-keys "^1.1.3" + tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -11234,43 +12184,35 @@ terser-webpack-plugin@^5.1.3: source-map "^0.6.1" terser "^5.7.0" -terser-webpack-plugin@^5.3.1: - version "5.3.3" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90" - integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ== +terser-webpack-plugin@^5.3.3: + version "5.3.6" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" + integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== dependencies: - "@jridgewell/trace-mapping" "^0.3.7" + "@jridgewell/trace-mapping" "^0.3.14" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.0" - terser "^5.7.2" + terser "^5.14.1" -terser@^5.10.0: - version "5.14.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.1.tgz#7c95eec36436cb11cf1902cc79ac564741d19eca" - integrity sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ== +terser@^5.10.0, terser@^5.7.0, terser@^5.7.2: + version "5.16.5" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.5.tgz#1c285ca0655f467f92af1bbab46ab72d1cb08e5a" + integrity sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" commander "^2.20.0" source-map-support "~0.5.20" -terser@^5.7.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784" - integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg== +terser@^5.14.1: + version "5.15.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.0.tgz#e16967894eeba6e1091509ec83f0c60e179f2425" + integrity sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA== dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - -terser@^5.7.2: - version "5.9.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.9.0.tgz#47d6e629a522963240f2b55fcaa3c99083d2c351" - integrity sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" source-map-support "~0.5.20" text-table@^0.2.0: @@ -11315,6 +12257,14 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +to-vfile@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/to-vfile/-/to-vfile-6.1.0.tgz#5f7a3f65813c2c4e34ee1f7643a5646344627699" + integrity sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw== + dependencies: + is-buffer "^2.0.0" + vfile "^4.0.0" + toggle-selection@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" @@ -11360,12 +12310,12 @@ ts-essentials@^2.0.3: resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-2.0.12.tgz#c9303f3d74f75fa7528c3d49b80e089ab09d8745" integrity sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w== -ts-node@^10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" - integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== dependencies: - "@cspotcode/source-map-support" "0.7.0" + "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" @@ -11376,6 +12326,7 @@ ts-node@^10.4.0: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" yn "3.1.1" tslib@^2.0.3, tslib@^2.3.0: @@ -11413,15 +12364,15 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" - integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== ua-parser-js@^0.7.18: - version "0.7.28" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" - integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== + version "0.7.34" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.34.tgz#afb439e2e3e394bdc90080acb661a39c685b67d7" + integrity sha512-cJMeh/eOILyGu0ejgTKB95yKT3zOenSe9UGE3vj6WfiOwgGYnmATUsnDixMFvdU+rNMvWih83hrUP8VwhF9yXQ== unbox-primitive@^1.0.1: version "1.0.1" @@ -11519,18 +12470,7 @@ unified@^10.0.0: trough "^2.0.0" vfile "^5.0.0" -unified@^8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/unified/-/unified-8.4.2.tgz#13ad58b4a437faa2751a4a4c6a16f680c500fff1" - integrity sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA== - dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" - -unified@^9.2.1: +unified@^9.0.0, unified@^9.2.1, unified@^9.2.2: version "9.2.2" resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975" integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== @@ -11561,6 +12501,13 @@ unist-builder@^3.0.0: dependencies: "@types/unist" "^2.0.0" +unist-util-find-after@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz#5c65fcebf64d4f8f496db46fa8fd0fbf354b43e6" + integrity sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ== + dependencies: + unist-util-is "^4.0.0" + unist-util-generated@^1.0.0: version "1.1.6" resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" @@ -11571,7 +12518,7 @@ unist-util-generated@^2.0.0: resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.0.tgz#86fafb77eb6ce9bfa6b663c3f5ad4f8e56a60113" integrity sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw== -unist-util-is@^4.0.0: +unist-util-is@^4.0.0, unist-util-is@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== @@ -11771,6 +12718,11 @@ use-latest@^1.0.0: dependencies: use-isomorphic-layout-effect "^1.0.0" +use-sync-external-store@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -11818,6 +12770,11 @@ uvu@^0.5.0: kleur "^4.0.3" sade "^1.7.3" +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + validate-peer-dependencies@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/validate-peer-dependencies/-/validate-peer-dependencies-2.1.0.tgz#1ad8218b1b168aeb500165f9de2a3f53269ece56" @@ -11900,7 +12857,7 @@ warning@^4.0.3: dependencies: loose-envify "^1.0.0" -watchpack@^2.3.1: +watchpack@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== @@ -11915,7 +12872,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -web-namespaces@^1.0.0, web-namespaces@^1.1.2: +web-namespaces@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== @@ -11951,10 +12908,10 @@ webpack-dev-middleware@^5.3.1: range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.9.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.9.2.tgz#c188db28c7bff12f87deda2a5595679ebbc3c9bc" - integrity sha512-H95Ns95dP24ZsEzO6G9iT+PNw4Q7ltll1GfJHV4fKphuHWgKFzGHWi4alTlTnpk1SPPk41X+l2RB7rLfIhnB9Q== +webpack-dev-server@^4.9.3: + version "4.11.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz#290ee594765cd8260adfe83b2d18115ea04484e7" + integrity sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" @@ -11968,7 +12925,7 @@ webpack-dev-server@^4.9.0: chokidar "^3.5.3" colorette "^2.0.10" compression "^1.7.4" - connect-history-api-fallback "^1.6.0" + connect-history-api-fallback "^2.0.0" default-gateway "^6.0.3" express "^4.17.3" graceful-fs "^4.2.6" @@ -12004,21 +12961,21 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.72.1: - version "5.73.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38" - integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA== +webpack@^5.73.0: + version "5.74.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980" + integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/wasm-edit" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.4.1" + acorn "^8.7.1" acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.9.3" + enhanced-resolve "^5.10.0" es-module-lexer "^0.9.0" eslint-scope "5.1.1" events "^3.2.0" @@ -12031,7 +12988,7 @@ webpack@^5.72.1: schema-utils "^3.1.0" tapable "^2.1.1" terser-webpack-plugin "^5.1.3" - watchpack "^2.3.1" + watchpack "^2.4.0" webpack-sources "^3.2.3" webpackbar@^5.0.2: @@ -12094,7 +13051,7 @@ which-typed-array@^1.1.2: has-tostringtag "^1.0.0" is-typed-array "^1.1.6" -which@^1.3.1: +which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -12108,6 +13065,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" @@ -12196,6 +13160,11 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" @@ -12231,6 +13200,30 @@ yargs@^15.1.0: y18n "^4.0.0" yargs-parser "^18.1.2" +yarn-audit-fix@^9.3.9: + version "9.3.9" + resolved "https://registry.yarnpkg.com/yarn-audit-fix/-/yarn-audit-fix-9.3.9.tgz#24b6e16af8fc7987d9ceb0f8d8612469261864b5" + integrity sha512-RVmbSVm0Dfr4qSUtqc8EM0wvOAKRL5wtw2i/nUfaIqg8m4seP/umyUcpEvkU8gBmizY7QdDeVWIVndy3qAWBhw== + dependencies: + "@types/find-cache-dir" "^3.2.1" + "@types/fs-extra" "^9.0.13" + "@types/lodash-es" "^4.17.6" + "@types/semver" "^7.3.12" + "@types/yarnpkg__lockfile" "^1.1.5" + "@yarnpkg/lockfile" "^1.1.0" + chalk "^5.0.1" + commander "^10.0.0" + find-cache-dir "^4.0.0" + find-up "^6.3.0" + fs-extra "^10.1.0" + globby "^13.1.2" + js-yaml "^4.1.0" + lodash-es "^4.17.21" + pkg-dir "^7.0.0" + semver "^7.3.7" + synp "^1.9.10" + tslib "^2.4.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" @@ -12241,6 +13234,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" diff --git a/xplat/Flipper/ConnectionContextStore.cpp b/xplat/Flipper/ConnectionContextStore.cpp index 806261281..350a9be89 100644 --- a/xplat/Flipper/ConnectionContextStore.cpp +++ b/xplat/Flipper/ConnectionContextStore.cpp @@ -48,7 +48,8 @@ bool ConnectionContextStore::hasRequiredFiles() { std::string config = loadStringFromFile(absoluteFilePath(CONNECTION_CONFIG_FILE)); - if (caCert == "" || clientCert == "" || privateKey == "" || config == "") { + if (caCert.empty() || clientCert.empty() || privateKey.empty() || + config.empty()) { return false; } return true; @@ -56,14 +57,14 @@ bool ConnectionContextStore::hasRequiredFiles() { std::string ConnectionContextStore::getCertificateSigningRequest() { // Use in-memory CSR if already loaded - if (csr != "") { - return csr; + if (!csr_.empty()) { + return csr_; } // Attempt to load existing CSR from previous run of the app - csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME)); - if (csr != "") { - return csr; + csr_ = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME)); + if (!csr_.empty()) { + return csr_; } // Clean all state and generate a new one @@ -75,9 +76,9 @@ std::string ConnectionContextStore::getCertificateSigningRequest() { if (!success) { throw new std::runtime_error("Failed to generate CSR"); } - csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME)); + csr_ = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME)); - return csr; + return csr_; } std::string ConnectionContextStore::getDeviceId() { @@ -124,7 +125,8 @@ void ConnectionContextStore::storeConnectionConfig(folly::dynamic& config) { writeStringToFile(json, absoluteFilePath(CONNECTION_CONFIG_FILE)); } -std::string ConnectionContextStore::absoluteFilePath(const char* filename) { +std::string ConnectionContextStore::absoluteFilePath( + const char* filename) const { #ifndef WIN32 return std::string(deviceData_.privateAppDirectory + "/sonar/" + filename); #else @@ -159,7 +161,7 @@ std::string ConnectionContextStore::getPath(StoreItem storeItem) { bool ConnectionContextStore::resetState() { // Clear in-memory state - csr = ""; + csr_ = ""; // Delete state from disk std::string dirPath = absoluteFilePath(""); @@ -208,6 +210,18 @@ std::pair ConnectionContextStore::getCertificate() { return std::make_pair(certificate_path, std::string(CERTIFICATE_PASSWORD)); } +bool ConnectionContextStore::hasCertificateSigningRequest() const { + std::string csr = loadStringFromFile(absoluteFilePath(CSR_FILE_NAME)); + return !csr.empty(); +} + +bool ConnectionContextStore::hasClientCertificate() const { + std::string clientCertificate = + loadStringFromFile(absoluteFilePath(CLIENT_CERT_FILE_NAME)); + + return !clientCertificate.empty(); +} + std::string loadStringFromFile(std::string fileName) { if (!fileExists(fileName)) { return ""; diff --git a/xplat/Flipper/ConnectionContextStore.h b/xplat/Flipper/ConnectionContextStore.h index f5fdcc2c8..a10363da3 100644 --- a/xplat/Flipper/ConnectionContextStore.h +++ b/xplat/Flipper/ConnectionContextStore.h @@ -38,6 +38,16 @@ class ConnectionContextStore { */ folly::Optional getLastKnownMedium(); void storeConnectionConfig(folly::dynamic& config); + /** + * Reset state just removes all certificate exchange related files stored on + * the client. These are: + * - Certificate Sign Request (CSR) + * - CA Certificate + * - Server Certificate + * - Client Certificate + * - Client Key + * - Configuration file (includes device identifier) + */ bool resetState(); /** Convert and save to disk the existing certificate to PKCS #12 format. @@ -47,11 +57,18 @@ class ConnectionContextStore { */ std::pair getCertificate(); + /** Is there a CSR present. + */ + bool hasCertificateSigningRequest() const; + /** Is there a client certificate present. + */ + bool hasClientCertificate() const; + private: DeviceData deviceData_; - std::string csr = ""; + std::string csr_ = ""; - std::string absoluteFilePath(const char* filename); + std::string absoluteFilePath(const char* filename) const; }; } // namespace flipper diff --git a/xplat/Flipper/FlipperClient.cpp b/xplat/Flipper/FlipperClient.cpp index 5f1e48ea7..09137fd83 100644 --- a/xplat/Flipper/FlipperClient.cpp +++ b/xplat/Flipper/FlipperClient.cpp @@ -50,24 +50,26 @@ FlipperClient* FlipperClient::instance() { void FlipperClient::setStateListener( std::shared_ptr stateListener) { performAndReportError([this, &stateListener]() { - log("Setting state listener"); + log("[client] Set state listener"); flipperState_->setUpdateListener(stateListener); }); } void FlipperClient::addPlugin(std::shared_ptr plugin) { performAndReportError([this, plugin]() { - log("FlipperClient::addPlugin " + plugin->identifier()); + log("[client] Add plugin " + plugin->identifier()); auto step = flipperState_->start("Add plugin " + plugin->identifier()); + auto needs_refresh = false; std::lock_guard lock(mutex_); if (plugins_.find(plugin->identifier()) != plugins_.end()) { - throw std::out_of_range( - "plugin " + plugin->identifier() + " already added."); + log("[client] Plugin " + plugin->identifier() + " already added"); + } else { + plugins_[plugin->identifier()] = plugin; + needs_refresh = true; } - plugins_[plugin->identifier()] = plugin; step->complete(); - if (connected_) { + if (connected_ && needs_refresh) { refreshPlugins(); } }); @@ -76,7 +78,6 @@ void FlipperClient::addPlugin(std::shared_ptr plugin) { void FlipperClient::setCertificateProvider( const std::shared_ptr provider) { socket_->setCertificateProvider(provider); - log("cpp setCertificateProvider called"); } std::shared_ptr @@ -86,11 +87,12 @@ FlipperClient::getCertificateProvider() { void FlipperClient::removePlugin(std::shared_ptr plugin) { performAndReportError([this, plugin]() { - log("FlipperClient::removePlugin " + plugin->identifier()); + log("[client] Remove plugin: " + plugin->identifier()); std::lock_guard lock(mutex_); if (plugins_.find(plugin->identifier()) == plugins_.end()) { - throw std::out_of_range("plugin " + plugin->identifier() + " not added."); + log("[client] Plugin " + plugin->identifier() + " not added"); + return; } disconnect(plugin); plugins_.erase(plugin->identifier()); @@ -140,7 +142,7 @@ void FlipperClient::refreshPlugins() { void FlipperClient::onConnected() { performAndReportError([this]() { - log("FlipperClient::onConnected"); + log("[client] connected"); std::lock_guard lock(mutex_); connected_ = true; @@ -149,7 +151,7 @@ void FlipperClient::onConnected() { void FlipperClient::onDisconnected() { performAndReportError([this]() { - log("FlipperClient::onDisconnected"); + log("[client] disconnected"); auto step = flipperState_->start("Trigger onDisconnected callbacks"); std::lock_guard lock(mutex_); connected_ = false; @@ -160,6 +162,13 @@ void FlipperClient::onDisconnected() { }); } +bool FlipperClient::isConnected() { + if (socket_ != nullptr) { + return socket_->isConnected(); + } + return false; +} + void FlipperClient::onMessageReceived( const dynamic& message, std::unique_ptr uniqueResponder) { @@ -270,7 +279,7 @@ void FlipperClient::onMessageReceived( "stacktrace", callstack())("name", e.what())); } } catch (...) { - log("Unknown error suppressed in FlipperClient"); + log("[client] Unknown error suppressed"); if (responder) { responder->error(dynamic::object( "message", @@ -323,7 +332,7 @@ void FlipperClient::performAndReportError(const std::function& func) { } catch (...) { // Generic catch block for the exception of type not belonging to // std::exception - log("Unknown error suppressed in FlipperClient"); + log("[client] Unknown error suppressed"); } #endif } @@ -337,7 +346,7 @@ void FlipperClient::handleError(std::exception& e) { "name", e.what())); socket_->sendMessage(message); } else { - log("Error: " + std::string(e.what())); + log("[client] Error: " + std::string(e.what())); } } diff --git a/xplat/Flipper/FlipperClient.h b/xplat/Flipper/FlipperClient.h index e535be99a..244ef40a1 100644 --- a/xplat/Flipper/FlipperClient.h +++ b/xplat/Flipper/FlipperClient.h @@ -74,6 +74,8 @@ class FlipperClient : public FlipperConnectionManager::Callbacks { void onDisconnected() override; + bool isConnected(); + void onMessageReceived( const folly::dynamic& message, std::unique_ptr) override; diff --git a/xplat/Flipper/FlipperConnectionManager.h b/xplat/Flipper/FlipperConnectionManager.h index f83b971eb..c74736d13 100644 --- a/xplat/Flipper/FlipperConnectionManager.h +++ b/xplat/Flipper/FlipperConnectionManager.h @@ -47,7 +47,7 @@ class FlipperConnectionManager { True if there's an open connection. This method may block if the connection is busy. */ - virtual bool isOpen() const = 0; + virtual bool isConnected() const = 0; /** Send message to the ws server. diff --git a/xplat/Flipper/FlipperConnectionManagerImpl.cpp b/xplat/Flipper/FlipperConnectionManagerImpl.cpp index 0fde28c73..bd5e051d0 100644 --- a/xplat/Flipper/FlipperConnectionManagerImpl.cpp +++ b/xplat/Flipper/FlipperConnectionManagerImpl.cpp @@ -20,12 +20,12 @@ #define WRONG_THREAD_EXIT_MSG \ "ERROR: Aborting flipper initialization because it's not running in the flipper thread." -static constexpr int reconnectIntervalSeconds = 2; +static constexpr int RECONNECT_INTERVAL_SECONDS = 3; // Not a public-facing version number. // Used for compatibility checking with desktop flipper. // To be bumped for every core platform interface change. -static constexpr int sdkVersion = 4; +static constexpr int SDK_VERSION = 4; using namespace folly; @@ -53,29 +53,7 @@ class ConnectionEvents { if (impl == nullptr) { return; } - switch (event) { - case SocketEvent::OPEN: - impl->isOpen_ = true; - if (impl->connectionIsTrusted_) { - impl->callbacks_->onConnected(); - } - break; - case SocketEvent::SSL_ERROR: - // SSL errors are not handled as a connection event - // on this handler. - break; - case SocketEvent::CLOSE: - case SocketEvent::ERROR: - if (!impl->isOpen_) - return; - impl->isOpen_ = false; - if (impl->connectionIsTrusted_) { - impl->connectionIsTrusted_ = false; - impl->callbacks_->onDisconnected(); - } - impl->reconnect(); - break; - } + impl->handleSocketEvent(event); } } @@ -88,14 +66,14 @@ FlipperConnectionManagerImpl::FlipperConnectionManagerImpl( std::shared_ptr state, std::shared_ptr contextStore) : deviceData_(config.deviceData), - flipperState_(state), + state_(state), insecurePort(config.insecurePort), securePort(config.securePort), altInsecurePort(config.altInsecurePort), altSecurePort(config.altSecurePort), - flipperScheduler_(config.callbackWorker), + scheduler_(config.callbackWorker), connectionScheduler_(config.connectionWorker), - contextStore_(contextStore), + store_(contextStore), implWrapper_(std::make_shared(this)) { CHECK_THROW(config.callbackWorker, std::invalid_argument); CHECK_THROW(config.connectionWorker, std::invalid_argument); @@ -107,93 +85,115 @@ FlipperConnectionManagerImpl::~FlipperConnectionManagerImpl() { void FlipperConnectionManagerImpl::setCertificateProvider( const std::shared_ptr provider) { - certProvider_ = provider; + DEBUG_LOG("[conn] Set certificate provider"); + certificateProvider_ = provider; }; std::shared_ptr FlipperConnectionManagerImpl::getCertificateProvider() { - return certProvider_; -}; + return certificateProvider_; +} + +void FlipperConnectionManagerImpl::handleSocketEvent(SocketEvent event) { + // Ensure that the event is handled on the correct thread i.e. scheduler. + scheduler_->schedule([this, event]() { + switch (event) { + case SocketEvent::OPEN: + DEBUG_LOG("[conn] Socket event: open"); + isConnected_ = true; + if (isConnectionTrusted_) { + failedConnectionAttempts_ = 0; + callbacks_->onConnected(); + } else { + requestSignedCertificate(); + } + break; + case SocketEvent::SSL_ERROR: + DEBUG_LOG("[conn] Socket event: SSL error"); + failedConnectionAttempts_++; + reconnect(); + break; + case SocketEvent::CLOSE: + case SocketEvent::ERROR: + DEBUG_LOG("[conn] Socket event: close or error"); + if (!isConnected_) { + reconnect(); + return; + } + + failedConnectionAttempts_++; + isConnected_ = false; + + if (isConnectionTrusted_) { + isConnectionTrusted_ = false; + callbacks_->onDisconnected(); + } + + reconnect(); + break; + } + }); +} void FlipperConnectionManagerImpl::start() { + DEBUG_LOG("[conn] Start"); + if (!FlipperSocketProvider::hasProvider()) { - log("No socket provider has been set, unable to start"); + log("[conn] No socket provider has been set, unable to start"); return; } - if (isStarted_) { - log("Already started"); + if (started_) { + log("[conn] Already started"); return; } - isStarted_ = true; + started_ = true; - auto step = flipperState_->start("Start connection thread"); + auto step = state_->start("Start connection thread"); - flipperScheduler_->schedule([this, step]() { + scheduler_->schedule([this, step]() { step->complete(); startSync(); }); } void FlipperConnectionManagerImpl::startSync() { - if (!isStarted_) { - log("Not started"); + DEBUG_LOG("[conn] Start sync"); + + if (!started_) { + log("[conn] Not started"); return; } if (!isRunningInOwnThread()) { log(WRONG_THREAD_EXIT_MSG); return; } - if (isOpen()) { - log("Already connected"); + if (isConnected()) { + log("[conn] Already connected"); return; } + + socket_ = nullptr; + bool isClientSetupStep = isCertificateExchangeNeeded(); - auto step = flipperState_->start( - isClientSetupStep ? "Establish pre-setup connection" + auto step = state_->start( + isClientSetupStep ? "Establish certificate exchange connection" : "Establish main connection"); - try { - if (isClientSetupStep) { - bool success = connectAndExchangeCertificate(); - if (!success) { - reconnect(); - return; - } - } else { - if (!connectSecurely()) { - // The expected code path when flipper desktop is not running. - // Don't count as a failed attempt, or it would invalidate the - // connection files for no reason. On iOS devices, we can always connect - // to the local port forwarding server even when it can't connect to - // flipper. In that case we get a Network error instead of a Port not - // open error, so we treat them the same. - step->fail( - "No route to flipper found. Is flipper desktop running? Retrying..."); - reconnect(); - } - } - step->complete(); - } catch (const SSLException& e) { - auto message = std::string(e.what()) + - "\nMake sure the date and time of your device is up to date."; - log(message); - step->fail(message); - failedConnectionAttempts_++; - reconnect(); - } catch (const std::exception& e) { - log(e.what()); - step->fail(e.what()); - failedConnectionAttempts_++; - reconnect(); + if (isClientSetupStep) { + connectAndExchangeCertificate(); + } else { + connectSecurely(); } + step->complete(); } -bool FlipperConnectionManagerImpl::connectAndExchangeCertificate() { +void FlipperConnectionManagerImpl::connectAndExchangeCertificate() { + DEBUG_LOG("[conn] Connect and exchange certificate"); auto port = insecurePort; auto endpoint = FlipperConnectionEndpoint(deviceData_.host, port, false); - int medium = certProvider_ != nullptr - ? certProvider_->getCertificateExchangeMedium() + int medium = certificateProvider_ != nullptr + ? certificateProvider_->getCertificateExchangeMedium() : FlipperCertificateExchangeMedium::FS_ACCESS; auto payload = std::make_unique(); @@ -201,44 +201,32 @@ bool FlipperConnectionManagerImpl::connectAndExchangeCertificate() { payload->device = deviceData_.device; payload->device_id = "unknown"; payload->app = deviceData_.app; - payload->sdk_version = sdkVersion; + payload->sdk_version = SDK_VERSION; payload->medium = medium; - auto newClient = FlipperSocketProvider::socketCreate( - endpoint, std::move(payload), flipperScheduler_); - newClient->setEventHandler(ConnectionEvents(implWrapper_)); + socket_ = FlipperSocketProvider::socketCreate( + endpoint, std::move(payload), scheduler_); + socket_->setEventHandler(ConnectionEvents(implWrapper_)); - auto connectingInsecurely = flipperState_->start("Connect insecurely"); - connectionIsTrusted_ = false; + isConnectionTrusted_ = false; - if (!newClient->connect(this)) { - connectingInsecurely->fail("Failed to connect"); - return false; - } + auto step = state_->start("Attempt to connect for certificate exchange"); + step->complete(); - client_ = std::move(newClient); - connectingInsecurely->complete(); - - auto resettingState = flipperState_->start("Reset state"); - contextStore_->resetState(); - resettingState->complete(); - - requestSignedCertificate(); - return true; + socket_->connect(this); } -bool FlipperConnectionManagerImpl::connectSecurely() { - client_ = nullptr; - +void FlipperConnectionManagerImpl::connectSecurely() { + DEBUG_LOG("[conn] Connect securely"); auto port = securePort; auto endpoint = FlipperConnectionEndpoint(deviceData_.host, port, true); - int medium = certProvider_ != nullptr - ? certProvider_->getCertificateExchangeMedium() + int medium = certificateProvider_ != nullptr + ? certificateProvider_->getCertificateExchangeMedium() : FlipperCertificateExchangeMedium::FS_ACCESS; - auto loadingDeviceId = flipperState_->start("Load Device Id"); - auto deviceId = contextStore_->getDeviceId(); + auto loadingDeviceId = state_->start("Load Device Id"); + auto deviceId = store_->getDeviceId(); if (deviceId.compare("unknown")) { loadingDeviceId->complete(); } @@ -248,15 +236,15 @@ bool FlipperConnectionManagerImpl::connectSecurely() { payload->device = deviceData_.device; payload->device_id = deviceId; payload->app = deviceData_.app; - payload->sdk_version = sdkVersion; + payload->sdk_version = SDK_VERSION; payload->medium = medium; - payload->csr = contextStore_->getCertificateSigningRequest().c_str(); - payload->csr_path = contextStore_->getCertificateDirectoryPath().c_str(); + payload->csr = store_->getCertificateSigningRequest().c_str(); + payload->csr_path = store_->getCertificateDirectoryPath().c_str(); - auto newClient = FlipperSocketProvider::socketCreate( - endpoint, std::move(payload), connectionScheduler_, contextStore_.get()); - newClient->setEventHandler(ConnectionEvents(implWrapper_)); - newClient->setMessageHandler([this](const std::string& msg) { + socket_ = FlipperSocketProvider::socketCreate( + endpoint, std::move(payload), connectionScheduler_, store_.get()); + socket_->setEventHandler(ConnectionEvents(implWrapper_)); + socket_->setMessageHandler([this](const std::string& msg) { std::unique_ptr responder; auto message = folly::parseJson(msg); auto idItr = message.find("id"); @@ -270,55 +258,50 @@ bool FlipperConnectionManagerImpl::connectSecurely() { this->onMessageReceived(folly::parseJson(msg), std::move(responder)); }); - auto connectingSecurely = flipperState_->start("Connect securely"); - connectionIsTrusted_ = true; + isConnectionTrusted_ = true; - if (!newClient->connect(this)) { - connectingSecurely->fail("Failed to connect"); - return false; - } + auto step = + state_->start("Attempt to connect with existing client certificate"); + step->complete(); - client_ = std::move(newClient); - connectingSecurely->complete(); - failedConnectionAttempts_ = 0; - return true; + socket_->connect(this); } void FlipperConnectionManagerImpl::reconnect() { - if (!isStarted_) { - log("Not started"); + DEBUG_LOG("[conn] Reconnect"); + if (!started_) { + log("[conn] Not started"); return; } - flipperScheduler_->scheduleAfter( - [this]() { startSync(); }, reconnectIntervalSeconds * 1000.0f); + scheduler_->scheduleAfter( + [this]() { startSync(); }, RECONNECT_INTERVAL_SECONDS * 1000.0f); } void FlipperConnectionManagerImpl::stop() { - if (certProvider_ && certProvider_->shouldResetCertificateFolder()) { - contextStore_->resetState(); + DEBUG_LOG("[conn] Stop"); + if (certificateProvider_ && + certificateProvider_->shouldResetCertificateFolder()) { + store_->resetState(); } - if (!isStarted_) { - log("Not started"); + if (!started_) { + log("[conn] Not started"); return; } - isStarted_ = false; + started_ = false; std::shared_ptr> joinPromise = std::make_shared>(); std::future join = joinPromise->get_future(); - flipperScheduler_->schedule([this, joinPromise]() { - if (client_) { - client_->disconnect(); - } - client_ = nullptr; + scheduler_->schedule([this, joinPromise]() { + socket_ = nullptr; joinPromise->set_value(); }); join.wait(); } -bool FlipperConnectionManagerImpl::isOpen() const { - return isOpen_ && connectionIsTrusted_; +bool FlipperConnectionManagerImpl::isConnected() const { + return isConnected_ && isConnectionTrusted_; } void FlipperConnectionManagerImpl::setCallbacks(Callbacks* callbacks) { @@ -326,10 +309,10 @@ void FlipperConnectionManagerImpl::setCallbacks(Callbacks* callbacks) { } void FlipperConnectionManagerImpl::sendMessage(const folly::dynamic& message) { - flipperScheduler_->schedule([this, message]() { + scheduler_->schedule([this, message]() { try { - if (client_) { - client_->send(message, []() {}); + if (socket_) { + socket_->send(message, []() {}); } } catch (std::length_error& e) { // Skip sending messages that are too large. @@ -340,10 +323,10 @@ void FlipperConnectionManagerImpl::sendMessage(const folly::dynamic& message) { } void FlipperConnectionManagerImpl::sendMessageRaw(const std::string& message) { - flipperScheduler_->schedule([this, message]() { + scheduler_->schedule([this, message]() { try { - if (client_) { - client_->send(message, []() {}); + if (socket_) { + socket_->send(message, []() {}); } } catch (std::length_error& e) { // Skip sending messages that are too large. @@ -360,11 +343,12 @@ void FlipperConnectionManagerImpl::onMessageReceived( } bool FlipperConnectionManagerImpl::isCertificateExchangeNeeded() { + DEBUG_LOG("[conn] Certificate exchange needed verification"); if (failedConnectionAttempts_ >= 2) { return true; } - auto last_known_medium = contextStore_->getLastKnownMedium(); + auto last_known_medium = store_->getLastKnownMedium(); if (!last_known_medium) { return true; } @@ -372,15 +356,15 @@ bool FlipperConnectionManagerImpl::isCertificateExchangeNeeded() { // When we exchange certs over WWW, we use a fake generated serial number and // a virtual device. If medium changes to FS_ACCESS at some point, we should // restart the exchange process to get the device ID of the real device. - int medium = certProvider_ != nullptr - ? certProvider_->getCertificateExchangeMedium() + int medium = certificateProvider_ != nullptr + ? certificateProvider_->getCertificateExchangeMedium() : FlipperCertificateExchangeMedium::FS_ACCESS; if (last_known_medium != medium) { return true; } - auto step = flipperState_->start("Check required certificates are present"); - bool hasRequiredFiles = contextStore_->hasRequiredFiles(); + auto step = state_->start("Check required certificates are present"); + bool hasRequiredFiles = store_->hasRequiredFiles(); if (hasRequiredFiles) { step->complete(); } @@ -391,105 +375,90 @@ void FlipperConnectionManagerImpl::processSignedCertificateResponse( std::shared_ptr gettingCert, std::string response, bool isError) { - /** - Need to keep track of whether the response has been - handled. On success, the completion handler deallocates the socket which in - turn triggers a disconnect. A disconnect is called within - the context of a subscription handler. This means that the completion - handler can be called again to notify that the stream has - been interrupted because we are effectively still handing the response - read. So, if already handled, ignore and return; - */ - if (certificateExchangeCompleted_) - return; - certificateExchangeCompleted_ = true; + DEBUG_LOG("[conn] Process signed certificate response"); if (isError) { auto error = - "Desktop failed to provide certificates. Error from flipper desktop:\n" + + "Flipper failed to provide certificates. Error from Flipper Desktop:\n" + response; log(error); gettingCert->fail(error); - client_ = nullptr; - return; - } - int medium = certProvider_ != nullptr - ? certProvider_->getCertificateExchangeMedium() - : FlipperCertificateExchangeMedium::FS_ACCESS; - if (!response.empty()) { - folly::dynamic config = folly::parseJson(response); - config["medium"] = medium; - contextStore_->storeConnectionConfig(config); - } - if (certProvider_) { - certProvider_->setFlipperState(flipperState_); - auto gettingCertFromProvider = - flipperState_->start("Getting cert from Cert Provider"); + } else { + int medium = certificateProvider_ != nullptr + ? certificateProvider_->getCertificateExchangeMedium() + : FlipperCertificateExchangeMedium::FS_ACCESS; - try { - // Certificates should be present in app's sandbox after it is - // returned. The reason we can't have a completion block here - // is because if the certs are not present after it returns - // then the flipper tries to reconnect on insecured channel - // and recreates the app.csr. By the time completion block is - // called the DeviceCA cert doesn't match app's csr and it - // throws an SSL error. - certProvider_->getCertificates( - contextStore_->getCertificateDirectoryPath(), - contextStore_->getDeviceId()); - gettingCertFromProvider->complete(); - } catch (std::exception& e) { - gettingCertFromProvider->fail(e.what()); - gettingCert->fail(e.what()); - } catch (...) { - gettingCertFromProvider->fail("Exception from certProvider"); - gettingCert->fail("Exception from certProvider"); + if (!response.empty()) { + folly::dynamic config = folly::parseJson(response); + config["medium"] = medium; + store_->storeConnectionConfig(config); } - } - log("Certificate exchange complete."); - gettingCert->complete(); + if (certificateProvider_) { + certificateProvider_->setFlipperState(state_); + auto gettingCertFromProvider = + state_->start("Getting client certificate from Certificate Provider"); - // Disconnect after message sending is complete. - // The client destructor will send a disconnected event - // which will be handled by Flipper which will initiate - // a reconnect sequence. - client_ = nullptr; + try { + // Certificates should be present in app's sandbox after it is + // returned. The reason we can't have a completion block here + // is because if the certs are not present after it returns + // then the flipper tries to reconnect on insecured channel + // and recreates the app.csr. By the time completion block is + // called the DeviceCA cert doesn't match app's csr and it + // throws an SSL error. + certificateProvider_->getCertificates( + store_->getCertificateDirectoryPath(), store_->getDeviceId()); + gettingCertFromProvider->complete(); + } catch (std::exception& e) { + gettingCertFromProvider->fail(e.what()); + gettingCert->fail(e.what()); + } catch (...) { + gettingCertFromProvider->fail( + "Exception thrown from Certificate Provider"); + gettingCert->fail("Exception thrown from Certificate Provider"); + } + } + log("[conn] Certificate exchange complete"); + gettingCert->complete(); + } + + socket_ = nullptr; + reconnect(); } void FlipperConnectionManagerImpl::requestSignedCertificate() { - auto generatingCSR = flipperState_->start("Generate CSR"); - std::string csr = contextStore_->getCertificateSigningRequest(); + DEBUG_LOG("[conn] Request signed certificate"); + auto resettingState = state_->start("Reset connection store state"); + store_->resetState(); + resettingState->complete(); + + auto generatingCSR = state_->start("Generate CSR"); + std::string csr = store_->getCertificateSigningRequest(); generatingCSR->complete(); - int medium = certProvider_ != nullptr - ? certProvider_->getCertificateExchangeMedium() + int medium = certificateProvider_ != nullptr + ? certificateProvider_->getCertificateExchangeMedium() : FlipperCertificateExchangeMedium::FS_ACCESS; - folly::dynamic message = folly::dynamic::object("method", "signCertificate")( - "csr", csr.c_str())( - "destination", - contextStore_->getCertificateDirectoryPath().c_str())("medium", medium); + folly::dynamic message = + folly::dynamic::object("method", "signCertificate")("csr", csr.c_str())( + "destination", + store_->getCertificateDirectoryPath().c_str())("medium", medium); - auto gettingCert = flipperState_->start("Getting cert from desktop"); + auto gettingCert = state_->start("Getting cert from desktop"); - certificateExchangeCompleted_ = false; - flipperScheduler_->schedule([this, message, gettingCert]() { - if (!client_) { - return; - } - client_->sendExpectResponse( - folly::toJson(message), - [this, gettingCert](const std::string& response, bool isError) { - flipperScheduler_->schedule([this, gettingCert, response, isError]() { - this->processSignedCertificateResponse( - gettingCert, response, isError); - }); + socket_->sendExpectResponse( + folly::toJson(message), + [this, gettingCert](const std::string& response, bool isError) { + scheduler_->schedule([this, gettingCert, response, isError]() { + this->processSignedCertificateResponse( + gettingCert, response, isError); }); - }); + }); failedConnectionAttempts_ = 0; } bool FlipperConnectionManagerImpl::isRunningInOwnThread() { - return flipperScheduler_->isRunningInOwnThread(); + return scheduler_->isRunningInOwnThread(); } } // namespace flipper diff --git a/xplat/Flipper/FlipperConnectionManagerImpl.h b/xplat/Flipper/FlipperConnectionManagerImpl.h index c9f4875ad..60247107a 100644 --- a/xplat/Flipper/FlipperConnectionManagerImpl.h +++ b/xplat/Flipper/FlipperConnectionManagerImpl.h @@ -36,7 +36,7 @@ class FlipperConnectionManagerImpl : public FlipperConnectionManager { void stop() override; - bool isOpen() const override; + bool isConnected() const override; void setCallbacks(Callbacks* callbacks) override; @@ -54,34 +54,37 @@ class FlipperConnectionManagerImpl : public FlipperConnectionManager { std::shared_ptr getCertificateProvider() override; private: - bool isOpen_ = false; - bool isStarted_ = false; - std::shared_ptr certProvider_ = nullptr; + bool isConnected_ = false; + bool started_ = false; + bool isConnectionTrusted_ = false; + + std::shared_ptr certificateProvider_ = nullptr; + Callbacks* callbacks_; + DeviceData deviceData_; - std::shared_ptr flipperState_; + + std::shared_ptr state_; + int insecurePort; int securePort; int altInsecurePort; int altSecurePort; - Scheduler* flipperScheduler_; + Scheduler* scheduler_; Scheduler* connectionScheduler_; - std::unique_ptr client_; - - bool connectionIsTrusted_; - bool certificateExchangeCompleted_ = false; + std::unique_ptr socket_; int failedConnectionAttempts_ = 0; - int failedSocketConnectionAttempts = 0; - std::shared_ptr contextStore_; + std::shared_ptr store_; std::shared_ptr implWrapper_; void startSync(); - bool connectAndExchangeCertificate(); - bool connectSecurely(); + void connectAndExchangeCertificate(); + void connectSecurely(); + void handleSocketEvent(const SocketEvent event); bool isCertificateExchangeNeeded(); void requestSignedCertificate(); void processSignedCertificateResponse( diff --git a/xplat/Flipper/FlipperSocket.h b/xplat/Flipper/FlipperSocket.h index 0692e708d..51d9c69df 100644 --- a/xplat/Flipper/FlipperSocket.h +++ b/xplat/Flipper/FlipperSocket.h @@ -37,7 +37,7 @@ class FlipperSocket { used or error. @param manager An instance of FlipperConnectionManager. */ - virtual bool connect(FlipperConnectionManager* manager) = 0; + virtual void connect(FlipperConnectionManager* manager) = 0; /** Disconnect from the endpoint. */ diff --git a/xplat/Flipper/FlipperSocketProvider.cpp b/xplat/Flipper/FlipperSocketProvider.cpp index 17c3d4e73..824396c90 100644 --- a/xplat/Flipper/FlipperSocketProvider.cpp +++ b/xplat/Flipper/FlipperSocketProvider.cpp @@ -12,17 +12,18 @@ namespace facebook { namespace flipper { -std::unique_ptr FlipperSocketProvider::provider_ = - nullptr; - -std::unique_ptr FlipperSocketProvider::shelvedProvider_ = - nullptr; +std::unique_ptr& +FlipperSocketProvider::defaultProvider() { + static std::unique_ptr provider; + return provider; +} std::unique_ptr FlipperSocketProvider::socketCreate( FlipperConnectionEndpoint endpoint, std::unique_ptr payload, Scheduler* scheduler) { - return provider_->create(std::move(endpoint), std::move(payload), scheduler); + return defaultProvider()->create( + std::move(endpoint), std::move(payload), scheduler); } std::unique_ptr FlipperSocketProvider::socketCreate( @@ -30,7 +31,7 @@ std::unique_ptr FlipperSocketProvider::socketCreate( std::unique_ptr payload, Scheduler* scheduler, ConnectionContextStore* connectionContextStore) { - return provider_->create( + return defaultProvider()->create( std::move(endpoint), std::move(payload), scheduler, @@ -39,11 +40,11 @@ std::unique_ptr FlipperSocketProvider::socketCreate( void FlipperSocketProvider::setDefaultProvider( std::unique_ptr provider) { - provider_ = std::move(provider); + defaultProvider() = std::move(provider); } bool FlipperSocketProvider::hasProvider() { - return provider_ != nullptr; + return defaultProvider() != nullptr; } } // namespace flipper diff --git a/xplat/Flipper/FlipperSocketProvider.h b/xplat/Flipper/FlipperSocketProvider.h index 8e26f38aa..99c6e9bff 100644 --- a/xplat/Flipper/FlipperSocketProvider.h +++ b/xplat/Flipper/FlipperSocketProvider.h @@ -71,8 +71,7 @@ class FlipperSocketProvider { static bool hasProvider(); private: - static std::unique_ptr provider_; - static std::unique_ptr shelvedProvider_; + static std::unique_ptr& defaultProvider(); }; } // namespace flipper diff --git a/xplat/Flipper/FlipperState.cpp b/xplat/Flipper/FlipperState.cpp index d601d33f3..a21cd90d6 100644 --- a/xplat/Flipper/FlipperState.cpp +++ b/xplat/Flipper/FlipperState.cpp @@ -9,17 +9,17 @@ #include #include "FlipperStateUpdateListener.h" #include "FlipperStep.h" - -#if FLIPPER_DEBUG_LOG #include "Log.h" -#endif + +#define FLIPPER_LOGS_CAPACITY 4096 using namespace facebook::flipper; /* Class responsible for collecting state updates and combining them into a * view of the current state of the flipper client. */ -FlipperState::FlipperState() : logs("") {} +FlipperState::FlipperState() {} + void FlipperState::setUpdateListener( std::shared_ptr listener) { std::lock_guard lock(mutex); @@ -30,9 +30,9 @@ void FlipperState::started(std::string step) { std::shared_ptr localListener; { std::lock_guard lock(mutex); -#if FLIPPER_DEBUG_LOG - log("[started] " + step); -#endif + + DEBUG_LOG("[step] [started] " + step); + if (stateMap.find(step) == stateMap.end()) { insertOrder.push_back(step); } @@ -46,14 +46,25 @@ void FlipperState::started(std::string step) { } } +void FlipperState::ensureLogsCapacity() { + if (logs.tellp() > FLIPPER_LOGS_CAPACITY) { + logs.str(""); + logs.clear(); + logs << "[truncated]" << std::endl; + } +} + void FlipperState::success(std::string step) { std::shared_ptr localListener; { std::lock_guard lock(mutex); -#if FLIPPER_DEBUG_LOG - log("[finished] " + step); -#endif - logs = logs + "[Success] " + step + "\n"; + std::string message = "[step] [success] " + step; + + DEBUG_LOG(message); + + ensureLogsCapacity(); + logs << message << std::endl; + stateMap[step] = State::success; localListener = mListener; } @@ -68,11 +79,12 @@ void FlipperState::failed(std::string step, std::string errorMessage) { std::shared_ptr localListener; { std::lock_guard lock(mutex); - std::string message = "[Failed] " + step + ": " + errorMessage; -#if FLIPPER_DEBUG_LOG - log(message); -#endif - logs = logs + message + "\n"; + std::string message = "[step] [failed] " + step + ": " + errorMessage; + + DEBUG_LOG(message); + + ensureLogsCapacity(); + logs << message << std::endl; stateMap[step] = State::failed; localListener = mListener; } @@ -83,12 +95,12 @@ void FlipperState::failed(std::string step, std::string errorMessage) { } } -// TODO: Currently returns string, but should really provide a better +// Currently returns string, but should really provide a better // representation of the current state so the UI can show it in a more intuitive // way std::string FlipperState::getState() { std::lock_guard lock(mutex); - return logs; + return logs.str(); } std::vector FlipperState::getStateElements() { diff --git a/xplat/Flipper/FlipperState.h b/xplat/Flipper/FlipperState.h index daa5b4f88..aa0f0e710 100644 --- a/xplat/Flipper/FlipperState.h +++ b/xplat/Flipper/FlipperState.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -53,10 +54,11 @@ class FlipperState { void success(std::string); void failed(std::string, std::string); void started(std::string); + void ensureLogsCapacity(); std::mutex mutex; // Protects all our member variables. std::shared_ptr mListener = nullptr; - std::string logs; + std::stringstream logs; std::vector insertOrder; std::map stateMap; }; diff --git a/xplat/Flipper/Log.cpp b/xplat/Flipper/Log.cpp index c365a3ee2..661f6d03a 100644 --- a/xplat/Flipper/Log.cpp +++ b/xplat/Flipper/Log.cpp @@ -14,12 +14,31 @@ namespace facebook { namespace flipper { +namespace { +static LogHandlerFunc* getHandle() { + static LogHandlerFunc sHandler = defaultLogHandler; + return &sHandler; +} +} // namespace + void log(const std::string& message) { + return (*getHandle())(message); +} + +void setLogHandler(LogHandlerFunc handler) { + *getHandle() = handler; +} + +LogHandlerFunc getLogHandler() { + return *getHandle(); +} + +void defaultLogHandler(const std::string& message) { #ifdef __ANDROID__ __android_log_print( - ANDROID_LOG_INFO, "flipper", "flipper: %s", message.c_str()); + ANDROID_LOG_INFO, "flipper", "[flipper] %s", message.c_str()); #else - printf("flipper: %s\n", message.c_str()); + printf("[flipper] %s\n", message.c_str()); #endif } diff --git a/xplat/Flipper/Log.h b/xplat/Flipper/Log.h index e6928df22..e5bf6ca80 100644 --- a/xplat/Flipper/Log.h +++ b/xplat/Flipper/Log.h @@ -14,5 +14,17 @@ namespace flipper { void log(const std::string& message); +using LogHandlerFunc = void (*)(const std::string& message); + +void setLogHandler(LogHandlerFunc handler); +LogHandlerFunc getLogHandler(); +void defaultLogHandler(const std::string& message); + } // namespace flipper } // namespace facebook + +#if FLIPPER_DEBUG_LOG +#define DEBUG_LOG(...) facebook::flipper::log(__VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif diff --git a/xplat/FlipperTestLib/FlipperConnectionManagerMock.h b/xplat/FlipperTestLib/FlipperConnectionManagerMock.h index 0970d18aa..c56718112 100644 --- a/xplat/FlipperTestLib/FlipperConnectionManagerMock.h +++ b/xplat/FlipperTestLib/FlipperConnectionManagerMock.h @@ -19,21 +19,21 @@ class FlipperConnectionManagerMock : public FlipperConnectionManager { FlipperConnectionManagerMock() : callbacks(nullptr) {} void start() override { - open = true; + connected = true; if (callbacks) { callbacks->onConnected(); } } void stop() override { - open = false; + connected = false; if (callbacks) { callbacks->onDisconnected(); } } - bool isOpen() const override { - return open; + bool isConnected() const override { + return connected; } void sendMessage(const folly::dynamic& message) override { @@ -77,7 +77,7 @@ class FlipperConnectionManagerMock : public FlipperConnectionManager { } public: - bool open = false; + bool connected = false; Callbacks* callbacks; std::vector messages; std::vector messagesReceived; diff --git a/xplat/FlipperTests/FlipperClientTests.cpp b/xplat/FlipperTests/FlipperClientTests.cpp index 6ecd07161..959302c1b 100644 --- a/xplat/FlipperTests/FlipperClientTests.cpp +++ b/xplat/FlipperTests/FlipperClientTests.cpp @@ -47,9 +47,9 @@ class FlipperClientTest : public ::testing::Test { TEST_F(FlipperClientTest, testSaneMocks) { FlipperConnectionManagerMock socket; socket.start(); - EXPECT_TRUE(socket.isOpen()); + EXPECT_TRUE(socket.isConnected()); socket.stop(); - EXPECT_FALSE(socket.isOpen()); + EXPECT_FALSE(socket.isConnected()); FlipperPluginMock plugin("Test"); EXPECT_EQ(plugin.identifier(), "Test"); @@ -103,10 +103,10 @@ TEST_F(FlipperClientTest, testRemovePlugin) { TEST_F(FlipperClientTest, testStartStop) { client->start(); - EXPECT_TRUE(socket->isOpen()); + EXPECT_TRUE(socket->isConnected()); client->stop(); - EXPECT_FALSE(socket->isOpen()); + EXPECT_FALSE(socket->isConnected()); } TEST_F(FlipperClientTest, testConnectDisconnect) { diff --git a/xplat/FlipperWebSocket/BaseClient.h b/xplat/FlipperWebSocket/BaseClient.h index 053987e88..b9e1e55bc 100644 --- a/xplat/FlipperWebSocket/BaseClient.h +++ b/xplat/FlipperWebSocket/BaseClient.h @@ -68,7 +68,7 @@ class BaseClient { messageHandler_ = std::move(messageHandler); } - virtual bool connect(FlipperConnectionManager* manager) = 0; + virtual void connect(FlipperConnectionManager* manager) = 0; virtual void disconnect() = 0; virtual void send( diff --git a/xplat/FlipperWebSocket/FlipperWebSocket.cpp b/xplat/FlipperWebSocket/FlipperWebSocket.cpp index b01dd5618..48754aa4c 100644 --- a/xplat/FlipperWebSocket/FlipperWebSocket.cpp +++ b/xplat/FlipperWebSocket/FlipperWebSocket.cpp @@ -60,8 +60,8 @@ void FlipperWebSocket::setMessageHandler(SocketMessageHandler messageHandler) { socket_->setMessageHandler(messageHandler); } -bool FlipperWebSocket::connect(FlipperConnectionManager* manager) { - return socket_->connect(manager); +void FlipperWebSocket::connect(FlipperConnectionManager* manager) { + socket_->connect(manager); } void FlipperWebSocket::disconnect() { diff --git a/xplat/FlipperWebSocket/FlipperWebSocket.h b/xplat/FlipperWebSocket/FlipperWebSocket.h index 700c940bb..b42f6d75b 100644 --- a/xplat/FlipperWebSocket/FlipperWebSocket.h +++ b/xplat/FlipperWebSocket/FlipperWebSocket.h @@ -41,7 +41,7 @@ class FlipperWebSocket : public FlipperSocket { virtual void setEventHandler(SocketEventHandler eventHandler) override; virtual void setMessageHandler(SocketMessageHandler messageHandler) override; - virtual bool connect(FlipperConnectionManager* manager) override; + virtual void connect(FlipperConnectionManager* manager) override; virtual void disconnect() override; virtual void send(const folly::dynamic& message, SocketSendHandler completion) diff --git a/xplat/FlipperWebSocket/WebSocketClient.cpp b/xplat/FlipperWebSocket/WebSocketClient.cpp index f50c788c1..2f628c635 100644 --- a/xplat/FlipperWebSocket/WebSocketClient.cpp +++ b/xplat/FlipperWebSocket/WebSocketClient.cpp @@ -62,9 +62,9 @@ WebSocketClient::~WebSocketClient() { disconnect(); } -bool WebSocketClient::connect(FlipperConnectionManager* manager) { +void WebSocketClient::connect(FlipperConnectionManager*) { if (status_ != Status::Unconnected) { - return false; + return; } status_ = Status::Connecting; @@ -89,7 +89,8 @@ bool WebSocketClient::connect(FlipperConnectionManager* manager) { if (ec) { status_ = Status::Failed; - return false; + eventHandler_(SocketEvent::ERROR); + return; } handle_ = connection_->get_handle(); @@ -119,18 +120,7 @@ bool WebSocketClient::connect(FlipperConnectionManager* manager) { &socket_, websocketpp::lib::placeholders::_1)); - auto connected = connected_.get_future(); - socket_.connect(connection_); - - auto state = connected.wait_for(std::chrono::seconds(10)); - if (state == std::future_status::ready) { - return connected.get(); - } - - disconnect(); - - return false; } void WebSocketClient::disconnect() { @@ -147,8 +137,6 @@ void WebSocketClient::disconnect() { thread_->join(); } thread_ = nullptr; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); }); } void WebSocketClient::send( @@ -204,8 +192,7 @@ void WebSocketClient::onOpen(SocketClient* c, websocketpp::connection_hdl hdl) { } status_ = Status::Initializing; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::OPEN); }); + eventHandler_(SocketEvent::OPEN); } void WebSocketClient::onMessage( @@ -229,16 +216,14 @@ void WebSocketClient::onFail(SocketClient* c, websocketpp::connection_hdl hdl) { connected_.set_value(false); } status_ = Status::Failed; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::ERROR); }); + eventHandler_(SocketEvent::ERROR); } void WebSocketClient::onClose( SocketClient* c, websocketpp::connection_hdl hdl) { status_ = Status::Closed; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); }); + eventHandler_(SocketEvent::CLOSE); } } // namespace flipper diff --git a/xplat/FlipperWebSocket/WebSocketClient.h b/xplat/FlipperWebSocket/WebSocketClient.h index 47331d7ec..6619a89a8 100644 --- a/xplat/FlipperWebSocket/WebSocketClient.h +++ b/xplat/FlipperWebSocket/WebSocketClient.h @@ -47,7 +47,7 @@ class WebSocketClient : public BaseClient { virtual ~WebSocketClient(); - virtual bool connect(FlipperConnectionManager* manager) override; + virtual void connect(FlipperConnectionManager* manager) override; virtual void disconnect() override; virtual void send(const folly::dynamic& message, SocketSendHandler completion) diff --git a/xplat/FlipperWebSocket/WebSocketTLSClient.cpp b/xplat/FlipperWebSocket/WebSocketTLSClient.cpp index 24fa9bb49..6d1c8e227 100644 --- a/xplat/FlipperWebSocket/WebSocketTLSClient.cpp +++ b/xplat/FlipperWebSocket/WebSocketTLSClient.cpp @@ -65,9 +65,9 @@ WebSocketTLSClient::~WebSocketTLSClient() { disconnect(); } -bool WebSocketTLSClient::connect(FlipperConnectionManager* manager) { +void WebSocketTLSClient::connect(FlipperConnectionManager*) { if (status_ != Status::Unconnected) { - return false; + return; } status_ = Status::Connecting; @@ -98,7 +98,8 @@ bool WebSocketTLSClient::connect(FlipperConnectionManager* manager) { if (ec) { status_ = Status::Failed; - return false; + eventHandler_(SocketEvent::ERROR); + return; } handle_ = connection_->get_handle(); @@ -128,18 +129,7 @@ bool WebSocketTLSClient::connect(FlipperConnectionManager* manager) { &socket_, websocketpp::lib::placeholders::_1)); - auto connected = connected_.get_future(); - socket_.connect(connection_); - - auto state = connected.wait_for(std::chrono::seconds(10)); - if (state == std::future_status::ready) { - return connected.get(); - } - - disconnect(); - - return false; } void WebSocketTLSClient::disconnect() { @@ -157,8 +147,6 @@ void WebSocketTLSClient::disconnect() { } thread_ = nullptr; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); }); } void WebSocketTLSClient::send( @@ -211,13 +199,8 @@ void WebSocketTLSClient::sendExpectResponse( void WebSocketTLSClient::onOpen( SocketTLSClient* c, websocketpp::connection_hdl hdl) { - if (status_ == Status::Connecting) { - connected_.set_value(true); - } - status_ = Status::Initializing; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::OPEN); }); + eventHandler_(SocketEvent::OPEN); } void WebSocketTLSClient::onMessage( @@ -242,36 +225,19 @@ void WebSocketTLSClient::onFail( (reason.find("TLS handshake failed") != std::string::npos || reason.find("Generic TLS related error") != std::string::npos); - if (status_ == Status::Connecting) { - if (sslError) { - try { - connected_.set_exception( - std::make_exception_ptr(SSLException("SSL handshake failed"))); - } catch (...) { - // set_exception() may throw an exception - // In that case, just set the value to false. - connected_.set_value(false); - } - } else { - connected_.set_value(false); - } - } status_ = Status::Failed; - scheduler_->schedule([eventHandler = eventHandler_, sslError]() { - if (sslError) { - eventHandler(SocketEvent::SSL_ERROR); - } else { - eventHandler(SocketEvent::ERROR); - } - }); + if (sslError) { + eventHandler_(SocketEvent::SSL_ERROR); + } else { + eventHandler_(SocketEvent::ERROR); + } } void WebSocketTLSClient::onClose( SocketTLSClient* c, websocketpp::connection_hdl hdl) { status_ = Status::Closed; - scheduler_->schedule( - [eventHandler = eventHandler_]() { eventHandler(SocketEvent::CLOSE); }); + eventHandler_(SocketEvent::CLOSE); } SocketTLSContext WebSocketTLSClient::onTLSInit( diff --git a/xplat/FlipperWebSocket/WebSocketTLSClient.h b/xplat/FlipperWebSocket/WebSocketTLSClient.h index 398037973..d5072f98a 100644 --- a/xplat/FlipperWebSocket/WebSocketTLSClient.h +++ b/xplat/FlipperWebSocket/WebSocketTLSClient.h @@ -50,7 +50,7 @@ class WebSocketTLSClient : public BaseClient { virtual ~WebSocketTLSClient(); - virtual bool connect(FlipperConnectionManager* manager) override; + virtual void connect(FlipperConnectionManager* manager) override; virtual void disconnect() override; virtual void send(const folly::dynamic& message, SocketSendHandler completion) @@ -76,7 +76,6 @@ class WebSocketTLSClient : public BaseClient { SocketTLSThread thread_; websocketpp::connection_hdl handle_; - std::promise connected_; }; } // namespace flipper diff --git a/xplat/build.gradle b/xplat/build.gradle index 71e3e2fa1..450908e6e 100644 --- a/xplat/build.gradle +++ b/xplat/build.gradle @@ -8,6 +8,7 @@ apply plugin: 'com.android.library' android { + namespace 'com.facebook.xplat.sonar' compileSdkVersion rootProject.compileSdkVersion buildToolsVersion rootProject.buildToolsVersion ndkVersion rootProject.ndkVersion diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index fb57ccd13..000000000 --- a/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - -