From a089cc2efcbea01de1a2b87c852b09d525a105ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Stefa=C5=84czyk?= Date: Fri, 23 Aug 2024 13:29:38 +0200 Subject: [PATCH] feat: e2e snapshot tests (#2338) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Summary This PR adds E2E tests based on view screenshots done via `react-native-view-shot`. It only works with devices that have their [pixel ratio](https://reactnative.dev/docs/pixelratio) equal `3`. If you want to use device with different pixel ratio, you need to adjust it in `e2e/generateReferences.ts` viewport and regenerate reference images (see below). Steps to run tests: - Run Metro server for example app via `yarn start` in example app's directory - Run `example` app on platform of your choice (currently only Android & iOS are supported) via `yarn android` or `yarn ios` in example app's directory - Run `yarn e2e` in project's root directory to start Jest server - Select `E2E` tab in example app - Wait for tests to finish - You can see test results, as well as diffs (actual rendered svg vs reference image) in `e2e/diffs` directory Steps to add new test cases: - Put SVG of your choice to `e2e/cases` directory - Run `yarn generateE2eRefrences`, this will open headless chrome browser via `puppeteer` and snapshot all rendered SVGs to .png files and later use them as reference in tests - You should see new .png files in `e2e/references` - When you run E2E tests again, it will use new test case(s) you've added ## Test Plan https://github.com/software-mansion/react-native-svg/assets/41289688/24ee5447-ce9a-43b6-9dde-76229d25a30a https://github.com/software-mansion/react-native-svg/assets/41289688/71d1873f-8155-4494-80bd-e4c1fa72a065 ### What's required for testing (prerequisites)? See Summary ### What are the steps to reproduce (after prerequisites)? See Summary ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | Android | ✅ | | Web | ❌ | ## Checklist - [X] I have tested this on a device and a simulator - [x] I added documentation in `README.md` - [X] I updated the typed files (typescript) - [X] I added a test for the API in the `__tests__` folder --------- Co-authored-by: bohdanprog Co-authored-by: Jakub Grzywacz --- .github/workflows/android-e2e.yml | 120 +++++ .github/workflows/ios-e2e.yml | 93 ++++ .github/workflows/windows-build-test.yml | 2 +- .gitignore | 3 + CONTRIBUTING.md | 44 +- .../e2e/GeneralSvgRenderingTest.spec.tsx | 94 ++++ apps/examples/index.tsx | 90 ++-- apps/examples/src/e2e/TestingView.tsx | 158 +++++++ apps/examples/src/e2e/index.macos.tsx | 3 + apps/examples/src/e2e/index.tsx | 8 + apps/examples/src/e2e/index.web.tsx | 3 + apps/examples/src/examples.macos.tsx | 49 ++ apps/examples/src/examples.tsx | 2 + apps/examples/tsconfig.json | 3 + apps/examples/utils/names.ts | 28 ++ apps/examples/utils/type.ts | 3 + e2e/cases/1.svg | 1 + e2e/cases/2.svg | 10 + e2e/cases/3.svg | 28 ++ e2e/diffs/.gitignore | 2 + e2e/env.ts | 4 + e2e/failedCases.json | 10 + e2e/generateReferences.ts | 35 ++ e2e/globals.d.ts | 12 + e2e/helpers.ts | 57 +++ e2e/matchTestCases.ts | 30 ++ e2e/readFailedCases.ts | 56 +++ e2e/references/1.png | Bin 0 -> 20868 bytes e2e/references/2.png | Bin 0 -> 17964 bytes e2e/references/3.png | Bin 0 -> 8405 bytes e2e/rendered/.gitignore | 3 + e2e/setupJest.ts | 35 ++ e2e/teardownJest.ts | 4 + e2e/types.ts | 38 ++ example/babel.config.js | 1 + example/ios/Podfile.lock | 14 +- example/package.json | 2 + ...react-native-view-shot+4.0.0-alpha.2.patch | 51 ++ example/yarn.lock | 53 +++ fabric-example/ios/Podfile.lock | 18 +- fabric-example/package.json | 7 +- ...react-native-view-shot+4.0.0-alpha.2.patch | 51 ++ fabric-example/yarn.lock | 180 +++++++- fabric-macos-example/macos/Podfile.lock | 8 +- jest.config.ts | 15 + package.json | 14 +- paper-macos-example/macos/Podfile.lock | 4 +- tests-example/ios/Podfile.lock | 6 +- tsconfig.json | 2 +- web-example/package.json | 2 + web-example/yarn.lock | 107 ++++- yarn.lock | 437 +++++++++++++++++- 52 files changed, 1899 insertions(+), 101 deletions(-) create mode 100644 .github/workflows/android-e2e.yml create mode 100644 .github/workflows/ios-e2e.yml create mode 100644 __tests__/e2e/GeneralSvgRenderingTest.spec.tsx create mode 100644 apps/examples/src/e2e/TestingView.tsx create mode 100644 apps/examples/src/e2e/index.macos.tsx create mode 100644 apps/examples/src/e2e/index.tsx create mode 100644 apps/examples/src/e2e/index.web.tsx create mode 100644 apps/examples/src/examples.macos.tsx create mode 100644 apps/examples/utils/names.ts create mode 100644 apps/examples/utils/type.ts create mode 100644 e2e/cases/1.svg create mode 100644 e2e/cases/2.svg create mode 100644 e2e/cases/3.svg create mode 100644 e2e/diffs/.gitignore create mode 100644 e2e/env.ts create mode 100644 e2e/failedCases.json create mode 100644 e2e/generateReferences.ts create mode 100644 e2e/globals.d.ts create mode 100644 e2e/helpers.ts create mode 100644 e2e/matchTestCases.ts create mode 100644 e2e/readFailedCases.ts create mode 100644 e2e/references/1.png create mode 100644 e2e/references/2.png create mode 100644 e2e/references/3.png create mode 100644 e2e/rendered/.gitignore create mode 100644 e2e/setupJest.ts create mode 100644 e2e/teardownJest.ts create mode 100644 e2e/types.ts create mode 100644 example/patches/react-native-view-shot+4.0.0-alpha.2.patch create mode 100644 fabric-example/patches/react-native-view-shot+4.0.0-alpha.2.patch diff --git a/.github/workflows/android-e2e.yml b/.github/workflows/android-e2e.yml new file mode 100644 index 00000000..4f2e2413 --- /dev/null +++ b/.github/workflows/android-e2e.yml @@ -0,0 +1,120 @@ +name: Test Android e2e +on: + pull_request: + paths: + - '.github/workflows/android-e2e-test.yml' + - 'apps/examples/**' + - 'example/**' + - 'android/**' + - 'src/**' + - 'e2e/**' + - 'package.json' + # push: + # branches: + # - main + workflow_dispatch: +jobs: + test: + runs-on: macos-12 + timeout-minutes: 60 + env: + WORKING_DIRECTORY: example + API_LEVEL: 34 + SYSTEM_IMAGES: system-images;android-34;google_apis;x86_64 + AVD_NAME: rn-svg-avd + concurrency: + group: android-e2e-example-${{ github.ref }} + cancel-in-progress: true + steps: + - name: checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: 'yarn' + - name: Set up JDK 17 + uses: actions/setup-java@v2 + with: + java-version: '17' + distribution: 'zulu' + cache: 'gradle' + - name: Install NDK + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26d + local-cache: true + - name: Set ANDROID_NDK + run: echo "ANDROID_NDK=$ANDROID_HOME/ndk-bundle" >> $GITHUB_ENV + - name: Cache SDK image + id: cache-sdk-img + uses: actions/cache@v3 + with: + path: $ANDROID_HOME/system-images/ + key: ${{ runner.os }}-build-system-images-${{ env.SYSTEM_IMAGES }} + - name: SKDs - download required images + if: ${{ steps.cache-sdd-img.outputs.cache-hit != 'true' }} + run: $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "system-images;android-34;google_apis;x86_64" + - name: Cache AVD + id: cache-avd + uses: actions/cache@v3 + with: + path: ~/.android/avd/${{ env.AVD_NAME }}.avd + key: ${{ runner.os }}-avd-images-${{ env.SYSTEM_IMAGES }}-${{ env.AVD_NAME }} + - name: Emulator - Create + if: ${{ steps.cache-avd.outputs.cache-hit != 'true' }} + run: $ANDROID_HOME/cmdline-tools/latest/bin/avdmanager create avd -n ${{ env.AVD_NAME }} --device 28 --package "${{ env.SYSTEM_IMAGES }}" --sdcard 512M + - name: Emulator - Set screen settings + if: ${{ steps.cache-avd.outputs.cache-hit != 'true' }} + run: | + echo "AVD config path: $HOME/.android/avd/${{ env.AVD_NAME }}.avd/config.ini" + sed -i '' 's/.*hw\.lcd\.density.*/hw\.lcd\.density = 480/g' $HOME/.android/avd/${{ env.AVD_NAME }}.avd/config.ini + sed -i '' 's/.*hw\.lcd\.width.*/hw\.lcd\.width = 1344/g' $HOME/.android/avd/${{ env.AVD_NAME }}.avd/config.ini + sed -i '' 's/.*hw\.lcd\.height.*/hw\.lcd\.height = 2992/g' $HOME/.android/avd/${{ env.AVD_NAME }}.avd/config.ini + - name: Emulator - Boot + run: $ANDROID_HOME/emulator/emulator -memory 4096 -avd ${{ env.AVD_NAME }} -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim & + + - name: ADB Wait For Device + run: adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;' + timeout-minutes: 10 + + - name: Reverse TCP + working-directory: ${{ env.WORKING_DIRECTORY }} + run: adb devices | grep '\t' | awk '{print $1}' | sed 's/\\s//g' | xargs -I {} adb -s {} reverse tcp:8081 tcp:8081 + + - name: Install root node dependencies + run: yarn + + - name: Install example app node dependencies + run: yarn + working-directory: ${{ env.WORKING_DIRECTORY }} + + - name: Build Android app + working-directory: ${{ env.WORKING_DIRECTORY }}/android + run: ./gradlew assembleDebug + + - name: Start Metro server + working-directory: ${{ env.WORKING_DIRECTORY }} + run: E2E=true yarn start &> output.log & + + - name: Install APK + run: adb install -r ${{ env.WORKING_DIRECTORY }}/android/app/build/outputs/apk/debug/app-debug.apk + + - name: Launch APK + run: 'while ! (adb shell monkey -p com.example 1 | grep -q "Events injected: 1"); do sleep 1; echo "Retrying due to errors in previous run..."; done' + + - name: Run e2e Tests + run: E2E=true yarn e2e + + - name: Upload test report + uses: actions/upload-artifact@v4 + with: + name: report + path: | + report.html + jest-html-reporters-attach/ + + - name: Kill emulator (so it can be cached safely) + run: adb devices | grep emulator | cut -f1 | while read line; do adb -s $line emu kill; done diff --git a/.github/workflows/ios-e2e.yml b/.github/workflows/ios-e2e.yml new file mode 100644 index 00000000..61cc6f24 --- /dev/null +++ b/.github/workflows/ios-e2e.yml @@ -0,0 +1,93 @@ +name: Test iOS e2e +on: + pull_request: + paths: + - '.github/workflows/android-e2e-test.yml' + - 'apps/examples/**' + - 'example/**' + - 'apple/**' + - 'src/**' + - 'e2e/**' + - 'package.json' + push: + branches: + - main + workflow_dispatch: +jobs: + test: + runs-on: macos-14 + timeout-minutes: 60 + env: + WORKING_DIRECTORY: example + DEVICE: iPhone 14 Pro + XCODE_VERSION: latest-stable + concurrency: + group: ios-e2e-example-${{ github.ref }} + cancel-in-progress: true + steps: + - name: checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Use latest stable Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: ${{ env.XCODE_VERSION }} + + - name: Restore react-native-svg node_modules from cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node-modules-svg-${{ hashFiles('yarn.lock') }} + restore-keys: ${{ runner.os }}-node-modules-svg- + + - name: Install react-native-svg node_modules + run: yarn install --frozen-lockfile + + - name: Restore app node_modules from cache + uses: actions/cache@v3 + with: + path: ${{ env.WORKING_DIRECTORY }}/node_modules + key: ${{ runner.os }}-node-modules-${{ env.WORKING_DIRECTORY }}-${{ hashFiles(format('{0}/yarn.lock', env.WORKING_DIRECTORY)) }} + restore-keys: ${{ runner.os }}-node-modules-${{ env.WORKING_DIRECTORY }}- + + - name: Install app node_modules + working-directory: ${{ env.WORKING_DIRECTORY }} + run: yarn install --frozen-lockfile + + - name: Restore Pods from cache + uses: actions/cache@v3 + with: + path: | + ${{ env.WORKING_DIRECTORY }}/ios/Pods + ~/Library/Caches/CocoaPods + ~/.cocoapods + key: ${{ runner.os }}-pods-${{ env.WORKING_DIRECTORY }}-${{ hashFiles(format('{0}/ios/Podfile.lock', env.WORKING_DIRECTORY)) }} + + - name: Install Pods + working-directory: ${{ env.WORKING_DIRECTORY }}/ios + run: pod install + + - name: Restore build artifacts from cache + uses: actions/cache@v3 + with: + path: ~/Library/Developer/Xcode/DerivedData + key: ${{ runner.os }}-ios-derived-data-${{ env.WORKING_DIRECTORY }}-${{ hashFiles(format('{0}/ios/Podfile.lock', env.WORKING_DIRECTORY)) }} + - name: Start Metro server + working-directory: ${{ env.WORKING_DIRECTORY }} + run: E2E=true yarn start &> output.log & + + - name: Build app + working-directory: ${{ env.WORKING_DIRECTORY }} + run: E2E=true npx react-native@latest run-ios --simulator="${{ env.DEVICE }}" --mode Debug --verbose + + - name: Run e2e Tests + run: E2E=true yarn e2e + + - name: Upload test report + uses: actions/upload-artifact@v4 + with: + name: report + path: | + report.html + jest-html-reporters-attach/ diff --git a/.github/workflows/windows-build-test.yml b/.github/workflows/windows-build-test.yml index a9160177..e0ace47b 100644 --- a/.github/workflows/windows-build-test.yml +++ b/.github/workflows/windows-build-test.yml @@ -57,4 +57,4 @@ jobs: - name: Build app working-directory: ${{ matrix.working-directory }}/windows - run: npx react-native run-windows --logging --no-packager --no-deploy \ No newline at end of file + run: npx react-native run-windows --logging --no-packager --no-deploy --no-autolink diff --git a/.gitignore b/.gitignore index 85569792..984257db 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ experimental/ # VS Code .vscode/ + +jest-html-reporters-attach/ +report.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ed3f034..4bfc519d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,8 @@ # Contributing to React Native Svg Thank you for helping out with react-native-svg! -We'd like to make contributions as pleasant as possible, so here's a small guide of how we see it. Happy to hear your feedback about anything, so please let us know. +We'd like to make contributions as pleasant as possible, so here's a small guide of how we see it. Happy to hear your +feedback about anything, so please let us know. ### Modifying react-native-svg @@ -13,24 +14,51 @@ We'd like to make contributions as pleasant as possible, so here's a small guide ### Testing your changes -Add test example in [tests-example](https://github.com/react-native-svg/react-native-svg/tree/main/tests-example) concerning your change following the convention of `TestX.tsx` where `X` is your PR number. +Add test example in [tests-example](https://github.com/react-native-svg/react-native-svg/tree/main/tests-example) +concerning your change following the convention of `TestX.tsx` where `X` is your PR number. ## Tests -We use `typescript` for type checks, `eslint` with `prettier` for linting/formatting. All tests are run by github actions for all opened pull requests. +We use `typescript` for type checks, `eslint` with `prettier` for linting/formatting. All tests are run by github +actions for all opened pull requests. - `yarn test`: Run all tests, except for e2e (see note below). - `yarn lint`: Run `eslint` check. - `yarn tsc`: Run `typescript` check. - `yarn jest`: Run `jest` type check. +- `yarn e2e`: Run E2E tests (see section below) -Currently e2e tests exist here: https://github.com/msand/react-native-svg-e2e/ +### Running E2E tests: + +> [!WARNING] +> Reference images in this repository are generated with [pixel ratio](https://reactnative.dev/docs/pixelratio) = `3`. +> Make sure to run tests on a device that also has pixel ratio equal 3. Otherwise tests will fail. +> In order to use device with different pixel ratio, adjust it in `e2e/generateRefereces.ts` viewport and regenerate +> references. + +1. Navigate to the example application's directory and initiate the Metro server using the yarn start command. +2. To run the example application on your preferred platform (note: currently only Android and iOS are supported), + execute the command `yarn android` or `yarn ios` within the example app's directory. +3. Start the Jest server by running `yarn e2e` in the project's root directory. +4. In the example application, select the E2E tab. +5. Allow the tests to complete. +6. The test results, along with any differences (i.e. the actual rendered svg versus the reference image), can be viewed + in the `e2e/diffs` directory. + +### To add new E2E test cases, proceed as follows: + +1. Put an SVG file of your selection into the `e2e/cases` directory. +2. Execute `yarn generateE2eRefrences`. This action launches a headless Chrome browser via Puppeteer, capturing + snapshots of all rendered SVGs as .png files. These files will serve as a reference during testing. +3. Check the `e2e/references` directory to observe newly created .png files. +4. When you rerun the E2E tests, the new test case(s) you've added will be incorporated. ## Sending a pull request When you're sending a pull request: -- Communication is a key. If you want fix/add something, please consider either opening a new issue or finding an existing one so we can further discuss it. +- Communication is a key. If you want fix/add something, please consider either opening a new issue or finding an + existing one so we can further discuss it. - We prefer small pull requests focused on one change, as those are easier to test/check. - Please make sure that all tests are passing on your local machine. - Follow the template when opening a PR. @@ -43,7 +71,8 @@ Most notably prefixes you'll see: - **fix**: Bug fixes - **feat**: New feature implemented -- **chore**: Changes that are not affecting end user (CI config changes, scripts, ["grunt work"](https://stackoverflow.com/a/26944812/3510245)) +- **chore**: Changes that are not affecting end user (CI config changes, + scripts, ["grunt work"](https://stackoverflow.com/a/26944812/3510245)) - **docs**: Documentation changes. - **perf**: A code change that improves performance. - **refactor**: A code change that neither fixes a bug nor adds a feature. @@ -55,7 +84,8 @@ We use [release-it](https://github.com/release-it/release-it) to release new ver ## Reporting issues -You can report issues on our [bug tracker](https://github.com/react-native-community/react-native-svg/issues). Please search for existing issues and follow the issue template when opening one. +You can report issues on our [bug tracker](https://github.com/react-native-community/react-native-svg/issues). Please +search for existing issues and follow the issue template when opening one. ## License diff --git a/__tests__/e2e/GeneralSvgRenderingTest.spec.tsx b/__tests__/e2e/GeneralSvgRenderingTest.spec.tsx new file mode 100644 index 00000000..38a1513f --- /dev/null +++ b/__tests__/e2e/GeneralSvgRenderingTest.spec.tsx @@ -0,0 +1,94 @@ +import { SvgFromXml } from 'react-native-svg'; +import * as fs from 'node:fs'; +import { compareImages, sendToDeviceAndReceive } from '../../e2e/helpers'; +import { HandshakeMessageData, RenderResponse } from '../../e2e/types'; +import path from 'path'; +import { + addAttach as attachImageToReport, + addMsg as addMessageToReport, +} from 'jest-html-reporters/helper'; +import { PNG } from 'pngjs'; +import failedCases from '../../e2e/failedCases.json'; +import { verifyComparisons } from '../../e2e/matchTestCases'; +import { height, targetPixelRatio, width } from '../../e2e/env'; + +const testCases = fs.readdirSync(path.resolve('e2e', 'cases')); +testCases.forEach((testCase) => { + jest.setTimeout(90_000); + test(`Web browser rendered SVG should have less than 0.05% differences between device rendered SVG (${testCase})`, async () => { + await addMessageToReport({ + message: JSON.stringify({ + os: global.os, + arch: global.arch, + }), + }); + const testCaseSvg = path.resolve('e2e', 'cases', testCase); + + const svgXml = fs.readFileSync(testCaseSvg).toString('utf-8'); + const response = await sendToDeviceAndReceive({ + type: 'renderRequest', + data: , + height, + width, + }); + + const referenceFilePath = path.resolve( + 'e2e', + 'references', + testCase.replace('.svg', '.png') + ); + const renderedFilePath = path.resolve( + 'e2e', + 'rendered', + `${testCase.replace('.svg', '')}-${global.os}-${global.arch}-rendered.png` + ); + const diffFilePath = path.resolve( + 'e2e', + 'diffs', + `${testCase.replace('.svg', '')}-${global.os}-${global.arch}-diff.png` + ); + const referenceFileBuffer = fs.readFileSync(referenceFilePath); + const renderedDataBuffer = Buffer.from(response.data, 'base64'); + + // We use await everywhere instead Promise.all as we need to maintain order for ease of inspecting tests + // Adding reference & rendered before comparison in case compareImages fails, so we can see why it failed + await attachImageToReport({ + attach: fs.readFileSync(referenceFilePath), + description: 'Reference image', + bufferFormat: 'png', + }); + await attachImageToReport({ + attach: PNG.sync.write(PNG.sync.read(renderedDataBuffer)), + description: 'Actual rendered image', + bufferFormat: 'png', + }); + + // Compare reference file (from /e2e/references) with SVG rendered on actual device. + // Reference files can be generated off of /e2e/cases with `yarn generateE2eReferences`. + const amountOfDifferentPixels = compareImages( + referenceFileBuffer, + renderedDataBuffer, + { + width, + height, + pixelRatio: targetPixelRatio, + diffFilePath, + renderedFilePath, + } + ); + + await attachImageToReport({ + attach: fs.readFileSync(diffFilePath), + description: 'Differences', + bufferFormat: 'png', + }); + + // Check if there is more than 0.5% different pixels in whole snapshot + verifyComparisons( + amountOfDifferentPixels, + failedCases, + global as unknown as HandshakeMessageData, + testCase + ); + }); +}); diff --git a/apps/examples/index.tsx b/apps/examples/index.tsx index 086a108f..35ea3814 100644 --- a/apps/examples/index.tsx +++ b/apps/examples/index.tsx @@ -7,45 +7,22 @@ import React, {Component} from 'react'; import { Dimensions, + Modal, + Platform, + SafeAreaView, + ScrollView, StyleSheet, Text, - View, - ScrollView, TouchableHighlight, TouchableOpacity, - SafeAreaView, + View, } from 'react-native'; -import {Modal, Platform} from 'react-native'; -import {Svg, Circle, Line} from 'react-native-svg'; +import {Circle, Line, Svg} from 'react-native-svg'; -import * as examples from './src/examples'; import {commonStyles} from './src/commonStyles'; - -const names: (keyof typeof examples)[] = [ - 'Svg', - 'Stroking', - 'Path', - 'Line', - 'Rect', - 'Polygon', - 'Polyline', - 'Circle', - 'Ellipse', - 'G', - 'Text', - 'Gradients', - 'Clipping', - 'Image', - 'TouchEvents', - 'PanResponder', - 'Reusable', - 'Reanimated', - 'Transforms', - 'Markers', - 'Mask', - 'Filters', - 'FilterImage', -]; +import E2eTestingView from './src/e2e'; +import * as examples from './src/examples'; +import {names} from './utils/names'; const initialState = { modal: false, @@ -88,25 +65,30 @@ export default class SvgExample extends Component { }; getExamples = () => { - return names.map(name => { - var icon; - let example = examples[name]; - if (example) { - icon = example.icon; - } - return ( - this.show(name)}> - - {icon} - {name} - - - ); - }); + return names + .filter(el => { + if (el !== 'E2E') return true; + return Platform.OS === 'android' || Platform.OS === 'ios'; + }) + .map(name => { + var icon; + let example = examples[name as keyof typeof examples]; + if (example) { + icon = example.icon; + } + return ( + this.show(name as keyof typeof examples)}> + + {icon} + {name} + + + ); + }); }; modalContent = () => ( @@ -132,6 +114,12 @@ export default class SvgExample extends Component { ); render() { + if (process.env.E2E) { + console.log( + 'Opening E2E example, as E2E env is set to ' + process.env.E2E, + ); + return ; + } return ( SVG library for React Apps diff --git a/apps/examples/src/e2e/TestingView.tsx b/apps/examples/src/e2e/TestingView.tsx new file mode 100644 index 00000000..5ca086b8 --- /dev/null +++ b/apps/examples/src/e2e/TestingView.tsx @@ -0,0 +1,158 @@ +import React, { + Component, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; +import {Platform, Text, View} from 'react-native'; +import * as RNSVG from 'react-native-svg'; +import ViewShot from 'react-native-view-shot'; + +const address = ['ios', 'web'].includes(Platform.OS) ? 'localhost' : '10.0.2.2'; +const wsUri = `ws://${address}:7123`; + +const TestingView = () => { + const wrapperRef = useRef(null); + const [wsClient, setWsClient] = useState(null); + const [renderedContent, setRenderedContent] = + useState(); + const [readyToSnapshot, setReadyToSnapshot] = useState(false); + const [resolution, setResolution] = useState([0, 0]); // placeholder value, later updated by incoming render requests + const [message, setMessage] = useState('⏳ Connecting to Jest server...'); + + const connect = useCallback(() => { + const client = new WebSocket(wsUri); + setWsClient(client); + setMessage('⏳ Connecting to Jest server...'); + client.onopen = () => { + client.send( + JSON.stringify({ + os: Platform.OS, + version: Platform.Version, + arch: isFabric() ? 'fabric' : 'paper', + connectionTime: new Date(), + }), + ); + setMessage('✅ Connected to Jest server. Waiting for render requests.'); + }; + client.onerror = (err: any) => { + if (!err.message) { + return; + } + console.error( + `Error while connecting to E2E WebSocket server at ${wsUri}: ${err.message}. Will retry in 3 seconds.`, + ); + setMessage( + `🚨 Failed to connect to Jest server at ${wsUri}: ${err.message}! Will retry in 3 seconds.`, + ); + setTimeout(() => { + connect(); + }, 3000); + }; + client.onmessage = ({data: rawMessage}) => { + const message = JSON.parse(rawMessage); + if (message.type == 'renderRequest') { + setMessage(`✅ Rendering tests, please don't close this tab.`); + setResolution([message.width, message.height]); + setRenderedContent( + createElementFromObject( + message.data.type || 'SvgFromXml', + message.data.props, + ), + ); + setReadyToSnapshot(true); + } + }; + client.onclose = event => { + if (event.code == 1006 && event.reason) { + // this is an error, let error handler take care of it + return; + } + setMessage( + `✅ Connection to Jest server has been closed. You can close this tab safely. (${event.code})`, + ); + }; + }, [wsClient]); + + // Create initial connection when rendering the view + useEffect(connect, []); + + // Whenever new content is rendered, send renderResponse with snapshot view + useEffect(() => { + if (!readyToSnapshot || !wrapperRef.current) { + return; + } + wrapperRef.current.capture?.().then((value: string) => { + wsClient?.send( + JSON.stringify({ + type: 'renderResponse', + data: value, + }), + ); + setReadyToSnapshot(false); + }); + }, [wrapperRef, readyToSnapshot]); + + return ( + + {message} + + {renderedContent} + + + ); +}; + +class TestingViewWrapper extends Component { + static title = 'E2E Testing'; + + render() { + return ; + } +} + +const samples = [TestingViewWrapper]; +const icon = ( + + + +); + +function isFabric(): boolean { + // @ts-expect-error nativeFabricUIManager is not yet included in the RN types + return !!global?.nativeFabricUIManager; +} + +export {samples, icon}; + +const createElementFromObject = ( + element: keyof typeof RNSVG, + props: any, +): React.ReactElement => { + const children: any[] = []; + if (props.children) { + if (Array.isArray(props.children)) { + props?.children.forEach((child: {type: any; props: any}) => + children.push(createElementFromObject(child.type, child?.props)), + ); + } else if (typeof props.children === 'object') { + children.push( + createElementFromObject(props.children.type, props.children?.props), + ); + } else { + children.push(props.children); + } + } + return React.createElement(RNSVG[element] as any, {...props, children}); +}; diff --git a/apps/examples/src/e2e/index.macos.tsx b/apps/examples/src/e2e/index.macos.tsx new file mode 100644 index 00000000..dc37d27e --- /dev/null +++ b/apps/examples/src/e2e/index.macos.tsx @@ -0,0 +1,3 @@ +export default function () { + return null; +} diff --git a/apps/examples/src/e2e/index.tsx b/apps/examples/src/e2e/index.tsx new file mode 100644 index 00000000..1cfc0097 --- /dev/null +++ b/apps/examples/src/e2e/index.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import {SafeAreaView} from 'react-native'; +import {samples} from './TestingView'; + +export default function () { + const e2eTab = React.createElement(samples[0]); + return {e2eTab}; +} diff --git a/apps/examples/src/e2e/index.web.tsx b/apps/examples/src/e2e/index.web.tsx new file mode 100644 index 00000000..dc37d27e --- /dev/null +++ b/apps/examples/src/e2e/index.web.tsx @@ -0,0 +1,3 @@ +export default function () { + return null; +} diff --git a/apps/examples/src/examples.macos.tsx b/apps/examples/src/examples.macos.tsx new file mode 100644 index 00000000..b5567a9d --- /dev/null +++ b/apps/examples/src/examples.macos.tsx @@ -0,0 +1,49 @@ +import * as Svg from './examples/Svg'; +import * as Rect from './examples/Rect'; +import * as Circle from './examples/Circle'; +import * as Ellipse from './examples/Ellipse'; +import * as Line from './examples/Line'; +import * as Polygon from './examples/Polygon'; +import * as Polyline from './examples/Polyline'; +import * as Path from './examples/Path'; +import * as Text from './examples/Text'; +import * as G from './examples/G'; +import * as Stroking from './examples/Stroking'; +import * as Gradients from './examples/Gradients'; +import * as Clipping from './examples/Clipping'; +import * as Image from './examples/Image'; +import * as Reusable from './examples/Reusable'; +import * as TouchEvents from './examples/TouchEvents'; +import * as PanResponder from './examples/PanResponder'; +import * as Reanimated from './examples/Reanimated'; +import * as Transforms from './examples/Transforms'; +import * as Markers from './examples/Markers'; +import * as Mask from './examples/Mask'; +import * as Filters from './examples/Filters'; +import * as FilterImage from './examples/FilterImage'; + +export { + Svg, + Rect, + Circle, + Ellipse, + Line, + Polygon, + Polyline, + Path, + Text, + Stroking, + G, + Gradients, + Clipping, + Image, + TouchEvents, + Reusable, + PanResponder, + Reanimated, + Transforms, + Markers, + Mask, + Filters, + FilterImage, +}; diff --git a/apps/examples/src/examples.tsx b/apps/examples/src/examples.tsx index b5567a9d..046e2bb1 100644 --- a/apps/examples/src/examples.tsx +++ b/apps/examples/src/examples.tsx @@ -19,6 +19,7 @@ import * as Reanimated from './examples/Reanimated'; import * as Transforms from './examples/Transforms'; import * as Markers from './examples/Markers'; import * as Mask from './examples/Mask'; +import * as E2E from './e2e/TestingView'; import * as Filters from './examples/Filters'; import * as FilterImage from './examples/FilterImage'; @@ -44,6 +45,7 @@ export { Transforms, Markers, Mask, + E2E, Filters, FilterImage, }; diff --git a/apps/examples/tsconfig.json b/apps/examples/tsconfig.json index 19aefad2..1ca4a89a 100644 --- a/apps/examples/tsconfig.json +++ b/apps/examples/tsconfig.json @@ -1,4 +1,7 @@ { + "compilerOptions": { + "moduleSuffixes": [".macos", ""] + }, "extends": "../../tsconfig.json", "include": ["**/*.ts", "**/*.tsx", "**/*.js"] } diff --git a/apps/examples/utils/names.ts b/apps/examples/utils/names.ts new file mode 100644 index 00000000..9380caaa --- /dev/null +++ b/apps/examples/utils/names.ts @@ -0,0 +1,28 @@ +import {ExamplesKey} from './type'; + +export const names: ExamplesKey[] = [ + 'Svg', + 'Stroking', + 'Path', + 'Line', + 'Rect', + 'Polygon', + 'Polyline', + 'Circle', + 'Ellipse', + 'G', + 'Text', + 'Gradients', + 'Clipping', + 'Image', + 'TouchEvents', + 'PanResponder', + 'Reusable', + 'Reanimated', + 'Transforms', + 'Markers', + 'Mask', + 'E2E', + 'Filters', + 'FilterImage', +]; diff --git a/apps/examples/utils/type.ts b/apps/examples/utils/type.ts new file mode 100644 index 00000000..98a1f7b6 --- /dev/null +++ b/apps/examples/utils/type.ts @@ -0,0 +1,3 @@ +import * as examples from '../src/examples'; + +export type ExamplesKey = keyof typeof examples | 'E2E'; diff --git a/e2e/cases/1.svg b/e2e/cases/1.svg new file mode 100644 index 00000000..a36cea7e --- /dev/null +++ b/e2e/cases/1.svg @@ -0,0 +1 @@ + diff --git a/e2e/cases/2.svg b/e2e/cases/2.svg new file mode 100644 index 00000000..4f3f6bf0 --- /dev/null +++ b/e2e/cases/2.svg @@ -0,0 +1,10 @@ + + + + + + + + + SVG + diff --git a/e2e/cases/3.svg b/e2e/cases/3.svg new file mode 100644 index 00000000..b398be2a --- /dev/null +++ b/e2e/cases/3.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/e2e/diffs/.gitignore b/e2e/diffs/.gitignore new file mode 100644 index 00000000..568233f6 --- /dev/null +++ b/e2e/diffs/.gitignore @@ -0,0 +1,2 @@ +# This directory will contain difference images for each test after the tests are ran. Do not commit those. +** diff --git a/e2e/env.ts b/e2e/env.ts new file mode 100644 index 00000000..103634f9 --- /dev/null +++ b/e2e/env.ts @@ -0,0 +1,4 @@ +export const width = 200; +export const height = 200; +export const maxPixelDiff = width * height * 0.005; +export const targetPixelRatio = 3.0; diff --git a/e2e/failedCases.json b/e2e/failedCases.json new file mode 100644 index 00000000..e2f2a75b --- /dev/null +++ b/e2e/failedCases.json @@ -0,0 +1,10 @@ +{ + "ios": { + "paper": [], + "fabric": [] + }, + "android": { + "paper": ["2"], + "fabric": ["2"] + } +} diff --git a/e2e/generateReferences.ts b/e2e/generateReferences.ts new file mode 100644 index 00000000..85b56a26 --- /dev/null +++ b/e2e/generateReferences.ts @@ -0,0 +1,35 @@ +const path = require('path'); +const puppeteer = require('puppeteer'); +const fs = require('fs'); + +const main = async () => { + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await page.setViewport({ + height: 200, + width: 200, + // This is hardcoded value which makes it possible to use only devices with pixel ratio = 3. You can change it + // and regenerate reference images if you want to use device with different pixel ratio + // see: https://reactnative.dev/docs/pixelratio + deviceScaleFactor: 3, + }); + const casesPath = path.resolve('e2e', 'cases'); + const referencesPath = path.resolve('e2e', 'references'); + const cases = fs.readdirSync(casesPath); + for (const testCase of cases) { + const svgPath = path.resolve(casesPath, testCase); + await page.goto(`file://${svgPath}`); + await page.$eval('svg', (ev: Element) => { + ev.setAttribute('width', '200'); + ev.setAttribute('height', '200'); + }); + const svg = await page.waitForSelector('svg'); + await svg.screenshot({ + path: path.resolve(referencesPath, testCase.replace('.svg', '.png')), + }); + } + + await browser.close(); +}; + +main(); diff --git a/e2e/globals.d.ts b/e2e/globals.d.ts new file mode 100644 index 00000000..f50d864c --- /dev/null +++ b/e2e/globals.d.ts @@ -0,0 +1,12 @@ +/* eslint-disable no-var */ +import { WebSocket as WsWebSocket, WebSocketServer } from 'ws'; + +declare global { + namespace globalThis { + // Leave it as var, changing to let will cause it not to work + var server: WebSocketServer; + var client: WsWebSocket; + var os: string; + var arch: string; + } +} diff --git a/e2e/helpers.ts b/e2e/helpers.ts new file mode 100644 index 00000000..7760b065 --- /dev/null +++ b/e2e/helpers.ts @@ -0,0 +1,57 @@ +import { E2EMessage } from './types'; +import { PNG } from 'pngjs'; +import fs from 'node:fs'; +import pixelmatch from 'pixelmatch'; + +const replacer = (key: string, value: any) => { + if (key === 'type' && typeof value !== 'string') return value.displayName; + return value; +}; + +export const sendToDeviceAndReceive = (message: E2EMessage) => + new Promise((resolve) => { + global.client.once('message', (message) => { + const parsedMessage: E2EMessage = JSON.parse(message.toString('utf-8')); + resolve(parsedMessage as R); + }); + global.client.send(JSON.stringify(message, replacer)); + }); + +export const compareImages = ( + image1: Buffer, + image2: Buffer, + opts: { + width: number; + height: number; + pixelRatio: number; + diffFilePath?: string; + renderedFilePath?: string; + } +) => { + const referencePng = PNG.sync.read(image1); + const responsePng = PNG.sync.read(image2); + + const diffPng = new PNG({ + height: referencePng.height, + width: referencePng.width, + }); + + const pixelDiff = pixelmatch( + referencePng.data, + responsePng.data, + diffPng.data, + opts.width * opts.pixelRatio, + opts.height * opts.pixelRatio, + { + // That #5f00a0 is the color of the diff pixels + diffColor: [95, 0, 160], + } + ); + if (opts.renderedFilePath) { + fs.writeFileSync(opts.renderedFilePath, PNG.sync.write(responsePng)); + } + if (opts.diffFilePath) { + fs.writeFileSync(opts.diffFilePath, PNG.sync.write(diffPng)); + } + return pixelDiff; +}; diff --git a/e2e/matchTestCases.ts b/e2e/matchTestCases.ts new file mode 100644 index 00000000..4e5482d5 --- /dev/null +++ b/e2e/matchTestCases.ts @@ -0,0 +1,30 @@ +import { maxPixelDiff, targetPixelRatio } from './env'; +// import { storeFailedResult } from './readFailedCases'; +import { FailedResults, HandshakeMessageData } from './types'; + +const extractSvgNumber = (testCase: string) => { + const match = testCase.match(/(\d+)\.svg$/)!; + return parseInt(match[1], 10).toString(); +}; + +export function verifyComparisons( + amountOfDifferentPixels: number, + failedCases: FailedResults, + global: HandshakeMessageData, + testCase: string +) { + // if (amountOfDifferentPixels > maxPixelDiff * targetPixelRatio) { + // storeFailedResult(global.os, global.arch, matchTestCase(testCase)); + // } + if ( + failedCases[global.os][global.arch].includes(extractSvgNumber(testCase)) + ) { + expect(amountOfDifferentPixels).toBeGreaterThan( + maxPixelDiff * targetPixelRatio + ); + } else { + expect(amountOfDifferentPixels).toBeLessThan( + maxPixelDiff * targetPixelRatio + ); + } +} diff --git a/e2e/readFailedCases.ts b/e2e/readFailedCases.ts new file mode 100644 index 00000000..541cef3a --- /dev/null +++ b/e2e/readFailedCases.ts @@ -0,0 +1,56 @@ +import { Arch, OS } from './types'; +import path from 'path'; +import fs from 'fs'; + +const filePath = path.join(__dirname, 'failedCases.json'); + +function readFileOrCreateIfNotExists(filePath: string): object { + try { + if (fs.existsSync(filePath)) { + const data = fs.readFileSync(filePath, 'utf8'); + return JSON.parse(data); + } else { + const emptyObject = {}; + fs.writeFileSync(filePath, JSON.stringify(emptyObject, null, 2), 'utf8'); + return emptyObject; + } + } catch (error) { + console.error('Error occurred:', error); + return {}; + } +} + +interface UpdateFailedResults { + ios?: { + paper?: string[]; + fabric?: string[]; + }; + android?: { paper?: string[]; fabric?: string[] }; +} + +export function storeFailedResult( + platform: OS, + architecture: Arch, + value: string +): void { + try { + const data = readFileOrCreateIfNotExists(filePath) as UpdateFailedResults; + + if (!data[platform]) { + data[platform] = {}; + } + + if (!data[platform]?.[architecture]) { + data[platform]![architecture] = []; + } + + if (!data[platform]?.[architecture]?.includes(value)) { + data[platform]?.[architecture]?.push(value); + } + + fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8'); + console.log('Data updated successfully:', data); + } catch (error) { + console.error('Error occurred while updating the file:', error); + } +} diff --git a/e2e/references/1.png b/e2e/references/1.png new file mode 100644 index 0000000000000000000000000000000000000000..8b034a5021f1e2b0c8b612d679e13f71d978b256 GIT binary patch literal 20868 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

85t^8vj`hKrsgtECk3Q=k`! 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 MIW^gG>34zu53#mq1poj5 literal 0 HcmV?d00001 diff --git a/e2e/references/3.png b/e2e/references/3.png new file mode 100644 index 0000000000000000000000000000000000000000..2724d2ea2a418dc7225793cb367a4d321bf0c3fc GIT binary patch literal 8405 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 DczEEz literal 0 HcmV?d00001 diff --git a/e2e/rendered/.gitignore b/e2e/rendered/.gitignore new file mode 100644 index 00000000..51fd3c5d --- /dev/null +++ b/e2e/rendered/.gitignore @@ -0,0 +1,3 @@ +# This directory will contain rendered images for each test after the tests are ran. Do not commit those. +!.gitignore +** diff --git a/e2e/setupJest.ts b/e2e/setupJest.ts new file mode 100644 index 00000000..8330f44b --- /dev/null +++ b/e2e/setupJest.ts @@ -0,0 +1,35 @@ +import { WebSocketServer } from 'ws'; + +// This is little hack, we don't use syntax sugar with `async` here as we need to manually resolve promise +// whenever a client connects using resolve callback. In result the Jest will wait until some device connects, +// so it can be used to render test cases. +const setupJest = () => + new Promise((resolve) => { + const wsServer = new WebSocketServer({ port: 7123 }); + wsServer.on('connection', (client) => { + global.client = client; + + // Add handler for one-time handshake message that confirms the client has connected properly + client.once('message', (message) => { + const parsedMessage = JSON.parse(message.toString('utf-8')); + console.log( + `[react-native-svg] Received handshake from a test client: ${JSON.stringify( + parsedMessage + )}` + ); + + global.os = parsedMessage.os; + global.arch = parsedMessage.arch; + + console.log(`[react-native-svg] Running E2E test suites...\n`); + resolve(); + }); + }); + + console.log( + '\n\n[react-native-svg] E2E WebSocket server is running, waiting for client connection. Run example app and select E2E from examples list.\n' + ); + global.server = wsServer; + }); + +export default setupJest; diff --git a/e2e/teardownJest.ts b/e2e/teardownJest.ts new file mode 100644 index 00000000..68d08ab7 --- /dev/null +++ b/e2e/teardownJest.ts @@ -0,0 +1,4 @@ +export default () => { + global.server.clients.forEach((client) => client.close(1000)) + global.server.close() +}; diff --git a/e2e/types.ts b/e2e/types.ts new file mode 100644 index 00000000..7a056a96 --- /dev/null +++ b/e2e/types.ts @@ -0,0 +1,38 @@ +export type E2EMessage = HandshakeMessage | RenderRequest | RenderResponse; + +export interface HandshakeMessage { + type: 'handshake'; + data: { + os: string; + arch: 'paper' | 'fabric'; + platformVersion: string; + }; +} + +export type OS = 'ios' | 'android'; +export type Arch = 'paper' | 'fabric'; +export interface HandshakeMessageData { + os: OS; + arch: Arch; + platformVersion: string; +} + +export interface RenderRequest { + type: 'renderRequest'; + data: any; + width: number; + height: number; +} + +export interface RenderResponse { + type: 'renderResponse'; + data: string; // as base64 +} + +export interface FailedResults { + ios: { + paper: string[]; + fabric: string[]; + }; + android: { paper: string[]; fabric: string[] }; +} diff --git a/example/babel.config.js b/example/babel.config.js index 950d3b2e..bd798c39 100644 --- a/example/babel.config.js +++ b/example/babel.config.js @@ -2,6 +2,7 @@ module.exports = { presets: ['module:@react-native/babel-preset'], plugins: [ '@babel/plugin-proposal-export-namespace-from', + 'module:react-native-dotenv', 'react-native-reanimated/plugin', ], }; diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 63b3f625..ccbb7925 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -935,6 +935,8 @@ PODS: - React-Mapbuffer (0.74.2): - glog - React-debug + - react-native-view-shot (4.0.0-alpha.2): + - React-Core - React-nativeconfig (0.74.2) - React-NativeModulesApple (0.74.2): - glog @@ -1185,7 +1187,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSVG (15.5.0): + - RNSVG (15.6.0): - React-Core - SocketRocket (0.7.0) - Yoga (0.0.0) @@ -1223,6 +1225,7 @@ DEPENDENCIES: - React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`) - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) + - react-native-view-shot (from `../node_modules/react-native-view-shot`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) @@ -1316,6 +1319,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/logger" React-Mapbuffer: :path: "../node_modules/react-native/ReactCommon" + react-native-view-shot: + :path: "../node_modules/react-native-view-shot" React-nativeconfig: :path: "../node_modules/react-native/ReactCommon" React-NativeModulesApple: @@ -1374,9 +1379,9 @@ SPEC CHECKSUMS: DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 FBLazyVector: 4bc164e5b5e6cfc288d2b5ff28643ea15fa1a589 fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 - glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 + glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f hermes-engine: 01d3e052018c2a13937aca1860fbedbccd4a41b7 - RCT-Folly: 045d6ecaa59d826c5736dfba0b2f4083ff8d79df + RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 RCTDeprecation: b03c35057846b685b3ccadc9bfe43e349989cdb2 RCTRequired: 194626909cfa8d39ca6663138c417bc6c431648c RCTTypeSafety: 552aff5b8e8341660594db00e53ac889682bc120 @@ -1400,6 +1405,7 @@ SPEC CHECKSUMS: React-jsitracing: 0fa7f78d8fdda794667cb2e6f19c874c1cf31d7e React-logger: 29fa3e048f5f67fe396bc08af7606426d9bd7b5d React-Mapbuffer: bf56147c9775491e53122a94c423ac201417e326 + react-native-view-shot: 8ffbe24e3b5207d66816afb5b3c2ca82731ab636 React-nativeconfig: 9f223cd321823afdecf59ed00861ab2d69ee0fc1 React-NativeModulesApple: ff7efaff7098639db5631236cfd91d60abff04c0 React-perflogger: 32ed45d9cee02cf6639acae34251590dccd30994 @@ -1424,7 +1430,7 @@ SPEC CHECKSUMS: React-utils: 4476b7fcbbd95cfd002f3e778616155241d86e31 ReactCommon: ecad995f26e0d1e24061f60f4e5d74782f003f12 RNReanimated: 9c213184c27dc4a2ed7e9ff41a4b0b9258bb54f0 - RNSVG: b986585e367f4a49d8aa43065066cc9c290b3d9b + RNSVG: 5da7a24f31968ec74f0b091e3440080f347e279b SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: 2f71ecf38d934aecb366e686278102a51679c308 diff --git a/example/package.json b/example/package.json index 4364c9f0..d67c425e 100644 --- a/example/package.json +++ b/example/package.json @@ -18,6 +18,7 @@ "react-native": "0.74.2", "react-native-reanimated": "3.13.0", "react-native-svg": "link:../", + "react-native-view-shot": "4.0.0-alpha.2", "react-native-windows": "0.74.9" }, "devDependencies": { @@ -40,6 +41,7 @@ "patch-package": "^8.0.0", "postinstall-postinstall": "^2.1.0", "prettier": "2.8.8", + "react-native-dotenv": "^3.4.11", "react-test-renderer": "18.2.0", "typescript": "5.0.4" }, diff --git a/example/patches/react-native-view-shot+4.0.0-alpha.2.patch b/example/patches/react-native-view-shot+4.0.0-alpha.2.patch new file mode 100644 index 00000000..0caa5602 --- /dev/null +++ b/example/patches/react-native-view-shot+4.0.0-alpha.2.patch @@ -0,0 +1,51 @@ +diff --git a/node_modules/react-native-view-shot/ios/RNViewShot.m b/node_modules/react-native-view-shot/ios/RNViewShot.m +index bd55b92..6a20e9d 100644 +--- a/node_modules/react-native-view-shot/ios/RNViewShot.m ++++ b/node_modules/react-native-view-shot/ios/RNViewShot.m +@@ -106,7 +106,7 @@ - (dispatch_queue_t)methodQueue + scrollView.frame = CGRectMake(0, 0, scrollView.contentSize.width, scrollView.contentSize.height); + } + +- UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size]; ++ UIGraphicsBeginImageContextWithOptions(size, NO, 0); + + if (renderInContext) { + // this comes with some trade-offs such as inability to capture gradients or scrollview's content in full but it works for large views +@@ -117,8 +117,8 @@ - (dispatch_queue_t)methodQueue + // this doesn't work for large views and reports incorrect success even though the image is blank + success = [rendered drawViewHierarchyInRect:(CGRect){CGPointZero, size} afterScreenUpdates:YES]; + } +- +- UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {}]; ++ UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); ++ UIGraphicsEndImageContext(); + + if (snapshotContentContainer) { + // Restore scroll & frame +@@ -152,11 +152,11 @@ - (dispatch_queue_t)methodQueue + NSString *res = nil; + if ([result isEqualToString:@"base64"]) { + // Return as a base64 raw string +- res = [data base64EncodedStringWithOptions: 0]; ++ res = [data base64EncodedStringWithOptions: NSDataBase64EncodingEndLineWithLineFeed]; + } + else if ([result isEqualToString:@"data-uri"]) { + // Return as a base64 data uri string +- NSString *base64 = [data base64EncodedStringWithOptions: 0]; ++ NSString *base64 = [data base64EncodedStringWithOptions: NSDataBase64EncodingEndLineWithLineFeed]; + NSString *imageFormat = ([format isEqualToString:@"jpg"]) ? @"jpeg" : format; + res = [NSString stringWithFormat:@"data:image/%@;base64,%@", imageFormat, base64]; + } +diff --git a/node_modules/react-native-view-shot/src/specs/NativeRNViewShot.ts b/node_modules/react-native-view-shot/src/specs/NativeRNViewShot.ts +index a6f4c00..1e9e6ce 100644 +--- a/node_modules/react-native-view-shot/src/specs/NativeRNViewShot.ts ++++ b/node_modules/react-native-view-shot/src/specs/NativeRNViewShot.ts +@@ -2,7 +2,7 @@ import type { TurboModule } from 'react-native'; + import { TurboModuleRegistry } from 'react-native'; + + export interface Spec extends TurboModule { +- releaseCapture: () => string; ++ releaseCapture: (uri: string) => void; + captureRef: (tag: number, options: Object) => Promise + captureScreen: (options: Object) => Promise; + } \ No newline at end of file diff --git a/example/yarn.lock b/example/yarn.lock index 82d07ac5..1fec35ae 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -3217,6 +3217,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -3641,6 +3646,13 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + css-select@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" @@ -3841,6 +3853,11 @@ domutils@^3.0.1: domelementtype "^2.3.0" domhandler "^5.0.3" +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -4757,6 +4774,14 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html2canvas@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -7060,6 +7085,13 @@ 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-native-dotenv@^3.4.11: + version "3.4.11" + resolved "https://registry.yarnpkg.com/react-native-dotenv/-/react-native-dotenv-3.4.11.tgz#2e6c4eabd55d5f1bf109b3dd9141dadf9c55cdd4" + integrity sha512-6vnIE+WHABSeHCaYP6l3O1BOEhWxKH6nHAdV7n/wKn/sciZ64zPPp2NUdEUf1m7g4uuzlLbjgr+6uDt89q2DOg== + dependencies: + dotenv "^16.4.5" + react-native-reanimated@3.13.0: version "3.13.0" resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.13.0.tgz#723687cd6ff4ce674800299c6917e4a3b088d89e" @@ -7078,6 +7110,13 @@ react-native-reanimated@3.13.0: version "0.0.0" uid "" +react-native-view-shot@4.0.0-alpha.2: + version "4.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/react-native-view-shot/-/react-native-view-shot-4.0.0-alpha.2.tgz#389f5323722c3acad1c002bf4f0f0539fd2508c9" + integrity sha512-BNQ+FoOUrB2Y6zxwhSZtZMMXVLewN2Ic8y5gS+5Y7hdgphqWmlHZIGbcLn/NmJS+soz0Pe3WO1sM5vX6u7h6VQ== + dependencies: + html2canvas "^1.4.1" + react-native-windows@0.74.9: version "0.74.9" resolved "https://registry.yarnpkg.com/react-native-windows/-/react-native-windows-0.74.9.tgz#2abcc8eb99a4ce0ce80dfe2dea8ce249b8e21469" @@ -7937,6 +7976,13 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -8173,6 +8219,13 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" diff --git a/fabric-example/ios/Podfile.lock b/fabric-example/ios/Podfile.lock index 02655332..93b354ef 100644 --- a/fabric-example/ios/Podfile.lock +++ b/fabric-example/ios/Podfile.lock @@ -1237,6 +1237,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - react-native-view-shot (4.0.0-alpha.2): + - React-Core - React-nativeconfig (0.75.1) - React-NativeModulesApple (0.75.1): - glog @@ -1562,7 +1564,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSVG (15.5.0): + - RNSVG (15.6.0): - DoubleConversion - glog - hermes-engine @@ -1582,9 +1584,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNSVG/common (= 15.5.0) + - RNSVG/common (= 15.6.0) - Yoga - - RNSVG/common (15.5.0): + - RNSVG/common (15.6.0): - DoubleConversion - glog - hermes-engine @@ -1646,6 +1648,7 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) + - react-native-view-shot (from `../node_modules/react-native-view-shot`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) @@ -1752,6 +1755,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" React-microtasksnativemodule: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" + react-native-view-shot: + :path: "../node_modules/react-native-view-shot" React-nativeconfig: :path: "../node_modules/react-native/ReactCommon" React-NativeModulesApple: @@ -1847,6 +1852,7 @@ SPEC CHECKSUMS: React-logger: 0a81d1a40650bbdafb255fe4616edb83feed0ee9 React-Mapbuffer: b758bec0d9994c10a2841dfd5ec70673665fd3e2 React-microtasksnativemodule: 988e6ed065c061554ec09fdbc47c1f7c7f6478fe + react-native-view-shot: 8ffbe24e3b5207d66816afb5b3c2ca82731ab636 React-nativeconfig: 7af2ccce165f86b233a9f9d63295f6207e62640e React-NativeModulesApple: db1c1ee9dda26c9e58d824b4100fed83add82ae9 React-perflogger: 7c4e97b47d8bc58c03fad1a6b97d96181b59aa41 @@ -1874,10 +1880,10 @@ SPEC CHECKSUMS: ReactCodegen: e9156d86a166f3e10dc8fb6160764048df3528bc ReactCommon: 3ae48fa4cfa7052a270c2150ececfc94e541fb8a RNReanimated: 070deccceef093bec96ac3f87ef8c7f361caf754 - RNSVG: 811ae7bc0e07e20acd704638214b0c5fad40f802 + RNSVG: 1079f96b39a35753d481a20e30603fd6fc4f6fa9 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - Yoga: 06fc4b2c3664ae0e278964b8fbcb0ee9d21f0a5a + Yoga: d36331de5ab7fb61bc9d91fbbd76307464418323 PODFILE CHECKSUM: 313328930c1300de5979bee64deca30459b8453a -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/fabric-example/package.json b/fabric-example/package.json index ccad78b6..162e7e0c 100644 --- a/fabric-example/package.json +++ b/fabric-example/package.json @@ -7,13 +7,15 @@ "ios": "react-native run-ios", "lint": "eslint .", "start": "react-native start", - "test": "jest" + "test": "jest", + "postinstall": "patch-package" }, "dependencies": { "react": "18.3.1", "react-native": "0.75.1", "react-native-reanimated": "3.15.0", - "react-native-svg": "link:../" + "react-native-svg": "link:../", + "react-native-view-shot": "4.0.0-alpha.2" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -29,6 +31,7 @@ "eslint": "^8.19.0", "jest": "^29.6.3", "prettier": "2.8.8", + "patch-package": "^8.0.0", "react-test-renderer": "18.3.1", "typescript": "5.0.4" }, diff --git a/fabric-example/patches/react-native-view-shot+4.0.0-alpha.2.patch b/fabric-example/patches/react-native-view-shot+4.0.0-alpha.2.patch new file mode 100644 index 00000000..0caa5602 --- /dev/null +++ b/fabric-example/patches/react-native-view-shot+4.0.0-alpha.2.patch @@ -0,0 +1,51 @@ +diff --git a/node_modules/react-native-view-shot/ios/RNViewShot.m b/node_modules/react-native-view-shot/ios/RNViewShot.m +index bd55b92..6a20e9d 100644 +--- a/node_modules/react-native-view-shot/ios/RNViewShot.m ++++ b/node_modules/react-native-view-shot/ios/RNViewShot.m +@@ -106,7 +106,7 @@ - (dispatch_queue_t)methodQueue + scrollView.frame = CGRectMake(0, 0, scrollView.contentSize.width, scrollView.contentSize.height); + } + +- UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size]; ++ UIGraphicsBeginImageContextWithOptions(size, NO, 0); + + if (renderInContext) { + // this comes with some trade-offs such as inability to capture gradients or scrollview's content in full but it works for large views +@@ -117,8 +117,8 @@ - (dispatch_queue_t)methodQueue + // this doesn't work for large views and reports incorrect success even though the image is blank + success = [rendered drawViewHierarchyInRect:(CGRect){CGPointZero, size} afterScreenUpdates:YES]; + } +- +- UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {}]; ++ UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); ++ UIGraphicsEndImageContext(); + + if (snapshotContentContainer) { + // Restore scroll & frame +@@ -152,11 +152,11 @@ - (dispatch_queue_t)methodQueue + NSString *res = nil; + if ([result isEqualToString:@"base64"]) { + // Return as a base64 raw string +- res = [data base64EncodedStringWithOptions: 0]; ++ res = [data base64EncodedStringWithOptions: NSDataBase64EncodingEndLineWithLineFeed]; + } + else if ([result isEqualToString:@"data-uri"]) { + // Return as a base64 data uri string +- NSString *base64 = [data base64EncodedStringWithOptions: 0]; ++ NSString *base64 = [data base64EncodedStringWithOptions: NSDataBase64EncodingEndLineWithLineFeed]; + NSString *imageFormat = ([format isEqualToString:@"jpg"]) ? @"jpeg" : format; + res = [NSString stringWithFormat:@"data:image/%@;base64,%@", imageFormat, base64]; + } +diff --git a/node_modules/react-native-view-shot/src/specs/NativeRNViewShot.ts b/node_modules/react-native-view-shot/src/specs/NativeRNViewShot.ts +index a6f4c00..1e9e6ce 100644 +--- a/node_modules/react-native-view-shot/src/specs/NativeRNViewShot.ts ++++ b/node_modules/react-native-view-shot/src/specs/NativeRNViewShot.ts +@@ -2,7 +2,7 @@ import type { TurboModule } from 'react-native'; + import { TurboModuleRegistry } from 'react-native'; + + export interface Spec extends TurboModule { +- releaseCapture: () => string; ++ releaseCapture: (uri: string) => void; + captureRef: (tag: number, options: Object) => Promise + captureScreen: (options: Object) => Promise; + } \ No newline at end of file diff --git a/fabric-example/yarn.lock b/fabric-example/yarn.lock index 7fbb8530..134c97d1 100644 --- a/fabric-example/yarn.lock +++ b/fabric-example/yarn.lock @@ -2624,6 +2624,11 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@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== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2848,6 +2853,11 @@ async-limiter@~1.0.0: resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== +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== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -2956,6 +2966,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -2997,6 +3012,13 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + browserslist@^4.22.2, browserslist@^4.23.0: version "4.23.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" @@ -3146,7 +3168,7 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^3.2.0: +ci-info@^3.2.0, ci-info@^3.7.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== @@ -3353,6 +3375,13 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + css-select@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" @@ -4041,6 +4070,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + finalhandler@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -4086,6 +4122,13 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + flat-cache@^3.0.4: version "3.2.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" @@ -4131,6 +4174,16 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -4350,6 +4403,14 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html2canvas@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -5218,6 +5279,16 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stable-stringify@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" + integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== + dependencies: + call-bind "^1.0.5" + isarray "^2.0.5" + jsonify "^0.0.1" + object-keys "^1.1.1" + json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" @@ -5230,6 +5301,20 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== + "jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.3.5" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" @@ -5252,6 +5337,13 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -5594,6 +5686,14 @@ metro@0.80.8, metro@^0.80.3: ws "^7.5.1" yargs "^17.6.2" +micromatch@^4.0.2: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -5870,7 +5970,7 @@ open@^6.2.0: dependencies: is-wsl "^1.1.0" -open@^7.0.3: +open@^7.0.3, open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== @@ -5905,6 +6005,11 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -5975,6 +6080,27 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +patch-package@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.0.tgz#d191e2f1b6e06a4624a0116bcb88edd6714ede61" + integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^4.1.2" + ci-info "^3.7.0" + cross-spawn "^7.0.3" + find-yarn-workspace-root "^2.0.0" + fs-extra "^9.0.0" + json-stable-stringify "^1.0.2" + klaw-sync "^6.0.0" + minimist "^1.2.6" + open "^7.4.2" + rimraf "^2.6.3" + semver "^7.5.3" + slash "^2.0.0" + tmp "^0.0.33" + yaml "^2.2.2" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -6183,6 +6309,13 @@ react-native-reanimated@3.15.0: version "0.0.0" uid "" +react-native-view-shot@4.0.0-alpha.2: + version "4.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/react-native-view-shot/-/react-native-view-shot-4.0.0-alpha.2.tgz#389f5323722c3acad1c002bf4f0f0539fd2508c9" + integrity sha512-BNQ+FoOUrB2Y6zxwhSZtZMMXVLewN2Ic8y5gS+5Y7hdgphqWmlHZIGbcLn/NmJS+soz0Pe3WO1sM5vX6u7h6VQ== + dependencies: + html2canvas "^1.4.1" + react-native@0.75.1: version "0.75.1" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.75.1.tgz#0b7738bbfa44afe5895b1c02ec737c9d1bc19dcb" @@ -6432,6 +6565,13 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + 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" @@ -6636,6 +6776,11 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -6883,6 +7028,13 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -6901,6 +7053,13 @@ through2@^2.0.1: readable-stream "~2.3.6" xtend "~4.0.1" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -7069,6 +7228,11 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -7107,6 +7271,13 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + v8-to-istanbul@^9.0.1: version "9.2.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" @@ -7314,6 +7485,11 @@ yaml@^2.2.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed" integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg== +yaml@^2.2.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d" + integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== + yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" diff --git a/fabric-macos-example/macos/Podfile.lock b/fabric-macos-example/macos/Podfile.lock index 7f10112a..48a38eb9 100644 --- a/fabric-macos-example/macos/Podfile.lock +++ b/fabric-macos-example/macos/Podfile.lock @@ -1066,7 +1066,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSVG (15.5.0): + - RNSVG (15.6.0): - glog - RCT-Folly (= 2022.05.16.00) - RCTRequired @@ -1084,9 +1084,9 @@ PODS: - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNSVG/common (= 15.5.0) + - RNSVG/common (= 15.6.0) - Yoga - - RNSVG/common (15.5.0): + - RNSVG/common (15.6.0): - glog - RCT-Folly (= 2022.05.16.00) - RCTRequired @@ -1325,7 +1325,7 @@ SPEC CHECKSUMS: React-utils: f0bb613a2b0a7f8011bdafb8ac098e30d4825256 ReactCommon: 4afd22a8615d591e925c67434b6d947b9ae24ca8 RNReanimated: 096c06ec3b073d0bb04f6dd1c99e40fad2d2fb31 - RNSVG: d0638e8b3bff721b712b4c128af78cda17b6165c + RNSVG: 99ccf6fc927760cb39709ec16748c3a03b055597 SocketRocket: f6c6249082c011e6de2de60ed641ef8bbe0cfac9 Yoga: 35603207c576afc16e93625844f6d469ae6d5f03 diff --git a/jest.config.ts b/jest.config.ts index 60db5611..7df13396 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -9,6 +9,21 @@ const config: Config.InitialOptions = { ], preset: 'react-native', verbose: true, + globalSetup: '/e2e/setupJest.ts', + globalTeardown: '/e2e/teardownJest.ts', + modulePathIgnorePatterns: [ + 'lib/typescript', + 'helpers.ts|globals.d.ts|setupJest.ts|teardownJest.ts', + ], + reporters: [ + 'default', + [ + 'jest-html-reporters', + { + filename: 'report.html', + }, + ], + ], }; export default config; diff --git a/package.json b/package.json index a0a468f4..4e6694a6 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,8 @@ "release": "npm login && release-it", "test": "npm run lint && npm run tsc", "tsc": "tsc --noEmit", + "e2e": "jest e2e", + "generateE2eReferences": "ts-node e2e/generateReferences.ts", "check-archs-consistency": "node ./scripts/codegen-check-consistency.js", "sync-archs": "node ./scripts/codegen-sync-archs.js" }, @@ -78,7 +80,10 @@ "@types/css-tree": "^1.0.3", "@types/jest": "^27.5.2", "@types/node": "*", + "@types/pixelmatch": "^5.2.0", + "@types/pngjs": "^6.0.5", "@types/react": "^18.2.18", + "@types/ws": "^8.5.10", "@typescript-eslint/eslint-plugin": "^5.11.0", "@typescript-eslint/parser": "^5.11.0", "babel-eslint": "^10.1.0", @@ -96,18 +101,23 @@ "eslint-plugin-standard": "^5.0.0", "husky": "^8.0.1", "jest": "^28.1.0", + "jest-html-reporters": "^3.1.7", "lint-staged": "^13.0.3", "peggy": "4.0.3", "pegjs": "^0.10.0", + "pixelmatch": "5.3.0", + "pngjs": "^7.0.0", "prettier": "3.0.1", + "puppeteer": "^22.12.1", "react": "^18.2.0", "react-native": "^0.72.3", "react-native-builder-bob": "^0.20.4", "react-native-windows": "^0.72.4", "react-test-renderer": "^18.2.0", "release-it": "^14.12.5", - "ts-node": "^10.8.0", - "typescript": "^5.1.6" + "ts-node": "^10.9.2", + "typescript": "^5.1.6", + "ws": "^8.18.0" }, "lint-staged": { "{src,Example}/**/*.{js,ts,tsx}": "yarn format-js", diff --git a/paper-macos-example/macos/Podfile.lock b/paper-macos-example/macos/Podfile.lock index 717de5e1..660a1afb 100644 --- a/paper-macos-example/macos/Podfile.lock +++ b/paper-macos-example/macos/Podfile.lock @@ -1070,7 +1070,7 @@ PODS: - RCT-Folly (= 2022.05.16.00) - React-Core - ReactCommon/turbomodule/core - - RNSVG (15.5.0): + - RNSVG (15.6.0): - React-Core - SocketRocket (0.7.0) - Yoga (1.14.0) @@ -1294,7 +1294,7 @@ SPEC CHECKSUMS: React-utils: f0bb613a2b0a7f8011bdafb8ac098e30d4825256 ReactCommon: cd9602a38f340d44b1cca3ad06d6f13106a46dd0 RNReanimated: ba95f6d26ca4b8a8f7f2b15f62a3513750762f6b - RNSVG: b986585e367f4a49d8aa43065066cc9c290b3d9b + RNSVG: 5da7a24f31968ec74f0b091e3440080f347e279b SocketRocket: f6c6249082c011e6de2de60ed641ef8bbe0cfac9 Yoga: 35603207c576afc16e93625844f6d469ae6d5f03 diff --git a/tests-example/ios/Podfile.lock b/tests-example/ios/Podfile.lock index 3de92b0e..cbb2ebdb 100644 --- a/tests-example/ios/Podfile.lock +++ b/tests-example/ios/Podfile.lock @@ -1058,7 +1058,7 @@ PODS: - RCT-Folly (= 2022.05.16.00) - React-Core - ReactCommon/turbomodule/core - - RNSVG (15.3.0): + - RNSVG (15.6.0): - React-Core - SocketRocket (0.6.1) - Yoga (1.14.0) @@ -1278,10 +1278,10 @@ SPEC CHECKSUMS: React-utils: debda2c206770ee2785bdebb7f16d8db9f18838a ReactCommon: ddb128564dcbfa0287d3d1a2d10f8c7457c971f6 RNReanimated: 2fb8bf314df48f4968d9080aec46c47eb44a446a - RNSVG: a48668fd382115bc89761ce291a81c4ca5f2fd2e + RNSVG: 5da7a24f31968ec74f0b091e3440080f347e279b SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Yoga: 2b33a7ac96c58cdaa7b810948fc6a2a76ed2d108 -PODFILE CHECKSUM: 0ddaec8b74c24b39086422661a8aeb46f68d0382 +PODFILE CHECKSUM: 867081623c4821048817e3d8ade1e288af6d41a7 COCOAPODS: 1.15.2 diff --git a/tsconfig.json b/tsconfig.json index e47cd830..062849b5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,5 +21,5 @@ "resolveJsonModule": true, "isolatedModules": true }, - "include": ["src"] + "include": ["src", "e2e", "__tests__/e2e/GeneralSvgRenderingTest.spec.tsx"] } diff --git a/web-example/package.json b/web-example/package.json index 03c1337e..2c58ed5d 100644 --- a/web-example/package.json +++ b/web-example/package.json @@ -22,6 +22,7 @@ "react-native-reanimated": "3.13.0", "react-native-svg": "link:../", "react-native-svg-transformer": "^1.4.0", + "react-native-view-shot": "3.8.0", "react-native-web": "0.19.11" }, "devDependencies": { @@ -33,6 +34,7 @@ "@types/react-test-renderer": "^18.0.7", "jest": "^29.2.1", "jest-expo": "~51.0.3", + "patch-package": "^8.0.0", "react-test-renderer": "18.2.0", "typescript": "~5.3.3" }, diff --git a/web-example/yarn.lock b/web-example/yarn.lock index 2b6e7162..f51d3862 100644 --- a/web-example/yarn.lock +++ b/web-example/yarn.lock @@ -2358,6 +2358,11 @@ resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.13.tgz#ff34942667a4e19a9f4a0996a76814daac364cf3" integrity sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g== +"@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== + abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" @@ -2719,6 +2724,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + base64-js@^1.2.3, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -2986,7 +2996,7 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^3.2.0, ci-info@^3.3.0: +ci-info@^3.2.0, ci-info@^3.3.0, ci-info@^3.7.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== @@ -3272,6 +3282,13 @@ css-in-js-utils@^3.1.0: dependencies: hyphenate-style-name "^1.0.3" +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + css-select@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" @@ -4079,7 +4096,7 @@ find-up@^5.0.0, find-up@~5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -find-yarn-workspace-root@~2.0.0: +find-yarn-workspace-root@^2.0.0, find-yarn-workspace-root@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== @@ -4473,6 +4490,14 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html2canvas@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -5501,6 +5526,16 @@ json-schema-deref-sync@^0.13.0: traverse "~0.6.6" valid-url "~1.0.9" +json-stable-stringify@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" + integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== + dependencies: + call-bind "^1.0.5" + isarray "^2.0.5" + jsonify "^0.0.1" + object-keys "^1.1.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" @@ -5522,11 +5557,23 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonify@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== + kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -6341,7 +6388,7 @@ open@^6.2.0: dependencies: is-wsl "^1.1.0" -open@^7.0.3: +open@^7.0.3, open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== @@ -6512,6 +6559,27 @@ password-prompt@^1.0.4: ansi-escapes "^4.3.2" cross-spawn "^7.0.3" +patch-package@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.0.tgz#d191e2f1b6e06a4624a0116bcb88edd6714ede61" + integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^4.1.2" + ci-info "^3.7.0" + cross-spawn "^7.0.3" + find-yarn-workspace-root "^2.0.0" + fs-extra "^9.0.0" + json-stable-stringify "^1.0.2" + klaw-sync "^6.0.0" + minimist "^1.2.6" + open "^7.4.2" + rimraf "^2.6.3" + semver "^7.5.3" + slash "^2.0.0" + tmp "^0.0.33" + yaml "^2.2.2" + path-dirname@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -6841,6 +6909,13 @@ react-native-svg-transformer@^1.4.0: version "0.0.0" uid "" +react-native-view-shot@3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/react-native-view-shot/-/react-native-view-shot-3.8.0.tgz#1aa1905f0e79428ca32bf80c16fd4abc719c600b" + integrity sha512-4cU8SOhMn3YQIrskh+5Q8VvVRxQOu8/s1M9NAL4z5BY1Rm0HXMWkQJ4N0XsZ42+Yca+y86ISF3LC5qdLPvPuiA== + dependencies: + html2canvas "^1.4.1" + react-native-web@0.19.11: version "0.19.11" resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.19.11.tgz#1b96ac3cea9af4e1280fd5fa3b606b471f66edc3" @@ -7113,7 +7188,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.6.2: +rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -7377,6 +7452,11 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -7851,6 +7931,13 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -8181,6 +8268,13 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + uuid@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" @@ -8502,6 +8596,11 @@ yaml@^2.2.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e" integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== +yaml@^2.2.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d" + integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== + yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" diff --git a/yarn.lock b/yarn.lock index 9407b833..7a12b4a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1853,6 +1853,20 @@ dependencies: semver "7.6.0" +"@puppeteer/browsers@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-2.3.0.tgz#791ea7d80450fea24eb19fb1d70c367ad4e08cae" + integrity sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA== + dependencies: + debug "^4.3.5" + extract-zip "^2.0.1" + progress "^2.0.3" + proxy-agent "^6.4.0" + semver "^7.6.3" + tar-fs "^3.0.6" + unbzip2-stream "^1.4.3" + yargs "^17.7.2" + "@react-native-community/cli-clean@11.4.1": version "11.4.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-11.4.1.tgz#0155a02e4158c8a61ba3d7a2b08f3ebebed81906" @@ -2271,6 +2285,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@tootallnate/quickjs-emscripten@^0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" + integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== + "@tsconfig/node10@^1.0.7": version "1.0.11" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" @@ -2385,6 +2404,20 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== +"@types/pixelmatch@^5.2.0": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@types/pixelmatch/-/pixelmatch-5.2.6.tgz#fba6de304ac958495f27d85989f5c6bb7499a686" + integrity sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg== + dependencies: + "@types/node" "*" + +"@types/pngjs@^6.0.5": + version "6.0.5" + resolved "https://registry.yarnpkg.com/@types/pngjs/-/pngjs-6.0.5.tgz#6dec2f7eb8284543ca4e423f3c09b119fa939ea3" + integrity sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ== + dependencies: + "@types/node" "*" + "@types/prettier@^2.1.5": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" @@ -2418,6 +2451,13 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== +"@types/ws@^8.5.10": + version "8.5.12" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e" + integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -2444,6 +2484,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yauzl@^2.9.1": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@^5.11.0", "@typescript-eslint/eslint-plugin@^5.30.5": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" @@ -2582,6 +2629,13 @@ agent-base@6, agent-base@^6.0.0, agent-base@^6.0.2: dependencies: debug "4" +agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -2853,7 +2907,7 @@ ast-types@0.15.2: dependencies: tslib "^2.0.1" -ast-types@^0.13.2: +ast-types@^0.13.2, ast-types@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== @@ -2909,6 +2963,11 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +b4a@^1.6.4: + version "1.6.6" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.6.tgz#a4cc349a3851987c3c4ac2d7785c18744f6da9ba" + integrity sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg== + babel-core@^7.0.0-bridge.0: version "7.0.0-bridge.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" @@ -3071,11 +3130,49 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bare-events@^2.0.0, bare-events@^2.2.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.4.2.tgz#3140cca7a0e11d49b3edc5041ab560659fd8e1f8" + integrity sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q== + +bare-fs@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-2.3.1.tgz#cdbd63dac7a552dfb2b87d18c822298d1efd213d" + integrity sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA== + dependencies: + bare-events "^2.0.0" + bare-path "^2.0.0" + bare-stream "^2.0.0" + +bare-os@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-2.4.0.tgz#5de5e3ba7704f459c9656629edca7cc736e06608" + integrity sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg== + +bare-path@^2.0.0, bare-path@^2.1.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-2.1.3.tgz#594104c829ef660e43b5589ec8daef7df6cedb3e" + integrity sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA== + dependencies: + bare-os "^2.1.0" + +bare-stream@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.1.3.tgz#070b69919963a437cc9e20554ede079ce0a129b2" + integrity sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ== + dependencies: + streamx "^2.18.0" + base64-js@^1.1.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +basic-ftp@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" + integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== + before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -3148,12 +3245,17 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^5.5.0: +buffer@^5.2.1, buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -3278,6 +3380,15 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +chromium-bidi@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.6.2.tgz#91f9daa20984833b52221084480fbe0465b29c67" + integrity sha512-4WVBa6ijmUTVr9cZD4eicQD8Mdy/HCX3bzEIYYpmk0glqYLoWH+LqQEvV9RpDRzoQSbY1KJHloYXbDMXMbDPhg== + dependencies: + mitt "3.0.1" + urlpattern-polyfill "10.0.0" + zod "3.23.8" + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -3603,6 +3714,16 @@ cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -3667,6 +3788,11 @@ data-uri-to-buffer@3: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== +data-uri-to-buffer@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b" + integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== + data-view-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" @@ -3785,6 +3911,11 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" +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.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" @@ -3804,6 +3935,15 @@ degenerator@^3.0.2: esprima "^4.0.0" vm2 "^3.9.17" +degenerator@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" + integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== + dependencies: + ast-types "^0.13.4" + escodegen "^2.1.0" + esprima "^4.0.1" + del@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" @@ -3857,6 +3997,11 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +devtools-protocol@0.0.1312386: + version "0.0.1312386" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz#5ab824d6f1669ec6c6eb0fba047e73601d969052" + integrity sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA== + diagnostic-channel-publishers@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.6.tgz#0e236cb4b7c4c81904b2e3741d0b16eff453dc5a" @@ -4008,6 +4153,11 @@ entities@^4.2.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + envinfo@^7.5.0, envinfo@^7.7.2, envinfo@^7.8.1: version "7.13.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31" @@ -4213,6 +4363,17 @@ escodegen@^1.8.1: optionalDependencies: source-map "~0.6.1" +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + eslint-compat-utils@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz#7fc92b776d185a70c4070d03fd26fde3d59652e4" @@ -4600,6 +4761,17 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" +extract-zip@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -4610,6 +4782,11 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== +fast-fifo@^1.2.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + fast-glob@^3.1.1, fast-glob@^3.2.9: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" @@ -4652,6 +4829,13 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -4791,7 +4975,7 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -fs-extra@^10.1.0: +fs-extra@^10.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== @@ -4800,6 +4984,15 @@ fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -4928,6 +5121,16 @@ get-uri@3: fs-extra "^8.1.0" ftp "^0.3.10" +get-uri@^6.0.1: + version "6.0.3" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.3.tgz#0d26697bc13cf91092e519aa63aa60ee5b6f385a" + integrity sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw== + dependencies: + basic-ftp "^5.0.2" + data-uri-to-buffer "^6.0.2" + debug "^4.3.4" + fs-extra "^11.2.0" + git-up@^4.0.0: version "4.0.5" resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" @@ -5196,6 +5399,14 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + https-proxy-agent@5, https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -5204,6 +5415,14 @@ https-proxy-agent@5, https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -5268,7 +5487,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.2.1: +import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -5501,7 +5720,7 @@ is-directory@^0.3.1: resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== -is-docker@^2.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== @@ -5752,7 +5971,7 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== -is-wsl@^2.1.1: +is-wsl@^2.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== @@ -6030,6 +6249,14 @@ jest-haste-map@^28.1.3: optionalDependencies: fsevents "^2.3.2" +jest-html-reporters@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/jest-html-reporters/-/jest-html-reporters-3.1.7.tgz#d8cb6f5d15fd518e601841f90165f37765e7ff34" + integrity sha512-GTmjqK6muQ0S0Mnksf9QkL9X9z2FGIpNSxC52E0PHDzjPQ1XDu2+XTI3B3FS43ZiUzD1f354/5FfwbNIBzT7ew== + dependencies: + fs-extra "^10.0.0" + open "^8.0.3" + 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" @@ -6708,6 +6935,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + macos-release@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.1.tgz#bccac4a8f7b93163a8d163b8ebf385b3c5f55bf9" @@ -7157,6 +7389,11 @@ minipass@^4.2.4: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +mitt@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -7447,6 +7684,15 @@ open@^6.2.0: dependencies: is-wsl "^1.1.0" +open@^8.0.3: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -7602,6 +7848,20 @@ pac-proxy-agent@^5.0.0: raw-body "^2.2.0" socks-proxy-agent "5" +pac-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz#0fb02496bd9fb8ae7eb11cfd98386daaac442f58" + integrity sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg== + dependencies: + "@tootallnate/quickjs-emscripten" "^0.23.0" + agent-base "^7.0.2" + debug "^4.3.4" + get-uri "^6.0.1" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.5" + pac-resolver "^7.0.1" + socks-proxy-agent "^8.0.4" + pac-resolver@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-5.0.1.tgz#c91efa3a9af9f669104fa2f51102839d01cde8e7" @@ -7611,6 +7871,14 @@ pac-resolver@^5.0.0: ip "^1.1.5" netmask "^2.0.2" +pac-resolver@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6" + integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg== + dependencies: + degenerator "^5.0.0" + netmask "^2.0.2" + package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -7740,6 +8008,11 @@ pegjs@^0.10.0: resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.10.0.tgz#cf8bafae6eddff4b5a7efb185269eaaf4610ddbd" integrity sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow== +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + picocolors@^1.0.0, picocolors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" @@ -7770,6 +8043,13 @@ pirates@^4.0.4, pirates@^4.0.6: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +pixelmatch@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-5.3.0.tgz#5e5321a7abedfb7962d60dbf345deda87cb9560a" + integrity sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q== + dependencies: + pngjs "^6.0.0" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -7791,6 +8071,16 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" +pngjs@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821" + integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== + +pngjs@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-7.0.0.tgz#a8b7446020ebbc6ac739db6c5415a65d17090e26" + integrity sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow== + possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" @@ -7866,6 +8156,11 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise.allsettled@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.5.tgz#2443f3d4b2aa8dfa560f6ac2aa6c4ea999d75f53" @@ -7926,7 +8221,21 @@ proxy-agent@5.0.0: proxy-from-env "^1.0.0" socks-proxy-agent "^5.0.0" -proxy-from-env@^1.0.0: +proxy-agent@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d" + integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ== + dependencies: + agent-base "^7.0.2" + debug "^4.3.4" + http-proxy-agent "^7.0.1" + https-proxy-agent "^7.0.3" + lru-cache "^7.14.1" + pac-proxy-agent "^7.0.1" + proxy-from-env "^1.1.0" + socks-proxy-agent "^8.0.2" + +proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== @@ -7951,6 +8260,27 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" +puppeteer-core@22.14.0: + version "22.14.0" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-22.14.0.tgz#5bb466adba725c966b0a86f0337a476d4c68ebec" + integrity sha512-rl4tOY5LcA3e374GAlsGGHc05HL3eGNf5rZ+uxkl6id9zVZKcwcp1Z+Nd6byb6WPiPeecT/dwz8f/iUm+AZQSw== + dependencies: + "@puppeteer/browsers" "2.3.0" + chromium-bidi "0.6.2" + debug "^4.3.5" + devtools-protocol "0.0.1312386" + ws "^8.18.0" + +puppeteer@^22.12.1: + version "22.14.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-22.14.0.tgz#11697c929f5d9d7eac5a3438a0ff12dc65aedcbe" + integrity sha512-MGTR6/pM8zmWbTdazb6FKnwIihzsSEXBPH49mFFU96DNZpQOevCAZMnjBZGlZRGRzRK6aADCavR6SQtrbv5dQw== + dependencies: + "@puppeteer/browsers" "2.3.0" + cosmiconfig "^9.0.0" + devtools-protocol "0.0.1312386" + puppeteer-core "22.14.0" + qs@^6.9.4: version "6.12.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.3.tgz#e43ce03c8521b9c7fd7f1f13e514e5ca37727754" @@ -7973,6 +8303,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + queue@6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" @@ -8592,7 +8927,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.1, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: +semver@^7.0.0, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.1, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -8774,7 +9109,16 @@ socks-proxy-agent@5, socks-proxy-agent@^5.0.0: debug "4" socks "^2.3.3" -socks@^2.3.3: +socks-proxy-agent@^8.0.2, socks-proxy-agent@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c" + integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== + dependencies: + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.8.3" + +socks@^2.3.3, socks@^2.8.3: version "2.8.3" resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== @@ -8874,6 +9218,17 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" +streamx@^2.15.0, streamx@^2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.18.0.tgz#5bc1a51eb412a667ebfdcd4e6cf6a6fc65721ac7" + integrity sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -9088,6 +9443,26 @@ supports-preserve-symlinks-flag@^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== +tar-fs@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.6.tgz#eaccd3a67d5672f09ca8e8f9c3d2b89fa173f217" + integrity sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w== + dependencies: + pump "^3.0.0" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^2.1.1" + bare-path "^2.1.0" + +tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + temp@^0.8.4: version "0.8.4" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" @@ -9122,6 +9497,13 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-decoder@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.1.1.tgz#5df9c224cebac4a7977720b9f083f9efa1aefde8" + integrity sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA== + dependencies: + b4a "^1.6.4" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -9140,7 +9522,7 @@ through2@^2.0.1: readable-stream "~2.3.6" xtend "~4.0.1" -through@^2.3.6: +through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -9184,7 +9566,7 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -ts-node@^10.8.0: +ts-node@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== @@ -9348,6 +9730,14 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +unbzip2-stream@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" @@ -9455,6 +9845,11 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +urlpattern-polyfill@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz#f0a03a97bfb03cdf33553e5e79a2aadd22cac8ec" + integrity sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg== + use-sync-external-store@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" @@ -9721,6 +10116,11 @@ ws@^7, ws@^7.5.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== +ws@^8.18.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" @@ -9843,7 +10243,7 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: +yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -9856,6 +10256,14 @@ yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: y18n "^5.0.5" yargs-parser "^21.1.1" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" @@ -9865,3 +10273,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod@3.23.8: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==