From 80eed9ae6af12d3b9936879c9706d17518048615 Mon Sep 17 00:00:00 2001 From: Aliaksandr Babrykovich Date: Tue, 29 Jul 2025 09:27:50 +0200 Subject: [PATCH 01/15] feat: add p2p-chat app to demo sandbox <-> sandbox communication --- apps/p2p-chat/.bundle/config | 2 + apps/p2p-chat/README.md | 13 + apps/p2p-chat/__tests__/App.test.tsx | 14 + apps/p2p-chat/android/app/build.gradle | 119 + apps/p2p-chat/android/app/debug.keystore | Bin 0 -> 2257 bytes apps/p2p-chat/android/app/proguard-rules.pro | 10 + .../android/app/src/debug/AndroidManifest.xml | 9 + .../android/app/src/main/AndroidManifest.xml | 26 + .../src/main/java/com/p2pchat/MainActivity.kt | 22 + .../main/java/com/p2pchat/MainApplication.kt | 38 + .../res/drawable/rn_edit_text_material.xml | 37 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3056 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2096 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2858 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4569 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7098 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6464 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10676 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9250 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15523 bytes .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/styles.xml | 9 + apps/p2p-chat/android/build.gradle | 21 + apps/p2p-chat/android/gradle.properties | 39 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43764 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + apps/p2p-chat/android/gradlew | 251 ++ apps/p2p-chat/android/gradlew.bat | 99 + apps/p2p-chat/android/settings.gradle | 6 + apps/p2p-chat/app.json | 4 + apps/p2p-chat/babel.config.js | 3 + apps/p2p-chat/docs/screenshot.png | Bin 0 -> 188732 bytes apps/p2p-chat/index.js | 11 + apps/p2p-chat/ios/.xcode.env | 11 + .../ios/P2PChat.xcodeproj/project.pbxproj | 486 ++++ .../xcshareddata/xcschemes/P2PChat.xcscheme | 88 + .../contents.xcworkspacedata | 10 + apps/p2p-chat/ios/P2PChat/AppDelegate.swift | 48 + .../AppIcon.appiconset/Contents.json | 53 + .../ios/P2PChat/Images.xcassets/Contents.json | 6 + apps/p2p-chat/ios/P2PChat/Info.plist | 53 + .../ios/P2PChat/LaunchScreen.storyboard | 47 + .../ios/P2PChat/PrivacyInfo.xcprivacy | 37 + apps/p2p-chat/ios/Podfile | 35 + apps/p2p-chat/ios/Podfile.lock | 2465 +++++++++++++++++ apps/p2p-chat/metro.config.js | 24 + apps/p2p-chat/package.json | 32 + apps/p2p-chat/src/App.tsx | 65 + apps/p2p-chat/src/ChatApp.tsx | 154 + apps/p2p-chat/src/components/ChatCarousel.tsx | 174 ++ apps/p2p-chat/src/components/ChatHeader.tsx | 49 + apps/p2p-chat/src/components/DebugInfo.tsx | 40 + .../src/components/FriendRequestsList.tsx | 99 + apps/p2p-chat/src/components/MessageInput.tsx | 86 + apps/p2p-chat/src/components/MessagesList.tsx | 158 ++ .../src/components/NotificationsList.tsx | 60 + .../src/components/TargetSelector.tsx | 182 ++ apps/p2p-chat/src/components/index.ts | 8 + apps/p2p-chat/src/constants/index.ts | 7 + apps/p2p-chat/src/constants/presets.ts | 23 + apps/p2p-chat/src/hooks/index.ts | 5 + apps/p2p-chat/src/hooks/useChatInstances.ts | 70 + apps/p2p-chat/src/hooks/useCommunication.ts | 104 + apps/p2p-chat/src/hooks/useFriendRequests.ts | 117 + apps/p2p-chat/src/hooks/useMessages.ts | 116 + apps/p2p-chat/src/hooks/useTargetSelection.ts | 50 + .../src/services/FriendshipManager.ts | 87 + apps/p2p-chat/src/services/MessageHandler.ts | 189 ++ apps/p2p-chat/src/services/index.ts | 2 + apps/p2p-chat/src/styles/carousel.ts | 170 ++ apps/p2p-chat/src/styles/common.ts | 108 + apps/p2p-chat/src/styles/index.ts | 2 + apps/p2p-chat/src/types/chat.ts | 43 + apps/p2p-chat/src/types/friends.ts | 20 + apps/p2p-chat/src/types/index.ts | 12 + apps/p2p-chat/src/utils/chatHelpers.ts | 40 + apps/p2p-chat/tsconfig.json | 3 + bun.lock | 36 +- package.json | 1 + .../ios/SandboxReactNativeDelegate.h | 43 +- .../ios/SandboxReactNativeDelegate.mm | 241 +- .../SandboxReactNativeViewComponentView.mm | 113 +- .../specs/NativeSandboxReactNativeView.ts | 3 + packages/react-native-sandbox/src/index.tsx | 52 +- 85 files changed, 6797 insertions(+), 73 deletions(-) create mode 100644 apps/p2p-chat/.bundle/config create mode 100644 apps/p2p-chat/README.md create mode 100644 apps/p2p-chat/__tests__/App.test.tsx create mode 100644 apps/p2p-chat/android/app/build.gradle create mode 100644 apps/p2p-chat/android/app/debug.keystore create mode 100644 apps/p2p-chat/android/app/proguard-rules.pro create mode 100644 apps/p2p-chat/android/app/src/debug/AndroidManifest.xml create mode 100644 apps/p2p-chat/android/app/src/main/AndroidManifest.xml create mode 100644 apps/p2p-chat/android/app/src/main/java/com/p2pchat/MainActivity.kt create mode 100644 apps/p2p-chat/android/app/src/main/java/com/p2pchat/MainApplication.kt create mode 100644 apps/p2p-chat/android/app/src/main/res/drawable/rn_edit_text_material.xml create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 apps/p2p-chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 apps/p2p-chat/android/app/src/main/res/values/strings.xml create mode 100644 apps/p2p-chat/android/app/src/main/res/values/styles.xml create mode 100644 apps/p2p-chat/android/build.gradle create mode 100644 apps/p2p-chat/android/gradle.properties create mode 100644 apps/p2p-chat/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 apps/p2p-chat/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 apps/p2p-chat/android/gradlew create mode 100644 apps/p2p-chat/android/gradlew.bat create mode 100644 apps/p2p-chat/android/settings.gradle create mode 100644 apps/p2p-chat/app.json create mode 100644 apps/p2p-chat/babel.config.js create mode 100644 apps/p2p-chat/docs/screenshot.png create mode 100644 apps/p2p-chat/index.js create mode 100644 apps/p2p-chat/ios/.xcode.env create mode 100644 apps/p2p-chat/ios/P2PChat.xcodeproj/project.pbxproj create mode 100644 apps/p2p-chat/ios/P2PChat.xcodeproj/xcshareddata/xcschemes/P2PChat.xcscheme create mode 100644 apps/p2p-chat/ios/P2PChat.xcworkspace/contents.xcworkspacedata create mode 100644 apps/p2p-chat/ios/P2PChat/AppDelegate.swift create mode 100644 apps/p2p-chat/ios/P2PChat/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 apps/p2p-chat/ios/P2PChat/Images.xcassets/Contents.json create mode 100644 apps/p2p-chat/ios/P2PChat/Info.plist create mode 100644 apps/p2p-chat/ios/P2PChat/LaunchScreen.storyboard create mode 100644 apps/p2p-chat/ios/P2PChat/PrivacyInfo.xcprivacy create mode 100644 apps/p2p-chat/ios/Podfile create mode 100644 apps/p2p-chat/ios/Podfile.lock create mode 100644 apps/p2p-chat/metro.config.js create mode 100644 apps/p2p-chat/package.json create mode 100644 apps/p2p-chat/src/App.tsx create mode 100644 apps/p2p-chat/src/ChatApp.tsx create mode 100644 apps/p2p-chat/src/components/ChatCarousel.tsx create mode 100644 apps/p2p-chat/src/components/ChatHeader.tsx create mode 100644 apps/p2p-chat/src/components/DebugInfo.tsx create mode 100644 apps/p2p-chat/src/components/FriendRequestsList.tsx create mode 100644 apps/p2p-chat/src/components/MessageInput.tsx create mode 100644 apps/p2p-chat/src/components/MessagesList.tsx create mode 100644 apps/p2p-chat/src/components/NotificationsList.tsx create mode 100644 apps/p2p-chat/src/components/TargetSelector.tsx create mode 100644 apps/p2p-chat/src/components/index.ts create mode 100644 apps/p2p-chat/src/constants/index.ts create mode 100644 apps/p2p-chat/src/constants/presets.ts create mode 100644 apps/p2p-chat/src/hooks/index.ts create mode 100644 apps/p2p-chat/src/hooks/useChatInstances.ts create mode 100644 apps/p2p-chat/src/hooks/useCommunication.ts create mode 100644 apps/p2p-chat/src/hooks/useFriendRequests.ts create mode 100644 apps/p2p-chat/src/hooks/useMessages.ts create mode 100644 apps/p2p-chat/src/hooks/useTargetSelection.ts create mode 100644 apps/p2p-chat/src/services/FriendshipManager.ts create mode 100644 apps/p2p-chat/src/services/MessageHandler.ts create mode 100644 apps/p2p-chat/src/services/index.ts create mode 100644 apps/p2p-chat/src/styles/carousel.ts create mode 100644 apps/p2p-chat/src/styles/common.ts create mode 100644 apps/p2p-chat/src/styles/index.ts create mode 100644 apps/p2p-chat/src/types/chat.ts create mode 100644 apps/p2p-chat/src/types/friends.ts create mode 100644 apps/p2p-chat/src/types/index.ts create mode 100644 apps/p2p-chat/src/utils/chatHelpers.ts create mode 100644 apps/p2p-chat/tsconfig.json diff --git a/apps/p2p-chat/.bundle/config b/apps/p2p-chat/.bundle/config new file mode 100644 index 0000000..848943b --- /dev/null +++ b/apps/p2p-chat/.bundle/config @@ -0,0 +1,2 @@ +BUNDLE_PATH: "vendor/bundle" +BUNDLE_FORCE_RUBY_PLATFORM: 1 diff --git a/apps/p2p-chat/README.md b/apps/p2p-chat/README.md new file mode 100644 index 0000000..feae3b4 --- /dev/null +++ b/apps/p2p-chat/README.md @@ -0,0 +1,13 @@ +# P2P Chat Carousel + +![Platform: iOS](https://img.shields.io/badge/platform-iOS-blue.svg) + +This example demonstrates advanced peer-to-peer communication between multiple isolated React Native sandboxes using `react-native-sandbox`. It features a carousel interface where users can dynamically add/remove chat instances, with each sandbox operating independently while communicating directly with others through a friendship system and real-time messaging. + +The demo showcases dynamic instance management, multi-target communication, friend request handling, and a sophisticated message routing system - all while maintaining complete isolation between sandbox environments. Users can swipe between different chat instances (Alice, Bob, Charlie, Diana, Eve, Frank), send friend requests, accept/reject friendships, and engage in real-time conversations. + +## Screenshot + +
+ +
\ No newline at end of file diff --git a/apps/p2p-chat/__tests__/App.test.tsx b/apps/p2p-chat/__tests__/App.test.tsx new file mode 100644 index 0000000..8e508b3 --- /dev/null +++ b/apps/p2p-chat/__tests__/App.test.tsx @@ -0,0 +1,14 @@ +/** + * @format + */ + +import React from 'react' +import ReactTestRenderer from 'react-test-renderer' + +import App from '../src/App' + +test('renders correctly', async () => { + await ReactTestRenderer.act(() => { + ReactTestRenderer.create() + }) +}) diff --git a/apps/p2p-chat/android/app/build.gradle b/apps/p2p-chat/android/app/build.gradle new file mode 100644 index 0000000..d20fd2e --- /dev/null +++ b/apps/p2p-chat/android/app/build.gradle @@ -0,0 +1,119 @@ +apply plugin: "com.android.application" +apply plugin: "org.jetbrains.kotlin.android" +apply plugin: "com.facebook.react" + +/** + * This is the configuration block to customize your React Native Android app. + * By default you don't need to apply any configuration, just uncomment the lines you need. + */ +react { + /* Folders */ + // The root of your project, i.e. where "package.json" lives. Default is '../..' + // root = file("../../") + // The folder where the react-native NPM package is. Default is ../../node_modules/react-native + // reactNativeDir = file("../../node_modules/react-native") + // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen + // codegenDir = file("../../node_modules/@react-native/codegen") + // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js + // cliFile = file("../../node_modules/react-native/cli.js") + + /* Variants */ + // The list of variants to that are debuggable. For those we're going to + // skip the bundling of the JS bundle and the assets. By default is just 'debug'. + // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. + // debuggableVariants = ["liteDebug", "prodDebug"] + + /* Bundling */ + // A list containing the node command and its flags. Default is just 'node'. + // nodeExecutableAndArgs = ["node"] + // + // The command to run when bundling. By default is 'bundle' + // bundleCommand = "ram-bundle" + // + // The path to the CLI configuration file. Default is empty. + // bundleConfig = file(../rn-cli.config.js) + // + // The name of the generated asset file containing your JS bundle + // bundleAssetName = "MyApplication.android.bundle" + // + // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' + // entryFile = file("../js/MyApplication.android.js") + // + // A list of extra flags to pass to the 'bundle' commands. + // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle + // extraPackagerArgs = [] + + /* Hermes Commands */ + // The hermes compiler command to run. By default it is 'hermesc' + // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" + // + // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" + // hermesFlags = ["-O", "-output-source-map"] + + /* Autolinking */ + autolinkLibrariesWithApp() +} + +/** + * Set this to true to Run Proguard on Release builds to minify the Java bytecode. + */ +def enableProguardInReleaseBuilds = false + +/** + * The preferred build flavor of JavaScriptCore (JSC) + * + * For example, to use the international variant, you can use: + * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+` + * + * The international variant includes ICU i18n library and necessary data + * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that + * give correct results when using with locales other than en-US. Note that + * this variant is about 6MiB larger per architecture than default. + */ +def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' + +android { + ndkVersion rootProject.ext.ndkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + compileSdk rootProject.ext.compileSdkVersion + + namespace "com.p2pchat" + defaultConfig { + applicationId "com.p2pchat" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + } + signingConfigs { + debug { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' + } + } + buildTypes { + debug { + signingConfig signingConfigs.debug + } + release { + // Caution! In production, you need to generate your own keystore file. + // see https://reactnative.dev/docs/signed-apk-android. + signingConfig signingConfigs.debug + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + } + } +} + +dependencies { + // The version of react-native is set by the React Native Gradle Plugin + implementation("com.facebook.react:react-android") + + if (hermesEnabled.toBoolean()) { + implementation("com.facebook.react:hermes-android") + } else { + implementation jscFlavor + } +} diff --git a/apps/p2p-chat/android/app/debug.keystore b/apps/p2p-chat/android/app/debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..364e105ed39fbfd62001429a68140672b06ec0de GIT binary patch literal 2257 zcmchYXEfYt8;7T1^dLH$VOTZ%2NOdOH5j5LYLtZ0q7x-V8_6gU5)#7dkq{HTmsfNq zB3ZqcAxeY^G10@?efK?Q&)M(qInVv!xjx+IKEL}p*K@LYvIzo#AZG>st5|P)KF1_Z;y){W{<7K{nl!CPuE z_^(!C(Ol0n8 zK13*rzAtW>(wULKPRYLd7G18F8#1P`V*9`(Poj26eOXYyBVZPno~Cvvhx7vPjAuZo zF?VD!zB~QG(!zbw#qsxT8%BSpqMZ4f70ZPn-3y$L8{EVbbN9$H`B&Z1quk9tgp5FM zuxp3pJ0b8u|3+#5bkJ4SRnCF2l7#DyLYXYY8*?OuAwK4E6J{0N=O3QNVzQ$L#FKkR zi-c@&!nDvezOV$i$Lr}iF$XEcwnybQ6WZrMKuw8gCL^U#D;q3t&HpTbqyD%vG=TeDlzCT~MXUPC|Leb-Uk+ z=vnMd(|>ld?Fh>V8poP;q;;nc@en$|rnP0ytzD&fFkCeUE^kG9Kx4wUh!!rpjwKDP zyw_e|a^x_w3E zP}}@$g>*LLJ4i0`Gx)qltL}@;mDv}D*xR^oeWcWdPkW@Uu)B^X&4W1$p6}ze!zudJ zyiLg@uggoMIArBr*27EZV7djDg@W1MaL+rcZ-lrANJQ%%>u8)ZMWU@R2qtnmG(acP z0d_^!t>}5W zpT`*2NR+0+SpTHb+6Js4b;%LJB;B_-ChhnU5py}iJtku*hm5F0!iql8Hrpcy1aYbT z1*dKC5ua6pMX@@iONI?Hpr%h;&YaXp9n!ND7-=a%BD7v&g zOO41M6EbE24mJ#S$Ui0-brR5ML%@|ndz^)YLMMV1atna{Fw<;TF@>d&F|!Z>8eg>>hkFrV)W+uv=`^F9^e zzzM2*oOjT9%gLoub%(R57p-`TXFe#oh1_{&N-YN z<}artH|m=d8TQuKSWE)Z%puU|g|^^NFwC#N=@dPhasyYjoy(fdEVfKR@cXKHZV-`06HsP`|Ftx;8(YD$fFXumLWbGnu$GMqRncXYY9mwz9$ap zQtfZB^_BeNYITh^hA7+(XNFox5WMeG_LtJ%*Q}$8VKDI_p8^pqX)}NMb`0e|wgF7D zuQACY_Ua<1ri{;Jwt@_1sW9zzdgnyh_O#8y+C;LcZq6=4e^cs6KvmK@$vVpKFGbQ= z$)Eux5C|Fx;Gtmv9^#Y-g@7Rt7*eLp5n!gJmn7&B_L$G?NCN`AP>cXQEz}%F%K;vUs{+l4Q{}eWW;ATe2 zqvXzxoIDy(u;F2q1JH7Sf;{jy_j})F+cKlIOmNfjBGHoG^CN zM|Ho&&X|L-36f}Q-obEACz`sI%2f&k>z5c$2TyTSj~vmO)BW~+N^kt`Jt@R|s!){H ze1_eCrlNaPkJQhL$WG&iRvF*YG=gXd1IyYQ9ew|iYn7r~g!wOnw;@n42>enAxBv*A zEmV*N#sxdicyNM=A4|yaOC5MByts}s_Hpfj|y<6G=o=!3S@eIFKDdpR7|FY>L&Wat&oW&cm&X~ z5Bt>Fcq(fgnvlvLSYg&o6>&fY`ODg4`V^lWWD=%oJ#Kbad2u~! zLECFS*??>|vDsNR&pH=Ze0Eo`sC_G`OjoEKVHY|wmwlX&(XBE<@sx3Hd^gtd-fNwUHsylg06p`U2y_={u}Bc + + + + diff --git a/apps/p2p-chat/android/app/src/main/AndroidManifest.xml b/apps/p2p-chat/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e189252 --- /dev/null +++ b/apps/p2p-chat/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/apps/p2p-chat/android/app/src/main/java/com/p2pchat/MainActivity.kt b/apps/p2p-chat/android/app/src/main/java/com/p2pchat/MainActivity.kt new file mode 100644 index 0000000..c7eff90 --- /dev/null +++ b/apps/p2p-chat/android/app/src/main/java/com/p2pchat/MainActivity.kt @@ -0,0 +1,22 @@ +package com.p2pchat + +import com.facebook.react.ReactActivity +import com.facebook.react.ReactActivityDelegate +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled +import com.facebook.react.defaults.DefaultReactActivityDelegate + +class MainActivity : ReactActivity() { + + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + override fun getMainComponentName(): String = "P2PChat" + + /** + * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] + * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] + */ + override fun createReactActivityDelegate(): ReactActivityDelegate = + DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) +} diff --git a/apps/p2p-chat/android/app/src/main/java/com/p2pchat/MainApplication.kt b/apps/p2p-chat/android/app/src/main/java/com/p2pchat/MainApplication.kt new file mode 100644 index 0000000..c3dc7af --- /dev/null +++ b/apps/p2p-chat/android/app/src/main/java/com/p2pchat/MainApplication.kt @@ -0,0 +1,38 @@ +package com.p2pchat + +import android.app.Application +import com.facebook.react.PackageList +import com.facebook.react.ReactApplication +import com.facebook.react.ReactHost +import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative +import com.facebook.react.ReactNativeHost +import com.facebook.react.ReactPackage +import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost +import com.facebook.react.defaults.DefaultReactNativeHost + +class MainApplication : Application(), ReactApplication { + + override val reactNativeHost: ReactNativeHost = + object : DefaultReactNativeHost(this) { + override fun getPackages(): List = + PackageList(this).packages.apply { + // Packages that cannot be autolinked yet can be added manually here, for example: + // add(MyReactNativePackage()) + } + + override fun getJSMainModuleName(): String = "index" + + override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG + + override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED + } + + override val reactHost: ReactHost + get() = getDefaultReactHost(applicationContext, reactNativeHost) + + override fun onCreate() { + super.onCreate() + loadReactNative(this) + } +} diff --git a/apps/p2p-chat/android/app/src/main/res/drawable/rn_edit_text_material.xml b/apps/p2p-chat/android/app/src/main/res/drawable/rn_edit_text_material.xml new file mode 100644 index 0000000..5c25e72 --- /dev/null +++ b/apps/p2p-chat/android/app/src/main/res/drawable/rn_edit_text_material.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + diff --git a/apps/p2p-chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/p2p-chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a2f5908281d070150700378b64a84c7db1f97aa1 GIT binary patch literal 3056 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 axPAxe%7rDusX+w*00006jj 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⁣ zwiG+x#%j}{0gU~J8;<|!B1@-XaB@{KORFwrYg_8rOv({b0EO#DbeQRm;B6_9=mXGf z-x|VL{zd`)#@yN}HkCSJbjbNlE|zL3Wm9Q8HY`sV)}3%pgN>cL^67{Z;PPL(*wT8N zUjXU{@|*hvm}({wsAC=x0^ok0%UAz0;sogW{B!nDqk|JJ5x~4NfTDgP49^zeu`csl?5mY@JdQdISc zFs!E{^grmkLnUk9 zny~m)1vws@5BFI<-0Tuo2JWX(0v`W|t(wg;s--L47WTvTMz-8l#TL^=OJNRS2?_Qj z3AKT+gvbyBi#H*-tJ%tWD|>EV3wy|8qxfzS!5RW;Jpl5*zo&^UBU=fG#2}UvRyNkK zA06Dy9;K1ca@r2T>yThYgI!ont$(G{6q#2QT+00r_x0(b)gsE`lBB?2gr55gq^D3Fi&p%E(p9>U%bv zkg1Jco(RbyTX7FDHOnl7-O@ zI$AaIl?9NJKPm(WiBP`1-#CB1QzU>&hKm)fpa5DKE{2$X0hGz-0uZ?cyTk(YC!Y&| zL=1VrNERSA5NA2jq7FACfX4JfPyj5XXl1yv0>~s;eF7L2$>&oMqeTFT2m$y7FlkON z_yurD1yIOvA;5C6016pyxBznGUt0kJ&k5r#;&>Jow`r)sp9R~PmK~lz$3xH%LT*1U zJdOyABZ3!FvNoR*vN$5ykHS8f`jA4zV+|L}i1C4`B2c{R0;UdYxaU|H)2avz@ z=mEYc|2S<+(B2Tj+FkX+2D+yFI!k9lWMA61DJ{)e;lum$(;O87?vGJJe!KtK04+N_ zI*P~t@dUb>9Xh{dbyl{-ZQ(UMgz7$|QfL5XSPkskt^NgctYC#;4WcZB1@%@wy@2t3 z2z0DI7&%b$*Aw~abe?GxE`ez@+6hOh-6*8fHRV{1os$EL@}uUZeG4h1&Be`98q*7j z=3-v+lhIjfWVo12!<>%V^a6lTgW3+_#W6n|p*~==zOH7z$0{LSZk(Tpd7EaD04hnA zL;#fxS0aD{`5^&D`}>0Uq?byDD-l2=!wm_bLcUl4gc(% za1p|itVANvFF>hghAS07Im1;IK;|b*W)}VDyI;BIp2=K*yu2a)j?B|f<44NI$NbmJ z#dE0>jI$fMr&@>4kN8MLFb4&2O9fEKaQg%(QO$4_1rVQywG^CmBLh#}_7gKW3vd?| z2?1^&KWq8}8I^_S0|)MowU_pw$q@nl@Nkn$z>BQq_KA^9yaR`(R3u{{Ig;cwt z@AJ^{ODQCm^neroM9nKNUAXi9RCK`OsP_LuR0PUR(YZCCX5dNF6VzcoK&=b^r`W?ltt|*F zpkoae%ZT{C1h~EcFui~b7fF`vb<<~j_VquuUA$}QqIKYELPp#;{u?q8Dz}WAG-(3; zjrm$i%7UbyZMM(Y{>!uJ#vNB?R~B{6Htp=>e*<{fQQ5W7V(1coCWlOON!MzZxhum| ztZBQpGR z;~#ur^&PockKdV{Q6R>o`Pl{0x!DEbpZ7y9Y;*ZvE!*gU`V1W3znva{f=?WO5I&>B z&hw6}tjECtaghm5z|C#%M;Yf_*pI^};h}Vl=^r9EN=tVDj86D;C$jIJ?K7VP+00000NkvXXu0mjf D5i!M* literal 0 HcmV?d00001 diff --git a/apps/p2p-chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/apps/p2p-chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..459ca609d3ae0d3943ab44cdc27feef9256dc6d7 GIT binary patch literal 7098 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 literal 0 HcmV?d00001 diff --git a/apps/p2p-chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/p2p-chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..8ca12fe024be86e868d14e91120a6902f8e88ac6 GIT binary patch literal 6464 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 B0*L?s literal 0 HcmV?d00001 diff --git a/apps/p2p-chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/apps/p2p-chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..8e19b410a1b15ff180f3dacac19395fe3046cdec GIT binary patch literal 10676 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 literal 0 HcmV?d00001 diff --git a/apps/p2p-chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/apps/p2p-chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..4c19a13c239cb67b8a2134ddd5f325db1d2d5bee GIT binary patch literal 15523 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 BIP(Ai literal 0 HcmV?d00001 diff --git a/apps/p2p-chat/android/app/src/main/res/values/strings.xml b/apps/p2p-chat/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..66fbdd1 --- /dev/null +++ b/apps/p2p-chat/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + P2PChat + diff --git a/apps/p2p-chat/android/app/src/main/res/values/styles.xml b/apps/p2p-chat/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..7ba83a2 --- /dev/null +++ b/apps/p2p-chat/android/app/src/main/res/values/styles.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/apps/p2p-chat/android/build.gradle b/apps/p2p-chat/android/build.gradle new file mode 100644 index 0000000..b4f3ad9 --- /dev/null +++ b/apps/p2p-chat/android/build.gradle @@ -0,0 +1,21 @@ +buildscript { + ext { + buildToolsVersion = "35.0.0" + minSdkVersion = 24 + compileSdkVersion = 35 + targetSdkVersion = 35 + ndkVersion = "27.1.12297006" + kotlinVersion = "2.1.20" + } + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle") + classpath("com.facebook.react:react-native-gradle-plugin") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") + } +} + +apply plugin: "com.facebook.react.rootproject" diff --git a/apps/p2p-chat/android/gradle.properties b/apps/p2p-chat/android/gradle.properties new file mode 100644 index 0000000..5e24e3a --- /dev/null +++ b/apps/p2p-chat/android/gradle.properties @@ -0,0 +1,39 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m +org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true + +# Use this property to specify which architecture you want to build. +# You can also override it from the CLI using +# ./gradlew -PreactNativeArchitectures=x86_64 +reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 + +# Use this property to enable support to the new architecture. +# This will allow you to use TurboModules and the Fabric render in +# your application. You should enable this flag either if you want +# to write custom TurboModules/Fabric components OR use libraries that +# are providing them. +newArchEnabled=true + +# Use this property to enable or disable the Hermes JS engine. +# If set to false, you will be using JSC instead. +hermesEnabled=true diff --git a/apps/p2p-chat/android/gradle/wrapper/gradle-wrapper.jar b/apps/p2p-chat/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..1b33c55baabb587c669f562ae36f953de2481846 GIT binary patch literal 43764 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/apps/p2p-chat/android/gradlew.bat b/apps/p2p-chat/android/gradlew.bat new file mode 100644 index 0000000..11bf182 --- /dev/null +++ b/apps/p2p-chat/android/gradlew.bat @@ -0,0 +1,99 @@ +@REM Copyright (c) Meta Platforms, Inc. and affiliates. +@REM +@REM This source code is licensed under the MIT license found in the +@REM LICENSE file in the root directory of this source tree. + +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/apps/p2p-chat/android/settings.gradle b/apps/p2p-chat/android/settings.gradle new file mode 100644 index 0000000..6877ac9 --- /dev/null +++ b/apps/p2p-chat/android/settings.gradle @@ -0,0 +1,6 @@ +pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } +plugins { id("com.facebook.react.settings") } +extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } +rootProject.name = 'P2PChat' +include ':app' +includeBuild('../node_modules/@react-native/gradle-plugin') diff --git a/apps/p2p-chat/app.json b/apps/p2p-chat/app.json new file mode 100644 index 0000000..a11963a --- /dev/null +++ b/apps/p2p-chat/app.json @@ -0,0 +1,4 @@ +{ + "name": "p2p-chat", + "displayName": "P2P Chat Demo" +} diff --git a/apps/p2p-chat/babel.config.js b/apps/p2p-chat/babel.config.js new file mode 100644 index 0000000..3e0218e --- /dev/null +++ b/apps/p2p-chat/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:@react-native/babel-preset'], +} diff --git a/apps/p2p-chat/docs/screenshot.png b/apps/p2p-chat/docs/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..cd8384996398b59441c8e938e13a2c3f415c163b GIT binary patch literal 188732 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�V*jS$=^ID=Ut#lv7ndAc!32lnobla@L zLsOIT^Yd3$CQ8>LrlRZb{UyK7Y5$_n3ze>+j}azuOn;u?!rny-b8|hT)WQh@O8l!x zZm-OhPLAp58adaTlmZ6;lkA|2nvA=q5EuBS#c&5mKMu=@6DOc|{cimw3DMCu5)ihf zX7{eH3Re$Y%faLsco{dEI%zUEz*#LP_@*&!G@wPcN>P|t;P9f4F&f__MylZoHy5#qYEdw?O7x}vO-6!|G?i9+_q+#{22{%v%5XsI8K;zp!1^V z74CjuxkL8MEqmGl|L>8gobS27Y$h#DHUa*@v1CKz|LU{DQ&N#MczY`dQj!T;HM5rF zn??lRuXqpK6f@U)yr(T^=&^$Ok5w8wG0_pt+sCySX!jwCix&XB*_j_kC4c?PHUzAL ze^lnV^SI12c%IPYE0HvXgVD34JLt8r0}rvvFLncndpT_Gjf-rpVFZ$v0JR;3|Ety0 zdKVrytO1$w7P#>-$Yz#SVDQP+vB~Au zJC`t|rE!46hVTo~um01dY*b|f9MB#-_Nw7Kn_ua*Y9VGHMjCv|TN!$7mX)^j;O>{( zlp%yE5{ZnW%1BG^_d2BT7esW|OvS>D!$u`~iulQV@W6SFggf?w7S$1NClp&8bcC&INg0jTm%Cm2U+Rec!YjFv!9Ct1M?T_r?XqFz< zaCOVkzteOwO*(XPH1Z0a1+T%E$h!`i>coi@hkBG-bU*EU3ShQe+~)`@)@It#VM|M} zG2Q>{rI?%-8cJE8CXdz5_>~GSeRS$Bcz1Jw|IFNNLg}pqM1IX85#8YW;8kGZgTG{$ z-vJKJ;IKyO+fN0PWmh2gGxGzshkxi%|B2D|d>ISE@cfO5rxbi?{$3jxDMfkd&^@V3 zo;fdypJMVHIcze1A$B2$vekvX3YcPwJ(*+)655jxj@87ez0~=SKpl1V#fhk^S~@Un zA0((mN`U)*!&W*VvOlG7xg-NPZzmuP9qjgiQl2F4pOGb_-a1OqPdADT6DLn6(9Ayx z*@O}tT7x%Qvez$n_5{;AJ6VdoL^PztNCR#n{u&F-+??--re38M2#jVyoShkoS0 zIqH>m78Y1jE?yC7O?|Vr-DpzRh?jq6Hx5@gF;-|qxICUF&0e2B=bWX3DQy-1I5;i_ zIT^C??Uo(Jwe!LI{v(gx+3MqR z^>0AyC7ZAiwY%7OCb1howd%+{e&-5~lNBB}CSg@yUID`ID>O_F>DA_}yAnS+HoEaO zFilo0aIjiM$40HnQdp+jrGt-(-vW-4IbQTh|C)IoZ!?PF-jsV;4Ke4N zCR6V0Y=`!pPYBnA?Tm?*!kB~%)#q<`$yp#s=>h&hFnpVcvF2`Bq;(=#P;Y&L?rA)W z$(X3~L?O%Lu>%&9_PQMVQso> zZD)nh{s2Ztrh<7z{-rV}k?7UEHrX|#d+9>pP(&KOL$ZC0SF^>I#y(*4D<;1ULc)}i z1$q)N#*!)YJGO0MA2l3L)%9NuNi6BOimpHShh4cO5DBR5lQNR{V~IRoVqfK@B)h*b zbo%3y54?>R(I_#GhJI}cqgI#nxueNszrdBn;uS}D$c#VL4_kUd#=`Jx73!(F*^Z)qGZbuF=;Y8Cjl+qZMtf&55qtwFLVjA^g4E z43q=xoeEhc@_AZ{B zUr*1O2o}4P`O8;rNn+ZNWrVC<6L4O}oE}NJn*>hj2}tYzafVl0v<8LKW1NWoFSD?t zXvM+Nxx?}elaKTgE_H7T=5G%6`cBjRuEB!y479cPdwJ(6H&+gfy5?4v*U|+n9Xux! z{SbPa?5!k>X1Lz+tx?W;KkC^%3FZAqQZSkZr^Rm^miKjfBEL|;$#wKmZxCA2;fjC= zU|v{FHbKuq6eT6-?Coxt8Jh-%Ng{>Zt5Ir8va!Gv*y9!t)A_HwTC;@j?R$Mrhwwg} zKB{;3!*XlV4jap6TBPlM~e(z@=t&4@=UDWZq#NSka-TZO)>1<7{-<-okh`PhYlQ` zZOPtRe0-rkcM=BBsn86E2s&P|Ljz3^iO?ukJqYe5{o&DkT02lXBs>G`+>5<^&+QqB zGn09!X3I67tC`uUl4@PL;1cBtaw2Z!;j=6CqvqnT9i|X>MJ<-LNT6wCkvT{z5ORRqrkZmWj3mE|`ON1M zmPtux%pAflW9)?aICULMbp-|+CeXM7E^|;(a`nTjw&IcfhbzRzz9%_nU@p^hfg7~3 zW-vAbQEpXXrJ-I$&KA$lhHZD?1Gm13%f-{Hd)I4SzCVm$uQiLVQ0r5X6x}j;f`Jh{ z=q{nWJ$VJ-^a3BBN1o5OuvpU&h#uFJ?9drI@cqk+1uCh7tsf4Y0kon+2|+-@(x16A z(3HIlAIDTiqd>ciycyqMkPTD)TKVQ9Wc#ONDN$61C7R(@G36}EqT?O@rER5wfYGKW z0+mz4^XUB#--oT$usHXkTR&E_HXkE*$ZC>%dRO%Yc2o`1O@+Ir>z)4q#F4$f0xMSU zw^}!65-aEiG^K_R!nylioqDzn%?HhOL7hwRUr*Vy2B7)~3=SaX~*7=D-lA583jk!&w%&=jzIof1&i9qqgt3c8e%{easv zOU1v55)I<(hFL-3=YuEnI4fo-p^EenuS#WaG71qf&F$d~7ytVD>)phoZu&;q^9nBl zT?9J_Of>ev-L?KJ3O%4biT!f4PX3%qNoxkv)BXZtmyE1a^pEL0+i_4wz%?`{jO;7? zt`|DfUO!K*hvhs3)i=~+;kiu|iR^Z+R8S;J=4rY(GY$KU)8t;g@96oJOM^i&f6_*G?B9il4Qmv>9Zzw^EvmPt z+-cOzoQeEAk9r0(S{HBsqNtg_HKr}uo=3qCT0|pz-WcBX+>XVmNg=Z|Ar&)vMpZNq zKc0Y<@tFfGB8q@_26e}YmJTKp&wp(&?ju-XUnELs|Lb2FTT4Qhh4&67HQyT4Pfs4w z&Oz%tddAyShC9Dw;%d7)PlZy_F15iq5;WJjePR=5uI2B}+kh>^OO3qSV2!>Itn0TN7TwiYp^^OZ_7h)b8;#dbXC%m>fK=x>AO@B zND~jiTSwy$BU{zXL3F~>R66tYE?+#UQ0#30ZLNfn4q-8aul-(So(WC7&6UbyjvocF zx86Tq@HyA|!B3-l;;T9I5l+Z+HsD= z`S_HF=W07;Vp4R1mO3VK%-JU6PTCkcPj?~nk?TowdFe|e&#fJg$(j+TrBV;^zJ9;7 zfQXvM`YE2!Pm3WYeIHv|>hb!}`*ru03TLGA8%&^+3Yx@E#u(6xz&1NlTf0&X5m2MO zS0yR&ip6uD6}?C&sBK*+How6lTz`>vouJ0vr3@(@yrKlocnDHL2j_IJ-Xn&G<_2~a zh9}FZALV=OQD{5jiLIk}!C%%*@3~1~CFQop7vfRtI~mUCc#vH*tx5eyM?64cH1l`i zYdacb^Ge9hDsGZD^@bX6tj5!?yze!DG!4k)pfkmke;Th_E4WgISmR( zv8tfF+4hKsJFOxD(6=KTX&>@*xxIuji=TsQymCZcmup5)L6)H>uqf-z##v(G*iyU^ zwq%Uw4ldrAbrqa6(_YE>^h$t_l>1e;gkRblCDl`ABncjcLZ^kinInUQ1=@Ab%0gz? zQG!<`taJ#?b&ZH|CsJ$o#`j|TCQ=R-BX2S?@^$MMT+kwT>UhZP(8Cl@lnU2j;%W1G z<(5k4?bSv}!ce<*qINkVq@39}7U))q_W#2las=<2>6QclhiAd&Dz)sjES4OD=e~g3+8e`FFZ{i4V`^Q<68EcfAZZN^a&YDBMEc$Mh$}==H@?li z(e!GyTx)7gcIq}2BQ*Ns4by6<4J+*(T!D@RB7A`Onlis8#lOurESP*{_mOGeQ}iA0 zH^oXK8c<8D*%itTrj@aF)@u?(aBpmNX>A*`L`-9m1;(vr1+-}3T#s8nT*+CA_hRsR zgB~f`ZokqT{B6nrpbF>VPt#G8p+eaXMd6L{IpHF+ zu;CDQ{f_fUUp6z;6R+T7Zi#9wgNft+OZKo1@k$l_1c5RQoWj&rXf8s~9(JmZ}Zn%p5w6WkBrPkp*KuYxKHb&3t zVa%hh6VGJlt|;4L=W)tX=trl$u5MplVFJh17@hE^@46bR#h85`9tJ8N?Vk}|%T||D z4r{WQ=-@@C5a`Znw&yDgqc5P^9n@gP>Q)ie^S2gmaG~r?hnn~{1=>D=S_YDcJu&Az z3z}{ZOWxq%+n$jI+}LU;>jClB1TFOFtG?SA^9x8?Pn-)qDq4H<+eN(a)*4oDR-nbI zp81wq*rL;YvUeoHK{RJ*#x*zI8>j;BHqZEc#nq%k6t-K;^Irozz=ap$I&0ASl-V3D&-i*&_VOoy=FXGnT_L4QDgAsF%LbBClr`Hb07}a6lLj zi6u8bw`~cI=%Bv=oZx~My5BC~py=~|We7&V6_D|iskR!OC2>KCV3a-S!E!~bReAu( zr=B@rFUtCWT_JZwmIWPFEFh}Dm24pPqOwElJ38e~+v>R~?)XD;nuAI+q^-<9LxsZ< zk(>$kOj=2H(8Bt>Ut-?TmQ%`NUlVgUi?1BlXJo#&(h=tLjG!}pXBSFQ&i3>33!S?f zhK`<@&Z7$+S8*o;uO3Q0Q;Ba3%7iXvx{GkwbhrRlBmq?EoN`XDjLV1i*)x~hv-3Es zGHN`SKuBN}6~Gr(YOiM45_EAL8qwe~TGH&H)hem4Rrsn+7sp zd3+#4nUHhmYc^<@xTGqo5oXF9Vu#h;*OJ2Zrj9>=_to5OI$9{Kq@Ttp(;ZmvGe@{rm%;CTA@szC)>u(VDf`zsuN~uPK5iF9KdR~P25cB?{)XT zKD)&m{_LC*5Cr|ucejo9kO;W@aOg-j#6zl49~V?>@Dj^rVcB5rn+7gTjeDIxbS?Qr z)1TZvEeSc;Z0KzO>@(TH^t1RB`-BHnH@G>zvr<}#;;bW!rOjv2pTzpsEABqs znS)?aiE8yojoq-|lkQKTHr!s$7ZzRImC?R6)vuuH9jXl``1wz+psR1GqH2~r2@yuz zSM$4P-W%IBXFPo-65U+JTM2X9*g<7gbQF2_ocq&mYQIy7?wM3prOjrHQo*I%n-AUY zM|?|jDnFQybc*9v{`De`>V(tQgi{lz`*euFZ!Y z95(b1J&Obby(+in;_&ODHJdy$eRd3tYqNk~T23Bk{m~v|mR5SwnYaA1wihU1VQ{rl z*d4q2=0WjKc)$nb;wRg=ODbE#+NRs0y6#s2gnmjDsQH%i-egs?3e6%CcDDxsAM3lC=5veHXR0Z}?DrH{FlF^!* z%`2zb0^DRcy{!6b=9${R5dr56o`<+lubb@hJ0c)`ff7ap<3%R@x#QDwF1FUF?#VoQ z8a*-|TS)xAqElYhnxj}rk{YXjl|dtAFcUgjU`kzc{NnK08QFb_vu^_9k4-srIVmq> z@C{&=i&#CehL_~|KI3_z{(%dV=Nw?n0ZX&&`XC|MfM=Oqo|!Kwe6`*Up&EPP#{)oOmfOrN&4W<4 zwnLADz*WN$Mie!i+t!oP=4MgQWJ}B1f+6bu+@X-jKUC{fU}R8bTIRhws$yCntyu%1 zCHTcA7(!L~+jZtTd{$53Xya7{XT7Cwr(FHEUJrtVBn08H@B5SeGC{V{dvV&vLr8@0 z=mV9?w^Rk*_GWi*#}ACtvhM`)A*QmV0RoV7W9CbnXM)x6~j^T`;H?R-RdFtQ32jI$tXq_9$em;o)tM&^Lp|9ZG!C{Sg`T={H9e z`YFCk>G~?^j=q>s+G?7bS9(wrPlsSXOSvR;XWb6~1omyyaeu(2OS~LEA{>(9Su6Ic zbArnT&>B!1%a||E^;`!Z0$e-KS#TwOVqM6(qx+Ursno;+I?9lGc{hf|xt~C9nlyiZ zO4%~mzhi58csM9y32(F9t~5aX?mzu3Xhg#zKgZeO0;JQ;gn=yXB`i3J%dv5(4uoHH zRyK!V5sF1!A4ic78zqomJ7v`9K%@u>#U7$FmNLME7s6vmx7MUjT2MaRLrqE8!}wHP z5wxU**^Gar^JkKjg@%vZXh`-~yD=LnIUW=kAi9G5^uLL}ee=!)iuY1zuy`V;$=be$ zQ8m|-tKwu;*flFzYTP(Vnrn986&twQ7Y}uP* zL)69WTn@7V)o-tvt-5p$2QUvDNYQaTJk>{L@1Pnc9N0U~>eDa^Yrea&K**qP&lr>d zPI1><3lOee13}izxu*(Hj?a!e!2R;ujIV_@6Y+q|TV;lR&qMgsvj0z}tAx$kSqom6 zI|Mdyo_ayemgb*}E_@Yi?US_mbsp_B?OXqPq1CwkmCJd>KvIm zpC?Jx@ix)Esf5JtqUtuL*DL5Pj1PA|>oIEX&FoUp4D5UQW2BOWq9Pb6S``%kCTJK> z#zFiawHif^9gx0pDkI4|j5>j$K3pTh-@Oq+{VuCB>IKzn zyV<)f7$u`ik6P)KqoC3`r^}JO?43$j%GY^nN%*65sw-k(XRLxqb(O87VPmD6?bB}x zS7TueA|z0*f7O9|)um?x>OP9BY_OyM&M7)h?(-`f&hw2wl#XklTn(FT$qKyF5t@FW zgIF!3;TNpmI_y7ICbY#nRNkHHmF{wcCX{^yKOrNnk8uq?aPB$$)A^xd<|}+ zS8#rdIK`*=1r%&gwn5;QeP2ajF;k?smj$VuCScn=YQvseCW@hRu>b zqot+3giau>B8bDzN~yhn9)Q{?{luah&+hWT3f5BKXGa-6rs1(L z;f9xsn@C-1_K6omLtEvLzLSsoE|ZEA3s(S>-L2ArbcrZlIQK-g6lZ&0y>>?O#tHe6 z-Ot~eRffrGV?~S4^aT&W)ckku1g;Oa(^gitYVp%{lyAR9=>DRJ=ez7txPY6|qH+)mu{OFjS?~J=X%mj0nN(V7 znLO8pA4HeZT(|!%yc~?K&#lF}yA|Y-D#2Amj2fqTazBs#wW~f=AVTAX>iOVQYW>~< z!8&GMs%S+G_8&Uuy81C+6Hja_we4be6*mv=n?FeS;#ioiWz$`C=x!0zcx^SC{&7aK z`%yHnaK+JKfk2Us#a?ocmRZ5-RmFg~gvS%b_XUFYnB)bvuNTIH2{;}TEvfR5s4syWUk%JwCw@*4}oj$ z5`Y}mg$W?+fF-~aM;RB?YqiUj+neuL=R6*9vAYXI=4L<2?Ws#Tr@lzem$x14KbK-} zJk4U{c}YF%xN%yiTpqIFSx6RcxSwtc@@X8oY=4JTj9?NgJ86c?##Cg_yL-?gXM~=k z=lHe3qJb}D%IG=i4)-y^OWw+}J|PaYV(G+B-=H0WI^~G0hOxk&9lyPiK!Br;N2CxV zI6zli#-RhLe|UoZiN8D6hL%Q z7Pk^n3NK8^-yHXF7zWF2ZBCC}yjC->W&KQZNdgEJ+aNA>Be{Q-DS)7!N8w$Pv_t2b zsk^%H1I*Wc7}Z&MNpbk|v>d-nXi3OQO7q1*=uo-Uj@Rm>M%@^C`bX|zo$EVL>r+V^ z?9+1u_O+SP4=j`>3$erNTF>4>u)~)$w-W;flb-}cdq49NzG!iSEJkjF-Qf%9IsQic zQ!>?guEWBl-lPEtQS8YtG)hI9CE7mKo8wJwB1P;%gFo!BsR$M(Ezkd4DG(*rMhoHV zfUN6l`p?c55S`Qv-v!hJ79vAwl?@DU=x3P;f>C@UXM4~UqN#8-d#P`}F{fNt4w0xm zdFyuqvB2(su=nQSP_}RUaEXYDB1D!Fk`NWyCly(EWt%7jeZzg!#Tnvp(!o39nM2(&!B>dw zL2Ht<3D-G2u#=#?_mU;PqQ9Dft@kjbX>0M-jGBao+xu6;wi&Gn<^{Jx?9)Zx>qfv@ z6Z(S;r45=qPaGPaIO8wNe=8Q(-KwPKiY%D;x?2Y8JYl;zZx5A8GF~#zJ&Fsg!I`U0 zUIh-;CGNYD=4oZ+UEUz5Jv2Vff5-?`r??&6T^`z?4-B*_Tk5c~!Ij!5I|m*oXfU0X z|7&HnObDMo(k%YGo~O5bY<3WG$v3Y&p5SjuKd`vKreSEF%~R1I?mpKgX-}Zq-iPq!V!*?9y*ks*KqPo zZxU|Lmbz!BXyr7GXQCD^#$cV3WvpI&=`PhRQhg@;aIuN0QFgH8dUgMC!-fYgA(l2AA=Deg;SPOR zWsicCP)1_!b-IG)!?ukw1(aUlTntMSc6|_uWP4Y_cdgjAx0bQ#3a6^{B^ka|mu_n@ zkDxT$fQF=pLraO=m!3%|7QC@pzSLfeep6bmA@B6qs@Tlw-TD)^SXs6EP1^<`n`?Ou zPlpb`hh{{_ragqerurMh0Qb(?JnQ;8p63$dowBBtiG<>}9Yf z9(a3>Den}0@RPAd=^HT7eA1xT{U>W$s!TQpll>gZs$%cg1>ij98?ZnN5sw&T*?H!! z*o`?WwSB_Zinf~^8*HydB4_Mi0DR6a^qEczTgOXx$NtnCg|)@2Lo6(2$81c$Fa~ax zZ1t8KAjrfKGwb@ZQ&KKNLxq$5p&mrJz4!Wj_t>VW2TCp6kLLILtH@#|FiF!Llgph}a#V6|)hIHTjEq)bXcmf{)$UM5& z@vBk8nXD*=q~ma(6Lq${$u#yd)Q4{-M0jPYKbV{=zGlBLRD8+%ANW}ypFlV75sHry zr*_0tJG%~0`yWh4+@waQXUc0O$>|Fu`wc!3#k^=Xn*?5P{|opyKEyjkrz&k*S6pQd z;DtM~BjufSM%u^M`U`@OEW)F`AWf ze$DfBx#+p{F5xN0ar|VF(~7mnJ~0y!A^RkK~-@6IZT0EcG+x?y4~FC)Xy`t zl25*x-C8I76Fhk_kZRr|C_#9xI(4~fmbQUwG~o*)ViVRw^Rd--BsN_gKsRFt-itO@ ztH<`P4FFY5?JP06FMbh(>2)Efg=U}>HS6(M)z!0UokzyAXh?ccXyUagE1?*)u|Buj zv36)v_%h5{_x26wRuirm4WKW3XMudHo{{c9@Ugnz0lO~&xUS|)10Ex=`@50L5w&q_ zx3q=HC6{4)tLYVhJP|dcqzqUHSZnxh%o#(-8}nPmyT^XroO+Sxdp|#czip#c3(v1Q zhrg4P6@4yy+QuR_T@P57%wQU~R3K0tb8SWYLLDLBd#TaNk?<1+-B(`8|<%5n*kD$Z-&uXg3ot18w7)o zEBJ5bVIhQ`FVKGgq~+nKR;T*PRoA5}%V5p9dc>l@T_wDu5&>Kpyu)z|4Qq}C2Ma+rjM4hr-3i%plTqLLA z>u+kJkp&9jo-`^i=JRdY?=IRF`--=sawJ1{Mm;Y2WrZ(Qe=Ln-Z)aSNKO8yZ5ZRt4 zgpW`x-7-eG?hZ~i+=v2TQB_|UT-V~qf4eESjSWs+;Qnl6=;HZ3a%QG`(|ygG|Mck| zfC1Hks-6m2_FJTOXZt+Y*q@O$!v@fjBKbl6^&vh0=IW_E@T^!WZu?=?Z^~H8T9*&= zGe@2xOG!n|>Q%=`w0j+*n-ktWO9f-8D~pF+C4cN<1P&Mi3iZqym@~8tr^L)TUTwA{ z$~wC2m-DXttuJa{KDV8%_4BnGnhoNC5j*5J9i0=?`7_CE*|}v6^2WcFGh`rVqs-4+ z&D~CxmOZx~&MEa_5aaqUeo*MfjTCCvG#mD~LQd)!yXltlueyrz0fgwh3QwRem*N9= z?G;ZG)*%iz?_c%FDQr~IA+EQ@qO7)05n7c7EGFq1@u%=QE`OJJIBl3^Sx|wqE#}wJ zh1OK{w*kt}4^k!>#LZs+y-ff%C*6X#b^$0ziI@j!EHdi9O6i#!8D)l^rFU@_m{

zsPN|WU;VaeltVzVMsD-$@x?(*Id+T0<``r$^J%;Dwqmi#z1xF#^8cs*tow(ru6S^6OiP3x^Z^-+S&OhUoUL~2sLSRa(AKk7Z7A_^H9}s zTNK>SGBKl>nZAqN@}9c;Dp!KP+}#YJHQ7%0%9!Wb_?SS%bvF2$Q^i7Nj6R=ot|J-t6eg zZwPFVC6vYmkJUHBc{w+~It@tRnEjGdHQiV>FbOh z#46*&bLN(gI6sx!!%{Eh(cq0p?jf<4+7zQwAq#d*8K0H=do^n3ON)Nr+q_hGOA92f zZ3B3HTFxQEg(9zshK%kfdKt7ntDED_ZjQwoZ&!S|5xFgXXY=y%j3OP>v>jt!R`guJ zxMU4x)1o{4#Vq7<1GY!Wv(>A%!97iQuDyyu08zYZt=mAFo|Vfi`K@fnONW>F^GF#C zSLW#Bw=fuNt&n`XF_AG=Bh?&J)5s?FAp@v?3v;KOIRqfnm~o4@Vm?dBZ)p`Ch^46;i-2h&kxX4h^@w@1313LasG; z7L+ZnpvQwv)F4(Y&?p+wv{?S)B>1|olf2(nA;lQB>e5wzSYJnf!^kqQcyM}@Ph~b4 zaEe0D=3N;>%1ObIBZJHKCvlXkhpq5(}mpx^^b+Y2DC2l|u#c7A}DOdf#?TI;R> z)scpgJc{P43qU0zVB5Q2NMuA7mQ}1u42=84-J#r^spdb{Cj<`$4prVtsQNDiT)nMo z=~!dp2UO%iJko_dXNuPcU8NA+?qQ=|Y7d29HrjHF{x0^&L-#4!8b=jRwGCLLQx&_7 z>O#uax;oSy-mii5yTk0Tgu~&Gu;)`^~_YFasre`Vav2It!RXJh!rS=IG2N z+eR)%#Y|Q%U@9VZ}E?5z5-4RAuo9Wz3uTU3rT>e!s<{|RjvInRm`9LNvLVaz! zFY>2AX4m3oJJK4v;c5NJ3BFQZA+ZxB6V#Ys511|(|`LMz1$cj&t$FpDFUSDsG zYPI0c>rRG2P54jr`PRm8o1T2-H2F-n=m|wnw_629Hh+2|2#US z2Yx_Zb7M%6-l=(06gb0o{vws;w+Kinkkeee(|Ef_%S*b%lj3%^FFG0UC(i?=)pHI3 z8_~l0&8Y+nI$Mv)vNQ)&Ri@HEJKM*`-fh&9UuyCFz^4uL=lyi{O{_t!_tnj*v6r zL0B}X=PUf#VHxe)RLKtwLUz=~fITOCs%tQf8a(&F|fP`t9v`5=& zz=8Y<;dHuiuHo!H!xlq#X!Z8E5-j9m&y?`QtF*%!Tpmc)!9#F~2b%i3v8@&-bzkpu zU%3A|+5aeW8`$VVa^s4>g^$@lQbh?3`jH$4liW0qTcKH04{(XMsQ!C9q|+z`QJp!Y z8>6KFOwogvms*B4Jp1V{27H%7xG@Xh)KnYj3;yO@eru{YwRfe-H9o|dzp47;!IG5* zc(ZH1e)n(2;(OCYTELt3Xsi41$6Eqk-voRfV<&ycZ>4MB<**43l-r-W{PFy6ZrS%! zdK(?Up=LGFPyAb9em(%p$-DC3a{lAa{}+Xg)iz(3vwMa-!tJv5vG=D)|E-v%C$4(U zdAl~2B?b}lQCwGkD{1?#7C=7915hS;S1Ouc;pyK^d-olAb(Mtv3dDYDZ7ZO)8zqNW z{x<)AKUnVFL^P2KL|Ia2g zkj?mSQG3Mf`+tU0Uv_O5wJPS%&6Y&=mP$#%&w`XT_T9BO@F+1acuw{_?}5L9&xu9I z0Ey1&tE)}2k!86uIb(#sT2kK4@ygbTlFMl5WFK7t0h~Q#ya*#0O6|TZOH?2a;75Gj z-L;4_X|sB;tt3kF2M1~<(7yeD{{u%e0I$sUJMsM2N57unU}`$2?sqjZbi5DuQT^*5 zXEwmc*#Kttug|~zi2?}u;bu7GA7lOKct8|*1JArZ=-~fqbU4PFrys^a*cV;HYGTa*$FLO_awje zHS9g_hbE4>dXlYig<4NL5~R$by{7Iib<3(5fz?O2B%QPu#AJyVh_hj0Me>YSyXNx- z&4Y&<$8H2#Boh!9`%?Kba2Xi_>9NUyl3k5UFZl3>GGO&c^w#VZ=M22?&a$E6gGO=; zE|P2G0ftf&kCv51%6ziOZyJ7QO+cS1RmYf%6xYyHJEj`CH9ikj)x+kJ2(ivfqRGH*Cl zm;!h@V5cgO!B}zYvmRbK-dQ@6y`4x~dQ1R2v~-MbsP0Oy`^#RF5lwu6tyDmV3_`T$ znn#R?+3aNLC8_aSZ1U}El_kU>^;UUi8~NqB1Jvk}&&Xbj3GULxoO62Zz4Yg{cgnX% z-Yv%Zj?D;iKL}%kv2f?M_zpW`yCu_D7yF2lrLw)W5iz(lbynzo?hztMFDzkr_^kq+ zG}9~K)5i>~EGs#zo?x%_QNFqP2yaoCmiy`Qml;l9ddvd(F#TQ5RirFFiQ&;WyGc@R zmk(z20;Pn~CXd0%x?)?_NxZ9*gHBUN$>UFsjA$Y`pC#!rUR`~8eY4okdI8W)W%Wk#32Xwl2w+1Qc?>!&y-1){af@2hWfQhPRVrdoL3$Oop zrm$qwZj_4nsq<3R_5BfJl#Rnu4)fxg^^`@*YN*zJ5Nc`2eUR(T$4F%-&zb|oBJyGp z|2ETuj@>PXuL1>b6qBV<+68Q*sAq1;rs=(tvd)uMjKg~RmIQ!RBGf1uhr8ur(ciZw>Xh==4m~rRu#QRntfIw#LrcXMbOETlVG_-@}R||Fjcd2X(C)J%ghIB zeAK3+;?N&2OO#s)YIyP}&J-%UxVNEumMuOh61!amy&iKF@-oxipgtzE zEIeRo7`M+r5hb#3j<-TkPOO>lzlY^?R}2@)Njr|&$;IKMy;~u7$`-L z+A}}JC3Hhum=3kocX!Ktfn|jM9%A0>u(5t@3_%pf`^>dN$S3za4>ihQbav*XZYC75 zn>wX$kZp5tq1!G$o6QJsQG#5+T_3a>=Wq;(p~o$hmUpuIUJqFddDy~7#6K}iqY~Oa zH4JN9UwFE@%M=7AI2S#UB{5j2YqFK91?z_?Y}LuHZa!0Ud@mrs))&!xK<$ z(q#zmwFnTO3+&759(n}n@p!MCYxW?F4}$lfn#XLv)(ul4;#32keVmSMzem;_+%9%4 zJq9k!F7BCLss`Xy(??$#P=L;M@3`=fmcKa~eyvEoV(i##c+ABZsVw#!W(=t&o$7T3ANb ztx!>f!@iw7m{oto8ELxiA$2`_lHUrxIaMx@01Y$Nm$4XInNg;~1Lt>QbZQW^9)a;c8Y!Q>q;0oXYiB>%v^;; zN^{ZbV7+9WSCg94b_o{dlV&fwm~>U(9o259*FZDggAo1}vNKG}HR>DUmSxN*jQ~tl zG*yE02WqPa0OQLaie8Agx^SPEaVe1XpdB!L)f2b~gwdy+wp?=UKwJ!V-v#}5m=XUf zvp^D!R_k)A#a!f7N+*ALNlkD53_-M8(l(UgENbhP8WA5*;J1`BI22VKcCZxrKx9LD z^RJ74QBtJNFk|)F*JCYMu*o-UWKn+&cumdReb}qJ|IM_Fy*F%v#ui6}G1-=1kMwdR zgDUmelIHOT4;mz+S6v=g#85GU4QksADeJU1!@NeErHW}6V|FSeovzWGKDuFM9Y$0A zj%4KeUUMida4WejsF;MTDZkT_EX(7{Xhnfc>}TkAp|3=>$9I>=$u>HwW+bn z12FC@0xzkM%c-3APOUsCf8Z!&$)&oumC{4n-D#{l#TI1SbNn7#;Ba3R599J3f^B({ zm7!H?v+u%;k^w7;hx?sRudT?Cv3vFLiI%`-nZ+Km)Lmq|m6BU#hbWBZxnx<}Yo|q0 zymY4U*CrL7wK18k)_^B#eZj;KC{cdx9F$;@b0L4c!UUBhjPgQGWr2G;6$sMkHLF+P zCyK;z<^c7b0n=Obmh(OpckiAmU})XaFUqfdkt|c~{BfcOU6(TD^}5?{uAH}5 zR(y_=Bm_+7l+_*6V_Z988vF?G-3pZ+=KO`!w&4NXB)9#jfVi&*)YTmHfz4z48d*Cm zadrFVsHWjgek*AsgMf|`kDOJ*d@hUG0Rq%S--al(~y=D&L!9Z*_64c$F)5S8mS zI1v-Llw*>JEj^c+26G*!y_fj)06!it|F-)@jd$@nc04R1a+_;PRFMcBRZ@mOIrOr3 z9qJsD*-k|5TVLb0;06!qc@sVlIZsGNrpRJt5~&ZS4Ba%3{I;1!D$!jZe2SZR3K zXW`8h1`kw9@%HvesDWwP!>&(@A3a*!!(bThBf7Z@1;8Fdk0{yxS-1+tIJV`D_mK`t0+ zt5@pKa4JWhWMhrrnQdI?@SsRHqOyP91t93@8kQr~L@S|B^fh{@vu?jy^Nf?nOs>S` zvu$~dm7&PBXfa1v`abN$r8m3!5(yXI8&Bbtw>7VBjwOHaPWbTX9m+X|rs{fI18r^? z|BhzozAszL_rs7FF|^U$0Qso~aH}3FCqttb;^c#Yr}T^gqEdMaF1%xAj=gWcnX~+A z_XX0_A=H>J0!G73s~gMsiLG3<=FMr4;!<#(W7$plZ~H{_-af(K902SSqf}UJL*0iU zv%8lUg86A!SX*{4YKX4ElOp|vpC04u&BF-$mJxe6KRFN8-hML|p}4+y5Hi|9Q^9Y$ zD@iSr6gYbbGq2StJYd7mAivYcr?7dE$$P*$cV}n~cJ#vKDZ>;(*`VvPNU_V2{U4fP z6>4nJF%5^JU(}+N{kYlOq7LwvbXTO4kNauu0401 zzfwK2Z}hcTGB{IKB%?W^R9H}r2Aw5jsr$M+BZ@3e?o~aDEkhu$=z5tVro|d6~ zJ&ci%vUoj;FIGh8&# zNr*7Iqv$l`wWB|J)vtPYzN1)3XeK8{js9~i;iKtd@yDLkJ4iy7>G~-XDBeBMH7hM8 zhCw9w#Dzi+1lWBhG@UcIj)5vDr%6A@=f|LiBr=aKd%1|a3uYGA)|MxIT7n;VR5};UnJ9S$q#;nbcYk-YI#IR+EN+! zRL!(b$Yri;#bK}Nq}B z;1Ul5T-$7}UY?_?gPvXiAzJ{x_W{wzK#A}}r7OcW9ObcQ z+=8T64xC$xjGg!DJ$h-P_*iWjMN$|k?J&P&I;%(~I}%=jV~9Y|4Oc$I!dNWjz~}@x z_e^$C4{7KMeNgF71Zf1^tkd{)yD*GnOlq+;k-yL25omZFhe^sbw1oK7D}=daaGlhe!*oUsR|3_7mf zS5;-(nH|o2Se?+Hi$kW*zb-Ul?-b6e4u%}jXShyd`A(IL9y3C&MMa6Gu)OXP>3tbh zP&6_tJOP{OKx)P$v09|Qn*o>y zdH>iU68}`fs8Xp}VXTS?mFX~o7OIC~!YRzwYVGtjt;QgUT3QGcA&I&64`+jgt zq{^ZHctU+WN!U^Tb~iSuL8mo8O>}xv$LW~_M1Q0`(rmLKtd;*Gsu6=Xs#U>56^pvvQwRVAu1mYozoTFm(GCSr%uCiDvyOHyMIz@*4SdR+xZss zT1VnKD_K>4zYx7vMuS!pF`ShjG%QWt0Y~6~0!KIK=xLifadyac9;Q zlC|!`2q;qKP2dVl5r^#HeN6v%!0r}3Ky~3QwkSDBl z9Sgms(Dw*^{xBU(j}?$GshrUf6?&};+#_LrHq(ym-5SY!#}({1=dGC3&*m_!QAn^v zaf zCLY7DxwyevY}WBEC}%rs8@%1ip4)_y)Um|_JSkn_d|1P0dIq=qFQlBrhhSM}(~l1> zT!lqnt43LnKl z+gXj?y6S0OZ;bm1K4>0BMbq{V#a(EH*F+a@6FgCMPuhV4N+*=s8({MRRncTW4hlN? z9(FP1h3d6nPSNr@`JGL^y9M-;nKaP{Qw$%1py9f`;oi`XzN;w~Ezwo#<&4Qy>g4jpc}#7oIy+AmgI15PmH-?Xo>y2w{5X zgRl)>5E!?$+#)&b)OgL5K3$ke`$85ZA*fR(_JRpEyYJrJvTo9I^IcT-=f@uT;e`N2 zFEEC`9?4mC;af3ZlmtJ}!ng5MIc8ruUx?O^y+MeP8>@t08^bJ{*^Bh)e~=5mYS`nO z^Z`=p8-WxbI|w$N6=2-rG|QeqfWrl7Q1|<_3UndHGk%WQS9`N=X0{l5y$svj@@-wv zm3ME}S;oXE0|}lt0ziVtzR-yscoa$zyWMj}RYV}f@Z!RC;M$k&$d`AoA%i0nL=R)j zX1B~nk6I#Ha$ynpVx96N$C5OMfaR|0`OSL5qX*^bTlvYJ;=|dg7UXU`2xD%Ta(hf6 zt@Olm{4dd*skxcg9@JDioH?cp(o`EhvDY_G{lqoQR6x4=23~6|K2^vDoE` zA~`zt)9PnPuLsryY!C&ZEtwc-RKkoxwtYsQTWQ(dTK3Fn_Li!0=RBIM5;)bDA#1_KErukAPq<*oOP~|4qULVXcpIj&>@@mHH)pjGm3-V9&YoaY+>FbER zB&N8Vz-YJrh*Rs1ZiMnTbF7aum#^)41UZOh7X=b4rG(J4?0Ts*={tQf_xldnsFFT- z4vz~RkZNpSnGFCpffkL`$WSU_S|DNg{OJCLr>`S?&&1O4G9A-)j7Eql)b_KRn6RF# zko~CEEoK20SBS2Wd4t7kJt6FcX-`%Oankms>kJ5$+VOKurprnez!lR(&my~sGFvPE z8#wtJBd%SJJ0Hq-k0rYW5|M!(8L;)i(R*1sgD$;Z2WvKF>8&mad3ViT4){8L0yjs5 zc5t%03JWo&i6%wG^vaji*A~L|X;T`sA+I_iqUS_dowLN0SAW;vq z0ae#0yp6T+9?qia00JaJ*x+I-qaMRli2Hge}y1hhb9vW0pIIG#eR zOCtI%B`f!&8Y2(AOO@@)g6QgY+)Sy-7J`VsE=-zS&n>#hAX0g}g>%+jdu~Cc8f{$f z4mRA?g@1+FvK05$lW17N58e4?vY(ME=dHE5nDKkR>s`- zyY@+7s`FVUj)i1vPib?$hoDuOr-@;2%BMEwPeb7%yEq8Bu>TSj-JP>}ZO{jL71i8} zW%7d5FSu7MKDAPElZym-VCS4v4oLC%H)TAHQ1uu!E_a`zOtLL(?^3L9lEXy{DFX4K z^}XhACTxZRp{FZSMa^={*V)AT$*C6OuH~Z~BRgwimAONS3kEKz6+<97YRWFCDT2zmb3>H{c~Fa^c%kL+6W3lfr@NSV zH=5fq%@hP|1P&Q1?v{&kUq)Gb=a^dR@qjK)apo3X9r{ZNs;>`TYgZzUKMVC|T3NY> zjCsrHguUs_wR;;{sHZEe2@81{w4S)s-`f`u!(Bo^x0&^)3;Vci_+XOC4@HkFiZu2m zGV&0iN?nNoR|3YdoKB|t8wBa^yMg?P>|rZ)_?yIyK0$UN_8qbXvsFD*VN92 zVik#0$E+nJv+>HnDb%TB)};p6ES?^t7>{opU;lNf&)gbtw5RKo_x znN=VNF<>M6wjs5b2bFr+b^t4Vx-bE|jztXh9b)z;))u?WMDgD(Nwg2hj>4%ICS`eA zEKgicA-_o8Dr5xhhz?b%LGwGj&(OO}pShtr2AU5X7o->QcjA<(<=U!e#wWK@!sF*@ z50`JfQ+y1n&lI;(VxXTeQxyr0*zOWSbF>Omcb!pCrtcE=G z%4=|u7o+2Jad}BOaaXh7j#)92Jh~tmSR!;TEA*g{Awh)Z#+}95MBfnHCb3+rMEKpT z{#YVDu-4)_!&_Th1y&cPyn$K1A=!Y04ESIk^l?}OJV!kEQuh8dXG`DkdLY+S?Fzc+ zt(hK0z2+k~J|_66=Lm-*VL-iwb5m|>Ut0-u2h=58_T1^qff?FU5T_1-PbDH~yh0&; zyU+ab-TtF&5q=AVbd{QR!lPCSHXtwHNM>#iIC2P@;kQP?nJ%{7U0@j5!aP05?>gy& z87@C`Q&{k^YxX7U1G_}0cjb;#6@9LQ2JPU+rzWjGoGzi(@T=$zxi5K3{eGW$dc#;n z*8-V`8BUD}YI>1R`QPvVVkz77s#2MY%(pZ0jxR#C-^{17U*S3N9l1(uw@TAo+&`$y zHSe3lFHPL!#Zzs)yF5hetROfF$=zZK+?rap9WQ^Nh6`KJkv_rR~4;Qxu53MWQn+} zzkTNyoS*+3lhkTET=Z1+igv3%{xsn=^KA%m*vpDe`v~<}Q@i>!vG!S39j9%22(8RO zt{+jPHy-n#9ZHc}vuQqAKo(LOqs0!_US>2R_u#5-_020|AbOPCP?(HYaKHnah z0__rZZ8U{ol;^MT_l7RX2 z072|Ju36gzWPFSYt)JJ7=paOPs8V}Vw`jU{R)H9|hqiKOsvRTQU7Z}xm>GZFX~M)| zzHFqD|A6A);>S=%7tvLb<%NxF?-_?W($tUR8qnl|Tu$y&9$#(oTvBFZ=0v2~l|CtFrRvK)SD#>Z#AT z2&8nYas{58?4i3-P0`ZZQo5zI5AIKwnHE;o))U_)qPzc{j#Uk7&5JB(d~{B(sQQ+* z_cU+_bPmFm{nd~i|J4-P%}T0B0B6rIh-SI(r@1_l5u-b6V{JvJU-oz)_70|((VzO6xoa_lX{!SpZj%~h*?$TW@58Q$|D-7MaDbl2tW)Od6 zrLT8H5EasEc4nPuJ)b{@r&d(9;}5h_i(BHRqPwgN62XO@D8_kNZM6BA)LVxkTM!4` z0S~45c;h#THe}R;g_6`}EgWhRe%b zX1voyh^aokwj0omY=Lg(whuNy9h3ojy(bI&5LtHb`G#%?umj{`^zGzp1H|K%cQntT zcS>4vr%a6-ebicjbT0%UkM7#hl5%ZJRPW{z6MFcbw=^n9ZXyT_>G`xjy9yLF*7> z^kZOtiTuxNSxux5J9SeSJZ-sh#t(jjVZd8hwGA(G_G+4W%zbK@6e9L0Wf!O`Eii$2 zTdCs7oCVV%91fL!>(x_5K+ArJ1ahSg zTvS-x%%E%Os3+z&Uf(FvTbDs1T>Of2EPl*YJbdrmLIVWV>?NIZ6#{&ZoqI!_o%6L6 zOKMUH*W4?%y_KX~p(1Qn62?UGYqVb9=_APyT&HQT^T-WP42%h|3{TMjxRo|ls##ym z)c#PcCVFT5y*vplI+rKunzG%`-JpL#_w#YiQe z)9Ai2A2hfYA#ugKmPU*!{Nf72BNllam4K5Q~L@B)OG~psQx)K zV5!&lqk43m_*#WV(uh|^1_ueWQLU~PU2;6zueg5o_U#E?NPjNo?Q7*>y@dlNW@cum z=zMlrv;)2adw#)#bOlmUV(xMH+%9Hxn%Sq_SqcD1d=y#FDgJxObg3Ya_+gj7q_lOY z(FUjT4wxNZmHz^!SHRsr_V1xh8P?VgznwB3rvspGRM&%%N4OeN9A8)k15w<+xZHM) z>a(Z;@-UUjv6t}7DebLHWex8qe{f}8#)GS@P*+Dqo9PoZ%x>pVzsAnEhdw7@ik7X6!v9#6>iy!8%WbRYbU0(GJ z-5+Ff)9v-1hWvA~nryt8`bn=N;`pDj47uktikM+PudlC18%1G$(evjMyylQyigz{t zbb!&os|o}AWVd|~D72VgJq8N^siKYFVEz!6eef;n%b@MHx217GZ7*YR=&cL6U<`rQ3tZLS*5&)XCqI~xdmv&Xik=1rx8cdh3qt9c?m z2H;+TQ!+9H*El#OQZZ{mC9<~w03~n43bRZ_SCD!o(z-E5V#B~kBIe)Hybpec%Hh^~ zqu`p34$I}#Ss-(yKXoDojXQq$q{L@hOw6$_A1a^g|0KcB+13?2q$y-ENg2F5uR;Y! z4$OaoOL=B4Xs7J!UAb6PLS=UL-$Dr}jw%R#VVxS7VJ)WZ-YMkS_YRZ5dsLJWbsuGz z{QTCZ*UgwueEF!7OjukL{>WPJ2QhgGR8LRZmAHhzO;dqFZ})@^b*un)eqF>I)t2{v z)<}!ms$w^#_X7AA%G+DvQ9go5U)NCj4ZZhm_VCLLho6OapQx#uUFh+^L>}>cy!jA& zQo_q%)giXRVAZJ#?s%+UU?D`4@82T0kE)m@<&v!^B2npow9Nnrzi5g~D)ql)4EPfy z3gn+BJ`M<7{*!ah1NKvy%;a8I`*Sq9aa|xs`rmN>B-($({7+PVH{}26-G5f)HfgK9-WJB|N>$j`U=prp{y#QZJ}PRogzgKL13$OwUpszXJm652 zbYGpg`Dd>ZXF!(1d?!@J|Gm=#=ieoVfNSmk8}6S(`;VCaiOMh40`dN*cmKDo%Hetw zxRBTn=dRG=OS?2|VYq+CrDhPeFhL~a8+q1nWt4{1?(T_%-Kidnf<|PyL2SHa&Y))U z&;*)uB(A()#I&eQbH47u;g(az(xb5#85uvh{LVX!i6sNg;V7a z-MPI3vpueXMcPny%Tedcl+iMNb9-=vy-z=$U22PmEj*nf4_y%xg)+oSqVHGvCCd80 z=Z3Bc;9Y8;Bp&+$F0zFCxeog+?D(vjT3A@rQ$e9n97;Ld{k=-K7l4#MSL}os*6^d4 zOne3)g5Ru03JkEy1HhZpzQ;6m(G}9SMWItorrcoLD+OO1sv#>~+|U&R`Q{2rlSRL^d-}vi?2Q&{<(sv()Y3m?{W=@P39P+d>&IV2BYR@y!QsHd)-yq*|vyT1eH zKn%M7!>f&LOv96Jc)7iRX$q)_noYp+3Vz?&?`we$R1+dX=heRd9R3y3TcynJtUlg&I2q`mEgxv0Ha3t%v^>o`L4f;*7vnX2_!QpwrE;y)H(VoS1%ep?kV*+tnw_lK18J3GlWZhQF7Z z@j|i}l^PABTqoY@+plG|=e!tGwKo~CLFfXydR}k?KsbH-ej;BR*3~yr&&ECI$0s7k z`ySlG$oe=$a`uFTm<(iWAms|rAK?-I6n;{+1_J3GS=SkCLl-t1qyaP0*V-#D`bKpb zl@!RxnoLI1_|F6q_?#PHKNgLBctAt3n=EPC`^Z0V0GRUkLSS3KX%n;rc<6 zTcLo6dv-Aw-}tT>KbY`c{{K33(cAyfU#S1D#@9fn>~udx z=^xpj8NjMXe@Dpt8vCx3~&(t=|y+Uyb(w?Y_tR)5iah z!0&>bdL7`G%KeDdC*FFQpt_HpSUu*xrAPiIAQ~1R{6TjaeKtRqbQ_%f?H8L^t z=AAfj_xL}k{C%CgT)-MSbZ(FT&qe^DC4BJH*&in6_e3v923SMd`=mPl|JA4gEdDk?fVl5J2L3KbKa}P_4g607zZ=e9!ttL8{m+E{XF`AXD*kg~{$Jz799C74>`ZK= zP$-^k4~PCZy99Mp0cZmK+$#ERV9Wo|f_Rs9=)V5v&r1I`a`(Z!+&sK(-J7!ldEKDS z&HaNFngv3CIJdWoA66fsbGwpr=8#jjojt9U2%@*ZMTBhi)1m(5wc3?|()J@#i$G}v z$#RyNfdg>ld4llF7h&`hhZqs`k;y+ND?|OXdjI)D;C^iMh4`c4J>yXo29I039!8s# zbW9W`0zbo846FRxQ-^QRy<$+lnY$}&h8}XC7qXw4kFGF?lX=4JuNu}v4(O1C)iu(V zMvdGR{liBrG`IY6Fti>jec=S>U$mKD8*4@|QT^`K!dl6w$d4UFXPzk%-eDO~h!^z`q>w&t5LE^z> zWeU5~(8ekci??0v6C!A)L|ca0(p1t$KvFUe2=$aQFm=Feb?+N^pG@XS?5XVTyu{Qt zJ2Uenx8Z*rBQa6Oh*@w`nKs4}k8t?Oau+?l&1sPj^fsJy$O)ObY?pH0m+AAAN(NHD zhl!_2mvG^Kpn0^PJOn=wS-mFe*lbPse86yd=3Zm}c8NT5-^vy4BL+7aA8z``_gqV3 z1ydKk1DE{BRQw_u+=1po#R=!i6O1<18O}V_lY?X*?-rP4V~~FE`9Y#t_(tn9QD7Au z$Ei$$d+t*G`sL53OdSv$z(B-G%bPqsnDc3eow;T?Lez>dF(cgzN7_|1oQEyR;oB>Q zALL-5e-XGod6R{2dO!Z%ntfA+a26x1Q=AFb)A;Vo1NqrZJ@l(lRK}{S^qh5@VMEIH z2Wj&TI!MciZGp#Z*N@IDBMaHt6~>Zjp91*Lf9Mj(gr+MceNd$!MwF91&@rX5xdC_P zy!kQ4`~1z0j;FyztELg&+(*E15Kr?p6qftwx(JHSA&zD5zLkG=)uyq(n)l$nW5QLE z?M64(P!oLiNu{w9Q_)JsqSmQSswcP-ZFASND@Q{&Hr#8g{^Ak)BQ{JSQO(MyzkYO& zx!vgTeE(#BVbaUVn@92YcxC3(+@dyF)2pz;n!yJSHh(~<&W<2b&&WIbDntZ zXJIaDUg^pRSFj`9da$3-R@)Qa~lZJU4U0>GhZmi^TNEbq0m&v4-K zw);!&bxJBCT8rtOP9Sc`(yhsTW;~vI>)bWuJ#f>XT5Ul8=u1qb{yCdt;dcuq7!$F% zL)7T(ugEh|=~u4CpGomlyv+Y2>-x*+fKr&Ll`uDW3bg0W2&Eruw%XpA_A~ZN4686G zQF`CG;rnIZ%-xjDY_8E)KNBuL6&U`E&JIK}A8}~p7&p?mUL97Z^`?cc@_cojza`>M z^?;wR0rC1x`Mol%fAZ-eD1-jdu+5310bMHtNc_s3mEPWoN9%d)=lGY|M{6c9Z%OQW_1pHR=S=74W2@=lzo zFvzEWYVhGGHI|q4SlRfXkPyK=uJh**{EP;h}TsZ~K&W z&$BY9f+d?*qtufpkCRH+5OVRkQy0;!NR+O5=LjKl?Ei50mSI(PTiB=~2-1zxA>Gp5 zAlWbR&I0)GAKdOg7Yp`7DW1@BD`$@O|E?MJJB~HEVO!y4gPgW_pjX-?s36JfFM;EK z-I?FW*r}`{I7Mc26Y9+O%g=a#GJ!D*@yN%jVxlQ8lKCI{MA12Trrs5S;;{O6FB9%X zwKw4Ow7d&q3y0B0mV|=-sVqdwHxTud(J0T;FMVsF=0KEj5deJUXU7XHxiZlO?8N`e zH$F&pn3CY%9#O?x7Fbaad@1Re?C&R{t5eid7yLcZYU+tfN8gk7f3cqkWV%TcXH4@E zQ;HGq7pHZ2NBtVcWeH~Til)@-q#f{Zs;`wh*U6q3z&+MIs*OJOIi}1eeq+$Pkm0FGkG~ zT%k5Sg1mdsFZD&<`AIqax2rxm&<7QA!qdoJ@pn4o;$K_{*0*yRO><6w_J05SN#K}v zvtLw+!1Yvr%B8KFZ&nw+{ojj`$0G)-vB#;^Byi4scvqdIL7^3F5Zk$y=$G=p@%b6t zHhy(VM33mGL2AEC)^^8aDc;QC-2&_X&9OJ&Uz(EMf9!|Y*nJqDE6Mud2KbP`{l{JI z2kxic$N%%NqNNBY__seajTt9oXuka;qyeM#`YmwsASNQtKfn77&KfxJEeV`Tne~7B z&yVo$$fo;m$gMxxB)S?f_KzZ)nB(;CI#WT2XPvW*~ zhQgqK-d2d;xm_^DH&zOSC#;f?e0XYV z6U?lKPdbpy>X~|Qc<2daxhA_3;i%3RTie^#9i%hx|NY3KKop-Dg^FQY0RdWg49m%nfyYhdPepFF@-RDUwe*lJ=%=|{{=9Dl zddRlqNJNB_Cqs^E&;P^R{Kt!m4S-qN_b3OT(>K^EWlRXQ8$(%#i@t|#nA2*{}S?P)nfNZ6rtAeyRf0#dKL8u}pE zimE%w-x3OV*daNUNWK0KwfpaYlg$lGC8W6qPzM8d5UcQ9^rnS}a2A3=YV=nhKDL)7 zffp94HADwq*MEl0sx8Z7R8)EUXzel~{5(@(v%lDzp%~_}9?c(q_p71%e`22KIsij6 zG@8l+>{nz^h@$J}pgYjr!f(r>I2#N_pCvz>lp6rqXoBoetgLS*OXaKVeUY7JxX(cg zWL!G;U+L9fdzPtv0RMMoKnYxw@^vd;o9!I1CbB3O1AQ@`eH$K{gEfqF2fHMAeM-j+t?^>kmH_0##ektZJ9s{2MD}u1 z``g7*;3Fnod7~d8%6`L~gKM17MarB>1j#%wYnq2Nx=(BMdt9qtJ|<_GU;iQs$Sr}_ zanV}AB?uNop^HD8)N|y6Osh>vAk^8#dqGlb@zp2$OR=<$m%$f##^4Yuo6dqMsP8Ic zRV#LgUg}gNr|ZTqw)brf1upxor-SZ@zYYD*H7|iP>IrIj+;_&~H@iO5v^T9rF@_|2 znO&|Zu7uEN z!>wBDp2JFv>0SrToAWO)1Th2rhEqP@%;k_#oLx#Z2<+D<>N&QZGt8&D{^pI)Y&}KS zVe>&}0+XP<$~Jp10sjN~B`_{}Kk@o6m;0M?Q6(n`WLtug3*%+XE{H(esbn3xKma{w zqfV=II+Y?A^RP<;y=QK;Yq#MmBu{2Z9e1(KaG#`yX4ot?0W&5sz^NxgfTZWpT%b64 zb_X;EWQSk2TXzgNO=Cv0n+YJgzB?IU#h6Wp`}`Z(PHOkhpG0MvYOeR0pQK+&#?=*N z1wi*K+#OUfca}e5R9>Cl^%6NY)eZ6w&`*;nLwC!BWo%wG4+-Bk;VALu9Oa4m=_)o- zw#(i!b192;mQ}%ghOY;~k6U?hfWp8vJ;ZRzz|#O}ar<<>*tKz=HhO3TTAA+${WieT zvbTCMZ5B6d(4OG_uXKex3ow)u-NUbh!TQKsrV5?R20!RiK6Fw;tZFAgswq}%4E*hr zQ}}dabr+V;Ss;R=1YYY^lan~>C;8F8eBkt0+s@Qhr3{Ix$kkx51LfO<$Ga7P*TvvD)A;XHzHPxaX-{+Vc z`W;JMwjKHpk@)V_Yx8NTS$GN-Ir2pzOE=HOnT7C{TW$|px^o#Wr3eggA3Xf_%*g5{)Nkjb zhOX+Z$L3!W*|Gh2A4ql1qY{V$h-FA(RrN#{4Dmz-@6W~-XwvLV>;AnMk^{Wzswkjl z&_~)Ds3pCDXm_I6))W8>=HzIyjjsVRHEA{j#O_YW;F+~Q!qD0919MY>|8AEmL3E7L zY!wS<9PesmDNjYTaSTV|L_SN&oL!xwR4E10g!CC;f~P@{9i1`*!|L90F10n5>bos1$a zTyTUt?*@B?MDbyU$0R>$<|iXh*nY5(LH_zljLEiqH%JUBHAKAro2@Kd|jzm8GdpdoO|4 znz^4teyMq$Z1LLjqvI)PsDS^K3j_gBv+MR$<&6#mF zyFo9C>u(>-m}WWDK!Q+{D?8TUD5RIkSr|$B`~6N-vDT}J!a2wGwpmRb*ef{bNYH`D zS2IJN=Bk5@5%p})?K;oxX$eX5G}dTLlrBQiZx^d%4U*5HYK-d1w(!t z9C#S{RRUKe9@8cbHceu(cZIlNJkhPgmc~FowH)8P-o07@1$^Dsy*&#*SpPx&CzQR$ zr2vX1^OpF--#klpiFhJBYaC5LPU(b$kRi`_JqM&_(&@^3cW1M6bhmF3Ol`|oV2P0b^{m{wWvk)3!BTH>GN#>eKq_l( zcN*Fs(KeaJnaNX$vQUTqSv~ zfY=RPx{DRzlN09SqY6P#d)B-27Ayz&^1X zD@u2XnzJs-uzTl5aWm)%!zpyvzuU7EVQig>C8&LPxNIl$sBowwRG03{hDOnM6g%XA zzb?_M7lxw1&=~e^@}Xl=tt_Z{&zWOL@((m^+VW}~S4*^(wmdYr(fTX=cPSSWpa`~0 zyH)Klfs-Fp@-J~y_w$Bo+-2qY2e~f3LbLbVA0Ix#t!ja<>g<(2*4Zi)T8&!DW=XlX z&v~uI-)LQEAynM+ef@GuY=)dj;*Gwo0nzR+?n*5)sCq%~W$D|A z+iRQemaPeXlh9v4624FBvz3EsHOb8hn-N1K(eQAMxk9DM@Wf5gni>OgbWfFd5KoY* ztrTRVF5et4a?HB}TCpSK?mA)ln3#3G#Ew2v{v_xq!(K=+8i6O}1dTkJ7tc^$EBn zoB>PzmuN=C0~NH6wlk{MWw^|ayIbPt5jvkZX(H#WEcHbC;u7J!Cf`) z%Bhog`hiNl`A!SUnxvUUC6BDHmFD*FIGPO8KvI%wp#jeV3aQOZUx!snext_~j6kHU zHwf-g1=9Yoo$Jp81A0`OyN7G4G9>E{Ya$O+c3Q$!?GHXzw|aIWF?Xo5*S(pKXGJ2- z`QVtF#cbye;p0!rp&j|*1Xm+*0i0Ikegjp)WIlA$k1>vZd4EXA&Pk*V*yJJ5NtnCv z__dY@osv%A;ibVzaamB8wd^dQo$82bN0QrgrU>(8p0^X z`_1VZE3Hap`bISAUL z0Rce6Weh0NT)^#qcSg@7P$oF3794m$n?@V(evM0gh2sJ598UzTB^&yb*!P#Xr+~f} z3--5wB(`|RHZ?kp5q8|ZXdmR3e-TE;egFoR1b11*$$qz@5xoz_qBKFGsw4+((hznp z67lC=m7N>wv9C`8gH-Hldhm1fcN_{`KlhjC2j#)p+BA<;;_gcq1o=~qqOi8AOn(MB za=qhXXy>pTWcnb#c_jzc!PkmxRave~UCw~Z+|2Mayh^KC^w|v?YH;|Fvm7cshc7zv zF0~!VT_rCXPR#R!jn4ggwKocztGH*a@{T<91Jk~K(D~J>n9D`Bzv zbvaOsp3ns)^yY(olZ^trk>E=3)LU&ov3v-)-!+P03FD6)U(k4A*p~>7ZeY6VB7m_K z3zUWg$`Q`%F~fZ&1u9$24>baAlRITY5eGIFvIY&~B%GOUIYlS}2G2_8Z|>54fFu5U zUMlkR%Mi5zwGAQyWISXC=m2N5o-$?m@QY&zwbK@X99Ki7G0#iwTb>v*Li^>s>yX%7 zVhr3el<#8#l)i+ebpIv6l4r#jGEne$)Gg+-fCz78WIKP6c+S@>Dx!nhRj(ljA7`8P z1~Lc`&2wX4O|*qzKVHQ>z6PC3^AuD<+pm8+inD2E+26OxBHMADoNDe`3Z@=fv`;RP zBOJNMYBO*-=+y~-M0P1VFL~I=@|d;$C_GlmlhJop-jN!zR4Rht?vAMHkDmJzq-~uU zYnkzg`xc0lunF<`gaE5CPwd-8(^L0=o9m%K(uarbzE5>S!gKUZ6F&o6m=FJ&=5u7+ z!wB_5@L%=y7Ta_Gk?~{&1Y0KKJZt%psU6{BqmlD+)&iT(?8|d0wF*nzKcl`k*o2M< z`OiXog=_73V+BPU0FUA&%{5aJ7^qlFlX zR)C+9(<VJjIfOoh-eM_q}xjQv+`I2k|kNQCdo#< zpSJsYeeDe;C}Id+2?=oYBbS`93e+W7!h~Z=%xg^a&+;6CWeGkKV#tU|-=Z?onKRqk z`9r2P$h#6kKJ3WhFJ;2fWdRN@QHIW3HsoJ#o>s9GWr^@A&w$3(9`eC^qNg2d2O4!I zY%gxSwDETM5RjxTPdHVnDZ4P&egUbfz#bocIB%5j%&5-~ih#h^diywBY%)Ttl)OpM zprC;Yuf^2+H{+WU!IxK-p!197A^*yF692-9T=nyGw>z@HyW@|b^lh0)FEyZ<)@~K+ zFD-4|m@q&}-9_{eHmE#8hQ2CdBbmJZq->4ndJeKPuaKt4fI}*$%w`R`fh+9*v)rBA zbuuH^pYaRB5}*gxBnD?Y1g8GqCX+RU+{AL{;>z$oLnEzpseO%}vm1OZhJZn?B`8^o z(KL#TMm|=LU|Jzsf0LL>y)%+z2nwSJ zU-lI{<=i#ZP`F6cZRoRE7)Ef>TECX{NqdH=YYPi8$nIF=n;ESSOSO_7YuOy{b9Tvy z8Tuc5#m$d3eBFClWQ_ow2vOzHQWzbx$7?Gm^UaQdLQm7gm(U6JCHPo+Q+p9S4r$et4gPaw$pv+6rYA3F zsg}F(&8;jS8Q&Yyb~DO2;rBsj#GkgguvDoiSY5M8joVS&WJZzPISi~x>)fhq2!0Ii zg1%+IG#3$_xo``pJ;b;Xc3%BNJFE9;2znhnNX0F@0YEG&$7o2{JUaoTmXGW15Lg>2Sy%o@iMdb3A1$61gVU)gbW$q;k`yBI9e z;t|tWE{N?7@Qr9@Ph`z6**bFGl-}gje?C)22QBm>EU~(-XD)EKJn} zn_k9@&^DJU+iSn1ISYZsDZVbX`UAVn%(VMAOzs5g8Hsu4T9xN{A(~*OCc81824|LY zkVviOsHRqh%f4e1+)+}xJjDNrbd0wEkW%YPp_ABWaCooiRPs@n+&71taPa3x2_#Vz z=)DS}FNCS>P9oagl#HtN6 z1^{S8EEW(L>`J<&Wt!VPxI&9gZf8gHXHRoQT92js&pPLL2KuZJ>=-2CZSnf~XFL6I zA=UUP)iuC5_Ty41FZm{o=X{LJsuj|>nLp3et%LQr&%L5IsJ0=((a^0Z;RLXJ>J%MW z-I#C`nq)@(qv>g`nF#%l6b;&A_X)LMy(m$OvTQJY$k=AOtJ3Q#CRiqvYCtd(mv`jK z7eW(rY7s1+F)+xx_Bp|`>GNUH#P!5OUWN5|PYrO=v2TBY6_3aLeku3F>2;}xQ3XgC zB+~<$<~rpl_6OUlS});!P-BYQC^*$A$q!y<*7OO8dO2`*FTVLzda1SZ}lKyrXN0Q(9e?|*0UoFQ$pj+qD`>FxcQ^QQ3O z$LdH&g1{`IoE1iSTuL%PjaCHP%8l-Nwm`_FZ!okP%t@pt@2x`4bSO)JKhilh;Jrm~ zBMsl+#5fYtKCi4<$jr^fbQyneG(!%#Be7D!47vlXqJLi(B<9{7l>K zXE~F^J4!hbhP#xEvmqUmw&Qgn-h_v}2eZXNikx25c>s{XxTn<$IhPQNl0e_?0H8H8I+X@TK`lF93WY++2G%@%+>nyM6jAC$qHmouw!k32~PW8gx^1SE9B4 z@T4YL?Go8?5%{-{8F`*;mc8+CY(17Xsgn(^P`X3Kf2Y45?WJ3KJt98<;+eE^D(`2i z=6!NcLcl#sN*LZn0n7~2R<2@u{BCqEOFHhfTD;72CKS_|X)R=j?PN6+4XLbXgZkNb zBZ4+WBl*_8#RX9ab>lsfz(y^_3QHS%cB}<$ASPacJayG{-NZ^o!>Q3U${gh6kZpd_ z1J<-fboD%dQSlreiD5y8d0-nolh8}2F=~oeX~(ogEIf}OY6tAui9irq83Pp)A9XhP z4dYsd0O*z${!O{{FII$MTn*JSI0%5$l8QVW$Cb_aEezOXG35CJrvo=;C!$98PWhkQB4K4J)O4T?P8b+Ne3kMm0dyN>jJY()`_92iu! zU4RA<592nAQn!^J{WR6Va^Q)j{%;(X`1p$ate&%$)$ucghTjKrS;CU)&&Dcq4N@5m zio%&{k)J$~{mByx8GaxEo~SO`$rNLq+j372!0Z6MM>*5e!s5WCJOuqQezzr6#a!e! z8|EZ9)Sz(Yk7BW{TLBqokPBuKva_Q=fZ*=g{E@O3bmXq^-*NyUJ6JI#Y-R-t&$lTa z&IQ%^EJTW}X>Q0RF!6JhNXgV|$`ozXjrwg*YgZ3~#*267KfmD+k7g!H+W@!_v^B;3 zw#&sN2X1G+m~S6|9JXEwU=I@dnppmn<0binv3cgRzulylttYsXjW7J%d1bflmmB>a z1>IIJX%VkhOv+~4f)hUrsyBOm008RtR<`Y0xYBD;+lL>~Bp<%xC^rEBY}<~(90ndp zHBe?%>{BU^PA784!*yvkk|uL*@OZ^-t+4G#%a_^k+Oszry1)vIugxz~c5dIC?^>Dh zvEB~+5Rk+ua0tMQ0SNsVi(ib0(d`Y&2i++Nd-b`-4qz)1P=yp|VnEJH^4(=>{Ixkm2rZ&~B^6I=QkR;@sA@60z6t97`Jo4F& zgDdz6)Diz?XV`3+u^V{wk*vwlZ;farr*iSPlJ&J8SHGCZgTBvpLD=v6z>7in1DMU? zW6KR!%FlRu^Y_?cx$mRsr06xG5}KR5dM4bd0(r!zG{W^pC37j!+$G>}Pj{olM zhsm?D$J0fTr7@q2L)2SA%l*^zx*^{-!=bv%neJKJwkZJPJJ(%;;4|V})=Jup38eHQ zthB{Oqa#;JwUWt1=w>*1$H2cO52krVXU8S0-A+v&{E1TZMW%Um4Z65fjy4BgV^0HB{5b;`^Wqt^L7okvDy4g9@S*IkPj`NS{58`f&S26(VKQ@v4R7bm) z^ZA&EYus(Q{}zu+w1E1+SEEn+>{bX6aQwU_pNezj$9>re&WX6!=l@>3l8Y(E)%pFX_*(d_lJRjx|vw*0Nh`Bmp0i{hz~;j ztvJxd?`%3TuA%V$R5at3z~y(-VpYK5m3UWMyzkXu;iUD|7qF(Li2B3W1A3uzPF;?Y zkMTFs*f51&6QS2bu8hxLy*I8wqO)Vh_s#m>rkL6OM4HU0Y{ zo@FDXaZX7@-NU2U*86=&R>Mo^R8nB#B4z|u2i6s{rE79(ia+z|D3B;}uLLlV?BGuYCu$rr3w|NS}z#_a{CCJ;muya{!}5!#0V+ zS{JcXxy}4g&8&U7Qonn-M_FPJ{985@c-#gHilmr4__xCp9o0AtQ96txDKMa<9+1_V zd54SK`Dx8W7ta*0UHwbcy~gyapFU1s+0<}Q0ehr7?xg8mO&k|WHv?u`?{>;EAI&dI zDZ#D?GaV4Ik(Jk^zU%M73IP9=6EkbtS-$%c=a>IONS_f8siu`&$YR1IOiCiHmnTq$ z#9d>=cAbYNmX<73KEkqx9T-Iy1I~CXHf6!%@0Aj*UKlBy!k)tKyXPIVE*f6vAmDQ_)K1m?<&Qzt{bw!Cl{aOr)j@a6G|c>+tMuBA?&7_y}w_A^#7g?Vgr znY=#A=ZBY*d^w65LH@lJWbf3n-=N1gh3}lR_b?nGtie)?wQkB4iz@PAdR1@fX7XgN z)4Tr&;QpFPOzM1;2bUCKM(#_;>BqA7A$<6Gt+tL{-9%Aj_ggX8%n6`QZm;~S#fP5w=zA$p? zL?~Dme-Rdrnq>W*UP~@o69=mc6Va+V6i?c&OxQJDgz)(4C%7H63DX60(DaH5pNaFs z6Gffr8m5u;<&-CJl=8%Xjc94hOa4kGgN=A-OZ5q-)IolO#K8IR`oz7;ccJVqFCvJE=?b9r+LG`#?m}*xbKu3+E(?%nf z3%#^0oP93G@;1ZhI7<7gIF&iqWbCi)P_Hlq#$wPq;rvj73AHBQ{;jrU7>Ty)2Q!j8i?|{EUkcQujshFf-CW#X! z-;H?XseWtE4xI!i8=p0)&6h}fZtl|ChXE5 z?I%N-%L!g@NGl8H!drJ94I4^GVv#?AU+YX?LT=go0G8>tHYW@LH`bCPpN>T9yK`pq z>7W2Xq|wUgC1WfXWPKsvx2>T%vG<=ZBrKeic@(<-EaKY(h8MM&WihHAPc&5zpu$cs)R?bw<&Ol#LoDN3u>Tu52LF*ZEdpgKE{lY5&_1=D)$nq}AJl>gOxQMfTVQ4?E``k<4%C>#||c4uJL?R=535|Jb`!whF@b$;&i z*a`e3vxhAT3opY+y|!?)(c!$Jtai-)qc~}1w70P3=ll&=%rm7ACSG2EAI1mZxA3?Q@Qz zZVb0NzI<<)VoV)eKn#}b3>~KUlYsG_1AZyMK?kNo#8&^w+vta7E76Ko5PP{*q9XS< zTHL;CX9w@0%Nfh8ry!%{3`j-5>*6fOL)Zr}GZY`*dkL?J3xmU>3bjsZUR| z6_jgd%zG}bpSCF0zop%_qlw^p$4<js8is?r9!zm$Z-&T7?MOW&$~$zdXy&Qv9%t5DPNwO;9tbLj_>V5^pk6;D({-tb zrv;T9$Vh`lC;bu4a)EvUJBQ5h^H;or91F}5`17FN% z__rz%&l20~uqp9D6jl^Y>}Qy&(Rirte~ta$28`_*xEqZ~3~CNACEq#@10Qc!f#jNb zbJTQ@DYP7r+|$20@+m5 z_A|W6%GUnJ-OzziSWp)jG?c}c^5i)&i?n?|9b4Yr|71o zzUT!^%s)O6O#d|f>#wYi{>v)RuM(6 z#E!RQM4&o>_vbR_F3a~%1-bK>B2UtkJ%E7Ez|a)0ZUGFt+3T`p$>0vqfMgL=0jv{C zbhY`6Caz-sT&yyAS{;aM)$ z^)%y<378Xz7SK}R&z5Le6o|2+Ud=f|U_M6&;750T z#xD5>Dq&t02Y>3PJS|x|KUY5$;aGrQ#UMGQIy^33!nBD2$q&gu7U!Oh#YkNe&_ueW z+-q)bQELwXb9z#3E5Bue>ivE40bSL62&o}_BLHuF80lL5=!?U9)bRo(Jbn3x$R)r+ z`55he`$Rbm_WUKtt2$G99(>(R_ArbEiQGfunO7lqI2PWHS`}EjR_aqjsX6Rn4RE#| zX;_WAF7&aNpD_=3lOam9(~_q?jQHg3by7f3kseK>z$UvHj@bAF(zC0PvQBb8^j&Jv z@jsuCx2;&Z+u-TJ)7Dpwl0h=in8XmiS~dkV9?Yay9f&Oa6@XzJ0Eg7$^{EX|ct>Zx zHF)6!NW{OQ2`GhkOq`2jC=mxYw3=;nei{H(pbtOgr0c7F>|qt=I@OivjVs|q_!xm# z-D{Q&_Imx5cA^&Jc@5*9mYmn08=i&`0mz#{Y|w4!m*Z0kHnuo?2~@I^_=sQ8-xm%% z8bjLAxTmwd{!o$W61$J9BDk75Ej^W3s{n*^-?~e%i`dqts1ZfLC@icEmnhxgX*a6& z{YZg77_*^TG%8Fu;4F^}FYs=SEV^a!yfTsWuD3fG9Y=E7reD{COUlFN&R*T$)2En7 z?P;i@mC3Wvu2%bcFwe-38r$zH^rhI89DzD<;1kH1r2$Z{e2lgfxt%$#OZP;vWy$zY zxNO8W#=>WUcP>A={ADRz=lTx5YB2cCLJleR0Z`Oy??ID9wcsW_>sH4(o^GQqB^q5( zB3BuPaPcJDYbC1bEnRz(3#^W1J-G2ne%Y-8w1pui(PIbIq90$mX0y4i#Co=_y;tob z3%9GSeLLxdx~uQKo+NYPnvzz7Zk$BV;HL9({(8`pMfzLghnh7Gg?U$lImz_2qr{1! z>Sut6^FeJx?}QbHO*v|SM3^R>#?eex4o2G80<5>uOAi-~>+zeui>H}XZQ58D^PlO4 zbk1Jvg0vdK#^}sJIM*IEW9-vBko(yw*yM5Q1m+u>p+=mD>ht;{p$gZ46q3Rh%Lbdb z;BdfQ!+c`mlT{|Kz7k>%-*HH*{Owr_vygLR#NH%z;)J2;nD~KNm#v$4 zao)eoruTPZ1rC~4)0XZmybl1GnoCVLx`10->_;OYa6~<6_tvjzg|=mTfR9Z#;~!f+ z-e0l?s&@tg&N9bGAfRpJ@9sP-hL|LBjR`xiaAdv=yr|2Dfqvc)IngpGoyTv5nRP8$Wk1Xmlm`ZWdA9Wl1Ul!RcH1((rMyrm)-ddOexoNwv~BO#MC8>8l=-xajqWE!~*- ziSZSg-m2%Uby3n#kvKu@N!(@?WD?W^uo_F33pS~$e>iwM%XX~T1P`3!7zcv*DLt=b zX41+8iKt1+j=%j`@-t$EU9G;0{DZ|G`x^Z+8&RnE3K&r}Uyn}r}WZdf51vtQ&DUb-p zj!89MB2q--WwZm(nCAv^8?btbI7WqvnQL!8e_Z^Tbl4KcWBF}C0}44Nu`h+qNiKey z^iwxiyZOgVqYdZKg%PPegnp|9+ zVF9TYjFX7}yT*g*gak4xnhCdv%Cs`tK5m&SFGnw# zA%N}l?sEMe(U0$nf85tafSObZRt}}@!o957Pexqh&kP*o+r)js^onxvH>VR|Iu$1} z1z$Z;qMHp_y0GSmKYIaC+Fg#{_WEn?*hmb<4Ry)AgLRh;z05CEB)(zp*l}Z>V&9_8 z0^}r_iAjX~Y@Inbc98Y_;Z`u=i zJ6gV_eAJiwGJdA=n;!K9qrNrO0%yY#11ob+r9FNA-QnYd7P z>DFIQ{eSL1g`I72pH`qiW#lW`{w}=EMq=dXJ2w}V;W1l6-d5caeUjF6?vj{F+Savu zyiHv_N`%3?{CkY(ZbbsN!%&7AIQDf(jP;$oy;b6NS622y?NjA?`*5jR}H@YyVTD6-_ZGOv!eA5#K?2Ttf7^7hE_>_90 zGHTI$e<9PIz*f|zy8emcab!6z?j>6O6D<89)cXQZ&#Qr6&0oSUnd3SMWWrW+qgfcb zCkEtLE&Y~b&oRLgzuurhjtMont@z+O|v$^hW$455O>+{ess) zh=SSEZexlA((h>Qtk;rXvZC3BcgL>2Y%@~~J4`W)r4K7W5&*NNIl{cJBZA<~7FAF1 z@bn1%CQK$!ettX_?8p0_?d9oh`J)jD7pdAQjAxUc!>e(5p(3J|UYe}~4*OVy*pBXn z*AU%{9~>GF&S!trF4Py@aT;AV=8%+Q-B8;LO`<34S|cI{*g0b<7@ktVfxG@;A|@-u zPt1OCR-BSISbJ1F{BLx7(^}Oo0SKn!cRYA#az8-^D`O#>`_tI1^WUA1~IvgC>PgDkZ zWztiRaN4b>pG1D1K*xPTny7eT%LuH+hTMlhj^uSS zGZfHJKOBu{j1|>n#Qfs*jWnOwwam2wLdpuMX0pK2hFqTwFYP&x-`Z_&i)@4{FMsUT zuyHZ3I9&$KFTI%|6Xq23cIrS51sG`M&z#Gi{$?+h4Draes19o2tq=~DW8;FyuKo!? zN;IC@(+*8$R3%J+9~FIBB%9(@(O&UgMA{_9lh2G@XMnm8gCz8r6P!nTU> zVo~Y9H|f9DGA>oi&SG-@8x!c1p>I-`X%cIm=GDa!LDz@#)U(U{LK#Bo%Q=Y-O{}>@ zu)o>Kc#$xqoG|k%jjWCSmg>oBw$8;O>?@rr z%2nO*JcpVDjHPu58fbWw#v;Fy;IFnqlA$2hQg;5#9FvlA8@okK|m3&`%W=sUvJD;pmvnv zP;kOyg~o5OMc$_UXpGK?#cFi6#{%UwrXI>esKW0ES(sv1J>8?9T~Aa6KwMK|$xeLo zO*X&Jqidmlee^?l*<5%>ayFm7iPf1R5_eLm7eKb5tN7=3=kzHVE|_C#%~PE4OdAwP zOm{5x_DfmP5gdE2;qQr6_7t5wTXO|l1`H0=_F4{MK%JrgeO}4$vYP@|)8K%!{~Gq^ zg3&S~?2DiR4o)mL{L(jv*jVWe_n5(5hpF#1Kth6)H|K#hY+7cpJ;T9$mN`1ix1FEZ z|EhQl<;`Tb5wDLtCK28E)$oO@K0!AY7WHV9;3c~x*6wrS5EOh>qw9X{emm5cYP`d4E*=RLO6(|UdGl@@$nIo0nM`00vPVhwL-NZOvp$Xh)%oa~ z{+{Xz2YmBwSNgs;{Xg#_c;OE;<{c@QSUmW#m6;N;PO2ZIivI}oIz`dw>#VPOp3n8M z)pei=gaUeE@_CytZjo$;{5dTJ}VCu+$vtD3FmG5!^6bZ#TVE(zZ{kg z4IaaIk{s%eV!1V!?D-EBTwo;|$0h+B{P|O7q;6iP_X?Zzdt8dhOACL_?!5{i{>+^7$Oy840v#zZ>G<-s(Cg+|4NLDoy zv2NvfC_afLOpoo4myiAZ0ad-PT}?CmFJ^jx+GJz(1=&?c>$PwxhFk=SN15@MM6gPz zgmF|a6}}m#pqk8(=T+wpZ8PDfjxnlB_4_cg@E8pPeqF(1(mz+ceQe5voz{*&vVCte zEDf`E@@FP?x8KMU)hsZ{(@+bq{gP1p-uy~r?2MQ*G^xHffsVL4q;K0L6r+ZY_)vDX z?oA)9`9KQ+PAE=car`BU5xTUsL~<=BnnabQ`eOS!vm^cE=tc!|agT z0dgwu5*P=)*^bo*m*0$r$5Eql^g?~YNr<7Zkfjth%Y;T%b;4D`wwcR#O9K)ain``!^JUB%i)BWK=acr`P-<8G< z{@Z9%8O0lZET?G(Uq)SGXww>j2`sLX8T~Z;zUhFkNsvTPIF0v1@K8fsS+RsAaBaMU z3uw&MZ?t!No1Yxhyg?u#-8;uu95%HEK&v`=i5 z8b~^42}L-6dXKBea}m=v*#*O0xAj#NQ6=8Sx-++rO2fe!>|j10Tg(AUcYYxQ+x8vH ziZrPH*YLW5?ZKQKcfl+^G^a#dvM!F;e=QR@q$^%VK3pklgbnVMvQRGmPv?O@}6cJ?yIhbFUNWdiHih&jcDb$-(b$0-`jLyqtsU=1=c&f zA;T3Lh9wpYeoCt^77N6EC0kUvn@&ga5yoyr;jf2Girpg{P!scC6oI%CVb(=z=YCh3 z3+T{FpjKJ2I`JH=ttj!!>J^!~U6c$UPp>$<1prJIeMy?WVx_ZVx@VR z8D=;o`LB3^_t|A!x7fA7ojCkx%2{U?tBCD=@gm3ewkldx`<$4lNg2s@3N^l8kQL`i zpPT}aF81|@Q)g1`kV|Uz)w!aNL`_UPMXFQS)tJ6Iy$k_9kWn(vu7QDFW&8KQ z))}QXToWgi{DtNnv4`791+2G8U@phym?KT-4gNkn0&+QaMs)noq8&y0shrvsyr$0u zqHNzSv1swDO|lahFg$xn6p-&E>xLe)j#y_}-uIc*ofOaS5(@ zUGtoE#&H}ITOjuYBo=f;0@OKc(R8@D;ur6zj>XZ2khN%sJf$5zmBy_hekA@eu(^Gj zjZ1$~zvE-T1ovwchLiQ6HEx?18(sox@1#_;Cp21Hg99Dt${vJ&Thy&8{owy;O2mko zklBYEs~)$aI;KOf`e`r@lvJAXHBG<535h9Jh)&G*tjF<82*P(xiVRoBVIWG!e2XTr zugEv=YunQ;#Pv+dfjMRG3yx<=87boq^D^$~2k5hU!Ax$Q%eX4jq;QkF1GwYsT*U3R zh3oEh#OtW{A;a<-grQs;72PkB?mH%8ODxUSVj&f^51E1V{DwObA4CDC@d=zTbdHi7 zB0LI+rkp*OyvtT+p{A3?`zq@U;n_A6Ct(dBE#T!S%A1Fty&)sSfMJ1;* zZci!bcF+{6?*9+ z_r0}kf`gxjFoXHcn|4U=ck5Y~)o6*@6yR%yhN5q(Nm`S?B~14erA|QCK}F~Htq{X| z{3=5}Y<@5bDFan!vAb$fQQB8=h(j;er)$ayfGNZR%~I)ShTHibYTW7b37{e&?DEC= z!UOMrPZ=0{XuT+7;QI4U&0*r`)+8clRba};Q{*nn7-EyHLk9ciw|id=mh4~8+3#M4 z+o1sC3`h8Ep$i(@0vNPnMjZDDe+Iwjg%4rc6s9It9`?w*1}BMHy;-|yI-!ew^fUV`<)O(d-msewjX7fZ+7rE6zNz! zaW#MDEh6iDXC~3V3|6u3pc$LBKy$1h>rR7hTFV<9kPv8x?|B=)st1+*;}4L4Jy`&L z{bK!XQHd5Q2h<FF&&w-gq_UpEql7#fLpna*_7tvtxjxkkT8A-ciS>E5VRU{_NRsDB~Vh&LW)0K zD)yXg)SS#jHO*g@Tc}cooKoCxW5F`%zhGah8NLkX(Sj)qKUX5{Tqu2WuiN%0!5}u(FVMOG7;(7 zE1w?wvAIJZUJX51IoD!mr-xwHNMstFJj3*nyA|T>tSQLhju(@V|0WTWltUg$)& zO`!283tcs42qsaB&c(|_!V7r8_>fz|L?`J-ShZuyQ)9sj#=r-w!@HqOB5?U=@HZ_E zH{L4Ug?vr57ZZ}}jVi$X>-W}=^mAf8IaX{El6k;Ej=Y;jFd-fq;nq=swaK00^!-I_ zbUqx*m1nFHq>aym0oS#>p1#48<6ujCm;=kJ$)i#Ikni~XNpG;-JZ^PS z%}T<_cPBY3CB~lxphLpNCaDFAcwOGQOZgE=YdQQV?Tk5ZxKtif#isgj0V;u8 zQ3Ey}+wN0p0#eTOP>kbpa+B!(TFb49@omj68trdPDYfOH9d|bY{#jm$t;%a9-lU&t zF}7|Fb|+!KYW#o>?!4^|Q*%$0snT+mPAu3_l$bT8fpJB5Chqc@{CWJ0N+$cwA2N62 zhhy7MKH+#GL2e@8i$Kg}RS*u-`ie;;6%{ZPz80?wpSbj=GqhH#p~}HqBEY(s zJs>ohCzJjDz3=@`lO!9dD%BneFy)sixgOlHT|LiEpOLKXYZ07${>ux%Kh6FmH0#^M zKm2g@{KIf@_69Skoy0HOmjD@DF^QI8M2P-K*%ij9hJ=$v}OSTQdb%)Z)*ZD^Z z9ZwY^8ohrmsUgsppA;Kiy>sTin-w<%gRfmNtv0rNSj&u5Wc0)HhjTb#+e5lYZe(W~mvY7bO}6!&JujvI(Re?Dm%EMQiFC&)A$AEodn6q58eB zf>*?>6xzg{w=HV!aNL!yk+~xypt}zeZ;qy0Apaj2xx|g4JM{ih%D!+nWCc$UD4hxJ zNEWXGf2DX;;cO3NRzIEaytlOQl1?nTW+|SIswMk51$=uTO>QIky`S1*Z7D-9u76eL zd>x=4F6&Pf&$AXmx~qxZ&C(U&saQ>75!f*GH) zF1qN}X#Bhrtf$a^r{^x6%*%8OaxA_}YgFt4q4B=3|7?suI&um-*G4nM9eO!Bqh8!? zImwFE*S>TJ;XF{-s##6(fQ>@;=Rmp609}k)VAq`vZI#VI$O#ntSaF+L+IN>uW|m^~ z4d@s}fi#Ao`NOH9@W8{m{PR7$$ie%6QEcB232;w?2{t3V@1 zfg^DU0aetuy}Q^w`i*S%Ep0-Kny8Ltcb_x3wY(|e{>r_)leVh3af%orXT%73O4Cv# z{Iy-8Oqous{PG|bn-cQM23OobVM|>R$L9s%TS5n?$tUhjuZD$}8I(v~t-V``1S^v( zbh|15gph7SwubV!AJ`vc?Me9wB10rHgdDWO`YM+s&6 z(*YoV5FtO+fSR&IQXmrF8g5Iyk7-l0WnrbGZ3R7Q8LSnvoDix8#pI}%e9drWsmxZq z(m?kP&J<;Ag3Va3JAMqEE{s9;D(uqut?!5VQ+HF3?=$gst1{Q)ww~2YpYk{f{KPot z8_1Hl6K{3b62k^l8WV~kstU!pDoL63x`OvmSGgOgguLt@Mk)um5=`Op1_m#*VXj&* z2n@FsGIexd-?*^;9)wJHdJoaRj;!a&x6E8FZ`YKkj1*gPeOl*u2=tys{)By2$s3f! z;@C{y2==HPg$qt!I`+VXe5B)uf!&=~NSXX$l z8;H|WniKAij9ROti_iaAq6y;0Igux1fINQ~!>GcV=9~Oe0B|42Pn!$St^TJ>EEKIv$rYos} zFy1fepX|-f1t;x2oQ4h*RZp{ngl3~s|2#c<*e*z%lKs|oE?>_kiLU@c6JHa*Ah zFP06OoXhqF^Sd~u%M_6#AA^|suF4noztFA!)A0^@!>$hx+7D8W>y0wPco;>_zD2}@ z3>~5)k3YJw1|cbNPHgb>&k zIktv!=aCFbb#tI3=}~-~iSkFu35vT_XY6eYXoDs!ZZAu1mcEOLP01rs2-$*RiZ0@Z zy??G^yvAz6dT7JPm}YA*QT!LVhcyt>oDIZJO)P<5WSI)pguqJDA8ZJ583y`U5N5sF za!llfp=#ekKys>UN25=ACUOiU8`%Ih$L$_~xNIAx?|uiolN-p5RX1n?9QXHqX5fmstd!Rc#_cy6yhmI3e(+i*oo{ z5fKhLsa)pCFA+Nl(D-|1-f%S54mg8Xl}7PjowH0G0GzXsZ|==ae?pSdw@rZT-hCpx zNHP$o3GMl8K~t9I4c2e=8-O#u_s0jZBw^%3$yB%Pns$eAJ&kDqAY=j<%g9fD2<1LP zz?d>7xddQLHzTGgWUv8?wu)GKf*mW#MU3@ zxTma<+Kk|(Edbq2R!;yzjE&J=Bt_N|^*t)CUr8_v23)9*_0%XuXHITeDA@sz6KAKM z=CE#lQ~7G^BGMGbGnt!DK&Xm$u@~XR4AeSd_RM2efUdH@nR+U3%5eysZkI-B&Py}h z(~Y+ntPcxUDfa@Uh=L|eQa#XDCR<8fXEYl3qvabn z`$|v{%5Ovn-dsRK-{!BAEiDVBeB!lfi=th5QPbtw0)Spx)J)k*-c~DUJlkjo96=$TLxf->y1x<_rmKg; z7YH`yL`t&ALni?iM}5Ub$jku6c-~cmr&8m08~SqmU&&?UCBuy?8cjqF;wvl%`a%L$ zdT-V=K`YXj=V&0D7vP=sW={bxruYOb7G0rvG$qY9N=H^*T{V}gM91B9`)0|+%gm`D zMUezsw~2Bo*T`)u%Y*CfxQ>4o;gzj>&C%rIupcqr%LC@)cg3rqAmMB{vqsNr_EK_A z?+j|C2QLdN#uzIB%5VYOsam3t{h+k^a7Zo$5W$M80q#4tZWTb8o%X6bg^MNt<-wn@ zKUdNNKs}iJGDeIU>k-(gy@^f{9^v{lkG4?wF|4URHW-cw=$9|Nf63ZWO6R(5>cF&r zQk8MBLnYs#2HXc|Z+P&k&?Quu}23zbDBs$fGs_0uD~vr!v|u^9yr($s+`-I^p&@d&pXi}T$(n&7@XW= zn#QxsGc95-%?!n$2P>fVmFKvaMP@&!eJf&v@x)ndJEBu3^0<-hwGH%;MAe#KeL^O0<}iy6qs zCWE#_&~1({K0F9)1(f6U1t6oxBxviy4a)>lq@T^NMl4j9PgXeCW`pQcG;jNSh69qQ z9;`j{m0SI)@D9V*NZqEL#&g9HCo1RKWM)M2HJXx>h!nj5Bm#5zmZMY!m{zlJ*TfJy zs6C=0?!}%*`E{P5!)~}Lubj(lmD!}T)>Ko^)b=v&!*iYIF$^?+VjtJ?2!|UYl0-QH z0_U3X2vOV@LbivLL&LtC!5g!Eh0PEQzo6&A-La8CuNE2xlzBbv^Clw!wiGEmn(cQ&Z09CPi3;+jqZ@ zt@EpZm7-o%!=^l;vI7b|=h6N(1Cr zD9oSLf8wReo?FY^pnJXCU;L#E#$$4t#56-w|NeGqpECWI9k4|N%9N|8+dW!ni*dIe z(=s7IkafRjr)Sir_8j%K#?2wn(0IvWz3<02le2zEH!pv$&H+^OU~Tx_E;|ZjriQ&Y zH)p3$xo-;Q7k%!=Z2KJlPz6Owf&Tt3Let@w+w==>SlCRQ9grb?6XPB;wDzcgc6Yb5 zZ5}6vS+m~|eu!X_IIl_E*Tz0-Z8~`~C@4NKpBC|C-88~WjSuRR(K1vh9?@)%;82bm ze!or2+!c4_gQm_qV?R9fIRMIGn&It_j0v6Uqp<7c$eJKtr9AyA9TT}>2^_WuHy553 z3+S#XQ!803Y@m{bj-}}EAH+6Yya=E+eSBt{Br#|XKpQ>^YYi5 zc>PJR66Jk$Uz0%|g?#7nl9dc3CC|BY8NDDY2%Ge55OwH?aCK8+Py3%65GFnCEZ5m- z(OshOBRkFQwpW1&VTS^*ta0A=2C7$XTdT~ec3EUli#)|4wfI<1>5{da$Yic8_jGux zpu~BAF;rM-wg%zG3D8*-Te7b5MpWV-dCk7KSWeDj4II>VK%k;RiaoZ>mZ2^1bfSib zA+Pj2`k5~p5kN;rv(Ol5;`SXMKEkFPhx~Rx+|QuqG8;w6*33%FR7EC=d(v_t^PWAkkBB@s3IcT6+a35so!; zHZ#19dtv=HblnQE!&1+`>~XZcMv{)8mUyD_5tFIN+Py6|cm7p%p)mccBY?jMa)z)< zox}>J?opO3K&lZU1GgkFhuks9_6^;C;DyLUSVq6yHHl+!cgFcT#pco_*L~ai>;ig0 zx;&}79>v5KaW_`%cqMonPvA;cW#t%wt>m^9aNn*-%HL z+_*$U_uog{yE-zs;@NwlM?z!-b?j{^Pz_qFpj9O&t~%mwofv*_JWQ9~ad7Pu z2N~9&*TlB$-N^$bQjbxw0*HH_jlFJcXSDhSh^()U3C3tB;itg5CEwb`HoCg%-cQwJ zn>lQ>x-4cBdceOx>WMX}*m}djWhI1~0pYSZeI#>`}{(c8MN z7Rhh`ns`Q>@GxWT*%_b(OuBGkIW#iNI6ty_Aff#5fN<*D`&Qg)Ys}7K#H#=0a*m!v z92`fc9A*v~1(_Jtv|C@U$>_-N+1l!VJ=mO$>|ajM=5VY_J=oheEHtR-H>GKE#E?GH z?BB0hx9{0Yrqqa+)>f6y>HPtTFfK=fxXs6{Wa&^eezKw*z_E#T)NpVmlImy^>>Xjq z2=xr&t4*dr);8}`)HVrY5Pzl_KC&0*BK2%TReFHb%Yj^CGj~_Tkd}B)qe3=)9Gz2# ze5J~?HGPVc-Rgz2-eIFkwMM{5*74rQwNb$y2YKs=!PY@R^43Fky!){k@6`)S0MMA6 zUxe7Yt-F9URc%_1r9{QcD0TK|n$;;K*PrbU@E${D=^MiM+S)wrOuo4t0de1QV~c>t zPqVm^53)#C0id;nJZii3q-xO2?$;R9+6>^0*A683ALm=@Zs^7We}7hb5f>>=f7LU* zTX4suAVr$ZWs-Qy$?8aVcpc)Qf8>P!;-07fqF|Jxe*2!i+|2;&ijmLU!muTg^>6|t z2+KNo%!nK!&RV9NK`Bmhp*rR@=f7br^tizq$XGFA3+*=B)}m*}8eWcw?U)o(tt?G% zlJJKg^baEWz3Lx)G#jQb!Sr;;dZs%KL;^y^h~s|RzIORUF(SX0)@l%KazJ??lR0cZ zszkl#GhLHQeJbF}DanTDWn0akf8~v2X|fp!D4iR9YDf$my4Jz{$tSeU^|XM1q}_?0 zmsk?*$rXnaxOt~WEPDO$Qb3|H&hDWvOXVV`nY@0bks%w5uc~Q zFf=GtQiEPetu@(4dD}fkkq~CZC)ReWM{J*DUD_wa-iX)=o45aBlC7S9$W(jZHoW?$ zam}Dz$dM-tSN+U(PyC&@5RPdeuW8UfO5EgTL1Kj0LP+s&rctM1TW@X8FLNr*gj-oE zD{|+cfo>X34rBAzmzF4iRbcRU=JjH>7YTH0GAHE`_S>f*M6*VFdSD8>8)s>hckV2E zPs2(mzYtGB1r$E@cmRG0YigHZz>B*^(vg<9t8}Shus}6b9X`oG%NB?L=6jdRhjn?D zrTb=Jl9T3mHW2}zzHxYw@!;#ZL$|8HeumJCKjhUhE%y_p=`Rid?_yz40*56sIOr7b z_4kE1LA)=uiS1f0-dBGNA|P=%?3CL|H#k-Nbw(Txt#LL>jLqbZkA4Xn{sVZa}b>RO3Ej63MXZ*A+;%>S0CTJb?{hobYc z#DxY+l~w8sPLtgdCu9v%T1ltMKIot{OWCi)PmW$c`~c%SO`LimBhkV@?(vdr&eMS& z>u!Q^<@io`eJ}L>{-4%-9m<-uW9{6NXKm3Q?ZLuP*e+`VakE*tfrB^ z!QJN1K2UdlazEIB=g%_Rp<^&8t1ocwwdUsuXRenS$;beWE7$CnLV$TFO|+P@iZc*; zUg}CzqL_d1Lu{N%mm`9>rZ= zvbo#;At{YC9(Gm)>SXYiff^7XE_}%|p5Nd}*w81s#4gMF0xQJ)_^+4ojX~a=^9@)JjWqzD}QN$2vu09hD znGeK-$024_G5$!{#)?rFlvhg!HW5%gz$PyysC<+; z2aRC*qGX{*1~Z-VLdv6PH%Bd}CK$+=E9 zQ;HAVa@Cm?og*;LYtvVKV2f`U_KB|iJD6K`Q3nR3JBF$a!i9v#>fKTQQMN}Lu>^BmL>!wNnG@NhEUijqnL1( zF8v++O1|FF5{sNy(9m;G|Dv1IN$F<3moQ$R4av2&DLN{_Bzg~v6WjzqDh7=>V~4hq z$zW*UNNi1Zq8Ve8quW@TF$PQOp3~5wU8d~L9g(63S09v+dlSS8E&%yE?;F9Mm;-Y*{{i;Nn-ixKjt9ITJfAN)MDMib*YiX! z$dj--0_@Zd7c4x+_ZhWx`)#?<4M(qS_YTS3`89_m`&dO*ZIgZ~*IUC*PAUuI&-GKH z__F8YVvd=x?1s<5wtpxb7JQPn>vuBXfQs%=D5c^>PdzhKy~>!OBC4urI-P&+P@}O! zcEpQSga`^3`w$lZDRwHZ8h(!KV7XH~{cDy5ok{*+)%u+ID=GVDt#|>Vn6Pn?cPI^YPjSUK+0npdoMT>K@;`7b)RrDesu@6+h_s zyQAGICu9Qs7-Z=FaHMxk{E71DsTJ>gek^xJ!yLez@cQWDqS693t>Jyo(J-a{%?*Uv zo_yWWh@$I0qFXZ+pq_NS+w<2*Lur+)yZ z`O%PBON({9J}d8^hASWn5IfBq_uvl)ih{$6f)4cpAPE;7!djbqP&cJ$*$=1>rPI14 zj-g!*B;mw6vYKi+EgDtqATczP!qHtSwtP@MWD&(@y;I#Sl#TOAHRaJ6jV@^=U(8C# zG)$=AB!Yh>P7!W{2u|X5*b2(OwrXl)2TS{o7NWcb?#w%DK?w4?F0H1AA?s`pbPFMG zax5^z--wVo?0C-EktAN4n(@I7kz-QJTjw`ipVx9Mn6w4A)e$k*^uVY-;>aag9~KZ6E`){thbw3M=R22AR^(=lceqL-|^Qj|s z(I@+S9aj7mgq?GxMfTrQkZayda$X2ZhRrzk3mx=^-;%95P`Ga#cTZxvMcL}j6W-WF zjcuXFC%72aHJh@nOZk>RY$=T`xgfjSD94p~fS1z$g=FeudJnmhJnq2#{HV5ooM2?n-ZBDg)nk2DZj+ZO zAM}OULFPdpO1cbSjveSc>)jvRzmja+Z74L=H1$Pjm$XM~=xb4-a?*?U=DgP*KlH4v zy*vLvS3Q6Vj*xI1tJ7(48i6S+FDtiJ#2qVvd}xUBwoo&Bi+u&1fX>5#T7LhQVKNHr zkYEMg87@!k3Jb=WjhG&kO0b(^0R6M?nXL;<2B)%0DegT8!g+s1NJXpAlTYm%01!7_ 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 literal 0 HcmV?d00001 diff --git a/apps/p2p-chat/index.js b/apps/p2p-chat/index.js new file mode 100644 index 0000000..16d08ae --- /dev/null +++ b/apps/p2p-chat/index.js @@ -0,0 +1,11 @@ +import {AppRegistry} from 'react-native' + +import {name as appName} from './app.json' +import App from './src/App' +import ChatApp from './src/ChatApp' + +// Register main app +AppRegistry.registerComponent(appName, () => App) + +// Register chat component for sandbox usage +AppRegistry.registerComponent('ChatApp', () => ChatApp) diff --git a/apps/p2p-chat/ios/.xcode.env b/apps/p2p-chat/ios/.xcode.env new file mode 100644 index 0000000..3d5782c --- /dev/null +++ b/apps/p2p-chat/ios/.xcode.env @@ -0,0 +1,11 @@ +# This `.xcode.env` file is versioned and is used to source the environment +# used when running script phases inside Xcode. +# To customize your local environment, you can create an `.xcode.env.local` +# file that is not versioned. + +# NODE_BINARY variable contains the PATH to the node executable. +# +# Customize the NODE_BINARY variable here. +# For example, to use nvm with brew, add the following line +# . "$(brew --prefix nvm)/nvm.sh" --no-use +export NODE_BINARY=$(command -v node) diff --git a/apps/p2p-chat/ios/P2PChat.xcodeproj/project.pbxproj b/apps/p2p-chat/ios/P2PChat.xcodeproj/project.pbxproj new file mode 100644 index 0000000..786f769 --- /dev/null +++ b/apps/p2p-chat/ios/P2PChat.xcodeproj/project.pbxproj @@ -0,0 +1,486 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 0C80B921A6F3F58F76C31292 /* libPods-P2PChat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-P2PChat.a */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + D060FE2E174260A6AAFF51D7 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 13B07F961A680F5B00A75B9A /* P2PChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = P2PChat.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = P2PChat/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = P2PChat/Info.plist; sourceTree = ""; }; + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = P2PChat/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 3B4392A12AC88292D35C810B /* Pods-P2PChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-P2PChat.debug.xcconfig"; path = "Target Support Files/Pods-P2PChat/Pods-P2PChat.debug.xcconfig"; sourceTree = ""; }; + 5709B34CF0A7D63546082F79 /* Pods-P2PChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-P2PChat.release.xcconfig"; path = "Target Support Files/Pods-P2PChat/Pods-P2PChat.release.xcconfig"; sourceTree = ""; }; + 5DCACB8F33CDC322A6C60F78 /* libPods-P2PChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-P2PChat.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = P2PChat/AppDelegate.swift; sourceTree = ""; }; + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = P2PChat/LaunchScreen.storyboard; sourceTree = ""; }; + ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C80B921A6F3F58F76C31292 /* libPods-P2PChat.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 13B07FAE1A68108700A75B9A /* P2PChat */ = { + isa = PBXGroup; + children = ( + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 761780EC2CA45674006654EE /* AppDelegate.swift */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, + ); + name = P2PChat; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + 5DCACB8F33CDC322A6C60F78 /* libPods-P2PChat.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + ); + name = Libraries; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* P2PChat */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + BBD78D7AC51CEA395F1C20DB /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* P2PChat.app */, + ); + name = Products; + sourceTree = ""; + }; + BBD78D7AC51CEA395F1C20DB /* Pods */ = { + isa = PBXGroup; + children = ( + 3B4392A12AC88292D35C810B /* Pods-P2PChat.debug.xcconfig */, + 5709B34CF0A7D63546082F79 /* Pods-P2PChat.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 13B07F861A680F5B00A75B9A /* P2PChat */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "P2PChat" */; + buildPhases = ( + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */, + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = P2PChat; + productName = P2PChat; + productReference = 13B07F961A680F5B00A75B9A /* P2PChat.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1210; + TargetAttributes = { + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1120; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "P2PChat" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* P2PChat */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + D060FE2E174260A6AAFF51D7 /* PrivacyInfo.xcprivacy in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/.xcode.env", + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + }; + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-P2PChat/Pods-P2PChat-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-P2PChat/Pods-P2PChat-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-P2PChat/Pods-P2PChat-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-P2PChat-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-P2PChat/Pods-P2PChat-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-P2PChat/Pods-P2PChat-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-P2PChat/Pods-P2PChat-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-P2PChat.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = P2PChat/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = P2PChat; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-P2PChat.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = P2PChat/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = P2PChat; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); + REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + USE_HERMES = true; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); + REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native"; + SDKROOT = iphoneos; + USE_HERMES = true; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "P2PChat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "P2PChat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/apps/p2p-chat/ios/P2PChat.xcodeproj/xcshareddata/xcschemes/P2PChat.xcscheme b/apps/p2p-chat/ios/P2PChat.xcodeproj/xcshareddata/xcschemes/P2PChat.xcscheme new file mode 100644 index 0000000..c521b91 --- /dev/null +++ b/apps/p2p-chat/ios/P2PChat.xcodeproj/xcshareddata/xcschemes/P2PChat.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/p2p-chat/ios/P2PChat.xcworkspace/contents.xcworkspacedata b/apps/p2p-chat/ios/P2PChat.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..41890df --- /dev/null +++ b/apps/p2p-chat/ios/P2PChat.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/apps/p2p-chat/ios/P2PChat/AppDelegate.swift b/apps/p2p-chat/ios/P2PChat/AppDelegate.swift new file mode 100644 index 0000000..3fe013f --- /dev/null +++ b/apps/p2p-chat/ios/P2PChat/AppDelegate.swift @@ -0,0 +1,48 @@ +import UIKit +import React +import React_RCTAppDelegate +import ReactAppDependencyProvider + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? + + var reactNativeDelegate: ReactNativeDelegate? + var reactNativeFactory: RCTReactNativeFactory? + + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { + let delegate = ReactNativeDelegate() + let factory = RCTReactNativeFactory(delegate: delegate) + delegate.dependencyProvider = RCTAppDependencyProvider() + + reactNativeDelegate = delegate + reactNativeFactory = factory + + window = UIWindow(frame: UIScreen.main.bounds) + + factory.startReactNative( + withModuleName: "p2p-chat", + in: window, + launchOptions: launchOptions + ) + + return true + } +} + +class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { + override func sourceURL(for bridge: RCTBridge) -> URL? { + self.bundleURL() + } + + override func bundleURL() -> URL? { +#if DEBUG + RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") +#else + Bundle.main.url(forResource: "main", withExtension: "jsbundle") +#endif + } +} diff --git a/apps/p2p-chat/ios/P2PChat/Images.xcassets/AppIcon.appiconset/Contents.json b/apps/p2p-chat/ios/P2PChat/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..8121323 --- /dev/null +++ b/apps/p2p-chat/ios/P2PChat/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/p2p-chat/ios/P2PChat/Images.xcassets/Contents.json b/apps/p2p-chat/ios/P2PChat/Images.xcassets/Contents.json new file mode 100644 index 0000000..2d92bd5 --- /dev/null +++ b/apps/p2p-chat/ios/P2PChat/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/apps/p2p-chat/ios/P2PChat/Info.plist b/apps/p2p-chat/ios/P2PChat/Info.plist new file mode 100644 index 0000000..98ca0b8 --- /dev/null +++ b/apps/p2p-chat/ios/P2PChat/Info.plist @@ -0,0 +1,53 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + P2PChat + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + NSLocationWhenInUseUsageDescription + + RCTNewArchEnabled + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/apps/p2p-chat/ios/P2PChat/LaunchScreen.storyboard b/apps/p2p-chat/ios/P2PChat/LaunchScreen.storyboard new file mode 100644 index 0000000..ce5bd85 --- /dev/null +++ b/apps/p2p-chat/ios/P2PChat/LaunchScreen.storyboard @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/p2p-chat/ios/P2PChat/PrivacyInfo.xcprivacy b/apps/p2p-chat/ios/P2PChat/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..41b8317 --- /dev/null +++ b/apps/p2p-chat/ios/P2PChat/PrivacyInfo.xcprivacy @@ -0,0 +1,37 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/apps/p2p-chat/ios/Podfile b/apps/p2p-chat/ios/Podfile new file mode 100644 index 0000000..021e930 --- /dev/null +++ b/apps/p2p-chat/ios/Podfile @@ -0,0 +1,35 @@ +# Resolve react_native_pods.rb with node to allow for hoisting +require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + +platform :ios, min_ios_version_supported +prepare_react_native_project! + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end + +target 'P2PChat' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + post_install do |installer| + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) + end +end diff --git a/apps/p2p-chat/ios/Podfile.lock b/apps/p2p-chat/ios/Podfile.lock new file mode 100644 index 0000000..d10cc68 --- /dev/null +++ b/apps/p2p-chat/ios/Podfile.lock @@ -0,0 +1,2465 @@ +PODS: + - boost (1.84.0) + - DoubleConversion (1.1.6) + - fast_float (8.0.0) + - FBLazyVector (0.80.1) + - fmt (11.0.2) + - glog (0.3.5) + - hermes-engine (0.80.1): + - hermes-engine/Pre-built (= 0.80.1) + - hermes-engine/Pre-built (0.80.1) + - RCT-Folly (2024.11.18.00): + - boost + - DoubleConversion + - fast_float (= 8.0.0) + - fmt (= 11.0.2) + - glog + - RCT-Folly/Default (= 2024.11.18.00) + - RCT-Folly/Default (2024.11.18.00): + - boost + - DoubleConversion + - fast_float (= 8.0.0) + - fmt (= 11.0.2) + - glog + - RCT-Folly/Fabric (2024.11.18.00): + - boost + - DoubleConversion + - fast_float (= 8.0.0) + - fmt (= 11.0.2) + - glog + - RCTDeprecation (0.80.1) + - RCTRequired (0.80.1) + - RCTTypeSafety (0.80.1): + - FBLazyVector (= 0.80.1) + - RCTRequired (= 0.80.1) + - React-Core (= 0.80.1) + - React (0.80.1): + - React-Core (= 0.80.1) + - React-Core/DevSupport (= 0.80.1) + - React-Core/RCTWebSocket (= 0.80.1) + - React-RCTActionSheet (= 0.80.1) + - React-RCTAnimation (= 0.80.1) + - React-RCTBlob (= 0.80.1) + - React-RCTImage (= 0.80.1) + - React-RCTLinking (= 0.80.1) + - React-RCTNetwork (= 0.80.1) + - React-RCTSettings (= 0.80.1) + - React-RCTText (= 0.80.1) + - React-RCTVibration (= 0.80.1) + - React-callinvoker (0.80.1) + - React-Core (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default (= 0.80.1) + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/CoreModulesHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/Default (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/DevSupport (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default (= 0.80.1) + - React-Core/RCTWebSocket (= 0.80.1) + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTActionSheetHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTAnimationHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTBlobHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTImageHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTLinkingHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTNetworkHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTSettingsHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTTextHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTVibrationHeaders (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-Core/RCTWebSocket (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTDeprecation + - React-Core/Default (= 0.80.1) + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsinspectorcdp + - React-jsitooling + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-CoreModules (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - RCTTypeSafety (= 0.80.1) + - React-Core/CoreModulesHeaders (= 0.80.1) + - React-jsi (= 0.80.1) + - React-jsinspector + - React-jsinspectorcdp + - React-jsinspectortracing + - React-NativeModulesApple + - React-RCTBlob + - React-RCTFBReactNativeSpec + - React-RCTImage (= 0.80.1) + - ReactCommon + - SocketRocket + - React-cxxreact (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-callinvoker (= 0.80.1) + - React-debug (= 0.80.1) + - React-jsi (= 0.80.1) + - React-jsinspector + - React-jsinspectorcdp + - React-jsinspectortracing + - React-logger (= 0.80.1) + - React-perflogger (= 0.80.1) + - React-runtimeexecutor (= 0.80.1) + - React-timing (= 0.80.1) + - SocketRocket + - React-debug (0.80.1) + - React-defaultsnativemodule (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-domnativemodule + - React-featureflagsnativemodule + - React-hermes + - React-idlecallbacksnativemodule + - React-jsi + - React-jsiexecutor + - React-microtasksnativemodule + - React-RCTFBReactNativeSpec + - SocketRocket + - React-domnativemodule (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-Fabric + - React-FabricComponents + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-Fabric (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/animations (= 0.80.1) + - React-Fabric/attributedstring (= 0.80.1) + - React-Fabric/componentregistry (= 0.80.1) + - React-Fabric/componentregistrynative (= 0.80.1) + - React-Fabric/components (= 0.80.1) + - React-Fabric/consistency (= 0.80.1) + - React-Fabric/core (= 0.80.1) + - React-Fabric/dom (= 0.80.1) + - React-Fabric/imagemanager (= 0.80.1) + - React-Fabric/leakchecker (= 0.80.1) + - React-Fabric/mounting (= 0.80.1) + - React-Fabric/observers (= 0.80.1) + - React-Fabric/scheduler (= 0.80.1) + - React-Fabric/telemetry (= 0.80.1) + - React-Fabric/templateprocessor (= 0.80.1) + - React-Fabric/uimanager (= 0.80.1) + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/animations (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/attributedstring (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/componentregistry (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/componentregistrynative (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/components (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/components/legacyviewmanagerinterop (= 0.80.1) + - React-Fabric/components/root (= 0.80.1) + - React-Fabric/components/scrollview (= 0.80.1) + - React-Fabric/components/view (= 0.80.1) + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/components/legacyviewmanagerinterop (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/components/root (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/components/scrollview (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/components/view (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-renderercss + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-Fabric/consistency (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/core (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/dom (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/imagemanager (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/leakchecker (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/mounting (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/observers (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/observers/events (= 0.80.1) + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/observers/events (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/scheduler (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/observers/events + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-performancetimeline + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/telemetry (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/templateprocessor (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/uimanager (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/uimanager/consistency (= 0.80.1) + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererconsistency + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-Fabric/uimanager/consistency (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererconsistency + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - React-FabricComponents (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-FabricComponents/components (= 0.80.1) + - React-FabricComponents/textlayoutmanager (= 0.80.1) + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-FabricComponents/components/inputaccessory (= 0.80.1) + - React-FabricComponents/components/iostextinput (= 0.80.1) + - React-FabricComponents/components/modal (= 0.80.1) + - React-FabricComponents/components/rncore (= 0.80.1) + - React-FabricComponents/components/safeareaview (= 0.80.1) + - React-FabricComponents/components/scrollview (= 0.80.1) + - React-FabricComponents/components/text (= 0.80.1) + - React-FabricComponents/components/textinput (= 0.80.1) + - React-FabricComponents/components/unimplementedview (= 0.80.1) + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components/inputaccessory (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components/iostextinput (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components/modal (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components/rncore (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components/safeareaview (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components/scrollview (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components/text (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components/textinput (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/components/unimplementedview (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricComponents/textlayoutmanager (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-FabricImage (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired (= 0.80.1) + - RCTTypeSafety (= 0.80.1) + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-jsiexecutor (= 0.80.1) + - React-logger + - React-rendererdebug + - React-utils + - ReactCommon + - SocketRocket + - Yoga + - React-featureflags (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - SocketRocket + - React-featureflagsnativemodule (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec + - ReactCommon/turbomodule/core + - SocketRocket + - React-graphics (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-hermes + - React-jsi + - React-jsiexecutor + - React-utils + - SocketRocket + - React-hermes (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-cxxreact (= 0.80.1) + - React-jsi + - React-jsiexecutor (= 0.80.1) + - React-jsinspector + - React-jsinspectorcdp + - React-jsinspectortracing + - React-perflogger (= 0.80.1) + - React-runtimeexecutor + - SocketRocket + - React-idlecallbacksnativemodule (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-hermes + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec + - React-runtimescheduler + - ReactCommon/turbomodule/core + - SocketRocket + - React-ImageManager (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - React-Core/Default + - React-debug + - React-Fabric + - React-graphics + - React-rendererdebug + - React-utils + - SocketRocket + - React-jserrorhandler (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-cxxreact + - React-debug + - React-featureflags + - React-jsi + - ReactCommon/turbomodule/bridging + - SocketRocket + - React-jsi (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - SocketRocket + - React-jsiexecutor (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-cxxreact (= 0.80.1) + - React-jsi (= 0.80.1) + - React-jsinspector + - React-jsinspectorcdp + - React-jsinspectortracing + - React-perflogger (= 0.80.1) + - SocketRocket + - React-jsinspector (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-featureflags + - React-jsi + - React-jsinspectorcdp + - React-jsinspectornetwork + - React-jsinspectortracing + - React-perflogger (= 0.80.1) + - React-runtimeexecutor (= 0.80.1) + - SocketRocket + - React-jsinspectorcdp (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - SocketRocket + - React-jsinspectornetwork (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - React-jsinspectorcdp + - SocketRocket + - React-jsinspectortracing (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - React-oscompat + - SocketRocket + - React-jsitooling (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - React-cxxreact (= 0.80.1) + - React-jsi (= 0.80.1) + - React-jsinspector + - React-jsinspectorcdp + - React-jsinspectortracing + - SocketRocket + - React-jsitracing (0.80.1): + - React-jsi + - React-logger (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - SocketRocket + - React-Mapbuffer (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - React-debug + - SocketRocket + - React-microtasksnativemodule (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-hermes + - React-jsi + - React-jsiexecutor + - React-RCTFBReactNativeSpec + - ReactCommon/turbomodule/core + - SocketRocket + - React-NativeModulesApple (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-callinvoker + - React-Core + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsinspector + - React-jsinspectorcdp + - React-runtimeexecutor + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - React-oscompat (0.80.1) + - React-perflogger (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - SocketRocket + - React-performancetimeline (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - React-featureflags + - React-jsinspectortracing + - React-perflogger + - React-timing + - SocketRocket + - React-RCTActionSheet (0.80.1): + - React-Core/RCTActionSheetHeaders (= 0.80.1) + - React-RCTAnimation (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - RCTTypeSafety + - React-Core/RCTAnimationHeaders + - React-featureflags + - React-jsi + - React-NativeModulesApple + - React-RCTFBReactNativeSpec + - ReactCommon + - SocketRocket + - React-RCTAppDelegate (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-CoreModules + - React-debug + - React-defaultsnativemodule + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-jsitooling + - React-NativeModulesApple + - React-RCTFabric + - React-RCTFBReactNativeSpec + - React-RCTImage + - React-RCTNetwork + - React-RCTRuntime + - React-rendererdebug + - React-RuntimeApple + - React-RuntimeCore + - React-runtimescheduler + - React-utils + - ReactCommon + - SocketRocket + - React-RCTBlob (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-Core/RCTBlobHeaders + - React-Core/RCTWebSocket + - React-jsi + - React-jsinspector + - React-jsinspectorcdp + - React-NativeModulesApple + - React-RCTFBReactNativeSpec + - React-RCTNetwork + - ReactCommon + - SocketRocket + - React-RCTFabric (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-Core + - React-debug + - React-Fabric + - React-FabricComponents + - React-FabricImage + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-jsinspector + - React-jsinspectorcdp + - React-jsinspectornetwork + - React-jsinspectortracing + - React-performancetimeline + - React-RCTAnimation + - React-RCTImage + - React-RCTText + - React-rendererconsistency + - React-renderercss + - React-rendererdebug + - React-runtimescheduler + - React-utils + - SocketRocket + - Yoga + - React-RCTFBReactNativeSpec (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-hermes + - React-jsi + - React-jsiexecutor + - React-NativeModulesApple + - ReactCommon + - SocketRocket + - React-RCTImage (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - RCTTypeSafety + - React-Core/RCTImageHeaders + - React-jsi + - React-NativeModulesApple + - React-RCTFBReactNativeSpec + - React-RCTNetwork + - ReactCommon + - SocketRocket + - React-RCTLinking (0.80.1): + - React-Core/RCTLinkingHeaders (= 0.80.1) + - React-jsi (= 0.80.1) + - React-NativeModulesApple + - React-RCTFBReactNativeSpec + - ReactCommon + - ReactCommon/turbomodule/core (= 0.80.1) + - React-RCTNetwork (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - RCTTypeSafety + - React-Core/RCTNetworkHeaders + - React-featureflags + - React-jsi + - React-jsinspectorcdp + - React-jsinspectornetwork + - React-NativeModulesApple + - React-RCTFBReactNativeSpec + - ReactCommon + - SocketRocket + - React-RCTRuntime (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-Core + - React-hermes + - React-jsi + - React-jsinspector + - React-jsinspectorcdp + - React-jsinspectortracing + - React-jsitooling + - React-RuntimeApple + - React-RuntimeCore + - React-RuntimeHermes + - SocketRocket + - React-RCTSettings (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - RCTTypeSafety + - React-Core/RCTSettingsHeaders + - React-jsi + - React-NativeModulesApple + - React-RCTFBReactNativeSpec + - ReactCommon + - SocketRocket + - React-RCTText (0.80.1): + - React-Core/RCTTextHeaders (= 0.80.1) + - Yoga + - React-RCTVibration (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - React-Core/RCTVibrationHeaders + - React-jsi + - React-NativeModulesApple + - React-RCTFBReactNativeSpec + - ReactCommon + - SocketRocket + - React-rendererconsistency (0.80.1) + - React-renderercss (0.80.1): + - React-debug + - React-utils + - React-rendererdebug (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - React-debug + - SocketRocket + - React-rncore (0.80.1) + - React-RuntimeApple (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-callinvoker + - React-Core/Default + - React-CoreModules + - React-cxxreact + - React-featureflags + - React-jserrorhandler + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsitooling + - React-Mapbuffer + - React-NativeModulesApple + - React-RCTFabric + - React-RCTFBReactNativeSpec + - React-RuntimeCore + - React-runtimeexecutor + - React-RuntimeHermes + - React-runtimescheduler + - React-utils + - SocketRocket + - React-RuntimeCore (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-cxxreact + - React-Fabric + - React-featureflags + - React-hermes + - React-jserrorhandler + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-jsitooling + - React-performancetimeline + - React-runtimeexecutor + - React-runtimescheduler + - React-utils + - SocketRocket + - React-runtimeexecutor (0.80.1): + - React-jsi (= 0.80.1) + - React-RuntimeHermes (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-featureflags + - React-hermes + - React-jsi + - React-jsinspector + - React-jsinspectorcdp + - React-jsinspectortracing + - React-jsitooling + - React-jsitracing + - React-RuntimeCore + - React-utils + - SocketRocket + - React-runtimescheduler (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-callinvoker + - React-cxxreact + - React-debug + - React-featureflags + - React-hermes + - React-jsi + - React-jsinspectortracing + - React-performancetimeline + - React-rendererconsistency + - React-rendererdebug + - React-runtimeexecutor + - React-timing + - React-utils + - SocketRocket + - React-Sandbox (0.3.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga + - React-timing (0.80.1) + - React-utils (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-debug + - React-hermes + - React-jsi (= 0.80.1) + - SocketRocket + - ReactAppDependencyProvider (0.80.1): + - ReactCodegen + - ReactCodegen (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-FabricImage + - React-featureflags + - React-graphics + - React-hermes + - React-jsi + - React-jsiexecutor + - React-NativeModulesApple + - React-RCTAppDelegate + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - ReactCommon (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - RCT-Folly + - RCT-Folly/Fabric + - ReactCommon/turbomodule (= 0.80.1) + - SocketRocket + - ReactCommon/turbomodule (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-callinvoker (= 0.80.1) + - React-cxxreact (= 0.80.1) + - React-jsi (= 0.80.1) + - React-logger (= 0.80.1) + - React-perflogger (= 0.80.1) + - ReactCommon/turbomodule/bridging (= 0.80.1) + - ReactCommon/turbomodule/core (= 0.80.1) + - SocketRocket + - ReactCommon/turbomodule/bridging (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-callinvoker (= 0.80.1) + - React-cxxreact (= 0.80.1) + - React-jsi (= 0.80.1) + - React-logger (= 0.80.1) + - React-perflogger (= 0.80.1) + - SocketRocket + - ReactCommon/turbomodule/core (0.80.1): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - React-callinvoker (= 0.80.1) + - React-cxxreact (= 0.80.1) + - React-debug (= 0.80.1) + - React-featureflags (= 0.80.1) + - React-jsi (= 0.80.1) + - React-logger (= 0.80.1) + - React-perflogger (= 0.80.1) + - React-utils (= 0.80.1) + - SocketRocket + - SocketRocket (0.7.1) + - Yoga (0.0.0) + +DEPENDENCIES: + - boost (from `../../../node_modules/react-native/third-party-podspecs/boost.podspec`) + - DoubleConversion (from `../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - fast_float (from `../../../node_modules/react-native/third-party-podspecs/fast_float.podspec`) + - FBLazyVector (from `../../../node_modules/react-native/Libraries/FBLazyVector`) + - fmt (from `../../../node_modules/react-native/third-party-podspecs/fmt.podspec`) + - glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`) + - hermes-engine (from `../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) + - RCT-Folly (from `../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCTDeprecation (from `../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) + - RCTRequired (from `../../../node_modules/react-native/Libraries/Required`) + - RCTTypeSafety (from `../../../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../../../node_modules/react-native/`) + - React-callinvoker (from `../../../node_modules/react-native/ReactCommon/callinvoker`) + - React-Core (from `../../../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../../../node_modules/react-native/`) + - React-CoreModules (from `../../../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../../../node_modules/react-native/ReactCommon/cxxreact`) + - React-debug (from `../../../node_modules/react-native/ReactCommon/react/debug`) + - React-defaultsnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/defaults`) + - React-domnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/dom`) + - React-Fabric (from `../../../node_modules/react-native/ReactCommon`) + - React-FabricComponents (from `../../../node_modules/react-native/ReactCommon`) + - React-FabricImage (from `../../../node_modules/react-native/ReactCommon`) + - React-featureflags (from `../../../node_modules/react-native/ReactCommon/react/featureflags`) + - React-featureflagsnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`) + - React-graphics (from `../../../node_modules/react-native/ReactCommon/react/renderer/graphics`) + - React-hermes (from `../../../node_modules/react-native/ReactCommon/hermes`) + - React-idlecallbacksnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`) + - React-ImageManager (from `../../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) + - React-jserrorhandler (from `../../../node_modules/react-native/ReactCommon/jserrorhandler`) + - React-jsi (from `../../../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../../../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern`) + - React-jsinspectorcdp (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/cdp`) + - React-jsinspectornetwork (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/network`) + - React-jsinspectortracing (from `../../../node_modules/react-native/ReactCommon/jsinspector-modern/tracing`) + - React-jsitooling (from `../../../node_modules/react-native/ReactCommon/jsitooling`) + - 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-microtasksnativemodule (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) + - React-NativeModulesApple (from `../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) + - React-oscompat (from `../../../node_modules/react-native/ReactCommon/oscompat`) + - React-perflogger (from `../../../node_modules/react-native/ReactCommon/reactperflogger`) + - React-performancetimeline (from `../../../node_modules/react-native/ReactCommon/react/performance/timeline`) + - React-RCTActionSheet (from `../../../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../../../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTAppDelegate (from `../../../node_modules/react-native/Libraries/AppDelegate`) + - React-RCTBlob (from `../../../node_modules/react-native/Libraries/Blob`) + - React-RCTFabric (from `../../../node_modules/react-native/React`) + - React-RCTFBReactNativeSpec (from `../../../node_modules/react-native/React`) + - React-RCTImage (from `../../../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../../../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../../../node_modules/react-native/Libraries/Network`) + - React-RCTRuntime (from `../../../node_modules/react-native/React/Runtime`) + - React-RCTSettings (from `../../../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../../../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../../../node_modules/react-native/Libraries/Vibration`) + - React-rendererconsistency (from `../../../node_modules/react-native/ReactCommon/react/renderer/consistency`) + - React-renderercss (from `../../../node_modules/react-native/ReactCommon/react/renderer/css`) + - React-rendererdebug (from `../../../node_modules/react-native/ReactCommon/react/renderer/debug`) + - React-rncore (from `../../../node_modules/react-native/ReactCommon`) + - React-RuntimeApple (from `../../../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) + - React-RuntimeCore (from `../../../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimeexecutor (from `../../../node_modules/react-native/ReactCommon/runtimeexecutor`) + - React-RuntimeHermes (from `../../../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimescheduler (from `../../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) + - "React-Sandbox (from `../../../node_modules/@callstack/react-native-sandbox`)" + - React-timing (from `../../../node_modules/react-native/ReactCommon/react/timing`) + - React-utils (from `../../../node_modules/react-native/ReactCommon/react/utils`) + - ReactAppDependencyProvider (from `build/generated/ios`) + - ReactCodegen (from `build/generated/ios`) + - ReactCommon/turbomodule/core (from `../../../node_modules/react-native/ReactCommon`) + - SocketRocket (~> 0.7.1) + - Yoga (from `../../../node_modules/react-native/ReactCommon/yoga`) + +SPEC REPOS: + trunk: + - SocketRocket + +EXTERNAL SOURCES: + boost: + :podspec: "../../../node_modules/react-native/third-party-podspecs/boost.podspec" + DoubleConversion: + :podspec: "../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + fast_float: + :podspec: "../../../node_modules/react-native/third-party-podspecs/fast_float.podspec" + FBLazyVector: + :path: "../../../node_modules/react-native/Libraries/FBLazyVector" + fmt: + :podspec: "../../../node_modules/react-native/third-party-podspecs/fmt.podspec" + glog: + :podspec: "../../../node_modules/react-native/third-party-podspecs/glog.podspec" + hermes-engine: + :podspec: "../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" + :tag: hermes-2025-05-06-RNv0.80.0-4eb6132a5bf0450bf4c6c91987675381d7ac8bca + RCT-Folly: + :podspec: "../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" + RCTDeprecation: + :path: "../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" + RCTRequired: + :path: "../../../node_modules/react-native/Libraries/Required" + RCTTypeSafety: + :path: "../../../node_modules/react-native/Libraries/TypeSafety" + React: + :path: "../../../node_modules/react-native/" + React-callinvoker: + :path: "../../../node_modules/react-native/ReactCommon/callinvoker" + React-Core: + :path: "../../../node_modules/react-native/" + React-CoreModules: + :path: "../../../node_modules/react-native/React/CoreModules" + React-cxxreact: + :path: "../../../node_modules/react-native/ReactCommon/cxxreact" + React-debug: + :path: "../../../node_modules/react-native/ReactCommon/react/debug" + React-defaultsnativemodule: + :path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/defaults" + React-domnativemodule: + :path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/dom" + React-Fabric: + :path: "../../../node_modules/react-native/ReactCommon" + React-FabricComponents: + :path: "../../../node_modules/react-native/ReactCommon" + React-FabricImage: + :path: "../../../node_modules/react-native/ReactCommon" + React-featureflags: + :path: "../../../node_modules/react-native/ReactCommon/react/featureflags" + React-featureflagsnativemodule: + :path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/featureflags" + React-graphics: + :path: "../../../node_modules/react-native/ReactCommon/react/renderer/graphics" + React-hermes: + :path: "../../../node_modules/react-native/ReactCommon/hermes" + React-idlecallbacksnativemodule: + :path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks" + React-ImageManager: + :path: "../../../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" + React-jserrorhandler: + :path: "../../../node_modules/react-native/ReactCommon/jserrorhandler" + React-jsi: + :path: "../../../node_modules/react-native/ReactCommon/jsi" + React-jsiexecutor: + :path: "../../../node_modules/react-native/ReactCommon/jsiexecutor" + React-jsinspector: + :path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern" + React-jsinspectorcdp: + :path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/cdp" + React-jsinspectornetwork: + :path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/network" + React-jsinspectortracing: + :path: "../../../node_modules/react-native/ReactCommon/jsinspector-modern/tracing" + React-jsitooling: + :path: "../../../node_modules/react-native/ReactCommon/jsitooling" + React-jsitracing: + :path: "../../../node_modules/react-native/ReactCommon/hermes/executor/" + React-logger: + :path: "../../../node_modules/react-native/ReactCommon/logger" + React-Mapbuffer: + :path: "../../../node_modules/react-native/ReactCommon" + React-microtasksnativemodule: + :path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" + React-NativeModulesApple: + :path: "../../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" + React-oscompat: + :path: "../../../node_modules/react-native/ReactCommon/oscompat" + React-perflogger: + :path: "../../../node_modules/react-native/ReactCommon/reactperflogger" + React-performancetimeline: + :path: "../../../node_modules/react-native/ReactCommon/react/performance/timeline" + React-RCTActionSheet: + :path: "../../../node_modules/react-native/Libraries/ActionSheetIOS" + React-RCTAnimation: + :path: "../../../node_modules/react-native/Libraries/NativeAnimation" + React-RCTAppDelegate: + :path: "../../../node_modules/react-native/Libraries/AppDelegate" + React-RCTBlob: + :path: "../../../node_modules/react-native/Libraries/Blob" + React-RCTFabric: + :path: "../../../node_modules/react-native/React" + React-RCTFBReactNativeSpec: + :path: "../../../node_modules/react-native/React" + React-RCTImage: + :path: "../../../node_modules/react-native/Libraries/Image" + React-RCTLinking: + :path: "../../../node_modules/react-native/Libraries/LinkingIOS" + React-RCTNetwork: + :path: "../../../node_modules/react-native/Libraries/Network" + React-RCTRuntime: + :path: "../../../node_modules/react-native/React/Runtime" + React-RCTSettings: + :path: "../../../node_modules/react-native/Libraries/Settings" + React-RCTText: + :path: "../../../node_modules/react-native/Libraries/Text" + React-RCTVibration: + :path: "../../../node_modules/react-native/Libraries/Vibration" + React-rendererconsistency: + :path: "../../../node_modules/react-native/ReactCommon/react/renderer/consistency" + React-renderercss: + :path: "../../../node_modules/react-native/ReactCommon/react/renderer/css" + React-rendererdebug: + :path: "../../../node_modules/react-native/ReactCommon/react/renderer/debug" + React-rncore: + :path: "../../../node_modules/react-native/ReactCommon" + React-RuntimeApple: + :path: "../../../node_modules/react-native/ReactCommon/react/runtime/platform/ios" + React-RuntimeCore: + :path: "../../../node_modules/react-native/ReactCommon/react/runtime" + React-runtimeexecutor: + :path: "../../../node_modules/react-native/ReactCommon/runtimeexecutor" + React-RuntimeHermes: + :path: "../../../node_modules/react-native/ReactCommon/react/runtime" + React-runtimescheduler: + :path: "../../../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" + React-Sandbox: + :path: "../../../node_modules/@callstack/react-native-sandbox" + React-timing: + :path: "../../../node_modules/react-native/ReactCommon/react/timing" + React-utils: + :path: "../../../node_modules/react-native/ReactCommon/react/utils" + ReactAppDependencyProvider: + :path: build/generated/ios + ReactCodegen: + :path: build/generated/ios + ReactCommon: + :path: "../../../node_modules/react-native/ReactCommon" + Yoga: + :path: "../../../node_modules/react-native/ReactCommon/yoga" + +SPEC CHECKSUMS: + boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 + DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb + fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6 + FBLazyVector: 09f03e4b6f42f955734b64a118f86509cc719427 + fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd + glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 + hermes-engine: 4f07404533b808de66cf48ac4200463068d0e95a + RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 + RCTDeprecation: efa5010912100e944a7ac9a93a157e1def1988fe + RCTRequired: bbc4cf999ddc4a4b076e076c74dd1d39d0254630 + RCTTypeSafety: d877728097547d0a37786cc9130c43ad71739ac3 + React: 4b0b9cb962e694611e5e8a697c1b0300a2510c21 + React-callinvoker: 70f125c17c7132811a6b473946ac5e7ae93b5e57 + React-Core: bab40f5b1f46fe0c5896895a6f333e861a821a81 + React-CoreModules: 05647d952e521113c128360633896ba7ba652e82 + React-cxxreact: 2b4bac1ec6eecc6288ac8a6caea6afb42585740e + React-debug: deb3a146ef717fa3e8f4c23e0288369fe53199b7 + React-defaultsnativemodule: 11e2948787a15d3cf1b66d7f29f13770a177bff7 + React-domnativemodule: 2f4b279acdb2963736fb5de2f585811dd90070b5 + React-Fabric: 6f8d1a303c96f1d078c14d74c4005bf457e5b782 + React-FabricComponents: b106410970e9a0c4e592da656c7a7e0947306c23 + React-FabricImage: 1abaf230dfce9b58fdf53c4128f3f40c6e64af6a + React-featureflags: f7ef58d91079efde3ad223bcca6d197e845d5bcf + React-featureflagsnativemodule: ae5abc9849d1696f4f8f11ee3744bf5715e032cf + React-graphics: b306856c6ed9aac32f717a229550406a53b28a6d + React-hermes: b6edce8fa19388654b1aea30844497cbeade83bc + React-idlecallbacksnativemodule: cb386712842cb9e479c89311edb234d529b64db4 + React-ImageManager: 8ce94417853eaa22faaad1f4cc1952dd3f8e2275 + React-jserrorhandler: ab827d67dc270a9c8703eef524230baeafaf6876 + React-jsi: 545342ec5c78ab1277af5f0dbe8d489e7e73db14 + React-jsiexecutor: 20210891c7c77255c16dec6762faf68b373f9f74 + React-jsinspector: 4e73460e488132d70d2b4894e5578cc856f2cb74 + React-jsinspectorcdp: 8b2bcb5779289cb2b9ca517f2965ed23eb2fd3e0 + React-jsinspectornetwork: b5e0cb9e488d294eed2d8209dc3dc0f9587210c1 + React-jsinspectortracing: f3c4036e7b984405ac910f878576d325dd9f2834 + React-jsitooling: 75bbfd221b6173a5e848ca5a6680506bac064a56 + React-jsitracing: 11ed7d821864dd988c159d4943e0a1e0937c11b1 + React-logger: 984ebd897afad067555d081deaf03f57c4315723 + React-Mapbuffer: 0c045c844ce6d85cde53e85ab163294c6adad349 + React-microtasksnativemodule: d9499269ad1f484ae71319bac1d9231447f2094e + React-NativeModulesApple: 983f3483ef0a3446b56d490f09d579fba2442e17 + React-oscompat: 114036cd8f064558c9c1a0c04fc9ae5e1453706a + React-perflogger: e7287fee27c16e3c8bd4d470f2361572b63be16b + React-performancetimeline: 8ebbaa31d2d0cea680b0a2a567500d3cab8954fc + React-RCTActionSheet: 68c68b0a7a5d2b0cfc255c64889b6e485974e988 + React-RCTAnimation: d6c5c728b888a967ce9aff1ff71a8ed71a68d069 + React-RCTAppDelegate: 0fc048666bda159cd469a6fb9befb04b3fa62be4 + React-RCTBlob: 12d8c699a1f906840113ee8d8bb575e69a05509f + React-RCTFabric: 01e815845ebc185f44205dcbf50eeb712fec23fe + React-RCTFBReactNativeSpec: f57927fb0af6ce2f25c19f8b894e2986138aa89f + React-RCTImage: a82518168f4ee407913b23ca749ca79ef51959f3 + React-RCTLinking: 7f343b584c36f024f390fea563483568fe763ef6 + React-RCTNetwork: 3165eb757ceb62a7cde4cdad043d63314122e8a3 + React-RCTRuntime: feee590c459c4cb6aaa7a00f3abc8c04709b536f + React-RCTSettings: 6bad0ae45d8d872c873059f332f586f99875621f + React-RCTText: 657d60f35983062de8f0cea67c279aa7a3ea9858 + React-RCTVibration: 78f4770515141efb7f55f9b27c49dda95319c3a8 + React-rendererconsistency: f7baab26c6d0cd5b2eb7afcecfd2d8b957017b18 + React-renderercss: bdd2f83a4a054c3e4321fd61305c202b848e471b + React-rendererdebug: 9f8865ee038127a9d99d4b034c9da4935d204993 + React-rncore: f7438473c4c71ee1963fb06a8635bb96013c9e1c + React-RuntimeApple: 4d2ab9f72b9193da86eceded128a67254fc18aeb + React-RuntimeCore: 5fd73030438d094975ca0f549d162dd97746ae38 + React-runtimeexecutor: 17c70842d5e611130cb66f91e247bc4a609c3508 + React-RuntimeHermes: 3c88e6e1ea7ea0899dcffc77c10d61ea46688cfd + React-runtimescheduler: 024500621c7c93d65371498abb4ee26d34f5d47d + React-Sandbox: 7f628c364e1e4f691c2f7f2eb82bd9aaca1989fa + React-timing: c3c923df2b86194e1682e01167717481232f1dc7 + React-utils: 9154a037543147e1c24098f1a48fc8472602c092 + ReactAppDependencyProvider: afd905e84ee36e1678016ae04d7370c75ed539be + ReactCodegen: 06bf9ae2e01a2416250cf5e44e4a06b1c9ea201b + ReactCommon: 17fd88849a174bf9ce45461912291aca711410fc + SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 + Yoga: daa1e4de4b971b977b23bc842aaa3e135324f1f3 + +PODFILE CHECKSUM: f57c2d8a159b8fcf2a742eb959389b3a31be7cad + +COCOAPODS: 1.16.2 diff --git a/apps/p2p-chat/metro.config.js b/apps/p2p-chat/metro.config.js new file mode 100644 index 0000000..a4d147a --- /dev/null +++ b/apps/p2p-chat/metro.config.js @@ -0,0 +1,24 @@ +const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config') +const path = require('path') + +const projectRoot = __dirname +const workspaceRoot = path.resolve(projectRoot, '../..') + +/** + * Metro configuration + * https://reactnative.dev/docs/metro + * + * @type {import('@react-native/metro-config').MetroConfig} + */ +const config = { + watchFolders: [workspaceRoot], + resolver: { + nodeModulesPaths: [ + path.resolve(projectRoot, 'node_modules'), + path.resolve(workspaceRoot, 'node_modules'), + ], + disableHierarchicalLookup: true, + }, +} + +module.exports = mergeConfig(getDefaultConfig(projectRoot), config) diff --git a/apps/p2p-chat/package.json b/apps/p2p-chat/package.json new file mode 100644 index 0000000..56960c1 --- /dev/null +++ b/apps/p2p-chat/package.json @@ -0,0 +1,32 @@ +{ + "name": "@apps/p2p-chat", + "version": "0.0.1", + "private": true, + "scripts": { + "android": "react-native run-android", + "ios": "react-native run-ios", + "start": "react-native start" + }, + "dependencies": { + "react": "19.1.0", + "react-native": "0.80.1", + "react-native-toast-message": "^2.3.2", + "@callstack/react-native-sandbox": "workspace:*" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.3", + "@babel/runtime": "^7.25.0", + "@react-native-community/cli": "18.0.0", + "@react-native-community/cli-platform-android": "18.0.0", + "@react-native-community/cli-platform-ios": "18.0.0", + "@react-native/babel-preset": "0.80.1", + "@react-native/eslint-config": "0.80.1", + "@react-native/metro-config": "0.80.1", + "@react-native/typescript-config": "0.80.1", + "react-test-renderer": "19.1.0" + }, + "engines": { + "node": ">=18" + } +} \ No newline at end of file diff --git a/apps/p2p-chat/src/App.tsx b/apps/p2p-chat/src/App.tsx new file mode 100644 index 0000000..8c1126f --- /dev/null +++ b/apps/p2p-chat/src/App.tsx @@ -0,0 +1,65 @@ +import React, {useCallback, useRef} from 'react' +import { + NativeScrollEvent, + NativeSyntheticEvent, + SafeAreaView, + StatusBar, +} from 'react-native' + +import {ChatCarousel} from './components' +import {CHAT_WIDTH} from './constants' +import {useChatInstances} from './hooks' +import {MessageHandler} from './services' +import {carouselStyles} from './styles' + +const App = () => { + const { + chatInstances, + currentIndex, + setCurrentIndex, + friendshipTrigger, + friendshipManager, + addChatInstance, + removeChatInstance, + triggerFriendshipUpdate, + } = useChatInstances() + + const sandboxRefs = useRef>({}) + + const messageHandler = new MessageHandler( + chatInstances, + friendshipManager, + sandboxRefs, + triggerFriendshipUpdate + ) + + const onScroll = useCallback( + (event: NativeSyntheticEvent) => { + const slideIndex = Math.round( + event.nativeEvent.contentOffset.x / CHAT_WIDTH + ) + setCurrentIndex(slideIndex) + }, + [setCurrentIndex] + ) + + return ( + + + + + + ) +} + +export default App diff --git a/apps/p2p-chat/src/ChatApp.tsx b/apps/p2p-chat/src/ChatApp.tsx new file mode 100644 index 0000000..3d8590e --- /dev/null +++ b/apps/p2p-chat/src/ChatApp.tsx @@ -0,0 +1,154 @@ +import React, {useCallback, useState} from 'react' +import {LogBox, View} from 'react-native' + +import { + ChatHeader, + DebugInfo, + FriendRequestsList, + MessageInput, + MessagesList, + NotificationsList, + TargetSelector, +} from './components' +import { + useCommunication, + useFriendRequests, + useMessages, + useTargetSelection, +} from './hooks' +import {commonStyles} from './styles' +import {ChatAppProps, MessageData} from './types' + +LogBox.ignoreAllLogs() + +const ChatApp: React.FC = ({ + userId, + userName, + targetOptions, + potentialFriends, + pendingRequests, + backgroundColor, +}) => { + const [isConnected, setIsConnected] = useState(false) + + const {sendMessage, sendConnectionMessage} = useCommunication({ + userName, + userId, + onMessage: handleIncomingMessage, + onConnectionEstablished: () => { + const allTargets = [ + ...targetOptions, + ...potentialFriends.map(pf => pf.id), + ] + sendConnectionMessage(allTargets) + }, + }) + + const {selectedTarget, setSelectedTarget} = useTargetSelection({ + targetOptions, + potentialFriends, + }) + + const { + messages, + inputText, + setInputText, + sendChatMessage, + handleIncomingMessage: handleMessageIncoming, + } = useMessages({ + userId, + userName, + onSendMessage: (message: MessageData) => { + const success = sendMessage(message) + if (success) { + setIsConnected(true) + } + return success + }, + }) + + const { + friendNotifications, + clearNotification, + respondToFriendRequest, + handleFriendMessage, + sendFriendRequest, + } = useFriendRequests({ + userName, + onSendMessage: sendMessage, + }) + + function handleIncomingMessage(data: MessageData) { + console.log(`[${userName}] Processing message type: ${data.type}`) + + handleMessageIncoming(data) + handleFriendMessage(data) + if ( + data.type === 'chat_message' || + data.type === 'connection_established' + ) { + setIsConnected(true) + } + } + + const handleSendMessage = useCallback(() => { + sendChatMessage(selectedTarget) + }, [sendChatMessage, selectedTarget]) + + const handleTargetSelect = useCallback( + (target: string) => { + setSelectedTarget(target) + }, + [setSelectedTarget] + ) + + const handleSendFriendRequest = useCallback( + (targetId: string) => { + sendFriendRequest(targetId) + }, + [sendFriendRequest] + ) + + return ( + + + + + + + + + + + + + + + + ) +} + +export default ChatApp diff --git a/apps/p2p-chat/src/components/ChatCarousel.tsx b/apps/p2p-chat/src/components/ChatCarousel.tsx new file mode 100644 index 0000000..facdea7 --- /dev/null +++ b/apps/p2p-chat/src/components/ChatCarousel.tsx @@ -0,0 +1,174 @@ +import React, {useRef} from 'react' +import { + NativeScrollEvent, + NativeSyntheticEvent, + ScrollView, + Text, + TouchableOpacity, + View, +} from 'react-native' +import SandboxReactNativeView from '@callstack/react-native-sandbox' + +import {ChatInstance, MAX_CHAT_INSTANCES} from '../constants' +import {FriendshipManager, MessageHandler} from '../services' +import {carouselStyles} from '../styles' +import {getChatHelpers} from '../utils/chatHelpers' + +interface ChatCarouselProps { + chatInstances: ChatInstance[] + currentIndex: number + friendshipTrigger: number + friendshipManager: FriendshipManager + sandboxRefs: React.MutableRefObject> + messageHandler: MessageHandler + onScroll: (event: NativeSyntheticEvent) => void + onRemoveChatInstance: (chatId: string) => void + onAddChatInstance: () => void +} + +export const ChatCarousel: React.FC = ({ + chatInstances, + currentIndex, + friendshipTrigger, + friendshipManager, + sandboxRefs, + messageHandler, + onScroll, + onRemoveChatInstance, + onAddChatInstance, +}) => { + const scrollViewRef = useRef(null) + const {getTargetOptions, getPotentialFriends, getPendingRequests} = + getChatHelpers(chatInstances, friendshipManager) + + const canAddMore = chatInstances.length < MAX_CHAT_INSTANCES + + return ( + <> + + + {chatInstances.map((chat, index) => ( + + + onRemoveChatInstance(chat.id)} + disabled={chatInstances.length <= 1}> + + ร— + + + + + + {chat.userName} ({chat.id}) + + + Friends: {getTargetOptions(chat.id).join(', ') || 'None'} + + + messageHandler.testHostToSandboxCommunication(chat.id) + }> + Test + + + + + + { + if (ref) { + sandboxRefs.current[chat.id] = ref + console.log( + `[Host] Registered sandbox ref for ${chat.id}. Active refs:`, + Object.keys(sandboxRefs.current) + ) + } else { + console.log(`[Host] Removing sandbox ref for ${chat.id}`) + delete sandboxRefs.current[chat.id] + } + }} + id={chat.id} + moduleName="ChatApp" + initialProperties={{ + userId: chat.id, + userName: chat.userName, + targetOptions: getTargetOptions(chat.id), + potentialFriends: getPotentialFriends(chat.id), + pendingRequests: getPendingRequests(chat.id), + backgroundColor: chat.backgroundColor, + friendshipTrigger: friendshipTrigger, + }} + onError={messageHandler.handleChatError(chat.userName)} + onMessage={messageHandler.handleChatMessage(chat.id)} + style={carouselStyles.sandbox} + /> + + + ))} + + + + + + + + + + Add Chat + + + {chatInstances.length} / {MAX_CHAT_INSTANCES} + + + + + + + + + {[...chatInstances, {id: 'add-button'}].map((_, index) => ( + + ))} + + + + + ๐Ÿ’ฌ Multi-Instance P2P Chat Demo{'\n'} + Swipe between instances โ€ข Add friends โ€ข Send messages{'\n'} + Each instance runs in an isolated React Native sandbox + + + + ) +} diff --git a/apps/p2p-chat/src/components/ChatHeader.tsx b/apps/p2p-chat/src/components/ChatHeader.tsx new file mode 100644 index 0000000..04c310c --- /dev/null +++ b/apps/p2p-chat/src/components/ChatHeader.tsx @@ -0,0 +1,49 @@ +import React from 'react' +import {StyleSheet, Text, View} from 'react-native' + +import {colors, commonStyles, spacing, typography} from '../styles/common' + +interface ChatHeaderProps { + userName: string + isConnected: boolean +} + +export const ChatHeader: React.FC = ({ + userName, + isConnected, +}) => { + return ( + + {userName} + + + ) +} + +const styles = StyleSheet.create({ + header: { + ...commonStyles.row, + justifyContent: 'center', + padding: spacing.md, + borderBottomWidth: 1, + borderBottomColor: colors.border, + }, + headerTitle: { + fontSize: typography.sizes.xl, + fontWeight: typography.weights.bold, + color: colors.text.primary, + }, + statusDot: { + width: 6, + height: 6, + borderRadius: 3, + marginLeft: 6, + }, +}) diff --git a/apps/p2p-chat/src/components/DebugInfo.tsx b/apps/p2p-chat/src/components/DebugInfo.tsx new file mode 100644 index 0000000..b68529f --- /dev/null +++ b/apps/p2p-chat/src/components/DebugInfo.tsx @@ -0,0 +1,40 @@ +import React from 'react' +import {StyleSheet, Text, View} from 'react-native' + +import {colors, spacing, typography} from '../styles/common' + +interface DebugInfoProps { + userId: string + targetOptions: string[] + potentialFriendsCount: number + messageCount: number +} + +export const DebugInfo: React.FC = ({ + userId, + targetOptions, + potentialFriendsCount, + messageCount, +}) => { + return ( + + + ID: {userId} โ€ข Friends: [{targetOptions.join(', ')}] โ€ข Potential:{' '} + {potentialFriendsCount} โ€ข Messages: {messageCount} + + + ) +} + +const styles = StyleSheet.create({ + debugInfo: { + padding: spacing.sm, + backgroundColor: 'rgba(0,0,0,0.05)', + }, + debugText: { + fontSize: typography.sizes.xs, + color: colors.text.secondary, + textAlign: 'center', + fontFamily: 'monospace', + }, +}) diff --git a/apps/p2p-chat/src/components/FriendRequestsList.tsx b/apps/p2p-chat/src/components/FriendRequestsList.tsx new file mode 100644 index 0000000..897d32a --- /dev/null +++ b/apps/p2p-chat/src/components/FriendRequestsList.tsx @@ -0,0 +1,99 @@ +import React from 'react' +import {StyleSheet, Text, TouchableOpacity, View} from 'react-native' + +import { + buttonStyles, + colors, + commonStyles, + spacing, + typography, +} from '../styles/common' +import {FriendAction, FriendRequest} from '../types/friends' + +interface FriendRequestsListProps { + pendingRequests: FriendRequest[] + onRespondToRequest: (requestId: string, action: FriendAction) => void +} + +export const FriendRequestsList: React.FC = ({ + pendingRequests, + onRespondToRequest, +}) => { + if (pendingRequests.length === 0) { + return null + } + + return ( + + Friend Requests + {pendingRequests.map(request => ( + + + {request.from} wants to be friends + + + onRespondToRequest(request.id, 'accept')}> + Accept + + onRespondToRequest(request.id, 'reject')}> + Reject + + + + ))} + + ) +} + +const styles = StyleSheet.create({ + container: { + padding: spacing.md, + borderBottomWidth: 1, + borderBottomColor: colors.border, + backgroundColor: 'rgba(255,255,255,0.8)', + }, + title: { + fontSize: typography.sizes.lg, + fontWeight: typography.weights.semibold, + color: colors.text.primary, + marginBottom: spacing.sm, + }, + friendRequest: { + ...commonStyles.row, + justifyContent: 'space-between', + backgroundColor: '#f0f0f0', + padding: spacing.sm + 2, // 10px + ...commonStyles.rounded, + marginBottom: spacing.sm, + ...commonStyles.border, + }, + friendRequestText: { + flex: 1, + fontSize: typography.sizes.md + 1, // 13px + color: colors.text.primary, + marginRight: spacing.sm + 2, // 10px + }, + buttonContainer: { + flexDirection: 'row', + }, + actionButton: { + paddingHorizontal: spacing.md, + paddingVertical: 6, + marginLeft: spacing.sm, + }, + buttonText: { + fontSize: typography.sizes.md, + }, +}) diff --git a/apps/p2p-chat/src/components/MessageInput.tsx b/apps/p2p-chat/src/components/MessageInput.tsx new file mode 100644 index 0000000..7391fed --- /dev/null +++ b/apps/p2p-chat/src/components/MessageInput.tsx @@ -0,0 +1,86 @@ +import React from 'react' +import {StyleSheet, Text, TextInput, TouchableOpacity, View} from 'react-native' + +import {buttonStyles, colors, spacing, typography} from '../styles/common' + +interface MessageInputProps { + inputText: string + selectedTarget: string + onInputChange: (text: string) => void + onSendMessage: () => void +} + +export const MessageInput: React.FC = ({ + inputText, + selectedTarget, + onInputChange, + onSendMessage, +}) => { + const canSend = inputText.trim() && selectedTarget.trim() + + const handleSubmit = () => { + if (canSend) { + onSendMessage() + } + } + + return ( + + + + Send + + + ) +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + padding: spacing.md, + borderTopWidth: 1, + borderTopColor: colors.border, + backgroundColor: 'rgba(255,255,255,0.8)', + }, + textInput: { + flex: 1, + borderWidth: 1, + borderColor: 'rgba(0,0,0,0.2)', + borderRadius: 18, + paddingHorizontal: spacing.md, + paddingVertical: spacing.sm, + marginRight: spacing.sm, + maxHeight: 80, + backgroundColor: colors.surface, + fontSize: typography.sizes.lg, + }, + sendButton: { + borderRadius: 18, + paddingHorizontal: spacing.lg, + paddingVertical: spacing.sm + 2, // 10px + }, + sendButtonText: { + fontSize: typography.sizes.lg, + }, +}) diff --git a/apps/p2p-chat/src/components/MessagesList.tsx b/apps/p2p-chat/src/components/MessagesList.tsx new file mode 100644 index 0000000..424443e --- /dev/null +++ b/apps/p2p-chat/src/components/MessagesList.tsx @@ -0,0 +1,158 @@ +import React, {useEffect, useRef} from 'react' +import {ScrollView, StyleSheet, Text, View} from 'react-native' + +import {colors, commonStyles, spacing, typography} from '../styles/common' +import {Message} from '../types/chat' + +interface MessagesListProps { + messages: Message[] + selectedTarget: string +} + +export const MessagesList: React.FC = ({ + messages, + selectedTarget, +}) => { + const scrollRef = useRef(null) + + useEffect(() => { + // Auto-scroll to bottom when new messages arrive + if (messages.length > 0) { + setTimeout(() => { + scrollRef.current?.scrollToEnd({animated: true}) + }, 100) + } + }, [messages]) + + const formatTime = (timestamp: number) => { + return new Date(timestamp).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + }) + } + + return ( + + {messages.length === 0 && ( + + + {selectedTarget + ? `Send a message to start chatting with ${selectedTarget}!` + : 'Select or enter a target to start messaging!'} + + + )} + + {messages.map(message => ( + + {message.type === 'error' && ( + + โš ๏ธ + + )} + + {message.text} + + + {message.sender} โ€ข {formatTime(message.timestamp)} + + + ))} + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: spacing.md, + }, + emptyState: { + flex: 1, + ...commonStyles.centered, + padding: spacing.xl, + }, + emptyText: { + fontSize: typography.sizes.md + 1, // 13px + color: colors.text.secondary, + textAlign: 'center', + fontStyle: 'italic', + }, + messageBubble: { + marginVertical: 3, + padding: spacing.sm + 2, // 10px + borderRadius: 14, + maxWidth: '80%', + }, + sentMessage: { + alignSelf: 'flex-end', + backgroundColor: colors.primary, + }, + receivedMessage: { + alignSelf: 'flex-start', + backgroundColor: colors.surface, + ...commonStyles.border, + }, + errorMessage: { + alignSelf: 'flex-start', + backgroundColor: '#ffebee', // Light red background + borderWidth: 1, + borderColor: colors.error, // Red border + }, + errorIcon: { + alignSelf: 'flex-start', + marginLeft: 5, + marginTop: -5, + backgroundColor: colors.error, + borderRadius: 8, + padding: 2, + }, + errorIconText: { + fontSize: typography.sizes.lg, + color: colors.text.white, + }, + messageText: { + fontSize: typography.sizes.lg, + color: colors.text.primary, + marginBottom: 3, + }, + sentMessageText: { + color: colors.text.white, + }, + errorMessageText: { + color: '#d32f2f', // Darker red for error text + }, + messageTime: { + fontSize: typography.sizes.sm, + color: colors.text.secondary, + fontWeight: typography.weights.medium, + }, + sentMessageTime: { + color: 'rgba(255,255,255,0.8)', + }, + errorMessageTime: { + color: colors.error, // Red for error time + }, +}) diff --git a/apps/p2p-chat/src/components/NotificationsList.tsx b/apps/p2p-chat/src/components/NotificationsList.tsx new file mode 100644 index 0000000..094a5c7 --- /dev/null +++ b/apps/p2p-chat/src/components/NotificationsList.tsx @@ -0,0 +1,60 @@ +import React from 'react' +import {StyleSheet, Text, TouchableOpacity, View} from 'react-native' + +import {colors, commonStyles, spacing, typography} from '../styles/common' + +interface NotificationsListProps { + notifications: string[] + onClearNotification: (index: number) => void +} + +export const NotificationsList: React.FC = ({ + notifications, + onClearNotification, +}) => { + if (notifications.length === 0) { + return null + } + + return ( + + {notifications.map((notification, index) => ( + + {notification} + onClearNotification(index)}> + ร— + + + ))} + + ) +} + +const styles = StyleSheet.create({ + container: { + padding: spacing.md, + borderBottomWidth: 1, + borderBottomColor: colors.border, + backgroundColor: 'rgba(255,255,255,0.8)', + }, + notification: { + ...commonStyles.row, + justifyContent: 'space-between', + backgroundColor: '#f0f0f0', + padding: spacing.sm + 2, // 10px + ...commonStyles.rounded, + marginBottom: spacing.sm, + ...commonStyles.border, + }, + notificationText: { + flex: 1, + fontSize: typography.sizes.md + 1, // 13px + color: colors.text.primary, + marginRight: spacing.sm + 2, // 10px + }, + notificationClose: { + fontSize: typography.sizes.xl, + color: colors.text.secondary, + fontWeight: typography.weights.bold, + }, +}) diff --git a/apps/p2p-chat/src/components/TargetSelector.tsx b/apps/p2p-chat/src/components/TargetSelector.tsx new file mode 100644 index 0000000..2f3333d --- /dev/null +++ b/apps/p2p-chat/src/components/TargetSelector.tsx @@ -0,0 +1,182 @@ +import React from 'react' +import { + ScrollView, + StyleSheet, + Text, + TextInput, + TouchableOpacity, + View, +} from 'react-native' + +import {colors, commonStyles, spacing, typography} from '../styles/common' +import {PotentialFriend} from '../types/friends' + +interface TargetSelectorProps { + targetOptions: string[] + potentialFriends: PotentialFriend[] + selectedTarget: string + onTargetSelect: (target: string) => void + onTargetChange: (target: string) => void + onSendFriendRequest: (targetId: string) => void +} + +export const TargetSelector: React.FC = ({ + targetOptions, + potentialFriends, + selectedTarget, + onTargetSelect, + onTargetChange, + onSendFriendRequest, +}) => { + const allPossibleTargets = [ + ...targetOptions, // Current friends + ...potentialFriends.map(pf => pf.id), // Potential friends + ] + + const handleTargetPress = (target: string) => { + if (targetOptions.includes(target)) { + // This is a friend - select for messaging + onTargetSelect(target) + } else { + // This is a potential friend - send friend request + onSendFriendRequest(target) + } + } + + return ( + <> + {allPossibleTargets.length > 0 ? ( + + Friends & Available Users: + + {allPossibleTargets.map(target => { + const isFriend = targetOptions.includes(target) + const isSelected = selectedTarget === target + return ( + handleTargetPress(target)}> + + {isFriend ? '๐Ÿ‘ซ' : '๐Ÿ‘ฅ'} {target} + {!isFriend && ( + (tap to add) + )} + + + ) + })} + + + ) : ( + + + Type a target manually to start chatting! + + + )} + + + Or send to: + + + + ) +} + +const styles = StyleSheet.create({ + targetSelector: { + padding: spacing.md, + borderBottomWidth: 1, + borderBottomColor: colors.border, + backgroundColor: 'rgba(255,255,255,0.5)', + }, + targetLabel: { + fontSize: typography.sizes.md, + fontWeight: typography.weights.semibold, + color: colors.text.secondary, + marginBottom: 6, + }, + targetButton: { + backgroundColor: '#f0f0f0', + paddingHorizontal: spacing.md, + paddingVertical: 6, + borderRadius: 12, + marginRight: spacing.sm, + ...commonStyles.border, + }, + selectedTargetButton: { + backgroundColor: colors.primary, + borderColor: colors.primary, + }, + targetButtonText: { + fontSize: typography.sizes.md, + fontWeight: typography.weights.medium, + color: colors.text.primary, + }, + selectedTargetButtonText: { + color: colors.text.white, + fontWeight: typography.weights.semibold, + }, + potentialFriendButton: { + backgroundColor: '#fff3cd', + borderColor: '#ffc107', + }, + potentialFriendButtonText: { + color: '#856404', + }, + actionHint: { + fontSize: typography.sizes.sm, + fontStyle: 'italic', + opacity: 0.8, + }, + noTargetsContainer: { + padding: spacing.md, + borderBottomWidth: 1, + borderBottomColor: colors.border, + backgroundColor: 'rgba(255,193,7,0.1)', + }, + noTargetsText: { + fontSize: typography.sizes.md, + color: colors.warning, + textAlign: 'center', + fontStyle: 'italic', + }, + manualTargetContainer: { + padding: spacing.md, + borderBottomWidth: 1, + borderBottomColor: colors.border, + backgroundColor: 'rgba(255,255,255,0.8)', + }, + manualTargetLabel: { + fontSize: typography.sizes.md, + fontWeight: typography.weights.semibold, + color: colors.text.secondary, + marginBottom: 6, + }, + manualTargetInput: { + borderWidth: 1, + borderColor: 'rgba(0,0,0,0.2)', + borderRadius: 12, + paddingHorizontal: spacing.sm + 2, // 10px + paddingVertical: spacing.sm, + fontSize: typography.sizes.lg, + backgroundColor: colors.surface, + }, +}) diff --git a/apps/p2p-chat/src/components/index.ts b/apps/p2p-chat/src/components/index.ts new file mode 100644 index 0000000..0f9fe09 --- /dev/null +++ b/apps/p2p-chat/src/components/index.ts @@ -0,0 +1,8 @@ +export {ChatCarousel} from './ChatCarousel' +export {ChatHeader} from './ChatHeader' +export {DebugInfo} from './DebugInfo' +export {FriendRequestsList} from './FriendRequestsList' +export {MessageInput} from './MessageInput' +export {MessagesList} from './MessagesList' +export {NotificationsList} from './NotificationsList' +export {TargetSelector} from './TargetSelector' diff --git a/apps/p2p-chat/src/constants/index.ts b/apps/p2p-chat/src/constants/index.ts new file mode 100644 index 0000000..008bc5d --- /dev/null +++ b/apps/p2p-chat/src/constants/index.ts @@ -0,0 +1,7 @@ +export type {ChatInstance} from './presets' +export { + CHAT_WIDTH, + MAX_CHAT_INSTANCES, + MIN_CHAT_INSTANCES, + PRESET_USERS, +} from './presets' diff --git a/apps/p2p-chat/src/constants/presets.ts b/apps/p2p-chat/src/constants/presets.ts new file mode 100644 index 0000000..daf1776 --- /dev/null +++ b/apps/p2p-chat/src/constants/presets.ts @@ -0,0 +1,23 @@ +import {Dimensions} from 'react-native' + +const {width: screenWidth} = Dimensions.get('window') + +export const CHAT_WIDTH = screenWidth - 40 // Account for padding + +export const PRESET_USERS = [ + {name: 'Alice', color: '#e3f2fd'}, + {name: 'Bob', color: '#f3e5f5'}, + {name: 'Charlie', color: '#e8f5e8'}, + {name: 'Diana', color: '#fff3e0'}, + {name: 'Eve', color: '#fce4ec'}, + {name: 'Frank', color: '#e0f2f1'}, +] as const + +export const MAX_CHAT_INSTANCES = PRESET_USERS.length +export const MIN_CHAT_INSTANCES = 1 + +export interface ChatInstance { + id: string + userName: string + backgroundColor: string +} diff --git a/apps/p2p-chat/src/hooks/index.ts b/apps/p2p-chat/src/hooks/index.ts new file mode 100644 index 0000000..e58cc6d --- /dev/null +++ b/apps/p2p-chat/src/hooks/index.ts @@ -0,0 +1,5 @@ +export {useChatInstances} from './useChatInstances' +export {useCommunication} from './useCommunication' +export {useFriendRequests} from './useFriendRequests' +export {useMessages} from './useMessages' +export {useTargetSelection} from './useTargetSelection' diff --git a/apps/p2p-chat/src/hooks/useChatInstances.ts b/apps/p2p-chat/src/hooks/useChatInstances.ts new file mode 100644 index 0000000..3b9b596 --- /dev/null +++ b/apps/p2p-chat/src/hooks/useChatInstances.ts @@ -0,0 +1,70 @@ +import {useCallback, useRef, useState} from 'react' + +import {ChatInstance, MAX_CHAT_INSTANCES, PRESET_USERS} from '../constants' +import {FriendshipManager} from '../services' +import {createChatInstance} from '../utils/chatHelpers' + +export const useChatInstances = () => { + const [chatInstances, setChatInstances] = useState([ + {id: 'alice', userName: 'Alice', backgroundColor: '#e3f2fd'}, + {id: 'bob', userName: 'Bob', backgroundColor: '#f3e5f5'}, + ]) + const [currentIndex, setCurrentIndex] = useState(0) + const [friendshipTrigger, setFriendshipTrigger] = useState(0) + const friendshipManager = useRef(new FriendshipManager()).current + + const addChatInstance = useCallback(() => { + if (chatInstances.length >= MAX_CHAT_INSTANCES) { + console.log('Maximum number of chat instances reached') + return + } + + // Find the next available preset user + const usedNames = new Set(chatInstances.map(chat => chat.userName)) + const availableUser = PRESET_USERS.find(user => !usedNames.has(user.name)) + + if (!availableUser) { + console.log('No more preset users available') + return + } + + const newInstance = createChatInstance( + availableUser.name, + availableUser.color + ) + setChatInstances(prev => [...prev, newInstance]) + + console.log(`[Host] Added new chat instance: ${newInstance.userName}`) + }, [chatInstances]) + + const removeChatInstance = useCallback( + (chatId: string) => { + if (chatInstances.length <= 1) { + console.log('Cannot remove the last chat instance') + return + } + + setChatInstances(prev => prev.filter(chat => chat.id !== chatId)) + console.log(`[Host] Removed chat instance: ${chatId}`) + + // Trigger friendship updates since instances changed + setFriendshipTrigger(prev => prev + 1) + }, + [chatInstances.length] + ) + + const triggerFriendshipUpdate = useCallback(() => { + setFriendshipTrigger(prev => prev + 1) + }, []) + + return { + chatInstances, + currentIndex, + setCurrentIndex, + friendshipTrigger, + friendshipManager, + addChatInstance, + removeChatInstance, + triggerFriendshipUpdate, + } +} diff --git a/apps/p2p-chat/src/hooks/useCommunication.ts b/apps/p2p-chat/src/hooks/useCommunication.ts new file mode 100644 index 0000000..93f0710 --- /dev/null +++ b/apps/p2p-chat/src/hooks/useCommunication.ts @@ -0,0 +1,104 @@ +import {useCallback, useEffect} from 'react' + +import {MessageData} from '../types' + +// Global function declarations for sandbox environment +declare global { + var setOnMessage: (callback: (data: any) => void) => void + var postMessage: (message: any, targetOrigin?: string) => void +} + +interface UseCommunicationProps { + userName: string + userId: string + onMessage: (data: MessageData) => void + onConnectionEstablished?: () => void +} + +export const useCommunication = ({ + userName, + userId, + onMessage, + onConnectionEstablished, +}: UseCommunicationProps) => { + useEffect(() => { + // Set up message listener for P2P communication + if (global.setOnMessage) { + console.log( + `[${userName}] global.setOnMessage is available, setting up listener` + ) + global.setOnMessage((data: any) => { + console.log(`[${userName}] ๐Ÿ”ฅ MESSAGE RECEIVED:`, data) + + if (!data || !data.type) { + console.warn(`[${userName}] Received message without type:`, data) + return + } + + console.log(`[${userName}] Processing message type: ${data.type}`) + onMessage(data) + }) + + console.log(`[${userName}] โœ… Message listener setup complete`) + } else { + console.warn(`[${userName}] โŒ global.setOnMessage is not available!`) + console.log( + `[${userName}] Available global functions:`, + Object.keys(global).filter( + key => typeof (global as any)[key] === 'function' + ) + ) + } + + // Send initial connection message if callback provided + if (onConnectionEstablished) { + onConnectionEstablished() + } + + // Announce readiness + console.log( + `[${userName}] ๐Ÿ“ข Sandbox initialization complete and ready to receive messages` + ) + }, [userName, onMessage, onConnectionEstablished]) + + const sendMessage = useCallback( + (message: MessageData, targetOrigin?: string) => { + if (global.postMessage) { + try { + global.postMessage(message, targetOrigin) + return true + } catch (error) { + console.error(`[${userName}] Failed to send message:`, error) + return false + } + } + return false + }, + [userName] + ) + + const sendConnectionMessage = useCallback( + (targets: string[]) => { + if (global.postMessage && targets.length > 0) { + targets.forEach(targetId => { + global.postMessage!( + { + type: 'connection_established', + senderId: userId, + senderName: userName, + timestamp: Date.now(), + }, + targetId + ) + }) + } + }, + [userId, userName] + ) + + return { + sendMessage, + sendConnectionMessage, + isReady: !!global.postMessage, + } +} diff --git a/apps/p2p-chat/src/hooks/useFriendRequests.ts b/apps/p2p-chat/src/hooks/useFriendRequests.ts new file mode 100644 index 0000000..0c3791d --- /dev/null +++ b/apps/p2p-chat/src/hooks/useFriendRequests.ts @@ -0,0 +1,117 @@ +import {useCallback, useState} from 'react' + +import {FriendAction, MessageData} from '../types' + +interface UseFriendRequestsProps { + userName: string + onSendMessage: (message: MessageData) => boolean +} + +export const useFriendRequests = ({ + userName, + onSendMessage, +}: UseFriendRequestsProps) => { + const [friendNotifications, setFriendNotifications] = useState([]) + + const clearNotification = useCallback((index: number) => { + setFriendNotifications(prev => prev.filter((_, i) => i !== index)) + }, []) + + const addNotification = useCallback((notification: string) => { + setFriendNotifications(prev => [...prev, notification]) + }, []) + + const sendFriendRequest = useCallback( + (targetId: string) => { + const success = onSendMessage({ + type: 'make_friend', + target: targetId, + timestamp: Date.now(), + }) + + if (success) { + console.log( + `[${userName}] Friend request sent to host for routing to ${targetId}` + ) + } + }, + [userName, onSendMessage] + ) + + const respondToFriendRequest = useCallback( + (requestId: string, action: FriendAction) => { + console.log( + `[${userName}] Responding to friend request ${requestId}: ${action}` + ) + + const success = onSendMessage({ + type: 'friend_response', + requestId, + action, + timestamp: Date.now(), + }) + + if (success) { + console.log(`[${userName}] Friend response sent to host`) + } + }, + [userName, onSendMessage] + ) + + const handleFriendMessage = useCallback( + (data: MessageData) => { + switch (data.type) { + case 'friend_request': { + if (data.fromName) { + console.log( + `[${userName}] ๐Ÿšจ PROCESSING FRIEND REQUEST from ${data.fromName} (${data.from})` + ) + addNotification(`Friend request from ${data.fromName}`) + console.log(`[${userName}] Friend request notification added`) + } + break + } + + case 'friend_accepted': { + if (data.friendName) { + console.log( + `[${userName}] Friend request accepted by ${data.friendName}` + ) + addNotification(`${data.friendName} accepted your friend request!`) + } + break + } + + case 'friendship_established': { + if (data.friendName) { + console.log( + `[${userName}] Friendship established with ${data.friendName}` + ) + addNotification(`You are now friends with ${data.friendName}!`) + } + break + } + + case 'test_message': { + if (data.message) { + console.log( + `[${userName}] ๐Ÿงช RECEIVED TEST MESSAGE from host: ${data.message}` + ) + addNotification(`Test: ${data.message}`) + } + break + } + } + }, + [userName, addNotification] + ) + + return { + friendNotifications, + clearNotification, + addNotification, + sendFriendRequest, + respondToFriendRequest, + handleFriendMessage, + } +} diff --git a/apps/p2p-chat/src/hooks/useMessages.ts b/apps/p2p-chat/src/hooks/useMessages.ts new file mode 100644 index 0000000..67a8338 --- /dev/null +++ b/apps/p2p-chat/src/hooks/useMessages.ts @@ -0,0 +1,116 @@ +import {useCallback, useState} from 'react' + +import {Message, MessageData} from '../types' + +interface UseMessagesProps { + userId: string + userName: string + onSendMessage: (message: MessageData) => boolean +} + +export const useMessages = ({ + userId, + userName, + onSendMessage, +}: UseMessagesProps) => { + const [messages, setMessages] = useState([]) + const [inputText, setInputText] = useState('') + + const addMessage = useCallback((message: Message) => { + setMessages(prev => [...prev, message]) + }, []) + + const sendChatMessage = useCallback( + (target: string) => { + if (!inputText.trim() || !target.trim()) return + + const messageId = `${userId}_${Date.now()}` + const timestamp = Date.now() + + // Add to local messages + const newMessage: Message = { + id: messageId, + text: inputText.trim(), + sender: userName, + timestamp, + type: 'sent', + } + + addMessage(newMessage) + + // Send P2P message to selected target sandbox via host + const success = onSendMessage({ + type: 'chat_message', + messageId, + text: inputText.trim(), + senderId: userId, + senderName: userName, + timestamp, + target: target.trim(), + }) + + if (!success) { + // Add a local error message + const errorMessage: Message = { + id: `error_${Date.now()}`, + text: `Failed to send message: Communication error`, + sender: 'System', + timestamp: Date.now(), + type: 'error', + isError: true, + errorReason: 'send_failed', + } + addMessage(errorMessage) + } + + setInputText('') + }, + [inputText, userId, userName, onSendMessage, addMessage] + ) + + const handleIncomingMessage = useCallback( + (data: MessageData) => { + switch (data.type) { + case 'chat_message': { + if (data.messageId && data.text && data.senderName) { + const newMessage: Message = { + id: data.messageId, + text: data.text, + sender: data.senderName, + timestamp: data.timestamp, + type: 'received', + } + addMessage(newMessage) + } + break + } + + case 'message_error': { + if (data.errorText) { + const errorMessage: Message = { + id: `error_${Date.now()}`, + text: data.errorText, + sender: 'System', + timestamp: data.timestamp, + type: 'error', + isError: true, + errorReason: data.reason, + } + addMessage(errorMessage) + } + break + } + } + }, + [addMessage] + ) + + return { + messages, + inputText, + setInputText, + sendChatMessage, + handleIncomingMessage, + addMessage, + } +} diff --git a/apps/p2p-chat/src/hooks/useTargetSelection.ts b/apps/p2p-chat/src/hooks/useTargetSelection.ts new file mode 100644 index 0000000..4d3a8d7 --- /dev/null +++ b/apps/p2p-chat/src/hooks/useTargetSelection.ts @@ -0,0 +1,50 @@ +import {useEffect, useMemo, useState} from 'react' + +import {PotentialFriend} from '../types' + +interface UseTargetSelectionProps { + targetOptions: string[] + potentialFriends: PotentialFriend[] +} + +export const useTargetSelection = ({ + targetOptions, + potentialFriends, +}: UseTargetSelectionProps) => { + const [selectedTarget, setSelectedTarget] = useState('') + + // Create a list of all possible targets (friends + potential friends) + const allPossibleTargets = useMemo( + () => [ + ...targetOptions, // Current friends + ...potentialFriends.map(pf => pf.id), // Potential friends + ], + [targetOptions, potentialFriends] + ) + + useEffect(() => { + // Update selected target if current one is no longer available + if ( + allPossibleTargets.length > 0 && + !allPossibleTargets.includes(selectedTarget) + ) { + setSelectedTarget(allPossibleTargets[0]) + } else if (allPossibleTargets.length === 0 && selectedTarget) { + // Keep the selected target even if no options available + // This allows users to manually type or remember target IDs + } + }, [targetOptions, potentialFriends, selectedTarget, allPossibleTargets]) + + // Initialize with first available target + useEffect(() => { + if (!selectedTarget && allPossibleTargets.length > 0) { + setSelectedTarget(allPossibleTargets[0]) + } + }, [allPossibleTargets, selectedTarget]) + + return { + selectedTarget, + setSelectedTarget, + allPossibleTargets, + } +} diff --git a/apps/p2p-chat/src/services/FriendshipManager.ts b/apps/p2p-chat/src/services/FriendshipManager.ts new file mode 100644 index 0000000..9daab45 --- /dev/null +++ b/apps/p2p-chat/src/services/FriendshipManager.ts @@ -0,0 +1,87 @@ +import {FriendRequest} from '../types' + +export class FriendshipManager { + private friendships = new Set() // "alice-bob" format (sorted) + private pendingRequests = new Map() + private requestCounter = 0 + + private createFriendshipKey(user1: string, user2: string): string { + return [user1, user2].sort().join('-') + } + + sendFriendRequest(from: string, to: string): string { + const requestId = `req_${++this.requestCounter}_${Date.now()}` + const request: FriendRequest = { + id: requestId, + from, + to, + timestamp: Date.now(), + status: 'pending', + } + + this.pendingRequests.set(requestId, request) + console.log( + `[FriendshipManager] Friend request sent: ${from} โ†’ ${to} (${requestId})` + ) + return requestId + } + + respondToRequest( + requestId: string, + action: 'accept' | 'reject' + ): FriendRequest | null { + const request = this.pendingRequests.get(requestId) + if (!request) { + console.warn(`[FriendshipManager] Request ${requestId} not found`) + return null + } + + request.status = action === 'accept' ? 'accepted' : 'rejected' + + if (action === 'accept') { + const friendshipKey = this.createFriendshipKey(request.from, request.to) + this.friendships.add(friendshipKey) + console.log( + `[FriendshipManager] Friendship established: ${request.from} โ†” ${request.to}` + ) + } + + this.pendingRequests.delete(requestId) + return request + } + + canMessage(from: string, to: string): boolean { + const friendshipKey = this.createFriendshipKey(from, to) + const canSend = this.friendships.has(friendshipKey) + console.log(`[FriendshipManager] Can ${from} message ${to}? ${canSend}`) + return canSend + } + + getFriends(userId: string): string[] { + const friends: string[] = [] + for (const friendship of this.friendships) { + const [user1, user2] = friendship.split('-') + if (user1 === userId) friends.push(user2) + if (user2 === userId) friends.push(user1) + } + return friends + } + + getPendingRequestsFor(userId: string): FriendRequest[] { + return Array.from(this.pendingRequests.values()).filter( + req => req.to === userId && req.status === 'pending' + ) + } + + hasPendingRequestBetween(user1: string, user2: string): boolean { + return Array.from(this.pendingRequests.values()).some( + req => + (req.from === user1 && req.to === user2) || + (req.from === user2 && req.to === user1) + ) + } + + areFriends(user1: string, user2: string): boolean { + return this.friendships.has(this.createFriendshipKey(user1, user2)) + } +} diff --git a/apps/p2p-chat/src/services/MessageHandler.ts b/apps/p2p-chat/src/services/MessageHandler.ts new file mode 100644 index 0000000..b1ddfb1 --- /dev/null +++ b/apps/p2p-chat/src/services/MessageHandler.ts @@ -0,0 +1,189 @@ +import {ChatInstance} from '../constants' +import {FriendshipManager} from './FriendshipManager' + +export class MessageHandler { + constructor( + private chatInstances: ChatInstance[], + private friendshipManager: FriendshipManager, + private sandboxRefs: React.MutableRefObject>, + private triggerFriendshipUpdate: () => void + ) {} + + handleChatError = (chatId: string) => (error: any) => { + console.log(`[${chatId}] Error:`, error) + } + + handleChatMessage = (chatId: string) => (data: any) => { + console.log(`[${chatId}] Received message from sandbox:`, data) + + if (!data.type) { + console.warn(`[Host] Message from ${chatId} missing type:`, data) + return + } + + switch (data.type) { + case 'make_friend': + this.handleMakeFriend(chatId, data) + break + case 'friend_response': + this.handleFriendResponse(chatId, data) + break + case 'chat_message': + this.handleForwardMessage(chatId, data) + break + default: + console.log(`[Host] Unknown message type from ${chatId}:`, data.type) + } + } + + private handleMakeFriend(chatId: string, data: any) { + const {target} = data + if (!target) { + console.warn(`[Host] make_friend message from ${chatId} missing target`) + return + } + + const targetInstance = this.chatInstances.find(inst => inst.id === target) + if (!targetInstance) { + console.warn( + `[Host] Target ${target} not found for friend request from ${chatId}` + ) + return + } + + if (this.friendshipManager.areFriends(chatId, target)) { + console.log(`[Host] ${chatId} and ${target} are already friends`) + return + } + + if (this.friendshipManager.hasPendingRequestBetween(chatId, target)) { + console.log( + `[Host] Pending friend request already exists between ${chatId} and ${target}` + ) + return + } + + const requestId = this.friendshipManager.sendFriendRequest(chatId, target) + + setTimeout(() => { + const hostMessage = { + type: 'friend_request', + from: chatId, + fromName: + this.chatInstances.find(inst => inst.id === chatId)?.userName || + chatId, + requestId, + timestamp: Date.now(), + } + + this.sendToSandbox(target, hostMessage) + }, 500) + } + + private handleFriendResponse(chatId: string, data: any) { + const {requestId, action} = data + if (!requestId || !action) { + console.warn( + `[Host] friend_response from ${chatId} missing requestId or action` + ) + return + } + + const request = this.friendshipManager.respondToRequest(requestId, action) + if (!request) return + + this.triggerFriendshipUpdate() + + if (action === 'accept') { + // Notify the original requester + const acceptMessage = { + type: 'friend_accepted', + friend: chatId, + friendName: + this.chatInstances.find(inst => inst.id === chatId)?.userName || + chatId, + timestamp: Date.now(), + } + this.sendToSandbox(request.from, acceptMessage) + + // Notify both parties about established friendship + const friendshipMessage = { + type: 'friendship_established', + friendName: '', + timestamp: Date.now(), + } + + this.sendToSandbox(request.from, { + ...friendshipMessage, + friendName: + this.chatInstances.find(inst => inst.id === chatId)?.userName || + chatId, + }) + + this.sendToSandbox(chatId, { + ...friendshipMessage, + friendName: + this.chatInstances.find(inst => inst.id === request.from)?.userName || + request.from, + }) + } + } + + private handleForwardMessage(senderId: string, data: any) { + const {target, messageId, text, senderName, timestamp} = data + + if (!target || !messageId || !text || !senderName) { + console.warn( + `[Host] chat_message from ${senderId} missing required fields` + ) + return + } + + if (!this.friendshipManager.canMessage(senderId, target)) { + const errorMessage = { + type: 'message_error', + errorText: `Cannot send message to ${target}: not friends`, + reason: 'not_friends', + timestamp: Date.now(), + } + this.sendToSandbox(senderId, errorMessage) + return + } + + const forwardedMessage = { + type: 'chat_message', + messageId, + text, + senderId, + senderName, + timestamp, + } + + this.sendToSandbox(target, forwardedMessage) + } + + private sendToSandbox(targetId: string, message: any) { + const targetRef = this.sandboxRefs.current[targetId] + if (targetRef) { + try { + targetRef.postMessage(message) + console.log(`[Host] Message sent to ${targetId}:`, message.type) + } catch (error) { + console.error(`[Host] Error sending message to ${targetId}:`, error) + } + } else { + console.error(`[Host] Target ref for ${targetId} not found`) + } + } + + testHostToSandboxCommunication = (targetId: string) => { + const testMessage = { + type: 'test_message', + message: `Hello from host! Time: ${new Date().toLocaleTimeString()}`, + timestamp: Date.now(), + } + + console.log(`[Host] Sending test message to ${targetId}:`, testMessage) + this.sendToSandbox(targetId, testMessage) + } +} diff --git a/apps/p2p-chat/src/services/index.ts b/apps/p2p-chat/src/services/index.ts new file mode 100644 index 0000000..4243d9b --- /dev/null +++ b/apps/p2p-chat/src/services/index.ts @@ -0,0 +1,2 @@ +export {FriendshipManager} from './FriendshipManager' +export {MessageHandler} from './MessageHandler' diff --git a/apps/p2p-chat/src/styles/carousel.ts b/apps/p2p-chat/src/styles/carousel.ts new file mode 100644 index 0000000..cf5148d --- /dev/null +++ b/apps/p2p-chat/src/styles/carousel.ts @@ -0,0 +1,170 @@ +import {StyleSheet} from 'react-native' + +import {CHAT_WIDTH} from '../constants' + +export const carouselStyles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#f8f9fa', + }, + carouselContainer: { + flex: 1, + }, + carousel: { + flex: 1, + backgroundColor: '#e3f2fd', + }, + chatSlide: { + width: CHAT_WIDTH, + flex: 1, + marginHorizontal: 20, + backgroundColor: '#ffffff', + borderRadius: 12, + marginVertical: 20, + shadowColor: '#000', + shadowOffset: {width: 0, height: 4}, + shadowOpacity: 0.1, + shadowRadius: 8, + elevation: 4, + overflow: 'hidden', + }, + chatHeader: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: '#ffffff', + paddingHorizontal: 15, + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: 'rgba(0,0,0,0.1)', + minHeight: 60, + }, + chatHeaderContent: { + flex: 1, + alignItems: 'center', + }, + chatTitle: { + fontSize: 18, + fontWeight: 'bold', + color: '#1a1a1a', + marginBottom: 2, + }, + chatSubtitle: { + fontSize: 12, + color: '#666', + fontWeight: '500', + }, + chatContainer: { + flex: 1, + overflow: 'hidden', + }, + sandbox: { + flex: 1, + overflow: 'hidden', + }, + deleteButton: { + padding: 5, + borderRadius: 10, + backgroundColor: '#f44336', + width: 30, + height: 30, + justifyContent: 'center', + alignItems: 'center', + marginRight: 10, + }, + deleteButtonText: { + color: '#ffffff', + fontSize: 20, + fontWeight: 'bold', + margin: -10, + }, + deleteButtonDisabled: { + opacity: 0.5, + }, + addChatCard: { + flex: 1, + backgroundColor: '#ffffff', + borderRadius: 12, + padding: 40, + alignItems: 'center', + justifyContent: 'center', + shadowColor: '#000', + shadowOffset: {width: 0, height: 2}, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + borderWidth: 2, + borderColor: '#4caf50', + borderStyle: 'dashed', + }, + addChatContent: { + alignItems: 'center', + }, + addChatIcon: { + fontSize: 40, + color: '#4caf50', + marginBottom: 10, + }, + addChatIconDisabled: { + opacity: 0.5, + }, + addChatText: { + fontSize: 18, + fontWeight: 'bold', + color: '#4caf50', + marginBottom: 5, + }, + addChatTextDisabled: { + opacity: 0.5, + }, + addChatLimitText: { + fontSize: 12, + color: '#4caf50', + }, + testButton: { + marginTop: 5, + paddingVertical: 3, + paddingHorizontal: 8, + backgroundColor: '#2196f3', + borderRadius: 6, + }, + testButtonText: { + color: '#ffffff', + fontSize: 10, + fontWeight: '600', + }, + pageIndicators: { + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + paddingVertical: 15, + gap: 8, + }, + pageIndicator: { + width: 8, + height: 8, + borderRadius: 4, + backgroundColor: '#ccc', + }, + activePageIndicator: { + backgroundColor: '#2196f3', + width: 12, + height: 8, + }, + info: { + backgroundColor: '#ffffff', + margin: 20, + padding: 15, + borderRadius: 12, + shadowColor: '#000', + shadowOffset: {width: 0, height: 1}, + shadowOpacity: 0.05, + shadowRadius: 2, + elevation: 1, + }, + infoText: { + fontSize: 12, + color: '#666', + textAlign: 'center', + lineHeight: 18, + }, +}) diff --git a/apps/p2p-chat/src/styles/common.ts b/apps/p2p-chat/src/styles/common.ts new file mode 100644 index 0000000..cd97385 --- /dev/null +++ b/apps/p2p-chat/src/styles/common.ts @@ -0,0 +1,108 @@ +import {StyleSheet} from 'react-native' + +// Color Palette +export const colors = { + primary: '#2196f3', + success: '#4caf50', + error: '#f44336', + warning: '#ff9800', + background: '#f8f9fa', + surface: '#ffffff', + text: { + primary: '#1a1a1a', + secondary: '#666', + light: '#999', + white: '#fff', + }, + border: 'rgba(0,0,0,0.1)', + shadow: '#000', +} + +// Spacing +export const spacing = { + xs: 4, + sm: 8, + md: 12, + lg: 16, + xl: 20, +} + +// Typography +export const typography = { + sizes: { + xs: 9, + sm: 10, + md: 12, + lg: 14, + xl: 16, + xxl: 18, + }, + weights: { + normal: '400' as const, + medium: '500' as const, + semibold: '600' as const, + bold: 'bold' as const, + }, +} + +// Common Styles +export const commonStyles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.surface, + }, + row: { + flexDirection: 'row', + alignItems: 'center', + }, + centered: { + justifyContent: 'center', + alignItems: 'center', + }, + shadow: { + shadowColor: colors.shadow, + shadowOffset: {width: 0, height: 2}, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + border: { + borderWidth: 1, + borderColor: colors.border, + }, + rounded: { + borderRadius: 8, + }, + roundedLarge: { + borderRadius: 12, + }, +}) + +// Button Styles +export const buttonStyles = StyleSheet.create({ + base: { + paddingHorizontal: spacing.md, + paddingVertical: spacing.sm, + borderRadius: 8, + justifyContent: 'center', + alignItems: 'center', + }, + primary: { + backgroundColor: colors.primary, + }, + success: { + backgroundColor: colors.success, + }, + error: { + backgroundColor: colors.error, + }, + text: { + color: colors.text.white, + fontSize: typography.sizes.lg, + fontWeight: typography.weights.semibold, + }, + disabled: { + backgroundColor: '#ccc', + opacity: 0.5, + }, +}) diff --git a/apps/p2p-chat/src/styles/index.ts b/apps/p2p-chat/src/styles/index.ts new file mode 100644 index 0000000..4338270 --- /dev/null +++ b/apps/p2p-chat/src/styles/index.ts @@ -0,0 +1,2 @@ +export {carouselStyles} from './carousel' +export {buttonStyles, colors, commonStyles, spacing, typography} from './common' diff --git a/apps/p2p-chat/src/types/chat.ts b/apps/p2p-chat/src/types/chat.ts new file mode 100644 index 0000000..5e817ae --- /dev/null +++ b/apps/p2p-chat/src/types/chat.ts @@ -0,0 +1,43 @@ +export interface Message { + id: string + text: string + sender: string + timestamp: number + type: 'sent' | 'received' | 'error' + isError?: boolean + errorReason?: string +} + +export interface ChatAppProps { + userId: string + userName: string + targetOptions: string[] // Array of friend IDs who can receive messages + potentialFriends: {id: string; name: string}[] // Users who can be added as friends + pendingRequests: {id: string; from: string; to: string; timestamp: number}[] // Incoming friend requests + backgroundColor: string + friendshipTrigger?: number // Trigger prop to force re-renders +} + +export interface MessageData { + type: string + messageId?: string + text?: string + senderId?: string + senderName?: string + timestamp: number + target?: string + from?: string + fromName?: string + requestId?: string + action?: string + friend?: string + friendName?: string + reason?: string + errorText?: string + message?: string +} + +export interface GlobalCommunication { + setOnMessage: (callback: (data: any) => void) => void + postMessage: (message: any, targetOrigin?: string) => void +} diff --git a/apps/p2p-chat/src/types/friends.ts b/apps/p2p-chat/src/types/friends.ts new file mode 100644 index 0000000..9dbd71b --- /dev/null +++ b/apps/p2p-chat/src/types/friends.ts @@ -0,0 +1,20 @@ +export interface FriendRequest { + id: string + from: string + to: string + timestamp: number + status?: 'pending' | 'accepted' | 'rejected' +} + +export interface PotentialFriend { + id: string + name: string +} + +export type FriendAction = 'accept' | 'reject' + +export interface FriendNotification { + id: string + text: string + timestamp: number +} diff --git a/apps/p2p-chat/src/types/index.ts b/apps/p2p-chat/src/types/index.ts new file mode 100644 index 0000000..0f8be6a --- /dev/null +++ b/apps/p2p-chat/src/types/index.ts @@ -0,0 +1,12 @@ +export type { + ChatAppProps, + GlobalCommunication, + Message, + MessageData, +} from './chat' +export type { + FriendAction, + FriendNotification, + FriendRequest, + PotentialFriend, +} from './friends' diff --git a/apps/p2p-chat/src/utils/chatHelpers.ts b/apps/p2p-chat/src/utils/chatHelpers.ts new file mode 100644 index 0000000..59d344a --- /dev/null +++ b/apps/p2p-chat/src/utils/chatHelpers.ts @@ -0,0 +1,40 @@ +import {ChatInstance} from '../constants' +import {FriendshipManager} from '../services' + +export const getChatHelpers = ( + chatInstances: ChatInstance[], + friendshipManager: FriendshipManager +) => { + const getTargetOptions = (chatId: string): string[] => { + return friendshipManager.getFriends(chatId) + } + + const getPotentialFriends = (chatId: string) => { + return chatInstances + .filter(chat => chat.id !== chatId) // Exclude self + .filter(chat => !friendshipManager.areFriends(chatId, chat.id)) // Exclude existing friends + .filter( + chat => !friendshipManager.hasPendingRequestBetween(chatId, chat.id) + ) // Exclude pending requests + .map(chat => ({id: chat.id, name: chat.userName})) + } + + const getPendingRequests = (chatId: string) => { + return friendshipManager.getPendingRequestsFor(chatId) + } + + return { + getTargetOptions, + getPotentialFriends, + getPendingRequests, + } +} + +export const createChatInstance = ( + name: string, + color: string +): ChatInstance => ({ + id: name.toLowerCase(), + userName: name, + backgroundColor: color, +}) diff --git a/apps/p2p-chat/tsconfig.json b/apps/p2p-chat/tsconfig.json new file mode 100644 index 0000000..88fa317 --- /dev/null +++ b/apps/p2p-chat/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@react-native/typescript-config" +} diff --git a/bun.lock b/bun.lock index 09513a9..3817200 100644 --- a/bun.lock +++ b/bun.lock @@ -15,6 +15,7 @@ "@react-native/typescript-config": "0.80.1", "@release-it-plugins/workspaces": "^4.2.0", "@release-it/conventional-changelog": "^8.0.1", + "@types/react": "^19.1.9", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", "commitlint": "^17.0.2", @@ -76,6 +77,29 @@ "react-test-renderer": "19.1.0", }, }, + "apps/p2p-chat": { + "name": "@apps/p2p-chat", + "version": "0.0.1", + "dependencies": { + "@callstack/react-native-sandbox": "workspace:*", + "react": "19.1.0", + "react-native": "0.80.1", + "react-native-toast-message": "^2.3.2", + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.3", + "@babel/runtime": "^7.25.0", + "@react-native-community/cli": "18.0.0", + "@react-native-community/cli-platform-android": "18.0.0", + "@react-native-community/cli-platform-ios": "18.0.0", + "@react-native/babel-preset": "0.80.1", + "@react-native/eslint-config": "0.80.1", + "@react-native/metro-config": "0.80.1", + "@react-native/typescript-config": "0.80.1", + "react-test-renderer": "19.1.0", + }, + }, "apps/recursive": { "name": "@apps/recursive", "version": "1.0.0", @@ -121,7 +145,7 @@ }, "packages/react-native-sandbox": { "name": "@callstack/react-native-sandbox", - "version": "0.2.0", + "version": "0.3.0", "devDependencies": { "react": "19.1.0", "react-native": "0.80.1", @@ -139,6 +163,8 @@ "@apps/fs-experiment": ["@apps/fs-experiment@workspace:apps/fs-experiment"], + "@apps/p2p-chat": ["@apps/p2p-chat@workspace:apps/p2p-chat"], + "@apps/recursive": ["@apps/recursive@workspace:apps/recursive"], "@apps/side-by-side": ["@apps/side-by-side@workspace:apps/side-by-side"], @@ -683,12 +709,14 @@ "@types/minimist": ["@types/minimist@1.2.5", "", {}, "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag=="], - "@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + "@types/node": ["@types/node@24.2.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw=="], "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], "@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="], + "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="], + "@types/semver": ["@types/semver@7.7.0", "", {}, "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA=="], "@types/stack-utils": ["@types/stack-utils@2.0.3", "", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="], @@ -1013,6 +1041,8 @@ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "dargs": ["dargs@8.1.0", "", {}, "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw=="], "data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="], @@ -2141,7 +2171,7 @@ "unc-path-regex": ["unc-path-regex@0.1.2", "", {}, "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg=="], - "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + "undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], diff --git a/package.json b/package.json index 02b01ac..b2002b3 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@react-native/typescript-config": "0.80.1", "@release-it-plugins/workspaces": "^4.2.0", "@release-it/conventional-changelog": "^8.0.1", + "@types/react": "^19.1.9", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", "commitlint": "^17.0.2", diff --git a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.h b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.h index 7a84d63..c642a48 100644 --- a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.h +++ b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.h @@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) std::shared_ptr eventEmitter; @property (nonatomic, assign) BOOL hasOnMessageHandler; @property (nonatomic, assign) BOOL hasOnErrorHandler; +@property (nonatomic, copy, nullable) NSString *sandboxId; +@property (nonatomic, copy, nullable) NSString *jsBundleSource; /** * Sets the list of allowed TurboModules for this sandbox instance. @@ -31,11 +33,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) NSArray *allowedTurboModules; /** - * Initializes the delegate with a specific JS bundle source. - * @param jsBundleSource The source for the JavaScript bundle (file path or URL) + * Initializes the delegate. * @return Initialized delegate instance with filtered module access */ -- (instancetype)initWithJSBundleSource:(NSString *)jsBundleSource; +- (instancetype)init; /** * Posts a message to the JavaScript runtime. @@ -43,6 +44,42 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)postMessage:(NSString *)message; +/** + * Checks if the delegate is properly configured for bidirectional communication + * @return YES if both sandboxโ†’host and hostโ†’sandbox communication should work + */ +- (BOOL)isCommunicationReady; + +#pragma mark - Static Registry Methods + +/** + * Registers a sandbox delegate with the global registry + * @param sandboxId Unique identifier for the sandbox + * @param delegate The delegate instance to register + */ ++ (void)registerSandbox:(NSString *)sandboxId delegate:(SandboxReactNativeDelegate *)delegate; + +/** + * Unregisters a sandbox delegate from the global registry + * @param sandboxId The sandbox identifier to unregister + */ ++ (void)unregisterSandbox:(NSString *)sandboxId; + +/** + * Retrieves a sandbox delegate by ID + * @param sandboxId The sandbox identifier to look up + * @return The delegate instance or nil if not found + */ ++ (nullable SandboxReactNativeDelegate *)getSandbox:(NSString *)sandboxId; + +/** + * Routes a message to a specific sandbox + * @param message The message to send + * @param targetId The target sandbox identifier + * @return YES if message was routed successfully, NO if target not found + */ ++ (BOOL)routeMessage:(NSString *)message toSandbox:(NSString *)targetId; + @end NS_ASSUME_NONNULL_END diff --git a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm index 2b8b58a..b52cdf9 100644 --- a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm +++ b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #import #import @@ -51,12 +53,105 @@ @interface SandboxReactNativeDelegate () { std::set _allowedModules; } -@property (nonatomic, strong) NSString *jsBundleSource; - @end @implementation SandboxReactNativeDelegate +#pragma mark - Static Registry Implementation + +// Global static registry for sandbox communication +static std::map _sandboxRegistry; +static std::mutex _registryMutex; + ++ (void)registerSandbox:(NSString *)sandboxId delegate:(SandboxReactNativeDelegate *)delegate +{ + if (!sandboxId || !delegate) { + NSLog(@"[SandboxRegistry] Cannot register sandbox: sandboxId=%@ delegate=%@", sandboxId, delegate); + return; + } + + std::lock_guard lock(_registryMutex); + std::string cppSandboxId = [sandboxId UTF8String]; + + if (_sandboxRegistry.find(cppSandboxId) != _sandboxRegistry.end()) { + NSLog(@"[SandboxRegistry] Warning: Overwriting existing sandbox with ID: %@", sandboxId); + } + + _sandboxRegistry[cppSandboxId] = delegate; + // delegate.sandboxId = sandboxId; + + NSLog(@"[SandboxRegistry] Registered sandbox: %@ (total: %zu)", sandboxId, _sandboxRegistry.size()); +} + ++ (void)unregisterSandbox:(NSString *)sandboxId +{ + if (!sandboxId) { + return; + } + + std::lock_guard lock(_registryMutex); + std::string cppSandboxId = [sandboxId UTF8String]; + + auto it = _sandboxRegistry.find(cppSandboxId); + if (it != _sandboxRegistry.end()) { + it->second.sandboxId = nil; + _sandboxRegistry.erase(it); + NSLog(@"[SandboxRegistry] Unregistered sandbox: %@ (total: %zu)", sandboxId, _sandboxRegistry.size()); + } +} + ++ (nullable SandboxReactNativeDelegate *)getSandbox:(NSString *)sandboxId +{ + if (!sandboxId) { + return nil; + } + + std::lock_guard lock(_registryMutex); + std::string cppSandboxId = [sandboxId UTF8String]; + + auto it = _sandboxRegistry.find(cppSandboxId); + if (it != _sandboxRegistry.end()) { + return it->second; + } + + return nil; +} + ++ (BOOL)routeMessage:(NSString *)message toSandbox:(NSString *)targetId +{ + SandboxReactNativeDelegate *target = [self getSandbox:targetId]; + if (!target) { + NSLog(@"[SandboxRegistry] Cannot route message: target sandbox '%@' not found", targetId); + return NO; + } + + [target postMessage:message]; + NSLog(@"[SandboxRegistry] Routed message to sandbox: %@", targetId); + return YES; +} + +#pragma mark - Instance Methods + +- (void)setSandboxId:(NSString *)sandboxId +{ + if ([sandboxId isEqual:_sandboxId]) { + return; + } + + // Unregister old ID if it exists + if (_sandboxId) { + [SandboxReactNativeDelegate unregisterSandbox:_sandboxId]; + } + + // Set new ID + _sandboxId = [sandboxId copy]; + + // Register new ID if it's not nil + if (_sandboxId) { + [SandboxReactNativeDelegate registerSandbox:_sandboxId delegate:self]; + } +} + - (void)setAllowedTurboModules:(NSArray *)allowedTurboModules { _allowedModules.clear(); @@ -65,10 +160,9 @@ - (void)setAllowedTurboModules:(NSArray *)allowedTurboModules } } -- (instancetype)initWithJSBundleSource:(NSString *)jsBundleSource +- (instancetype)init { if (self = [super init]) { - _jsBundleSource = jsBundleSource; _hasOnMessageHandler = NO; _hasOnErrorHandler = NO; self.dependencyProvider = [[RCTAppDependencyProvider alloc] init]; @@ -76,6 +170,14 @@ - (instancetype)initWithJSBundleSource:(NSString *)jsBundleSource return self; } +- (void)dealloc +{ + // Auto-unregister when delegate is deallocated + if (self.sandboxId) { + [SandboxReactNativeDelegate unregisterSandbox:self.sandboxId]; + } +} + - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { return [self bundleURL]; @@ -83,6 +185,10 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge - (NSURL *)bundleURL { + if (!self.jsBundleSource) { + return nil; + } + NSURL *url = [NSURL URLWithString:self.jsBundleSource]; if (url && url.scheme) { return url; @@ -106,6 +212,9 @@ - (void)postMessage:(NSString *)message [_rctInstance callFunctionOnBufferedRuntimeExecutor:[=](jsi::Runtime &runtime) { try { + // Validate runtime before any JSI operations + runtime.global(); // Test if runtime is accessible + std::string jsonString = [message UTF8String]; jsi::Value parsedValue = runtime.global() @@ -123,15 +232,44 @@ - (void)postMessage:(NSString *)message } catch (const std::exception &e) { if (self.eventEmitter && self.hasOnErrorHandler) { SandboxReactNativeViewEventEmitter::OnError errorEvent = { - .isFatal = false, .name = "JSONParseError", .message = e.what(), .stack = ""}; + .isFatal = false, .name = "RuntimeError", .message = e.what(), .stack = ""}; self.eventEmitter->onError(errorEvent); } + } catch (...) { + NSLog(@"[SandboxReactNativeDelegate] Runtime invalid during postMessage for sandbox %@", self.sandboxId); } }]; } +- (BOOL)isCommunicationReady +{ + BOOL hasEventEmitter = (self.eventEmitter != nullptr); + BOOL hasMessageSandbox = (_onMessageSandbox != nullptr); + BOOL isRegistered = (self.sandboxId != nil); + + NSLog( + @"[SandboxReactNativeDelegate] Communication status for %@: eventEmitter=%@, messageSandbox=%@, registered=%@", + self.sandboxId ?: @"(nil)", + hasEventEmitter ? @"YES" : @"NO", + hasMessageSandbox ? @"YES" : @"NO", + isRegistered ? @"YES" : @"NO"); + + return hasEventEmitter && hasMessageSandbox && isRegistered; +} + - (void)hostDidStart:(RCTHost *)host { + // Safely clear any existing JSI function before new runtime setup + // This prevents crash on reload when old function is tied to invalid runtime + try { + _onMessageSandbox = nullptr; + } catch (...) { + // Old function destructor might crash if tied to invalid runtime + NSLog( + @"[SandboxReactNativeDelegate] Warning: Exception while clearing old JSI function for sandbox %@", + self.sandboxId); + } + Ivar ivar = class_getInstanceVariable([host class], "_instance"); _rctInstance = object_getIvar(host, ivar); @@ -142,21 +280,88 @@ - (void)hostDidStart:(RCTHost *)host jsi::Function::createFromHostFunction( runtime, jsi::PropNameID::forAscii(runtime, "postMessage"), - 1, + 2, // Updated to accept up to 2 arguments [=](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) { - if (count != 1) { - throw jsi::JSError(rt, "Expected exactly one argument"); + // Validate runtime before any JSI operations + try { + rt.global(); // Test if runtime is accessible + } catch (...) { + NSLog(@"[SandboxReactNativeDelegate] Runtime invalid in postMessage for sandbox %@", self.sandboxId); + return jsi::Value::undefined(); } - const jsi::Value &arg = args[0]; - if (!arg.isObject()) { - throw jsi::JSError(rt, "Expected a object as the first argument"); + if (count < 1 || count > 2) { + throw jsi::JSError(rt, "Expected 1 or 2 arguments: postMessage(message, targetOrigin?)"); } - if (self.eventEmitter && self.hasOnMessageHandler) { - SandboxReactNativeViewEventEmitter::OnMessage messageEvent = { - .data = jsi::dynamicFromValue(rt, args[0])}; - self.eventEmitter->onMessage(messageEvent); + const jsi::Value &messageArg = args[0]; + if (!messageArg.isObject()) { + throw jsi::JSError(rt, "Expected an object as the first argument"); + } + + // Check if targetOrigin is provided + if (count == 2 && !args[1].isNull() && !args[1].isUndefined()) { + const jsi::Value &targetOriginArg = args[1]; + if (!targetOriginArg.isString()) { + throw jsi::JSError(rt, "Expected a string as the second argument (targetOrigin)"); + } + + std::string targetOrigin = targetOriginArg.getString(rt).utf8(rt); + NSString *targetOriginNS = [NSString stringWithUTF8String:targetOrigin.c_str()]; + + // Prevent self-targeting + if (self.sandboxId && [self.sandboxId isEqualToString:targetOriginNS]) { + if (self.eventEmitter && self.hasOnErrorHandler) { + std::string errorMessage = "Cannot send message to self (sandbox '" + targetOrigin + "')"; + SandboxReactNativeViewEventEmitter::OnError errorEvent = { + .isFatal = false, .name = "SelfTargetingError", .message = errorMessage, .stack = ""}; + self.eventEmitter->onError(errorEvent); + } else { + // Fallback: throw JSError if no error handler + throw jsi::JSError(rt, ("Cannot send message to self (sandbox '" + targetOrigin + "')").c_str()); + } + return jsi::Value::undefined(); + } + + // Convert message to JSON string + jsi::Object jsonObject = rt.global().getPropertyAsObject(rt, "JSON"); + jsi::Function jsonStringify = jsonObject.getPropertyAsFunction(rt, "stringify"); + jsi::Value jsonResult = jsonStringify.call(rt, messageArg); + std::string messageJson = jsonResult.getString(rt).utf8(rt); + NSString *messageNS = [NSString stringWithUTF8String:messageJson.c_str()]; + + // Route message to specific sandbox + BOOL success = [SandboxReactNativeDelegate routeMessage:messageNS toSandbox:targetOriginNS]; + if (!success) { + // Target sandbox doesn't exist - trigger error event + if (self.eventEmitter && self.hasOnErrorHandler) { + std::string errorMessage = "Target sandbox '" + targetOrigin + "' not found"; + SandboxReactNativeViewEventEmitter::OnError errorEvent = { + .isFatal = false, .name = "SandboxRoutingError", .message = errorMessage, .stack = ""}; + self.eventEmitter->onError(errorEvent); + } else { + // Fallback: throw JSError if no error handler + throw jsi::JSError(rt, ("Target sandbox '" + targetOrigin + "' not found").c_str()); + } + } + } else { + // targetOrigin is undefined/null - route to host (backward compatibility) + if (self.eventEmitter && self.hasOnMessageHandler) { + SandboxReactNativeViewEventEmitter::OnMessage messageEvent = { + .data = jsi::dynamicFromValue(rt, args[0])}; + self.eventEmitter->onMessage(messageEvent); + } else { + // Log why message to host failed + if (!self.eventEmitter) { + NSLog( + @"[SandboxReactNativeDelegate] Cannot send message to host: eventEmitter is nullptr for sandbox %@", + self.sandboxId); + } else if (!self.hasOnMessageHandler) { + NSLog( + @"[SandboxReactNativeDelegate] Cannot send message to host: no message handler for sandbox %@", + self.sandboxId); + } + } } return jsi::Value::undefined(); @@ -178,7 +383,13 @@ - (void)hostDidStart:(RCTHost *)host throw jsi::JSError(rt, "Expected a function as the first argument"); } + NSLog(@"[SandboxReactNativeDelegate] setOnMessage for sandbox %@", self.sandboxId); + jsi::Function fn = arg.asObject(rt).asFunction(rt); + + // Safely reset existing function before assigning new one + // This prevents crash if old function is tied to invalid runtime + _onMessageSandbox.reset(); _onMessageSandbox = std::make_shared(std::move(fn)); return jsi::Value::undefined(); diff --git a/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm b/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm index fbc338a..a143a1c 100644 --- a/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm +++ b/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm @@ -35,38 +35,93 @@ - (instancetype)initWithFrame:(CGRect)frame if (self = [super initWithFrame:frame]) { static const auto defaultProps = std::make_shared(); _props = defaultProps; + + // Create delegate once during initialization + self.reactNativeDelegate = [[SandboxReactNativeDelegate alloc] init]; } return self; } +- (void)updateEventEmitter:(std::shared_ptr)eventEmitter +{ + [super updateEventEmitter:eventEmitter]; + + // EventEmitter has been updated, try to set it on the delegate + [self updateEventEmitterIfNeeded]; +} + +- (void)updateState:(const facebook::react::State::Shared &)state + oldState:(const facebook::react::State::Shared &)oldState +{ + [super updateState:state oldState:oldState]; + + // State has been updated, eventEmitter might be available now + [self updateEventEmitterIfNeeded]; +} + - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps { const auto &oldViewProps = *std::static_pointer_cast(_props); const auto &newViewProps = *std::static_pointer_cast(props); - bool shouldReload = false; - - if (oldViewProps.componentName != newViewProps.componentName || - oldViewProps.jsBundleSource != newViewProps.jsBundleSource || - oldViewProps.initialProperties != newViewProps.initialProperties || - oldViewProps.launchOptions != newViewProps.launchOptions || - oldViewProps.allowedTurboModules != newViewProps.allowedTurboModules) { - shouldReload = true; - } - [super updateProps:props oldProps:oldProps]; if (self.reactNativeDelegate) { + if (oldViewProps.id != newViewProps.id) { + self.reactNativeDelegate.sandboxId = RCTNSStringFromString(newViewProps.id); + } + + if (oldViewProps.jsBundleSource != newViewProps.jsBundleSource) { + self.reactNativeDelegate.jsBundleSource = RCTNSStringFromString(newViewProps.jsBundleSource); + } + + if (oldViewProps.allowedTurboModules != newViewProps.allowedTurboModules) { + NSArray *allowedTurboModules = @[]; + if (!newViewProps.allowedTurboModules.empty()) { + NSMutableArray *modules = [NSMutableArray new]; + for (const auto &module : newViewProps.allowedTurboModules) { + [modules addObject:RCTNSStringFromString(module)]; + } + allowedTurboModules = [modules copy]; + } + self.reactNativeDelegate.allowedTurboModules = allowedTurboModules; + } + self.reactNativeDelegate.hasOnMessageHandler = newViewProps.hasOnMessageHandler; self.reactNativeDelegate.hasOnErrorHandler = newViewProps.hasOnErrorHandler; + + // Always try to set the eventEmitter when props update + [self updateEventEmitterIfNeeded]; } - if (shouldReload) { + if (oldViewProps.componentName != newViewProps.componentName || + oldViewProps.initialProperties != newViewProps.initialProperties || + oldViewProps.launchOptions != newViewProps.launchOptions) { [self scheduleReactViewLoad]; } } +- (void)updateEventEmitterIfNeeded +{ + if (self.reactNativeDelegate && _eventEmitter) { + if (auto eventEmitter = std::static_pointer_cast(_eventEmitter)) { + self.reactNativeDelegate.eventEmitter = eventEmitter; + NSLog( + @"[SandboxReactNativeViewComponentView] EventEmitter set for sandbox: %@", + self.reactNativeDelegate.sandboxId); + } else { + NSLog( + @"[SandboxReactNativeViewComponentView] Failed to cast eventEmitter for sandbox: %@", + self.reactNativeDelegate.sandboxId); + } + } else { + NSLog( + @"[SandboxReactNativeViewComponentView] EventEmitter not available yet for sandbox: %@", + self.reactNativeDelegate.sandboxId); + } +} + - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args { RCTSandboxReactNativeViewHandleCommand(self, commandName, args); @@ -93,10 +148,10 @@ - (void)loadReactNativeView { const auto &props = *std::static_pointer_cast(_props); - NSString *componentName = RCTNSStringFromString(props.componentName); + NSString *moduleName = RCTNSStringFromString(props.componentName); NSString *jsBundleSource = RCTNSStringFromString(props.jsBundleSource); - if (componentName.length == 0 || jsBundleSource.length == 0) { + if (moduleName.length == 0 || jsBundleSource.length == 0 || !self.reactNativeDelegate) { return; } @@ -111,29 +166,13 @@ - (void)loadReactNativeView launchOptions = (NSDictionary *)convertFollyDynamicToId(props.launchOptions); } - NSArray *allowedTurboModules = @[]; - if (!props.allowedTurboModules.empty()) { - NSMutableArray *modules = [NSMutableArray new]; - for (const auto &module : props.allowedTurboModules) { - [modules addObject:RCTNSStringFromString(module)]; - } - allowedTurboModules = [modules copy]; - } - - SandboxReactNativeDelegate *delegate = [[SandboxReactNativeDelegate alloc] initWithJSBundleSource:jsBundleSource]; - delegate.allowedTurboModules = allowedTurboModules; - - if (auto eventEmitter = std::static_pointer_cast(_eventEmitter)) { - delegate.eventEmitter = eventEmitter; + // Use existing delegate (properties already updated in updateProps) + if (!self.reactNativeFactory) { + self.reactNativeFactory = [[RCTReactNativeFactory alloc] initWithDelegate:self.reactNativeDelegate]; } - - delegate.hasOnMessageHandler = props.hasOnMessageHandler; - delegate.hasOnErrorHandler = props.hasOnErrorHandler; - - RCTReactNativeFactory *factory = [[RCTReactNativeFactory alloc] initWithDelegate:delegate]; - UIView *rnView = [factory.rootViewFactory viewWithModuleName:componentName - initialProperties:initialProperties - launchOptions:launchOptions]; + UIView *rnView = [self.reactNativeFactory.rootViewFactory viewWithModuleName:moduleName + initialProperties:initialProperties + launchOptions:launchOptions]; [self.reactNativeRootView removeFromSuperview]; self.reactNativeRootView = rnView; @@ -141,8 +180,8 @@ - (void)loadReactNativeView rnView.frame = self.bounds; rnView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - self.reactNativeDelegate = delegate; - self.reactNativeFactory = factory; + // Try to set eventEmitter after React Native view is loaded + [self updateEventEmitterIfNeeded]; } - (void)prepareForRecycle diff --git a/packages/react-native-sandbox/specs/NativeSandboxReactNativeView.ts b/packages/react-native-sandbox/specs/NativeSandboxReactNativeView.ts index 8c25641..860e1b4 100644 --- a/packages/react-native-sandbox/specs/NativeSandboxReactNativeView.ts +++ b/packages/react-native-sandbox/specs/NativeSandboxReactNativeView.ts @@ -38,6 +38,9 @@ export interface MessageEvent { * Extends ViewProps and defines all properties that can be passed to the native view. */ export interface NativeProps extends ViewProps { + /** Optional unique identifier for the sandbox instance */ + id?: string + /** * The name of the React Native component to load in the sandbox. * Must match with the registered component name from the JavaScript bundle diff --git a/packages/react-native-sandbox/src/index.tsx b/packages/react-native-sandbox/src/index.tsx index 579f279..5b37520 100644 --- a/packages/react-native-sandbox/src/index.tsx +++ b/packages/react-native-sandbox/src/index.tsx @@ -42,6 +42,7 @@ const SANDBOX_TURBOMODULES_WHITELIST = [ 'NativeAnimatedTurboModule', 'KeyboardObserver', 'I18nManager', + 'FrameRateLogger', ] /** @@ -52,11 +53,16 @@ type GenericProps = { [key: string]: any } -/** - * Props for the SandboxReactNativeView component. - * Extends ViewProps to include all standard React Native View properties. - */ -export type SandboxReactNativeViewProps = ViewProps & { +let sandboxCounter = 0 +const generateSandboxId = (): string => { + return `sandbox:${++sandboxCounter}` +} + +export interface SandboxReactNativeViewProps extends ViewProps { + /** Optional unique identifier for the sandbox instance */ + // TODO rename to not colide with ViewProps.id + id?: string + /** * The name of the React Native component to load in the sandbox. * This should match the component name registered in your JavaScript bundle. @@ -74,7 +80,7 @@ export type SandboxReactNativeViewProps = ViewProps & { * Optional path or URL to the JavaScript bundle to load. * If not provided, the default bundle will be used. */ - jsBundleSource: string + jsBundleSource?: string /** * Initial properties to pass to the sandboxed React Native app. @@ -198,21 +204,25 @@ const SandboxReactNativeView = forwardRef< >( ( { - onMessage, - onError, + id, + jsBundleSource, allowedTurboModules, style, componentName, moduleName, + onMessage, + onError, ...rest }, ref ) => { const nativeRef = - useRef | null>( + useRef | null>( null ) + const sandboxId = useMemo(() => id || generateSandboxId(), [id]) + const postMessage = useCallback((message: any) => { if (nativeRef.current) { Commands.postMessage(nativeRef.current, JSON.stringify(message)) @@ -255,12 +265,15 @@ const SandboxReactNativeView = forwardRef< [] ) - const _allowedTurboModules = [ - ...new Set([ - ...(allowedTurboModules ?? []), - ...SANDBOX_TURBOMODULES_WHITELIST, - ]), - ] + const _allowedTurboModules = useMemo( + () => [ + ...new Set([ + ...(allowedTurboModules ?? []), + ...SANDBOX_TURBOMODULES_WHITELIST, + ]), + ], + [allowedTurboModules] + ) // Handle backward compatibility for moduleName -> componentName const resolvedComponentName = useMemo(() => { @@ -286,12 +299,21 @@ const SandboxReactNativeView = forwardRef< return componentName }, [componentName, moduleName]) + const _jsBundleSource = useMemo(() => { + if (jsBundleSource) { + return jsBundleSource + } + return 'index' + }, [jsBundleSource]) + return ( Date: Wed, 30 Jul 2025 20:43:40 +0200 Subject: [PATCH 02/15] fix: typecheck hook --- apps/side-by-side/SandboxApp.tsx | 4 ++-- lefthook.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/side-by-side/SandboxApp.tsx b/apps/side-by-side/SandboxApp.tsx index a74c24a..b41dff4 100644 --- a/apps/side-by-side/SandboxApp.tsx +++ b/apps/side-by-side/SandboxApp.tsx @@ -11,8 +11,8 @@ import { } from 'react-native' declare global { - var postMessage: (message: object) => void - var setOnMessage: (handler: (payload: object) => void) => void + var postMessage: (message: any, targetOrigin?: string) => void + var setOnMessage: (handler: (payload: any) => void) => void } type AppProps = { diff --git a/lefthook.yml b/lefthook.yml index ab3cae4..9ea2238 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -3,12 +3,12 @@ pre-commit: commands: lint: glob: '*.{js,ts,jsx,tsx}' - run: npx eslint {staged_files} + run: bun eslint {staged_files} types: - glob: '*.{js, ts, jsx, tsx}' - run: npx typecheck + glob: '*.{js,ts,jsx,tsx}' + run: bun typecheck commit-msg: parallel: true commands: commitlint: - run: npx commitlint --edit + run: bun commitlint --edit From ba097655851f98cde924098f4b35f8a4fb8702a6 Mon Sep 17 00:00:00 2001 From: Aliaksandr Babrykovich Date: Thu, 7 Aug 2025 12:59:03 +0200 Subject: [PATCH 03/15] chore: simplify and clean p2p-chat demo --- apps/p2p-chat/src/App.tsx | 41 +-- apps/p2p-chat/src/ChatApp.tsx | 57 ++-- apps/p2p-chat/src/components/ChatCarousel.tsx | 280 +++++++++++------- apps/p2p-chat/src/components/ChatHeader.tsx | 49 --- apps/p2p-chat/src/components/DebugInfo.tsx | 40 --- .../src/components/FriendRequestsList.tsx | 2 +- apps/p2p-chat/src/components/MessageInput.tsx | 2 +- apps/p2p-chat/src/components/MessagesList.tsx | 7 +- .../src/components/NotificationsList.tsx | 60 ---- .../src/components/TargetSelector.tsx | 44 +-- apps/p2p-chat/src/components/index.ts | 3 - apps/p2p-chat/src/constants.ts | 25 ++ apps/p2p-chat/src/constants/index.ts | 7 - apps/p2p-chat/src/constants/presets.ts | 23 -- apps/p2p-chat/src/hooks/index.ts | 1 + apps/p2p-chat/src/hooks/useChatInstances.ts | 10 +- apps/p2p-chat/src/hooks/useColorAnimation.ts | 46 +++ apps/p2p-chat/src/hooks/useFriendRequests.ts | 10 - .../src/hooks/useScrollColorInterpolation.ts | 46 +++ apps/p2p-chat/src/services/MessageHandler.ts | 28 +- apps/p2p-chat/src/styles/carousel.ts | 83 +++--- apps/p2p-chat/src/utils/chatHelpers.ts | 9 +- apps/p2p-chat/src/utils/colorUtils.ts | 36 +++ apps/p2p-chat/src/utils/index.ts | 4 + apps/p2p-chat/src/utils/scrollUtils.ts | 78 +++++ apps/side-by-side/ios/Podfile.lock | 4 +- 26 files changed, 542 insertions(+), 453 deletions(-) delete mode 100644 apps/p2p-chat/src/components/ChatHeader.tsx delete mode 100644 apps/p2p-chat/src/components/DebugInfo.tsx delete mode 100644 apps/p2p-chat/src/components/NotificationsList.tsx create mode 100644 apps/p2p-chat/src/constants.ts delete mode 100644 apps/p2p-chat/src/constants/index.ts delete mode 100644 apps/p2p-chat/src/constants/presets.ts create mode 100644 apps/p2p-chat/src/hooks/useColorAnimation.ts create mode 100644 apps/p2p-chat/src/hooks/useScrollColorInterpolation.ts create mode 100644 apps/p2p-chat/src/utils/colorUtils.ts create mode 100644 apps/p2p-chat/src/utils/index.ts create mode 100644 apps/p2p-chat/src/utils/scrollUtils.ts diff --git a/apps/p2p-chat/src/App.tsx b/apps/p2p-chat/src/App.tsx index 8c1126f..7bc1ae5 100644 --- a/apps/p2p-chat/src/App.tsx +++ b/apps/p2p-chat/src/App.tsx @@ -1,17 +1,13 @@ -import React, {useCallback, useRef} from 'react' -import { - NativeScrollEvent, - NativeSyntheticEvent, - SafeAreaView, - StatusBar, -} from 'react-native' +import React, {useRef} from 'react' +import {Dimensions, SafeAreaView, StatusBar} from 'react-native' import {ChatCarousel} from './components' -import {CHAT_WIDTH} from './constants' -import {useChatInstances} from './hooks' +import {useChatInstances, useScrollColorInterpolation} from './hooks' import {MessageHandler} from './services' import {carouselStyles} from './styles' +const {width: screenWidth} = Dimensions.get('window') + const App = () => { const { chatInstances, @@ -33,19 +29,24 @@ const App = () => { triggerFriendshipUpdate ) - const onScroll = useCallback( - (event: NativeSyntheticEvent) => { - const slideIndex = Math.round( - event.nativeEvent.contentOffset.x / CHAT_WIDTH - ) - setCurrentIndex(slideIndex) - }, - [setCurrentIndex] - ) + // Use the scroll color interpolation hook + const {currentBackgroundColor, onScroll} = useScrollColorInterpolation({ + chatInstances, + scrollStep: screenWidth, // Now each slide takes full screen width + onIndexChange: setCurrentIndex, + }) return ( - - + + { + // Convert hex to RGB + const hex = vibrantColor.replace('#', '') + const r = parseInt(hex.substr(0, 2), 16) + const g = parseInt(hex.substr(2, 2), 16) + const b = parseInt(hex.substr(4, 2), 16) + + // Create a very light version (90% white + 10% color) + const lightR = Math.round(255 * 0.9 + r * 0.1) + const lightG = Math.round(255 * 0.9 + g * 0.1) + const lightB = Math.round(255 * 0.9 + b * 0.1) + + return `rgb(${lightR}, ${lightG}, ${lightB})` +} + LogBox.ignoreAllLogs() const ChatApp: React.FC = ({ @@ -29,7 +42,7 @@ const ChatApp: React.FC = ({ pendingRequests, backgroundColor, }) => { - const [isConnected, setIsConnected] = useState(false) + const [, setIsConnected] = useState(false) const {sendMessage, sendConnectionMessage} = useCommunication({ userName, @@ -67,16 +80,11 @@ const ChatApp: React.FC = ({ }, }) - const { - friendNotifications, - clearNotification, - respondToFriendRequest, - handleFriendMessage, - sendFriendRequest, - } = useFriendRequests({ - userName, - onSendMessage: sendMessage, - }) + const {respondToFriendRequest, handleFriendMessage, sendFriendRequest} = + useFriendRequests({ + userName, + onSendMessage: sendMessage, + }) function handleIncomingMessage(data: MessageData) { console.log(`[${userName}] Processing message type: ${data.type}`) @@ -109,14 +117,11 @@ const ChatApp: React.FC = ({ [sendFriendRequest] ) - return ( - - + const subtleBackground = createSubtleBackground(backgroundColor) - + return ( + + = ({ potentialFriends={potentialFriends} selectedTarget={selectedTarget} onTargetSelect={handleTargetSelect} - onTargetChange={setSelectedTarget} onSendFriendRequest={handleSendFriendRequest} /> @@ -140,13 +144,6 @@ const ChatApp: React.FC = ({ onInputChange={setInputText} onSendMessage={handleSendMessage} /> - - ) } diff --git a/apps/p2p-chat/src/components/ChatCarousel.tsx b/apps/p2p-chat/src/components/ChatCarousel.tsx index facdea7..1b936c3 100644 --- a/apps/p2p-chat/src/components/ChatCarousel.tsx +++ b/apps/p2p-chat/src/components/ChatCarousel.tsx @@ -1,5 +1,7 @@ -import React, {useRef} from 'react' +import SandboxReactNativeView from '@callstack/react-native-sandbox' +import React, {useCallback, useEffect, useRef} from 'react' import { + Dimensions, NativeScrollEvent, NativeSyntheticEvent, ScrollView, @@ -7,19 +9,141 @@ import { TouchableOpacity, View, } from 'react-native' -import SandboxReactNativeView from '@callstack/react-native-sandbox' -import {ChatInstance, MAX_CHAT_INSTANCES} from '../constants' +import {ChatMeta, MAX_CHAT_INSTANCES} from '../constants' import {FriendshipManager, MessageHandler} from '../services' import {carouselStyles} from '../styles' import {getChatHelpers} from '../utils/chatHelpers' +// Individual Chat Instance Component +interface ChatInstanceViewProps { + chat: ChatMeta + friendshipTrigger: number + friendshipManager: FriendshipManager + messageHandler: MessageHandler + sandboxRefs: React.RefObject> + chatInstances: ChatMeta[] + onRemoveChatInstance: (chatId: string) => void +} + +const ChatInstanceView: React.FC = ({ + chat, + friendshipTrigger, + friendshipManager, + messageHandler, + sandboxRefs, + chatInstances, + onRemoveChatInstance, +}) => { + const {getTargetOptions, getPotentialFriends, getPendingRequests} = + getChatHelpers(chatInstances, friendshipManager) + + return ( + + + + + {chat.id} + + + onRemoveChatInstance(chat.id)} + disabled={chatInstances.length <= 1}> + + ร— + + + + + + { + if (ref) { + sandboxRefs.current[chat.id] = ref + console.log( + `[Host] Registered sandbox ref for ${chat.id}. Active refs:`, + Object.keys(sandboxRefs.current) + ) + } else { + console.log(`[Host] Removing sandbox ref for ${chat.id}`) + delete sandboxRefs.current[chat.id] + } + }} + id={chat.id} + componentName="ChatApp" + initialProperties={{ + userId: chat.id, + userName: chat.userName, + targetOptions: getTargetOptions(chat.id), + potentialFriends: getPotentialFriends(chat.id), + pendingRequests: getPendingRequests(chat.id), + backgroundColor: chat.backgroundColor, + friendshipTrigger: friendshipTrigger, + }} + onError={messageHandler.handleChatError(chat.userName)} + onMessage={messageHandler.handleChatMessage(chat.id)} + style={carouselStyles.sandbox} + /> + + + + ) +} + +// Add Chat Component +interface AddChatViewProps { + chatInstances: ChatMeta[] + onAddChatInstance: () => void +} + +const AddChatView: React.FC = ({ + chatInstances, + onAddChatInstance, +}) => { + const canAddMore = chatInstances.length < MAX_CHAT_INSTANCES + + return ( + + + + + + + + + Add Chat + + + {chatInstances.length} / {MAX_CHAT_INSTANCES} + + + + + ) +} + interface ChatCarouselProps { - chatInstances: ChatInstance[] + chatInstances: ChatMeta[] currentIndex: number friendshipTrigger: number friendshipManager: FriendshipManager - sandboxRefs: React.MutableRefObject> + sandboxRefs: React.RefObject> messageHandler: MessageHandler onScroll: (event: NativeSyntheticEvent) => void onRemoveChatInstance: (chatId: string) => void @@ -38,10 +162,37 @@ export const ChatCarousel: React.FC = ({ onAddChatInstance, }) => { const scrollViewRef = useRef(null) - const {getTargetOptions, getPotentialFriends, getPendingRequests} = - getChatHelpers(chatInstances, friendshipManager) - const canAddMore = chatInstances.length < MAX_CHAT_INSTANCES + // Function to scroll to specific chat + const scrollToChat = useCallback( + (targetChatId: string) => { + const targetIndex = chatInstances.findIndex( + chat => chat.id === targetChatId + ) + if (targetIndex !== -1 && scrollViewRef.current) { + // Each slide now takes full screen width for proper paging + const {width: screenWidth} = Dimensions.get('window') + const scrollX = targetIndex * screenWidth + + scrollViewRef.current.scrollTo({ + x: scrollX, + y: 0, + animated: true, + }) + } + }, + [chatInstances] + ) + + // Expose scroll function to messageHandler + useEffect(() => { + if ( + messageHandler && + typeof messageHandler.setScrollToChat === 'function' + ) { + messageHandler.setScrollToChat(scrollToChat) + } + }, [messageHandler, chatInstances, scrollToChat]) return ( <> @@ -52,101 +203,27 @@ export const ChatCarousel: React.FC = ({ pagingEnabled showsHorizontalScrollIndicator={false} onScroll={onScroll} - scrollEventThrottle={16} + scrollEventThrottle={8} + decelerationRate="fast" + bounces={false} style={carouselStyles.carousel}> - {chatInstances.map((chat, index) => ( - - - onRemoveChatInstance(chat.id)} - disabled={chatInstances.length <= 1}> - - ร— - - - - - - {chat.userName} ({chat.id}) - - - Friends: {getTargetOptions(chat.id).join(', ') || 'None'} - - - messageHandler.testHostToSandboxCommunication(chat.id) - }> - Test - - - - - - { - if (ref) { - sandboxRefs.current[chat.id] = ref - console.log( - `[Host] Registered sandbox ref for ${chat.id}. Active refs:`, - Object.keys(sandboxRefs.current) - ) - } else { - console.log(`[Host] Removing sandbox ref for ${chat.id}`) - delete sandboxRefs.current[chat.id] - } - }} - id={chat.id} - moduleName="ChatApp" - initialProperties={{ - userId: chat.id, - userName: chat.userName, - targetOptions: getTargetOptions(chat.id), - potentialFriends: getPotentialFriends(chat.id), - pendingRequests: getPendingRequests(chat.id), - backgroundColor: chat.backgroundColor, - friendshipTrigger: friendshipTrigger, - }} - onError={messageHandler.handleChatError(chat.userName)} - onMessage={messageHandler.handleChatMessage(chat.id)} - style={carouselStyles.sandbox} - /> - - + {chatInstances.map(chat => ( + ))} - - - - - + - - - Add Chat - - - {chatInstances.length} / {MAX_CHAT_INSTANCES} - - - - + @@ -165,8 +242,7 @@ export const ChatCarousel: React.FC = ({ ๐Ÿ’ฌ Multi-Instance P2P Chat Demo{'\n'} - Swipe between instances โ€ข Add friends โ€ข Send messages{'\n'} - Each instance runs in an isolated React Native sandbox + Each chat runs in an isolated React Native sandbox diff --git a/apps/p2p-chat/src/components/ChatHeader.tsx b/apps/p2p-chat/src/components/ChatHeader.tsx deleted file mode 100644 index 04c310c..0000000 --- a/apps/p2p-chat/src/components/ChatHeader.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react' -import {StyleSheet, Text, View} from 'react-native' - -import {colors, commonStyles, spacing, typography} from '../styles/common' - -interface ChatHeaderProps { - userName: string - isConnected: boolean -} - -export const ChatHeader: React.FC = ({ - userName, - isConnected, -}) => { - return ( - - {userName} - - - ) -} - -const styles = StyleSheet.create({ - header: { - ...commonStyles.row, - justifyContent: 'center', - padding: spacing.md, - borderBottomWidth: 1, - borderBottomColor: colors.border, - }, - headerTitle: { - fontSize: typography.sizes.xl, - fontWeight: typography.weights.bold, - color: colors.text.primary, - }, - statusDot: { - width: 6, - height: 6, - borderRadius: 3, - marginLeft: 6, - }, -}) diff --git a/apps/p2p-chat/src/components/DebugInfo.tsx b/apps/p2p-chat/src/components/DebugInfo.tsx deleted file mode 100644 index b68529f..0000000 --- a/apps/p2p-chat/src/components/DebugInfo.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react' -import {StyleSheet, Text, View} from 'react-native' - -import {colors, spacing, typography} from '../styles/common' - -interface DebugInfoProps { - userId: string - targetOptions: string[] - potentialFriendsCount: number - messageCount: number -} - -export const DebugInfo: React.FC = ({ - userId, - targetOptions, - potentialFriendsCount, - messageCount, -}) => { - return ( - - - ID: {userId} โ€ข Friends: [{targetOptions.join(', ')}] โ€ข Potential:{' '} - {potentialFriendsCount} โ€ข Messages: {messageCount} - - - ) -} - -const styles = StyleSheet.create({ - debugInfo: { - padding: spacing.sm, - backgroundColor: 'rgba(0,0,0,0.05)', - }, - debugText: { - fontSize: typography.sizes.xs, - color: colors.text.secondary, - textAlign: 'center', - fontFamily: 'monospace', - }, -}) diff --git a/apps/p2p-chat/src/components/FriendRequestsList.tsx b/apps/p2p-chat/src/components/FriendRequestsList.tsx index 897d32a..7c97403 100644 --- a/apps/p2p-chat/src/components/FriendRequestsList.tsx +++ b/apps/p2p-chat/src/components/FriendRequestsList.tsx @@ -62,7 +62,7 @@ const styles = StyleSheet.create({ padding: spacing.md, borderBottomWidth: 1, borderBottomColor: colors.border, - backgroundColor: 'rgba(255,255,255,0.8)', + backgroundColor: 'rgba(255,255,255,0.95)', }, title: { fontSize: typography.sizes.lg, diff --git a/apps/p2p-chat/src/components/MessageInput.tsx b/apps/p2p-chat/src/components/MessageInput.tsx index 7391fed..26b4646 100644 --- a/apps/p2p-chat/src/components/MessageInput.tsx +++ b/apps/p2p-chat/src/components/MessageInput.tsx @@ -61,7 +61,7 @@ const styles = StyleSheet.create({ padding: spacing.md, borderTopWidth: 1, borderTopColor: colors.border, - backgroundColor: 'rgba(255,255,255,0.8)', + backgroundColor: 'rgba(255,255,255,0.95)', }, textInput: { flex: 1, diff --git a/apps/p2p-chat/src/components/MessagesList.tsx b/apps/p2p-chat/src/components/MessagesList.tsx index 424443e..b1d26ab 100644 --- a/apps/p2p-chat/src/components/MessagesList.tsx +++ b/apps/p2p-chat/src/components/MessagesList.tsx @@ -2,7 +2,7 @@ import React, {useEffect, useRef} from 'react' import {ScrollView, StyleSheet, Text, View} from 'react-native' import {colors, commonStyles, spacing, typography} from '../styles/common' -import {Message} from '../types/chat' +import {Message} from '../types' interface MessagesListProps { messages: Message[] @@ -87,7 +87,8 @@ export const MessagesList: React.FC = ({ const styles = StyleSheet.create({ container: { flex: 1, - padding: spacing.md, + padding: spacing.sm, + backgroundColor: 'rgba(255,255,255,0.3)', }, emptyState: { flex: 1, @@ -112,7 +113,7 @@ const styles = StyleSheet.create({ }, receivedMessage: { alignSelf: 'flex-start', - backgroundColor: colors.surface, + backgroundColor: 'rgba(255,255,255,0.95)', ...commonStyles.border, }, errorMessage: { diff --git a/apps/p2p-chat/src/components/NotificationsList.tsx b/apps/p2p-chat/src/components/NotificationsList.tsx deleted file mode 100644 index 094a5c7..0000000 --- a/apps/p2p-chat/src/components/NotificationsList.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react' -import {StyleSheet, Text, TouchableOpacity, View} from 'react-native' - -import {colors, commonStyles, spacing, typography} from '../styles/common' - -interface NotificationsListProps { - notifications: string[] - onClearNotification: (index: number) => void -} - -export const NotificationsList: React.FC = ({ - notifications, - onClearNotification, -}) => { - if (notifications.length === 0) { - return null - } - - return ( - - {notifications.map((notification, index) => ( - - {notification} - onClearNotification(index)}> - ร— - - - ))} - - ) -} - -const styles = StyleSheet.create({ - container: { - padding: spacing.md, - borderBottomWidth: 1, - borderBottomColor: colors.border, - backgroundColor: 'rgba(255,255,255,0.8)', - }, - notification: { - ...commonStyles.row, - justifyContent: 'space-between', - backgroundColor: '#f0f0f0', - padding: spacing.sm + 2, // 10px - ...commonStyles.rounded, - marginBottom: spacing.sm, - ...commonStyles.border, - }, - notificationText: { - flex: 1, - fontSize: typography.sizes.md + 1, // 13px - color: colors.text.primary, - marginRight: spacing.sm + 2, // 10px - }, - notificationClose: { - fontSize: typography.sizes.xl, - color: colors.text.secondary, - fontWeight: typography.weights.bold, - }, -}) diff --git a/apps/p2p-chat/src/components/TargetSelector.tsx b/apps/p2p-chat/src/components/TargetSelector.tsx index 2f3333d..fa6783a 100644 --- a/apps/p2p-chat/src/components/TargetSelector.tsx +++ b/apps/p2p-chat/src/components/TargetSelector.tsx @@ -3,20 +3,18 @@ import { ScrollView, StyleSheet, Text, - TextInput, TouchableOpacity, View, } from 'react-native' -import {colors, commonStyles, spacing, typography} from '../styles/common' -import {PotentialFriend} from '../types/friends' +import {colors, commonStyles, spacing, typography} from '../styles' +import {PotentialFriend} from '../types' interface TargetSelectorProps { targetOptions: string[] potentialFriends: PotentialFriend[] selectedTarget: string onTargetSelect: (target: string) => void - onTargetChange: (target: string) => void onSendFriendRequest: (targetId: string) => void } @@ -25,7 +23,6 @@ export const TargetSelector: React.FC = ({ potentialFriends, selectedTarget, onTargetSelect, - onTargetChange, onSendFriendRequest, }) => { const allPossibleTargets = [ @@ -80,22 +77,10 @@ export const TargetSelector: React.FC = ({ ) : ( - Type a target manually to start chatting! + No friends or available users to chat with. )} - - - Or send to: - - ) } @@ -105,7 +90,7 @@ const styles = StyleSheet.create({ padding: spacing.md, borderBottomWidth: 1, borderBottomColor: colors.border, - backgroundColor: 'rgba(255,255,255,0.5)', + backgroundColor: 'rgba(255,255,255,0.9)', }, targetLabel: { fontSize: typography.sizes.md, @@ -158,25 +143,4 @@ const styles = StyleSheet.create({ textAlign: 'center', fontStyle: 'italic', }, - manualTargetContainer: { - padding: spacing.md, - borderBottomWidth: 1, - borderBottomColor: colors.border, - backgroundColor: 'rgba(255,255,255,0.8)', - }, - manualTargetLabel: { - fontSize: typography.sizes.md, - fontWeight: typography.weights.semibold, - color: colors.text.secondary, - marginBottom: 6, - }, - manualTargetInput: { - borderWidth: 1, - borderColor: 'rgba(0,0,0,0.2)', - borderRadius: 12, - paddingHorizontal: spacing.sm + 2, // 10px - paddingVertical: spacing.sm, - fontSize: typography.sizes.lg, - backgroundColor: colors.surface, - }, }) diff --git a/apps/p2p-chat/src/components/index.ts b/apps/p2p-chat/src/components/index.ts index 0f9fe09..b754d58 100644 --- a/apps/p2p-chat/src/components/index.ts +++ b/apps/p2p-chat/src/components/index.ts @@ -1,8 +1,5 @@ export {ChatCarousel} from './ChatCarousel' -export {ChatHeader} from './ChatHeader' -export {DebugInfo} from './DebugInfo' export {FriendRequestsList} from './FriendRequestsList' export {MessageInput} from './MessageInput' export {MessagesList} from './MessagesList' -export {NotificationsList} from './NotificationsList' export {TargetSelector} from './TargetSelector' diff --git a/apps/p2p-chat/src/constants.ts b/apps/p2p-chat/src/constants.ts new file mode 100644 index 0000000..47a4035 --- /dev/null +++ b/apps/p2p-chat/src/constants.ts @@ -0,0 +1,25 @@ +import {Dimensions} from 'react-native' + +const {width: screenWidth} = Dimensions.get('window') + +// Carousel slide dimensions +export const CHAT_WIDTH = screenWidth - 32 // Account for container margins +export const SLIDE_MARGIN = 8 // Horizontal margin between slides + +export const USER_THEMES = [ + {name: 'Alice', color: '#667eea'}, + {name: 'Bob', color: '#f093fb'}, + {name: 'Charlie', color: '#4facfe'}, + {name: 'Diana', color: '#43e97b'}, + {name: 'Eve', color: '#fa709a'}, + {name: 'Frank', color: '#a8edea'}, +] as const + +export const MAX_CHAT_INSTANCES = USER_THEMES.length +export const MIN_CHAT_INSTANCES = 1 + +export interface ChatMeta { + id: string + userName: string + backgroundColor: string +} diff --git a/apps/p2p-chat/src/constants/index.ts b/apps/p2p-chat/src/constants/index.ts deleted file mode 100644 index 008bc5d..0000000 --- a/apps/p2p-chat/src/constants/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type {ChatInstance} from './presets' -export { - CHAT_WIDTH, - MAX_CHAT_INSTANCES, - MIN_CHAT_INSTANCES, - PRESET_USERS, -} from './presets' diff --git a/apps/p2p-chat/src/constants/presets.ts b/apps/p2p-chat/src/constants/presets.ts deleted file mode 100644 index daf1776..0000000 --- a/apps/p2p-chat/src/constants/presets.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {Dimensions} from 'react-native' - -const {width: screenWidth} = Dimensions.get('window') - -export const CHAT_WIDTH = screenWidth - 40 // Account for padding - -export const PRESET_USERS = [ - {name: 'Alice', color: '#e3f2fd'}, - {name: 'Bob', color: '#f3e5f5'}, - {name: 'Charlie', color: '#e8f5e8'}, - {name: 'Diana', color: '#fff3e0'}, - {name: 'Eve', color: '#fce4ec'}, - {name: 'Frank', color: '#e0f2f1'}, -] as const - -export const MAX_CHAT_INSTANCES = PRESET_USERS.length -export const MIN_CHAT_INSTANCES = 1 - -export interface ChatInstance { - id: string - userName: string - backgroundColor: string -} diff --git a/apps/p2p-chat/src/hooks/index.ts b/apps/p2p-chat/src/hooks/index.ts index e58cc6d..f8a5339 100644 --- a/apps/p2p-chat/src/hooks/index.ts +++ b/apps/p2p-chat/src/hooks/index.ts @@ -2,4 +2,5 @@ export {useChatInstances} from './useChatInstances' export {useCommunication} from './useCommunication' export {useFriendRequests} from './useFriendRequests' export {useMessages} from './useMessages' +export {useScrollColorInterpolation} from './useScrollColorInterpolation' export {useTargetSelection} from './useTargetSelection' diff --git a/apps/p2p-chat/src/hooks/useChatInstances.ts b/apps/p2p-chat/src/hooks/useChatInstances.ts index 3b9b596..fecc0aa 100644 --- a/apps/p2p-chat/src/hooks/useChatInstances.ts +++ b/apps/p2p-chat/src/hooks/useChatInstances.ts @@ -1,13 +1,13 @@ import {useCallback, useRef, useState} from 'react' -import {ChatInstance, MAX_CHAT_INSTANCES, PRESET_USERS} from '../constants' +import {ChatMeta, MAX_CHAT_INSTANCES, USER_THEMES} from '../constants' import {FriendshipManager} from '../services' import {createChatInstance} from '../utils/chatHelpers' export const useChatInstances = () => { - const [chatInstances, setChatInstances] = useState([ - {id: 'alice', userName: 'Alice', backgroundColor: '#e3f2fd'}, - {id: 'bob', userName: 'Bob', backgroundColor: '#f3e5f5'}, + const [chatInstances, setChatInstances] = useState([ + createChatInstance(USER_THEMES[0].name, USER_THEMES[0].color), + createChatInstance(USER_THEMES[1].name, USER_THEMES[1].color), ]) const [currentIndex, setCurrentIndex] = useState(0) const [friendshipTrigger, setFriendshipTrigger] = useState(0) @@ -21,7 +21,7 @@ export const useChatInstances = () => { // Find the next available preset user const usedNames = new Set(chatInstances.map(chat => chat.userName)) - const availableUser = PRESET_USERS.find(user => !usedNames.has(user.name)) + const availableUser = USER_THEMES.find(user => !usedNames.has(user.name)) if (!availableUser) { console.log('No more preset users available') diff --git a/apps/p2p-chat/src/hooks/useColorAnimation.ts b/apps/p2p-chat/src/hooks/useColorAnimation.ts new file mode 100644 index 0000000..a8e68c8 --- /dev/null +++ b/apps/p2p-chat/src/hooks/useColorAnimation.ts @@ -0,0 +1,46 @@ +import {useCallback, useState} from 'react' +import {NativeScrollEvent, NativeSyntheticEvent} from 'react-native' + +import {ChatMeta} from '../constants' +import {calculateBackgroundColor, calculateSlideIndex} from '../utils' + +interface UseScrollColorInterpolationProps { + chatInstances: ChatMeta[] + scrollStep: number // Width of each scroll step (e.g., screen width for full-width slides) + onIndexChange: (index: number) => void +} + +export const useScrollColorInterpolation = ({ + chatInstances, + scrollStep, + onIndexChange, +}: UseScrollColorInterpolationProps) => { + // State for smooth color interpolation + const [currentBackgroundColor, setCurrentBackgroundColor] = useState( + chatInstances[0]?.backgroundColor || '#667eea' + ) + + const onScroll = useCallback( + (event: NativeSyntheticEvent) => { + const scrollX = event.nativeEvent.contentOffset.x + + // Update current index + const slideIndex = calculateSlideIndex(scrollX, scrollStep) + onIndexChange(slideIndex) + + // Calculate and update background color + const backgroundColor = calculateBackgroundColor( + scrollX, + scrollStep, + chatInstances + ) + setCurrentBackgroundColor(backgroundColor) + }, + [chatInstances, scrollStep, onIndexChange] + ) + + return { + currentBackgroundColor, + onScroll, + } +} diff --git a/apps/p2p-chat/src/hooks/useFriendRequests.ts b/apps/p2p-chat/src/hooks/useFriendRequests.ts index 0c3791d..ea03c80 100644 --- a/apps/p2p-chat/src/hooks/useFriendRequests.ts +++ b/apps/p2p-chat/src/hooks/useFriendRequests.ts @@ -91,16 +91,6 @@ export const useFriendRequests = ({ } break } - - case 'test_message': { - if (data.message) { - console.log( - `[${userName}] ๐Ÿงช RECEIVED TEST MESSAGE from host: ${data.message}` - ) - addNotification(`Test: ${data.message}`) - } - break - } } }, [userName, addNotification] diff --git a/apps/p2p-chat/src/hooks/useScrollColorInterpolation.ts b/apps/p2p-chat/src/hooks/useScrollColorInterpolation.ts new file mode 100644 index 0000000..a8e68c8 --- /dev/null +++ b/apps/p2p-chat/src/hooks/useScrollColorInterpolation.ts @@ -0,0 +1,46 @@ +import {useCallback, useState} from 'react' +import {NativeScrollEvent, NativeSyntheticEvent} from 'react-native' + +import {ChatMeta} from '../constants' +import {calculateBackgroundColor, calculateSlideIndex} from '../utils' + +interface UseScrollColorInterpolationProps { + chatInstances: ChatMeta[] + scrollStep: number // Width of each scroll step (e.g., screen width for full-width slides) + onIndexChange: (index: number) => void +} + +export const useScrollColorInterpolation = ({ + chatInstances, + scrollStep, + onIndexChange, +}: UseScrollColorInterpolationProps) => { + // State for smooth color interpolation + const [currentBackgroundColor, setCurrentBackgroundColor] = useState( + chatInstances[0]?.backgroundColor || '#667eea' + ) + + const onScroll = useCallback( + (event: NativeSyntheticEvent) => { + const scrollX = event.nativeEvent.contentOffset.x + + // Update current index + const slideIndex = calculateSlideIndex(scrollX, scrollStep) + onIndexChange(slideIndex) + + // Calculate and update background color + const backgroundColor = calculateBackgroundColor( + scrollX, + scrollStep, + chatInstances + ) + setCurrentBackgroundColor(backgroundColor) + }, + [chatInstances, scrollStep, onIndexChange] + ) + + return { + currentBackgroundColor, + onScroll, + } +} diff --git a/apps/p2p-chat/src/services/MessageHandler.ts b/apps/p2p-chat/src/services/MessageHandler.ts index b1ddfb1..58e3842 100644 --- a/apps/p2p-chat/src/services/MessageHandler.ts +++ b/apps/p2p-chat/src/services/MessageHandler.ts @@ -1,14 +1,20 @@ -import {ChatInstance} from '../constants' +import {ChatMeta} from '../constants' import {FriendshipManager} from './FriendshipManager' export class MessageHandler { + private scrollToChat?: (chatId: string) => void + constructor( - private chatInstances: ChatInstance[], + private chatInstances: ChatMeta[], private friendshipManager: FriendshipManager, private sandboxRefs: React.MutableRefObject>, private triggerFriendshipUpdate: () => void ) {} + setScrollToChat = (scrollFunction: (chatId: string) => void) => { + this.scrollToChat = scrollFunction + } + handleChatError = (chatId: string) => (error: any) => { console.log(`[${chatId}] Error:`, error) } @@ -65,6 +71,13 @@ export class MessageHandler { const requestId = this.friendshipManager.sendFriendRequest(chatId, target) + // Automatically scroll to target chat to make it easier to accept the invite + if (this.scrollToChat) { + setTimeout(() => { + this.scrollToChat!(target) + }, 100) // Small delay for UX + } + setTimeout(() => { const hostMessage = { type: 'friend_request', @@ -175,15 +188,4 @@ export class MessageHandler { console.error(`[Host] Target ref for ${targetId} not found`) } } - - testHostToSandboxCommunication = (targetId: string) => { - const testMessage = { - type: 'test_message', - message: `Hello from host! Time: ${new Date().toLocaleTimeString()}`, - timestamp: Date.now(), - } - - console.log(`[Host] Sending test message to ${targetId}:`, testMessage) - this.sendToSandbox(targetId, testMessage) - } } diff --git a/apps/p2p-chat/src/styles/carousel.ts b/apps/p2p-chat/src/styles/carousel.ts index cf5148d..4c16c2f 100644 --- a/apps/p2p-chat/src/styles/carousel.ts +++ b/apps/p2p-chat/src/styles/carousel.ts @@ -1,26 +1,37 @@ -import {StyleSheet} from 'react-native' +import {Dimensions, StyleSheet} from 'react-native' -import {CHAT_WIDTH} from '../constants' +import {CHAT_WIDTH, SLIDE_MARGIN} from '../constants' + +const {width: screenWidth} = Dimensions.get('window') export const carouselStyles = StyleSheet.create({ container: { flex: 1, - backgroundColor: '#f8f9fa', + backgroundColor: 'transparent', }, carouselContainer: { flex: 1, }, carousel: { flex: 1, - backgroundColor: '#e3f2fd', + backgroundColor: 'transparent', + }, + carouselContent: { + paddingHorizontal: SLIDE_MARGIN, }, chatSlide: { - width: CHAT_WIDTH, + width: screenWidth, // Full screen width for proper paging + flex: 1, + paddingHorizontal: SLIDE_MARGIN, // Use padding instead of margin + alignItems: 'center', // Center the content + justifyContent: 'center', + }, + chatContent: { + width: CHAT_WIDTH, // The actual chat content width flex: 1, - marginHorizontal: 20, backgroundColor: '#ffffff', borderRadius: 12, - marginVertical: 20, + marginVertical: 8, shadowColor: '#000', shadowOffset: {width: 0, height: 4}, shadowOpacity: 0.1, @@ -29,18 +40,20 @@ export const carouselStyles = StyleSheet.create({ overflow: 'hidden', }, chatHeader: { - flexDirection: 'row', - alignItems: 'center', + position: 'relative', backgroundColor: '#ffffff', paddingHorizontal: 15, paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: 'rgba(0,0,0,0.1)', minHeight: 60, + justifyContent: 'center', + alignItems: 'center', }, chatHeaderContent: { - flex: 1, alignItems: 'center', + justifyContent: 'center', + width: '100%', }, chatTitle: { fontSize: 18, @@ -62,6 +75,9 @@ export const carouselStyles = StyleSheet.create({ overflow: 'hidden', }, deleteButton: { + position: 'absolute', + left: 15, + top: 15, padding: 5, borderRadius: 10, backgroundColor: '#f44336', @@ -69,7 +85,7 @@ export const carouselStyles = StyleSheet.create({ height: 30, justifyContent: 'center', alignItems: 'center', - marginRight: 10, + zIndex: 1, }, deleteButtonText: { color: '#ffffff', @@ -81,10 +97,11 @@ export const carouselStyles = StyleSheet.create({ opacity: 0.5, }, addChatCard: { - flex: 1, - backgroundColor: '#ffffff', + width: '45%', // Make it 45% of the slide width + height: '60%', // Make it 60% of the slide height + backgroundColor: '#f8f9fa', borderRadius: 12, - padding: 40, + padding: 20, // Reduced padding alignItems: 'center', justifyContent: 'center', shadowColor: '#000', @@ -93,45 +110,35 @@ export const carouselStyles = StyleSheet.create({ shadowRadius: 4, elevation: 3, borderWidth: 2, - borderColor: '#4caf50', + borderColor: '#dee2e6', borderStyle: 'dashed', + alignSelf: 'center', // Center horizontally in the slide }, addChatContent: { alignItems: 'center', }, addChatIcon: { - fontSize: 40, - color: '#4caf50', - marginBottom: 10, + fontSize: 30, // Reduced from 40 + color: '#6c757d', + marginBottom: 8, // Reduced margin }, addChatIconDisabled: { opacity: 0.5, }, addChatText: { - fontSize: 18, + fontSize: 16, // Reduced from 18 fontWeight: 'bold', - color: '#4caf50', - marginBottom: 5, + color: '#6c757d', + marginBottom: 4, // Reduced margin }, addChatTextDisabled: { opacity: 0.5, }, addChatLimitText: { fontSize: 12, - color: '#4caf50', - }, - testButton: { - marginTop: 5, - paddingVertical: 3, - paddingHorizontal: 8, - backgroundColor: '#2196f3', - borderRadius: 6, - }, - testButtonText: { - color: '#ffffff', - fontSize: 10, - fontWeight: '600', + color: '#adb5bd', }, + pageIndicators: { flexDirection: 'row', justifyContent: 'center', @@ -151,10 +158,10 @@ export const carouselStyles = StyleSheet.create({ height: 8, }, info: { - backgroundColor: '#ffffff', - margin: 20, - padding: 15, - borderRadius: 12, + backgroundColor: 'rgba(255,255,255,0.9)', + margin: 10, + padding: 12, + borderRadius: 8, shadowColor: '#000', shadowOffset: {width: 0, height: 1}, shadowOpacity: 0.05, diff --git a/apps/p2p-chat/src/utils/chatHelpers.ts b/apps/p2p-chat/src/utils/chatHelpers.ts index 59d344a..4235440 100644 --- a/apps/p2p-chat/src/utils/chatHelpers.ts +++ b/apps/p2p-chat/src/utils/chatHelpers.ts @@ -1,8 +1,8 @@ -import {ChatInstance} from '../constants' +import {ChatMeta} from '../constants' import {FriendshipManager} from '../services' export const getChatHelpers = ( - chatInstances: ChatInstance[], + chatInstances: ChatMeta[], friendshipManager: FriendshipManager ) => { const getTargetOptions = (chatId: string): string[] => { @@ -30,10 +30,7 @@ export const getChatHelpers = ( } } -export const createChatInstance = ( - name: string, - color: string -): ChatInstance => ({ +export const createChatInstance = (name: string, color: string): ChatMeta => ({ id: name.toLowerCase(), userName: name, backgroundColor: color, diff --git a/apps/p2p-chat/src/utils/colorUtils.ts b/apps/p2p-chat/src/utils/colorUtils.ts new file mode 100644 index 0000000..2c5fbda --- /dev/null +++ b/apps/p2p-chat/src/utils/colorUtils.ts @@ -0,0 +1,36 @@ +// Color interpolation utility functions + +export const hexToRgb = (hex: string) => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : {r: 0, g: 0, b: 0} +} + +export const rgbToHex = (r: number, g: number, b: number) => { + return ( + '#' + + ((1 << 24) + (Math.round(r) << 16) + (Math.round(g) << 8) + Math.round(b)) + .toString(16) + .slice(1) + ) +} + +export const interpolateColor = ( + color1: string, + color2: string, + factor: number +) => { + const c1 = hexToRgb(color1) + const c2 = hexToRgb(color2) + + const r = c1.r + factor * (c2.r - c1.r) + const g = c1.g + factor * (c2.g - c1.g) + const b = c1.b + factor * (c2.b - c1.b) + + return rgbToHex(r, g, b) +} diff --git a/apps/p2p-chat/src/utils/index.ts b/apps/p2p-chat/src/utils/index.ts new file mode 100644 index 0000000..a9fbd81 --- /dev/null +++ b/apps/p2p-chat/src/utils/index.ts @@ -0,0 +1,4 @@ +// Export all utility functions +export * from './chatHelpers' +export * from './colorUtils' +export * from './scrollUtils' diff --git a/apps/p2p-chat/src/utils/scrollUtils.ts b/apps/p2p-chat/src/utils/scrollUtils.ts new file mode 100644 index 0000000..bb9bd0d --- /dev/null +++ b/apps/p2p-chat/src/utils/scrollUtils.ts @@ -0,0 +1,78 @@ +import {ChatMeta} from '../constants' +import {interpolateColor} from './colorUtils' + +export const calculateSlideIndex = ( + scrollX: number, + slideWidth: number +): number => { + return Math.round(scrollX / slideWidth) +} + +export const calculateInterpolationProgress = ( + scrollX: number, + slideWidth: number +): { + currentSlideIndex: number + factor: number +} => { + const progress = scrollX / slideWidth + const currentSlideIndex = Math.floor(progress) + const factor = progress - currentSlideIndex + + return {currentSlideIndex, factor} +} + +export const getSlideColors = ( + currentSlideIndex: number, + chatInstances: ChatMeta[] +): {currentSlideColor: string; nextSlideColor: string} => { + const addChatColor = '#f8f9fa' // Light gray for add chat slide + + let currentSlideColor = '#667eea' + let nextSlideColor = '#667eea' + + if (currentSlideIndex < chatInstances.length) { + // Normal chat instance + currentSlideColor = + chatInstances[currentSlideIndex]?.backgroundColor || '#667eea' + + if (currentSlideIndex + 1 < chatInstances.length) { + // Next slide is another chat instance + nextSlideColor = + chatInstances[currentSlideIndex + 1]?.backgroundColor || + currentSlideColor + } else { + // Next slide is the add chat slide + nextSlideColor = addChatColor + } + } else { + // Currently on add chat slide + currentSlideColor = addChatColor + nextSlideColor = addChatColor + } + + return {currentSlideColor, nextSlideColor} +} + +export const calculateBackgroundColor = ( + scrollX: number, + slideWidth: number, + chatInstances: ChatMeta[] +): string => { + const {currentSlideIndex, factor} = calculateInterpolationProgress( + scrollX, + slideWidth + ) + const {currentSlideColor, nextSlideColor} = getSlideColors( + currentSlideIndex, + chatInstances + ) + + const totalSlides = chatInstances.length + 1 // +1 for add chat slide + + if (currentSlideIndex < totalSlides - 1 && factor > 0) { + return interpolateColor(currentSlideColor, nextSlideColor, factor) + } else { + return currentSlideColor + } +} diff --git a/apps/side-by-side/ios/Podfile.lock b/apps/side-by-side/ios/Podfile.lock index 6d91ec6..16702f1 100644 --- a/apps/side-by-side/ios/Podfile.lock +++ b/apps/side-by-side/ios/Podfile.lock @@ -2019,7 +2019,7 @@ PODS: - React-timing - React-utils - SocketRocket - - React-Sandbox (0.2.0): + - React-Sandbox (0.3.0): - boost - DoubleConversion - fast_float @@ -2603,7 +2603,7 @@ SPEC CHECKSUMS: React-runtimeexecutor: 17c70842d5e611130cb66f91e247bc4a609c3508 React-RuntimeHermes: 3c88e6e1ea7ea0899dcffc77c10d61ea46688cfd React-runtimescheduler: 024500621c7c93d65371498abb4ee26d34f5d47d - React-Sandbox: 789583cf1f6db8c2bf95e54125aef4314fe9d8ca + React-Sandbox: 7f628c364e1e4f691c2f7f2eb82bd9aaca1989fa React-timing: c3c923df2b86194e1682e01167717481232f1dc7 React-utils: 9154a037543147e1c24098f1a48fc8472602c092 ReactAppDependencyProvider: afd905e84ee36e1678016ae04d7370c75ed539be From 609c671e8c941f2226b029a58279decfc9ac03b1 Mon Sep 17 00:00:00 2001 From: Aliaksandr Babrykovich Date: Thu, 7 Aug 2025 14:14:40 +0200 Subject: [PATCH 04/15] fix: deadlock because std::mutex --- .../ios/SandboxReactNativeDelegate.mm | 97 ++++++++----------- .../SandboxReactNativeViewComponentView.mm | 16 +-- 2 files changed, 42 insertions(+), 71 deletions(-) diff --git a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm index b52cdf9..86b8284 100644 --- a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm +++ b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm @@ -1,5 +1,5 @@ // -// SandboxReactNativeDelegate.h +// SandboxReactNativeDelegate.mm // react-native-sandbox // // Created by Aliaksandr Babrykovich on 25/06/2025. @@ -53,6 +53,8 @@ @interface SandboxReactNativeDelegate () { std::set _allowedModules; } +- (void)cleanupResources; + @end @implementation SandboxReactNativeDelegate @@ -61,16 +63,15 @@ @implementation SandboxReactNativeDelegate // Global static registry for sandbox communication static std::map _sandboxRegistry; -static std::mutex _registryMutex; +static std::recursive_mutex _registryMutex; + (void)registerSandbox:(NSString *)sandboxId delegate:(SandboxReactNativeDelegate *)delegate { if (!sandboxId || !delegate) { - NSLog(@"[SandboxRegistry] Cannot register sandbox: sandboxId=%@ delegate=%@", sandboxId, delegate); return; } - std::lock_guard lock(_registryMutex); + std::lock_guard lock(_registryMutex); std::string cppSandboxId = [sandboxId UTF8String]; if (_sandboxRegistry.find(cppSandboxId) != _sandboxRegistry.end()) { @@ -78,9 +79,6 @@ + (void)registerSandbox:(NSString *)sandboxId delegate:(SandboxReactNativeDelega } _sandboxRegistry[cppSandboxId] = delegate; - // delegate.sandboxId = sandboxId; - - NSLog(@"[SandboxRegistry] Registered sandbox: %@ (total: %zu)", sandboxId, _sandboxRegistry.size()); } + (void)unregisterSandbox:(NSString *)sandboxId @@ -89,14 +87,15 @@ + (void)unregisterSandbox:(NSString *)sandboxId return; } - std::lock_guard lock(_registryMutex); + std::lock_guard lock(_registryMutex); std::string cppSandboxId = [sandboxId UTF8String]; auto it = _sandboxRegistry.find(cppSandboxId); if (it != _sandboxRegistry.end()) { - it->second.sandboxId = nil; + // Clean up the delegate before removing from registry + [it->second cleanupResources]; + it->second->_sandboxId = nil; _sandboxRegistry.erase(it); - NSLog(@"[SandboxRegistry] Unregistered sandbox: %@ (total: %zu)", sandboxId, _sandboxRegistry.size()); } } @@ -106,7 +105,7 @@ + (nullable SandboxReactNativeDelegate *)getSandbox:(NSString *)sandboxId return nil; } - std::lock_guard lock(_registryMutex); + std::lock_guard lock(_registryMutex); std::string cppSandboxId = [sandboxId UTF8String]; auto it = _sandboxRegistry.find(cppSandboxId); @@ -121,12 +120,10 @@ + (BOOL)routeMessage:(NSString *)message toSandbox:(NSString *)targetId { SandboxReactNativeDelegate *target = [self getSandbox:targetId]; if (!target) { - NSLog(@"[SandboxRegistry] Cannot route message: target sandbox '%@' not found", targetId); return NO; } [target postMessage:message]; - NSLog(@"[SandboxRegistry] Routed message to sandbox: %@", targetId); return YES; } @@ -170,11 +167,19 @@ - (instancetype)init return self; } +- (void)cleanupResources +{ + _onMessageSandbox.reset(); + _rctInstance = nil; + _allowedModules.clear(); +} + - (void)dealloc { - // Auto-unregister when delegate is deallocated if (self.sandboxId) { [SandboxReactNativeDelegate unregisterSandbox:self.sandboxId]; + } else { + [self cleanupResources]; } } @@ -206,7 +211,7 @@ - (NSURL *)bundleURL - (void)postMessage:(NSString *)message { - if (!_onMessageSandbox) { + if (!_onMessageSandbox || !_rctInstance) { return; } @@ -215,6 +220,11 @@ - (void)postMessage:(NSString *)message // Validate runtime before any JSI operations runtime.global(); // Test if runtime is accessible + // Double-check the JSI function is still valid + if (!_onMessageSandbox) { + return; + } + std::string jsonString = [message UTF8String]; jsi::Value parsedValue = runtime.global() @@ -241,38 +251,27 @@ - (void)postMessage:(NSString *)message }]; } -- (BOOL)isCommunicationReady -{ - BOOL hasEventEmitter = (self.eventEmitter != nullptr); - BOOL hasMessageSandbox = (_onMessageSandbox != nullptr); - BOOL isRegistered = (self.sandboxId != nil); - - NSLog( - @"[SandboxReactNativeDelegate] Communication status for %@: eventEmitter=%@, messageSandbox=%@, registered=%@", - self.sandboxId ?: @"(nil)", - hasEventEmitter ? @"YES" : @"NO", - hasMessageSandbox ? @"YES" : @"NO", - isRegistered ? @"YES" : @"NO"); - - return hasEventEmitter && hasMessageSandbox && isRegistered; -} - - (void)hostDidStart:(RCTHost *)host { - // Safely clear any existing JSI function before new runtime setup - // This prevents crash on reload when old function is tied to invalid runtime - try { - _onMessageSandbox = nullptr; - } catch (...) { - // Old function destructor might crash if tied to invalid runtime - NSLog( - @"[SandboxReactNativeDelegate] Warning: Exception while clearing old JSI function for sandbox %@", - self.sandboxId); + if (!host) { + return; } + // Safely clear any existing JSI function and instance before new runtime setup + // This prevents crash on reload when old function is tied to invalid runtime + _onMessageSandbox.reset(); + _onMessageSandbox = nullptr; + + // Clear old instance reference before setting new one + _rctInstance = nil; + Ivar ivar = class_getInstanceVariable([host class], "_instance"); _rctInstance = object_getIvar(host, ivar); + if (!_rctInstance) { + return; + } + [_rctInstance callFunctionOnBufferedRuntimeExecutor:[=](jsi::Runtime &runtime) { facebook::react::defineReadOnlyGlobal( runtime, @@ -286,7 +285,6 @@ - (void)hostDidStart:(RCTHost *)host try { rt.global(); // Test if runtime is accessible } catch (...) { - NSLog(@"[SandboxReactNativeDelegate] Runtime invalid in postMessage for sandbox %@", self.sandboxId); return jsi::Value::undefined(); } @@ -350,17 +348,6 @@ - (void)hostDidStart:(RCTHost *)host SandboxReactNativeViewEventEmitter::OnMessage messageEvent = { .data = jsi::dynamicFromValue(rt, args[0])}; self.eventEmitter->onMessage(messageEvent); - } else { - // Log why message to host failed - if (!self.eventEmitter) { - NSLog( - @"[SandboxReactNativeDelegate] Cannot send message to host: eventEmitter is nullptr for sandbox %@", - self.sandboxId); - } else if (!self.hasOnMessageHandler) { - NSLog( - @"[SandboxReactNativeDelegate] Cannot send message to host: no message handler for sandbox %@", - self.sandboxId); - } } } @@ -383,8 +370,6 @@ - (void)hostDidStart:(RCTHost *)host throw jsi::JSError(rt, "Expected a function as the first argument"); } - NSLog(@"[SandboxReactNativeDelegate] setOnMessage for sandbox %@", self.sandboxId); - jsi::Function fn = arg.asObject(rt).asFunction(rt); // Safely reset existing function before assigning new one @@ -451,7 +436,6 @@ - (void)hostDidStart:(RCTHost *)host - (id)getModuleProvider:(const char *)name { - NSLog(@"SandboxReactNativeDelegate.getModuleProvider %s", name); return _allowedModules.contains(name) ? [super getModuleProvider:name] : nullptr; } @@ -461,9 +445,6 @@ - (void)hostDidStart:(RCTHost *)host if (_allowedModules.contains(name)) { return [super getTurboModule:name jsInvoker:jsInvoker]; } else { - NSLog( - @"SandboxReactNativeDelegate.getTurboModule: blocking access to C++ TurboModule '%s', returning C++ stub", - name.c_str()); // Return C++ stub instead of nullptr return std::make_shared(name, jsInvoker); } diff --git a/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm b/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm index a143a1c..71a8a59 100644 --- a/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm +++ b/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm @@ -107,18 +107,7 @@ - (void)updateEventEmitterIfNeeded if (self.reactNativeDelegate && _eventEmitter) { if (auto eventEmitter = std::static_pointer_cast(_eventEmitter)) { self.reactNativeDelegate.eventEmitter = eventEmitter; - NSLog( - @"[SandboxReactNativeViewComponentView] EventEmitter set for sandbox: %@", - self.reactNativeDelegate.sandboxId); - } else { - NSLog( - @"[SandboxReactNativeViewComponentView] Failed to cast eventEmitter for sandbox: %@", - self.reactNativeDelegate.sandboxId); } - } else { - NSLog( - @"[SandboxReactNativeViewComponentView] EventEmitter not available yet for sandbox: %@", - self.reactNativeDelegate.sandboxId); } } @@ -187,10 +176,11 @@ - (void)loadReactNativeView - (void)prepareForRecycle { [super prepareForRecycle]; + [self.reactNativeRootView removeFromSuperview]; self.reactNativeRootView = nil; - self.reactNativeDelegate = nil; - self.reactNativeFactory = nil; + + // Keep the delegate for reuse - it holds configuration and is designed to be persistent } Class SandboxReactNativeViewCls(void) From 6448b25165ae71a6b64202b0d387cf66627ebace Mon Sep 17 00:00:00 2001 From: Aliaksandr Babrykovich Date: Thu, 7 Aug 2025 22:21:22 +0200 Subject: [PATCH 05/15] chore: extract SandboxRegistry from SandboxReactNativeDelegate + tests --- .../ios/SandboxReactNativeDelegate.h | 40 +- .../ios/SandboxReactNativeDelegate.mm | 412 ++++++++---------- .../SandboxReactNativeViewComponentView.mm | 2 +- .../ios/SandboxRegistry.h | 50 +++ .../ios/SandboxRegistry.mm | 81 ++++ packages/react-native-sandbox/package.json | 7 +- .../react-native-sandbox/tests/CMakeLists.txt | 95 ++++ .../react-native-sandbox/tests/Info.plist | 26 ++ packages/react-native-sandbox/tests/README.md | 112 +++++ .../tests/SandboxRegistryTests.mm | 121 +++++ packages/react-native-sandbox/tests/main.m | 41 ++ 11 files changed, 723 insertions(+), 264 deletions(-) create mode 100644 packages/react-native-sandbox/ios/SandboxRegistry.h create mode 100644 packages/react-native-sandbox/ios/SandboxRegistry.mm create mode 100644 packages/react-native-sandbox/tests/CMakeLists.txt create mode 100644 packages/react-native-sandbox/tests/Info.plist create mode 100644 packages/react-native-sandbox/tests/README.md create mode 100644 packages/react-native-sandbox/tests/SandboxRegistryTests.mm create mode 100644 packages/react-native-sandbox/tests/main.m diff --git a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.h b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.h index c642a48..3bc34ee 100644 --- a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.h +++ b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.h @@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN * A React Native delegate that provides sandboxed environments with filtered module access. * This delegate uses RCTFilteredAppDependencyProvider to restrict which native modules * are available to the JavaScript runtime, enhancing security in multi-instance scenarios. + * + * Registry functionality has been moved to SandboxRegistry class to maintain single responsibility. */ @interface SandboxReactNativeDelegate : RCTDefaultReactNativeFactoryDelegate @@ -45,40 +47,12 @@ NS_ASSUME_NONNULL_BEGIN - (void)postMessage:(NSString *)message; /** - * Checks if the delegate is properly configured for bidirectional communication - * @return YES if both sandboxโ†’host and hostโ†’sandbox communication should work + * Routes a message to a specific sandbox delegate. + * @param message The message to route + * @param targetId The ID of the target sandbox + * @return YES if the message was successfully routed, NO otherwise */ -- (BOOL)isCommunicationReady; - -#pragma mark - Static Registry Methods - -/** - * Registers a sandbox delegate with the global registry - * @param sandboxId Unique identifier for the sandbox - * @param delegate The delegate instance to register - */ -+ (void)registerSandbox:(NSString *)sandboxId delegate:(SandboxReactNativeDelegate *)delegate; - -/** - * Unregisters a sandbox delegate from the global registry - * @param sandboxId The sandbox identifier to unregister - */ -+ (void)unregisterSandbox:(NSString *)sandboxId; - -/** - * Retrieves a sandbox delegate by ID - * @param sandboxId The sandbox identifier to look up - * @return The delegate instance or nil if not found - */ -+ (nullable SandboxReactNativeDelegate *)getSandbox:(NSString *)sandboxId; - -/** - * Routes a message to a specific sandbox - * @param message The message to send - * @param targetId The target sandbox identifier - * @return YES if message was routed successfully, NO if target not found - */ -+ (BOOL)routeMessage:(NSString *)message toSandbox:(NSString *)targetId; +- (BOOL)routeMessage:(NSString *)message toSandbox:(NSString *)targetId; @end diff --git a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm index 86b8284..cab816e 100644 --- a/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm +++ b/packages/react-native-sandbox/ios/SandboxReactNativeDelegate.mm @@ -6,7 +6,6 @@ // #import "SandboxReactNativeDelegate.h" -#import "StubTurboModuleCxx.h" #include #include @@ -23,6 +22,9 @@ #import +#import "SandboxRegistry.h" +#import "StubTurboModuleCxx.h" + namespace jsi = facebook::jsi; namespace TurboModuleConvertUtils = facebook::react::TurboModuleConvertUtils; using namespace facebook::react; @@ -55,77 +57,16 @@ @interface SandboxReactNativeDelegate () { - (void)cleanupResources; +- (jsi::Function)createPostMessageFunction:(jsi::Runtime &)runtime; +- (jsi::Function)createSetOnMessageFunction:(jsi::Runtime &)runtime; +- (void)setupErrorHandler:(jsi::Runtime &)runtime; + @end @implementation SandboxReactNativeDelegate -#pragma mark - Static Registry Implementation - -// Global static registry for sandbox communication -static std::map _sandboxRegistry; -static std::recursive_mutex _registryMutex; - -+ (void)registerSandbox:(NSString *)sandboxId delegate:(SandboxReactNativeDelegate *)delegate -{ - if (!sandboxId || !delegate) { - return; - } - - std::lock_guard lock(_registryMutex); - std::string cppSandboxId = [sandboxId UTF8String]; - - if (_sandboxRegistry.find(cppSandboxId) != _sandboxRegistry.end()) { - NSLog(@"[SandboxRegistry] Warning: Overwriting existing sandbox with ID: %@", sandboxId); - } - - _sandboxRegistry[cppSandboxId] = delegate; -} - -+ (void)unregisterSandbox:(NSString *)sandboxId -{ - if (!sandboxId) { - return; - } - - std::lock_guard lock(_registryMutex); - std::string cppSandboxId = [sandboxId UTF8String]; - - auto it = _sandboxRegistry.find(cppSandboxId); - if (it != _sandboxRegistry.end()) { - // Clean up the delegate before removing from registry - [it->second cleanupResources]; - it->second->_sandboxId = nil; - _sandboxRegistry.erase(it); - } -} - -+ (nullable SandboxReactNativeDelegate *)getSandbox:(NSString *)sandboxId -{ - if (!sandboxId) { - return nil; - } - - std::lock_guard lock(_registryMutex); - std::string cppSandboxId = [sandboxId UTF8String]; - - auto it = _sandboxRegistry.find(cppSandboxId); - if (it != _sandboxRegistry.end()) { - return it->second; - } - - return nil; -} - -+ (BOOL)routeMessage:(NSString *)message toSandbox:(NSString *)targetId -{ - SandboxReactNativeDelegate *target = [self getSandbox:targetId]; - if (!target) { - return NO; - } - - [target postMessage:message]; - return YES; -} +// Note: Registry functionality has been moved to SandboxRegistry class +// This class now focuses solely on delegate responsibilities #pragma mark - Instance Methods @@ -137,7 +78,7 @@ - (void)setSandboxId:(NSString *)sandboxId // Unregister old ID if it exists if (_sandboxId) { - [SandboxReactNativeDelegate unregisterSandbox:_sandboxId]; + [[SandboxRegistry shared] unregister:_sandboxId]; } // Set new ID @@ -145,7 +86,7 @@ - (void)setSandboxId:(NSString *)sandboxId // Register new ID if it's not nil if (_sandboxId) { - [SandboxReactNativeDelegate registerSandbox:_sandboxId delegate:self]; + [[SandboxRegistry shared] register:_sandboxId delegate:self]; } } @@ -177,7 +118,7 @@ - (void)cleanupResources - (void)dealloc { if (self.sandboxId) { - [SandboxReactNativeDelegate unregisterSandbox:self.sandboxId]; + [[SandboxRegistry shared] unregister:self.sandboxId]; } else { [self cleanupResources]; } @@ -251,6 +192,17 @@ - (void)postMessage:(NSString *)message }]; } +- (BOOL)routeMessage:(NSString *)message toSandbox:(NSString *)targetId +{ + id target = [[SandboxRegistry shared] find:targetId]; + if (!target || ![target respondsToSelector:@selector(postMessage:)]) { + return NO; + } + + [target postMessage:message]; + return YES; +} + - (void)hostDidStart:(RCTHost *)host { if (!host) { @@ -273,162 +225,9 @@ - (void)hostDidStart:(RCTHost *)host } [_rctInstance callFunctionOnBufferedRuntimeExecutor:[=](jsi::Runtime &runtime) { - facebook::react::defineReadOnlyGlobal( - runtime, - "postMessage", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "postMessage"), - 2, // Updated to accept up to 2 arguments - [=](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) { - // Validate runtime before any JSI operations - try { - rt.global(); // Test if runtime is accessible - } catch (...) { - return jsi::Value::undefined(); - } - - if (count < 1 || count > 2) { - throw jsi::JSError(rt, "Expected 1 or 2 arguments: postMessage(message, targetOrigin?)"); - } - - const jsi::Value &messageArg = args[0]; - if (!messageArg.isObject()) { - throw jsi::JSError(rt, "Expected an object as the first argument"); - } - - // Check if targetOrigin is provided - if (count == 2 && !args[1].isNull() && !args[1].isUndefined()) { - const jsi::Value &targetOriginArg = args[1]; - if (!targetOriginArg.isString()) { - throw jsi::JSError(rt, "Expected a string as the second argument (targetOrigin)"); - } - - std::string targetOrigin = targetOriginArg.getString(rt).utf8(rt); - NSString *targetOriginNS = [NSString stringWithUTF8String:targetOrigin.c_str()]; - - // Prevent self-targeting - if (self.sandboxId && [self.sandboxId isEqualToString:targetOriginNS]) { - if (self.eventEmitter && self.hasOnErrorHandler) { - std::string errorMessage = "Cannot send message to self (sandbox '" + targetOrigin + "')"; - SandboxReactNativeViewEventEmitter::OnError errorEvent = { - .isFatal = false, .name = "SelfTargetingError", .message = errorMessage, .stack = ""}; - self.eventEmitter->onError(errorEvent); - } else { - // Fallback: throw JSError if no error handler - throw jsi::JSError(rt, ("Cannot send message to self (sandbox '" + targetOrigin + "')").c_str()); - } - return jsi::Value::undefined(); - } - - // Convert message to JSON string - jsi::Object jsonObject = rt.global().getPropertyAsObject(rt, "JSON"); - jsi::Function jsonStringify = jsonObject.getPropertyAsFunction(rt, "stringify"); - jsi::Value jsonResult = jsonStringify.call(rt, messageArg); - std::string messageJson = jsonResult.getString(rt).utf8(rt); - NSString *messageNS = [NSString stringWithUTF8String:messageJson.c_str()]; - - // Route message to specific sandbox - BOOL success = [SandboxReactNativeDelegate routeMessage:messageNS toSandbox:targetOriginNS]; - if (!success) { - // Target sandbox doesn't exist - trigger error event - if (self.eventEmitter && self.hasOnErrorHandler) { - std::string errorMessage = "Target sandbox '" + targetOrigin + "' not found"; - SandboxReactNativeViewEventEmitter::OnError errorEvent = { - .isFatal = false, .name = "SandboxRoutingError", .message = errorMessage, .stack = ""}; - self.eventEmitter->onError(errorEvent); - } else { - // Fallback: throw JSError if no error handler - throw jsi::JSError(rt, ("Target sandbox '" + targetOrigin + "' not found").c_str()); - } - } - } else { - // targetOrigin is undefined/null - route to host (backward compatibility) - if (self.eventEmitter && self.hasOnMessageHandler) { - SandboxReactNativeViewEventEmitter::OnMessage messageEvent = { - .data = jsi::dynamicFromValue(rt, args[0])}; - self.eventEmitter->onMessage(messageEvent); - } - } - - return jsi::Value::undefined(); - })); - facebook::react::defineReadOnlyGlobal( - runtime, - "setOnMessage", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "setOnMessage"), - 1, - [=](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) { - if (count != 1) { - throw jsi::JSError(rt, "Expected exactly one argument"); - } - - const jsi::Value &arg = args[0]; - if (!arg.isObject() || !arg.asObject(rt).isFunction(rt)) { - throw jsi::JSError(rt, "Expected a function as the first argument"); - } - - jsi::Function fn = arg.asObject(rt).asFunction(rt); - - // Safely reset existing function before assigning new one - // This prevents crash if old function is tied to invalid runtime - _onMessageSandbox.reset(); - _onMessageSandbox = std::make_shared(std::move(fn)); - - return jsi::Value::undefined(); - })); - - // Get ErrorUtils - jsi::Object global = runtime.global(); - jsi::Value errorUtilsVal = global.getProperty(runtime, "ErrorUtils"); - if (!errorUtilsVal.isObject()) { - throw std::runtime_error("ErrorUtils is not available on global object"); - } - - jsi::Object errorUtils = errorUtilsVal.asObject(runtime); - - std::shared_ptr originalHandler = std::make_shared( - errorUtils.getProperty(runtime, "getGlobalHandler").asObject(runtime).asFunction(runtime).call(runtime)); - - auto handlerFunc = jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "customGlobalErrorHandler"), - 2, - [=, originalHandler = std::move(originalHandler)]( - jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { - if (count < 2) { - return jsi::Value::undefined(); - } - - if (self.eventEmitter && self.hasOnErrorHandler) { - const jsi::Object &error = args[0].asObject(rt); - bool isFatal = args[1].getBool(); - - SandboxReactNativeViewEventEmitter::OnError errorEvent = { - .isFatal = isFatal, - .name = safeGetStringProperty(rt, error, "name"), - .message = safeGetStringProperty(rt, error, "message"), - .stack = safeGetStringProperty(rt, error, "stack")}; - self.eventEmitter->onError(errorEvent); - } else { // Call the original handler - if (originalHandler->isObject() && originalHandler->asObject(rt).isFunction(rt)) { - jsi::Function original = originalHandler->asObject(rt).asFunction(rt); - original.call(rt, args, count); - } - } - - return jsi::Value::undefined(); - }); - - // Set the new global error handler - jsi::Function setHandler = - errorUtils.getProperty(runtime, "setGlobalHandler").asObject(runtime).asFunction(runtime); - setHandler.call(runtime, {std::move(handlerFunc)}); - - // Disable further setGlobalHandler from sandbox - stubJsiFunction(runtime, errorUtils, "setGlobalHandler"); + facebook::react::defineReadOnlyGlobal(runtime, "postMessage", [self createPostMessageFunction:runtime]); + facebook::react::defineReadOnlyGlobal(runtime, "setOnMessage", [self createSetOnMessageFunction:runtime]); + [self setupErrorHandler:runtime]; }]; } @@ -450,4 +249,163 @@ - (void)hostDidStart:(RCTHost *)host } } +- (jsi::Function)createPostMessageFunction:(jsi::Runtime &)runtime +{ + return jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, "postMessage"), + 2, // Updated to accept up to 2 arguments + [=](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) { + // Validate runtime before any JSI operations + try { + rt.global(); // Test if runtime is accessible + } catch (...) { + return jsi::Value::undefined(); + } + + if (count < 1 || count > 2) { + throw jsi::JSError(rt, "Expected 1 or 2 arguments: postMessage(message, targetOrigin?)"); + } + + const jsi::Value &messageArg = args[0]; + if (!messageArg.isObject()) { + throw jsi::JSError(rt, "Expected an object as the first argument"); + } + + // Check if targetOrigin is provided + if (count == 2 && !args[1].isNull() && !args[1].isUndefined()) { + const jsi::Value &targetOriginArg = args[1]; + if (!targetOriginArg.isString()) { + throw jsi::JSError(rt, "Expected a string as the second argument (targetOrigin)"); + } + + std::string targetOrigin = targetOriginArg.getString(rt).utf8(rt); + NSString *targetOriginNS = [NSString stringWithUTF8String:targetOrigin.c_str()]; + + // Prevent self-targeting + if (self.sandboxId && [self.sandboxId isEqualToString:targetOriginNS]) { + if (self.eventEmitter && self.hasOnErrorHandler) { + std::string errorMessage = "Cannot send message to self (sandbox '" + targetOrigin + "')"; + SandboxReactNativeViewEventEmitter::OnError errorEvent = { + .isFatal = false, .name = "SelfTargetingError", .message = errorMessage, .stack = ""}; + self.eventEmitter->onError(errorEvent); + } else { + // Fallback: throw JSError if no error handler + throw jsi::JSError(rt, ("Cannot send message to self (sandbox '" + targetOrigin + "')").c_str()); + } + return jsi::Value::undefined(); + } + + // Convert message to JSON string + jsi::Object jsonObject = rt.global().getPropertyAsObject(rt, "JSON"); + jsi::Function jsonStringify = jsonObject.getPropertyAsFunction(rt, "stringify"); + jsi::Value jsonResult = jsonStringify.call(rt, messageArg); + std::string messageJson = jsonResult.getString(rt).utf8(rt); + NSString *messageNS = [NSString stringWithUTF8String:messageJson.c_str()]; + + // Route message to specific sandbox + BOOL success = [self routeMessage:messageNS toSandbox:targetOriginNS]; + if (!success) { + // Target sandbox doesn't exist - trigger error event + if (self.eventEmitter && self.hasOnErrorHandler) { + std::string errorMessage = "Target sandbox '" + targetOrigin + "' not found"; + SandboxReactNativeViewEventEmitter::OnError errorEvent = { + .isFatal = false, .name = "SandboxRoutingError", .message = errorMessage, .stack = ""}; + self.eventEmitter->onError(errorEvent); + } else { + // Fallback: throw JSError if no error handler + throw jsi::JSError(rt, ("Target sandbox '" + targetOrigin + "' not found").c_str()); + } + } + } else { + // targetOrigin is undefined/null - route to host (backward compatibility) + if (self.eventEmitter && self.hasOnMessageHandler) { + SandboxReactNativeViewEventEmitter::OnMessage messageEvent = {.data = jsi::dynamicFromValue(rt, args[0])}; + self.eventEmitter->onMessage(messageEvent); + } + } + + return jsi::Value::undefined(); + }); +} + +- (jsi::Function)createSetOnMessageFunction:(jsi::Runtime &)runtime +{ + return jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, "setOnMessage"), + 1, + [=](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) { + if (count != 1) { + throw jsi::JSError(rt, "Expected exactly one argument"); + } + + const jsi::Value &arg = args[0]; + if (!arg.isObject() || !arg.asObject(rt).isFunction(rt)) { + throw jsi::JSError(rt, "Expected a function as the first argument"); + } + + jsi::Function fn = arg.asObject(rt).asFunction(rt); + + // Safely reset existing function before assigning new one + // This prevents crash if old function is tied to invalid runtime + _onMessageSandbox.reset(); + _onMessageSandbox = std::make_shared(std::move(fn)); + + return jsi::Value::undefined(); + }); +} + +- (void)setupErrorHandler:(jsi::Runtime &)runtime +{ + // Get ErrorUtils + jsi::Object global = runtime.global(); + jsi::Value errorUtilsVal = global.getProperty(runtime, "ErrorUtils"); + if (!errorUtilsVal.isObject()) { + throw std::runtime_error("ErrorUtils is not available on global object"); + } + + jsi::Object errorUtils = errorUtilsVal.asObject(runtime); + + std::shared_ptr originalHandler = std::make_shared( + errorUtils.getProperty(runtime, "getGlobalHandler").asObject(runtime).asFunction(runtime).call(runtime)); + + auto handlerFunc = jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, "customGlobalErrorHandler"), + 2, + [=, originalHandler = std::move(originalHandler)]( + jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + if (count < 2) { + return jsi::Value::undefined(); + } + + if (self.eventEmitter && self.hasOnErrorHandler) { + const jsi::Object &error = args[0].asObject(rt); + bool isFatal = args[1].getBool(); + + SandboxReactNativeViewEventEmitter::OnError errorEvent = { + .isFatal = isFatal, + .name = safeGetStringProperty(rt, error, "name"), + .message = safeGetStringProperty(rt, error, "message"), + .stack = safeGetStringProperty(rt, error, "stack")}; + self.eventEmitter->onError(errorEvent); + } else { // Call the original handler + if (originalHandler->isObject() && originalHandler->asObject(rt).isFunction(rt)) { + jsi::Function original = originalHandler->asObject(rt).asFunction(rt); + original.call(rt, args, count); + } + } + + return jsi::Value::undefined(); + }); + + // Set the new global error handler + jsi::Function setHandler = errorUtils.getProperty(runtime, "setGlobalHandler").asObject(runtime).asFunction(runtime); + setHandler.call(runtime, {std::move(handlerFunc)}); + + // Disable further setGlobalHandler from sandbox + stubJsiFunction(runtime, errorUtils, "setGlobalHandler"); +} + @end diff --git a/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm b/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm index 71a8a59..fa576e6 100644 --- a/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm +++ b/packages/react-native-sandbox/ios/SandboxReactNativeViewComponentView.mm @@ -43,7 +43,7 @@ - (instancetype)initWithFrame:(CGRect)frame return self; } -- (void)updateEventEmitter:(std::shared_ptr)eventEmitter +- (void)updateEventEmitter:(const facebook::react::EventEmitter::Shared &)eventEmitter { [super updateEventEmitter:eventEmitter]; diff --git a/packages/react-native-sandbox/ios/SandboxRegistry.h b/packages/react-native-sandbox/ios/SandboxRegistry.h new file mode 100644 index 0000000..513c2ac --- /dev/null +++ b/packages/react-native-sandbox/ios/SandboxRegistry.h @@ -0,0 +1,50 @@ +// +// SandboxRegistry.h +// react-native-sandbox +// +// Created by Aliaksandr Babrykovich on 25/06/2025. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A singleton registry for managing sandbox delegates across multiple instances. + * This class provides thread-safe registration, unregistration, and retrieval of delegates + * for inter-sandbox communication. + * + * The registry is type-agnostic and works with any delegate type that supports + * the required communication protocol. + */ +@interface SandboxRegistry : NSObject + +/** + * Returns the shared singleton instance of the registry. + * @return The shared registry instance + */ ++ (instancetype)shared; + +/** + * Registers a delegate with the specified ID. + * @param sandboxId Unique identifier for the sandbox + * @param delegate The delegate instance to register (any type) + */ +- (void)register:(NSString *)sandboxId delegate:(id)delegate; + +/** + * Unregisters a delegate by ID. + * @param sandboxId The ID of the sandbox to unregister + */ +- (void)unregister:(NSString *)sandboxId; + +/** + * Finds a delegate by ID. + * @param sandboxId The ID of the sandbox to find + * @return The registered delegate, or nil if not found + */ +- (nullable id)find:(NSString *)sandboxId; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/packages/react-native-sandbox/ios/SandboxRegistry.mm b/packages/react-native-sandbox/ios/SandboxRegistry.mm new file mode 100644 index 0000000..0458806 --- /dev/null +++ b/packages/react-native-sandbox/ios/SandboxRegistry.mm @@ -0,0 +1,81 @@ +// +// SandboxRegistry.mm +// react-native-sandbox +// +// Created by Aliaksandr Babrykovich on 25/06/2025. +// + +#import "SandboxRegistry.h" + +#include +#include + +@implementation SandboxRegistry + +#pragma mark - Singleton Implementation + ++ (instancetype)shared +{ + static SandboxRegistry *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + +#pragma mark - Instance Registry Implementation + +// Global static registry for sandbox communication +static std::map _sandboxRegistry; +static std::recursive_mutex _registryMutex; + +- (void)register:(NSString *)sandboxId delegate:(id)delegate +{ + if (!sandboxId || !delegate) { + return; + } + + std::lock_guard lock(_registryMutex); + std::string cppSandboxId = [sandboxId UTF8String]; + + if (_sandboxRegistry.find(cppSandboxId) != _sandboxRegistry.end()) { + NSLog(@"[SandboxRegistry] Warning: Overwriting existing sandbox with ID: %@", sandboxId); + } + + _sandboxRegistry[cppSandboxId] = delegate; +} + +- (void)unregister:(NSString *)sandboxId +{ + if (!sandboxId) { + return; + } + + std::lock_guard lock(_registryMutex); + std::string cppSandboxId = [sandboxId UTF8String]; + + auto it = _sandboxRegistry.find(cppSandboxId); + if (it != _sandboxRegistry.end()) { + _sandboxRegistry.erase(it); + } +} + +- (nullable id)find:(NSString *)sandboxId +{ + if (!sandboxId) { + return nil; + } + + std::lock_guard lock(_registryMutex); + std::string cppSandboxId = [sandboxId UTF8String]; + + auto it = _sandboxRegistry.find(cppSandboxId); + if (it != _sandboxRegistry.end()) { + return it->second; + } + + return nil; +} + +@end \ No newline at end of file diff --git a/packages/react-native-sandbox/package.json b/packages/react-native-sandbox/package.json index 5e0a494..271b71a 100644 --- a/packages/react-native-sandbox/package.json +++ b/packages/react-native-sandbox/package.json @@ -28,10 +28,11 @@ "React-Sandbox.podspec" ], "scripts": { - "lint": "clang-format --dry-run --Werror ios/*.{h,mm}", - "format": "clang-format -i ios/*.{h,mm}", + "lint": "clang-format --dry-run --Werror ios/*.{h,mm} tests/*.{m,mm}", + "format": "clang-format -i ios/*.{h,mm} tests/*.{m,mm}", "typecheck": "tsc --noEmit", - "prepare": "bob build" + "prepare": "bob build", + "xctest": "cmake -S tests -B build && cmake --build build --target test" }, "react-native-builder-bob": { "source": "src", diff --git a/packages/react-native-sandbox/tests/CMakeLists.txt b/packages/react-native-sandbox/tests/CMakeLists.txt new file mode 100644 index 0000000..1436ab0 --- /dev/null +++ b/packages/react-native-sandbox/tests/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.16) +project(SandboxRegistryTests) + +# Set C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Set build type if not specified +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() + +# Set source files +set(TEST_SOURCES + SandboxRegistryTests.mm + main.m + ../ios/SandboxRegistry.mm +) + +# Set include directories +set(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/../ios + ${CMAKE_CURRENT_SOURCE_DIR} +) + +# Create test executable +add_executable(SandboxRegistryTests ${TEST_SOURCES}) + +# Set include directories +target_include_directories(SandboxRegistryTests PRIVATE ${INCLUDE_DIRS}) + +# Find and link frameworks +find_library(FOUNDATION_FRAMEWORK Foundation) +find_library(XCTEST_FRAMEWORK XCTest) + +if(FOUNDATION_FRAMEWORK) + target_link_libraries(SandboxRegistryTests ${FOUNDATION_FRAMEWORK}) +else() + # Fallback for systems where find_library doesn't work + target_link_libraries(SandboxRegistryTests "-framework Foundation") +endif() + +if(XCTEST_FRAMEWORK) + target_link_libraries(SandboxRegistryTests ${XCTEST_FRAMEWORK}) +else() + # Fallback for systems where find_library doesn't work + target_link_libraries(SandboxRegistryTests "-framework XCTest") +endif() + +# Set compiler flags for macOS/iOS +if(APPLE) + target_compile_options(SandboxRegistryTests PRIVATE + -Wall + -Wextra + -fobjc-arc + ) + + # Set deployment target (create simple executable, not bundle) + set_target_properties(SandboxRegistryTests PROPERTIES + MACOSX_BUNDLE FALSE + ) +endif() + +# Add test +add_test(NAME SandboxRegistryTests COMMAND SandboxRegistryTests) + +# Set test properties +set_tests_properties(SandboxRegistryTests PROPERTIES + ENVIRONMENT "DYLD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib" +) + +# Custom target for running tests (build + run) +add_custom_target(test + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target SandboxRegistryTests + COMMAND ${CMAKE_BINARY_DIR}/SandboxRegistryTests + DEPENDS SandboxRegistryTests + COMMENT "Building and running SandboxRegistry tests" +) + +# Custom target for just building +# add_custom_target(build-sandbox-registry +# COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target SandboxRegistryTests +# DEPENDS SandboxRegistryTests +# COMMENT "Building SandboxRegistry tests" +# ) + +# Print configuration info +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS "C++ standard: ${CMAKE_CXX_STANDARD}") +message(STATUS "Source files: ${TEST_SOURCES}") +message(STATUS "Include directories: ${INCLUDE_DIRS}") +message(STATUS "Foundation framework: ${FOUNDATION_FRAMEWORK}") +message(STATUS "XCTest framework: ${XCTEST_FRAMEWORK}") + + \ No newline at end of file diff --git a/packages/react-native-sandbox/tests/Info.plist b/packages/react-native-sandbox/tests/Info.plist new file mode 100644 index 0000000..98edc08 --- /dev/null +++ b/packages/react-native-sandbox/tests/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + SandboxRegistryTests + CFBundleIdentifier + com.example.SandboxRegistryTests + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + SandboxRegistryTests + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.15 + NSHighResolutionCapable + + + diff --git a/packages/react-native-sandbox/tests/README.md b/packages/react-native-sandbox/tests/README.md new file mode 100644 index 0000000..2517f1f --- /dev/null +++ b/packages/react-native-sandbox/tests/README.md @@ -0,0 +1,112 @@ +# SandboxRegistry Tests + +This directory contains tests for the `SandboxRegistry` class, which was separated from `SandboxReactNativeDelegate` to maintain single responsibility principle. + +## Test Structure + +- `SandboxRegistryTests.mm` - Unit tests using XCTest framework +- `CMakeLists.txt` - Complete CMake build system + +## Running Tests + +### Using NPM/Bun (Recommended) + +```bash +# From project root +npm run test:sandbox-registry +# or +bun run test:sandbox-registry +``` + +### Using CMake Directly + +```bash +cd packages/react-native-sandbox/ios/tests + +# Modern CMake approach (creates build directory automatically) +cmake -S . -B build +cmake --build build --target test-sandbox-registry + +# Or run tests directly +cmake --build build +./build/SandboxRegistryTests +``` + +### Alternative: Traditional CMake + +```bash +cd packages/react-native-sandbox/ios/tests + +# Create build directory and configure +mkdir build && cd build +cmake .. +make test-sandbox-registry + +# Or just build +make build-sandbox-registry +``` + +## Test Coverage + +The tests verify: + +1. **Registry Registration** - Testing `registerSandbox:delegate:` +2. **Registry Retrieval** - Testing `getSandbox:` +3. **Registry Unregistration** - Testing `unregisterSandbox:` +4. **Message Routing** - Testing `routeMessage:toSandbox:` +5. **Separation of Concerns** - Verifying that registry functionality is properly separated from delegate functionality +6. **Concurrent Access** - Testing thread safety of the registry + +## Build System Features + +The CMake build system provides: + +- **Framework Detection** - Automatically finds and links Foundation and XCTest frameworks +- **Cross-Platform Support** - Works on macOS and iOS +- **Custom Targets** - `test-sandbox-registry` for build+run, `build-sandbox-registry` for build only +- **Modern CMake** - Uses `-S` and `-B` flags for automatic directory management +- **Compiler Flags** - Sets appropriate flags for Objective-C++ compilation +- **Error Handling** - Comprehensive error checking and fallbacks + +## Test Dependencies + +The tests require: +- Foundation framework (automatically detected) +- XCTest framework (automatically detected) +- CMake 3.16 or later +- C++17 compatible compiler + +## Build Output + +Tests are compiled to `build/SandboxRegistryTests` and can be run directly. + +## CMake Targets + +- `SandboxRegistryTests` - Main executable target +- `test-sandbox-registry` - Build and run tests +- `build-sandbox-registry` - Build tests only +- `test` - Standard CMake test target + +## XCTest Features + +The tests use proper XCTest framework: +- `XCTestCase` subclass for test organization +- `setUp` and `tearDown` methods for test isolation +- `XCTAssert*` macros for assertions +- Proper test method naming (`test*` prefix) +- Automatic test discovery and execution + +## Troubleshooting + +If compilation fails: + +1. **Check CMake version**: Ensure you have CMake 3.16+ +2. **Check compiler**: Ensure you have a C++17 compatible compiler +3. **Check frameworks**: Foundation and XCTest frameworks should be available on macOS +4. **Clean build**: Remove `build/` directory and reconfigure + +```bash +rm -rf build +cmake -S . -B build +cmake --build build --target test-sandbox-registry +``` \ No newline at end of file diff --git a/packages/react-native-sandbox/tests/SandboxRegistryTests.mm b/packages/react-native-sandbox/tests/SandboxRegistryTests.mm new file mode 100644 index 0000000..e50b3ed --- /dev/null +++ b/packages/react-native-sandbox/tests/SandboxRegistryTests.mm @@ -0,0 +1,121 @@ + + +#import +#import +#import "../ios/SandboxRegistry.h" + +@interface MockSandboxDelegate : NSObject +@property (nonatomic, copy, nullable) NSString *sandboxId; +@property (nonatomic, copy, nullable) NSString *lastMessage; +- (void)postMessage:(NSString *)message; +@end + +@implementation MockSandboxDelegate + +- (void)postMessage:(NSString *)message +{ + self.lastMessage = [message copy]; +} + +@end + +@interface SandboxRegistryTests : XCTestCase +@end + +@implementation SandboxRegistryTests + +- (void)setUp +{ + [super setUp]; + + [[SandboxRegistry shared] unregister:@"test-sandbox-1"]; + [[SandboxRegistry shared] unregister:@"test-sandbox-2"]; + [[SandboxRegistry shared] unregister:@"test-sandbox-3"]; +} + +- (void)tearDown +{ + [super tearDown]; + + [[SandboxRegistry shared] unregister:@"test-sandbox-1"]; + [[SandboxRegistry shared] unregister:@"test-sandbox-2"]; + [[SandboxRegistry shared] unregister:@"test-sandbox-3"]; +} + +- (void)testRegisterAndGetSandbox +{ + MockSandboxDelegate *delegate = [[MockSandboxDelegate alloc] init]; + delegate.sandboxId = @"test-sandbox-1"; + + [[SandboxRegistry shared] register:@"test-sandbox-1" delegate:delegate]; + + MockSandboxDelegate *retrieved = [[SandboxRegistry shared] find:@"test-sandbox-1"]; + + XCTAssertEqual(delegate, retrieved, @"Retrieved delegate should be the same instance"); +} + +- (void)testUnregisterSandbox +{ + MockSandboxDelegate *delegate = [[MockSandboxDelegate alloc] init]; + [[SandboxRegistry shared] register:@"test-sandbox-1" delegate:delegate]; + + XCTAssertNotNil([[SandboxRegistry shared] find:@"test-sandbox-1"]); + + [[SandboxRegistry shared] unregister:@"test-sandbox-1"]; + + XCTAssertNil([[SandboxRegistry shared] find:@"test-sandbox-1"]); +} + +- (void)testRouteMessage +{ + MockSandboxDelegate *delegate1 = [[MockSandboxDelegate alloc] init]; + MockSandboxDelegate *delegate2 = [[MockSandboxDelegate alloc] init]; + + [[SandboxRegistry shared] register:@"test-sandbox-1" delegate:delegate1]; + [[SandboxRegistry shared] register:@"test-sandbox-2" delegate:delegate2]; + + // Test direct message routing using registry + id target = [[SandboxRegistry shared] find:@"test-sandbox-2"]; + XCTAssertNotNil(target, @"Should retrieve delegate from registry"); + + if ([target respondsToSelector:@selector(postMessage:)]) { + [target postMessage:@"test message"]; + XCTAssertEqualObjects(delegate2.lastMessage, @"test message", @"Message should be received"); + } + + id nonExistent = [[SandboxRegistry shared] find:@"non-existent"]; + XCTAssertNil(nonExistent, @"Should return nil for non-existent sandbox"); +} + +- (void)testRegistrySeparation +{ + MockSandboxDelegate *delegate = [[MockSandboxDelegate alloc] init]; + + [[SandboxRegistry shared] register:@"test-sandbox-3" delegate:delegate]; + MockSandboxDelegate *retrieved = [[SandboxRegistry shared] find:@"test-sandbox-3"]; + + XCTAssertEqual(delegate, retrieved, @"Registry should work independently of delegate"); +} + +- (void)testConcurrentAccess +{ + MockSandboxDelegate *delegate1 = [[MockSandboxDelegate alloc] init]; + MockSandboxDelegate *delegate2 = [[MockSandboxDelegate alloc] init]; + + dispatch_group_t group = dispatch_group_create(); + + dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [[SandboxRegistry shared] register:@"concurrent-sandbox-1" delegate:delegate1]; + }); + + dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [[SandboxRegistry shared] register:@"concurrent-sandbox-2" delegate:delegate2]; + }); + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + XCTAssertNotNil([[SandboxRegistry shared] find:@"concurrent-sandbox-1"]); + XCTAssertNotNil([[SandboxRegistry shared] find:@"concurrent-sandbox-2"]); +} + +@end \ No newline at end of file diff --git a/packages/react-native-sandbox/tests/main.m b/packages/react-native-sandbox/tests/main.m new file mode 100644 index 0000000..a05c7d7 --- /dev/null +++ b/packages/react-native-sandbox/tests/main.m @@ -0,0 +1,41 @@ +#import +#import + +int main([[maybe_unused]] int argc, [[maybe_unused]] const char *argv[]) +{ + int totalTests = 0; + int passedTests = 0; + int failedTests = 0; + + @autoreleasepool { + NSArray *testCaseNames = @[ + @"SandboxRegistryTests", + ]; + + for (NSString *testCaseName in testCaseNames) { + XCTestSuite *testSuite = [XCTestSuite testSuiteForTestCaseWithName:testCaseName]; + + for (XCTest *test in testSuite.tests) { + totalTests++; + + XCTestRun *testRun = [[XCTestRun alloc] initWithTest:test]; + [test performTest:testRun]; + + if (testRun.totalFailureCount == 0) { + passedTests++; + NSLog(@"โœ… %@ - PASSED", test.name); + } else { + failedTests++; + NSLog(@"โŒ %@ - FAILED", test.name); + } + } + } + } + + NSLog(@"๐Ÿงช SandboxRegistry Tests Summary:"); + NSLog(@" Tests run: %d", totalTests); + NSLog(@" Tests passed: %d", passedTests); + NSLog(@" Tests failed: %d", failedTests); + + return failedTests == 0 ? 0 : 1; +} \ No newline at end of file From fb33917b706c02a7916749518547c9d5233e8c0a Mon Sep 17 00:00:00 2001 From: Aliaksandr Babrykovich Date: Fri, 8 Aug 2025 00:31:55 +0200 Subject: [PATCH 06/15] chore: run tests on CI --- .github/workflows/check.yml | 3 + apps/demo/App.tsx | 4 +- apps/demo/jest.config.js | 4 + apps/demo/package.json | 3 +- apps/fs-experiment/package.json | 3 +- apps/p2p-chat/jest.config.js | 7 + apps/recursive/package.json | 3 +- apps/side-by-side/jest.config.js | 4 + bun.lock | 480 +++++++++++++++++++++++++++++-- jest.config.js | 10 + jest.setup.js | 8 + package.json | 8 +- 12 files changed, 509 insertions(+), 28 deletions(-) create mode 100644 apps/p2p-chat/jest.config.js create mode 100644 jest.config.js create mode 100644 jest.setup.js diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 5ce42bc..3d1afa4 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -48,6 +48,9 @@ jobs: - name: Run ESLint run: bun run lint + - name: Run Test + run: bun run test + - name: Test pack working-directory: packages/react-native-sandbox run: npm pack diff --git a/apps/demo/App.tsx b/apps/demo/App.tsx index 4ec0823..605f70e 100644 --- a/apps/demo/App.tsx +++ b/apps/demo/App.tsx @@ -5,7 +5,7 @@ import Toast from 'react-native-toast-message' import CrashIfYouCanDemo from './CrashIfYouCanDemo' -const SideBySideDemo: React.FC = () => { +const DemoApp: React.FC = () => { return ( @@ -69,4 +69,4 @@ const styles = StyleSheet.create({ }, }) -export default SideBySideDemo +export default DemoApp diff --git a/apps/demo/jest.config.js b/apps/demo/jest.config.js index 1fbafc9..f9902db 100644 --- a/apps/demo/jest.config.js +++ b/apps/demo/jest.config.js @@ -1,3 +1,7 @@ module.exports = { preset: 'react-native', + transformIgnorePatterns: [ + 'node_modules/(?!(react-native|@react-native|@react-native-community|@callstack/react-native-sandbox|react-native-toast-message|react-native-reanimated)/)', + ], + setupFilesAfterEnv: ['/../../jest.setup.js'], } diff --git a/apps/demo/package.json b/apps/demo/package.json index 5956b31..26b6849 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -6,7 +6,8 @@ "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "test": "jest" }, "dependencies": { "react": "19.1.0", diff --git a/apps/fs-experiment/package.json b/apps/fs-experiment/package.json index cf4c45e..384693a 100644 --- a/apps/fs-experiment/package.json +++ b/apps/fs-experiment/package.json @@ -9,7 +9,8 @@ "bundle:sandbox": "bun run bundle:sandbox-fs && bun run bundle:sandbox-file-access", "bundle:sandbox-fs": "npx react-native bundle --platform ios --dev false --entry-file sandbox-fs.js --bundle-output ios/sandbox-fs.jsbundle --assets-dest ios/", "bundle:sandbox-file-access": "npx react-native bundle --platform ios --dev false --entry-file sandbox-file-access.js --bundle-output ios/sandbox-file-access.jsbundle --assets-dest ios/", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "jest": "echo 'No tests'" }, "dependencies": { "react": "19.1.0", diff --git a/apps/p2p-chat/jest.config.js b/apps/p2p-chat/jest.config.js new file mode 100644 index 0000000..f9902db --- /dev/null +++ b/apps/p2p-chat/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'react-native', + transformIgnorePatterns: [ + 'node_modules/(?!(react-native|@react-native|@react-native-community|@callstack/react-native-sandbox|react-native-toast-message|react-native-reanimated)/)', + ], + setupFilesAfterEnv: ['/../../jest.setup.js'], +} diff --git a/apps/recursive/package.json b/apps/recursive/package.json index 9807d84..8f511f6 100644 --- a/apps/recursive/package.json +++ b/apps/recursive/package.json @@ -6,7 +6,8 @@ "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "jest": "echo 'No tests'" }, "dependencies": { "react": "19.1.0", diff --git a/apps/side-by-side/jest.config.js b/apps/side-by-side/jest.config.js index 1fbafc9..6a2a014 100644 --- a/apps/side-by-side/jest.config.js +++ b/apps/side-by-side/jest.config.js @@ -1,3 +1,7 @@ module.exports = { preset: 'react-native', + transformIgnorePatterns: [ + 'node_modules/(?!(react-native|@react-native|@react-native-community|@callstack/react-native-sandbox|react-native-reanimated)/)', + ], + setupFilesAfterEnv: ['/../../jest.setup.js'], } diff --git a/bun.lock b/bun.lock index 3817200..bf5b058 100644 --- a/bun.lock +++ b/bun.lock @@ -3,9 +3,14 @@ "workspaces": { "": { "name": "multi-instance-monorepo", + "dependencies": { + "@types/jest": "^30.0.0", + }, "devDependencies": { "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", + "@babel/preset-react": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", "@babel/runtime": "^7.25.0", "@commitlint/config-conventional": "^17.0.2", "@evilmartians/lefthook": "^1.5.0", @@ -16,8 +21,10 @@ "@release-it-plugins/workspaces": "^4.2.0", "@release-it/conventional-changelog": "^8.0.1", "@types/react": "^19.1.9", + "@types/react-test-renderer": "^19.1.0", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", + "babel-jest": "^30.0.5", "commitlint": "^17.0.2", "eslint": "^9.31.0", "eslint-config-expo": "^9.2.0", @@ -28,6 +35,7 @@ "jest": "^29.7.0", "prettier": "^3.6.2", "react-native-builder-bob": "^0.23.2", + "react-native-web": "^0.21.0", "react-test-renderer": "19.1.0", "release-it": "^17.6.0", "typescript": "^5.2.2", @@ -517,16 +525,22 @@ "@jest/create-cache-key-function": ["@jest/create-cache-key-function@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3" } }, "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA=="], + "@jest/diff-sequences": ["@jest/diff-sequences@30.0.1", "", {}, "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw=="], + "@jest/environment": ["@jest/environment@29.7.0", "", { "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0" } }, "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw=="], "@jest/expect": ["@jest/expect@29.7.0", "", { "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" } }, "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ=="], - "@jest/expect-utils": ["@jest/expect-utils@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3" } }, "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA=="], + "@jest/expect-utils": ["@jest/expect-utils@30.0.5", "", { "dependencies": { "@jest/get-type": "30.0.1" } }, "sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew=="], "@jest/fake-timers": ["@jest/fake-timers@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", "jest-message-util": "^29.7.0", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ=="], + "@jest/get-type": ["@jest/get-type@30.0.1", "", {}, "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw=="], + "@jest/globals": ["@jest/globals@29.7.0", "", { "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", "jest-mock": "^29.7.0" } }, "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ=="], + "@jest/pattern": ["@jest/pattern@30.0.1", "", { "dependencies": { "@types/node": "*", "jest-regex-util": "30.0.1" } }, "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA=="], + "@jest/reporters": ["@jest/reporters@29.7.0", "", { "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", "v8-to-istanbul": "^9.0.1" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "optionalPeers": ["node-notifier"] }, "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg=="], "@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], @@ -537,7 +551,7 @@ "@jest/test-sequencer": ["@jest/test-sequencer@29.7.0", "", { "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "slash": "^3.0.0" } }, "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw=="], - "@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], + "@jest/transform": ["@jest/transform@30.0.5", "", { "dependencies": { "@babel/core": "^7.27.4", "@jest/types": "30.0.5", "@jridgewell/trace-mapping": "^0.3.25", "babel-plugin-istanbul": "^7.0.0", "chalk": "^4.1.2", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.11", "jest-haste-map": "30.0.5", "jest-regex-util": "30.0.1", "jest-util": "30.0.5", "micromatch": "^4.0.8", "pirates": "^4.0.7", "slash": "^3.0.0", "write-file-atomic": "^5.0.1" } }, "sha512-Vk8amLQCmuZyy6GbBht1Jfo9RSdBtg7Lks+B0PecnjI8J+PCLQPGh7uI8Q/2wwpW2gLdiAfiHNsmekKlywULqg=="], "@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="], @@ -645,7 +659,7 @@ "@react-native/metro-config": ["@react-native/metro-config@0.80.1", "", { "dependencies": { "@react-native/js-polyfills": "0.80.1", "@react-native/metro-babel-transformer": "0.80.1", "metro-config": "^0.82.2", "metro-runtime": "^0.82.2" } }, "sha512-VLfHJ1G/lHvpE0YzxPal3sslfr5e5RNQVFeVebMwS3zSf57ZiZbfAevWJJT+7/gfGADMIoiX7AUteCVlbxMu/g=="], - "@react-native/normalize-colors": ["@react-native/normalize-colors@0.80.1", "", {}, "sha512-YP12bjz0bzo2lFxZDOPkRJSOkcqAzXCQQIV1wd7lzCTXE0NJNwoaeNBobJvcPhiODEWUYCXPANrZveFhtFu5vw=="], + "@react-native/normalize-colors": ["@react-native/normalize-colors@0.74.89", "", {}, "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg=="], "@react-native/typescript-config": ["@react-native/typescript-config@0.80.1", "", {}, "sha512-rcDV6BMh+rjsYIh/f0pd6mWOKN4eTS9nov7CyeU2cAfuIEslScEJ7Kv6YZtlLZ5PgC9teUf5L9pqzx9GwmzY/w=="], @@ -701,6 +715,8 @@ "@types/istanbul-reports": ["@types/istanbul-reports@3.0.4", "", { "dependencies": { "@types/istanbul-lib-report": "*" } }, "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ=="], + "@types/jest": ["@types/jest@30.0.0", "", { "dependencies": { "expect": "^30.0.0", "pretty-format": "^30.0.0" } }, "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA=="], + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], @@ -717,6 +733,8 @@ "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="], + "@types/react-test-renderer": ["@types/react-test-renderer@19.1.0", "", { "dependencies": { "@types/react": "*" } }, "sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ=="], + "@types/semver": ["@types/semver@7.7.0", "", {}, "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA=="], "@types/stack-utils": ["@types/stack-utils@2.0.3", "", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="], @@ -745,6 +763,8 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@7.18.0", "", { "dependencies": { "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" } }, "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="], @@ -863,11 +883,11 @@ "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], - "babel-jest": ["babel-jest@29.7.0", "", { "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, "peerDependencies": { "@babel/core": "^7.8.0" } }, "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg=="], + "babel-jest": ["babel-jest@30.0.5", "", { "dependencies": { "@jest/transform": "30.0.5", "@types/babel__core": "^7.20.5", "babel-plugin-istanbul": "^7.0.0", "babel-preset-jest": "30.0.1", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "slash": "^3.0.0" }, "peerDependencies": { "@babel/core": "^7.11.0" } }, "sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg=="], - "babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], + "babel-plugin-istanbul": ["babel-plugin-istanbul@7.0.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.3", "istanbul-lib-instrument": "^6.0.2", "test-exclude": "^6.0.0" } }, "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw=="], - "babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@29.6.3", "", { "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" } }, "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg=="], + "babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@30.0.1", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.27.3", "@types/babel__core": "^7.20.5" } }, "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ=="], "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="], @@ -881,7 +901,7 @@ "babel-preset-current-node-syntax": ["babel-preset-current-node-syntax@1.2.0", "", { "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg=="], - "babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="], + "babel-preset-jest": ["babel-preset-jest@30.0.1", "", { "dependencies": { "babel-plugin-jest-hoist": "30.0.1", "babel-preset-current-node-syntax": "^1.1.0" }, "peerDependencies": { "@babel/core": "^7.11.0" } }, "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw=="], "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], @@ -1039,8 +1059,12 @@ "create-require": ["create-require@1.1.1", "", {}, "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="], + "cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "css-in-js-utils": ["css-in-js-utils@3.1.0", "", { "dependencies": { "hyphenate-style-name": "^1.0.3" } }, "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A=="], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "dargs": ["dargs@8.1.0", "", {}, "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw=="], @@ -1215,7 +1239,7 @@ "exit": ["exit@0.1.2", "", {}, "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ=="], - "expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], + "expect": ["expect@30.0.5", "", { "dependencies": { "@jest/expect-utils": "30.0.5", "@jest/get-type": "30.0.1", "jest-matcher-utils": "30.0.5", "jest-message-util": "30.0.5", "jest-mock": "30.0.5", "jest-util": "30.0.5" } }, "sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ=="], "exponential-backoff": ["exponential-backoff@3.1.2", "", {}, "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA=="], @@ -1239,6 +1263,10 @@ "fb-watchman": ["fb-watchman@2.0.2", "", { "dependencies": { "bser": "2.1.1" } }, "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA=="], + "fbjs": ["fbjs@3.0.5", "", { "dependencies": { "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", "loose-envify": "^1.0.0", "object-assign": "^4.1.0", "promise": "^7.1.1", "setimmediate": "^1.0.5", "ua-parser-js": "^1.0.35" } }, "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg=="], + + "fbjs-css-vars": ["fbjs-css-vars@1.0.2", "", {}, "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="], + "fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="], "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], @@ -1353,6 +1381,8 @@ "human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], + "hyphenate-style-name": ["hyphenate-style-name@1.1.0", "", {}, "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw=="], + "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], @@ -1375,6 +1405,8 @@ "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "inline-style-prefixer": ["inline-style-prefixer@7.0.1", "", { "dependencies": { "css-in-js-utils": "^3.1.0" } }, "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw=="], + "inquirer": ["inquirer@9.3.2", "", { "dependencies": { "@inquirer/figures": "^1.0.3", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "external-editor": "^3.1.0", "mute-stream": "1.0.0", "ora": "^5.4.1", "run-async": "^3.0.0", "rxjs": "^7.8.1", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.1" } }, "sha512-+ynEbhWKhyomnaX0n2aLIMSkgSlGB5RrWbNXnEqj6mdaIydu6y40MdBjL38SAB0JcdmOaIaMua1azdjLEr3sdw=="], "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], @@ -1525,19 +1557,19 @@ "jest-get-type": ["jest-get-type@29.6.3", "", {}, "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw=="], - "jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + "jest-haste-map": ["jest-haste-map@30.0.5", "", { "dependencies": { "@jest/types": "30.0.5", "@types/node": "*", "anymatch": "^3.1.3", "fb-watchman": "^2.0.2", "graceful-fs": "^4.2.11", "jest-regex-util": "30.0.1", "jest-util": "30.0.5", "jest-worker": "30.0.5", "micromatch": "^4.0.8", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.3" } }, "sha512-dkmlWNlsTSR0nH3nRfW5BKbqHefLZv0/6LCccG0xFCTWcJu8TuEwG+5Cm75iBfjVoockmO6J35o5gxtFSn5xeg=="], "jest-leak-detector": ["jest-leak-detector@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw=="], - "jest-matcher-utils": ["jest-matcher-utils@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g=="], + "jest-matcher-utils": ["jest-matcher-utils@30.0.5", "", { "dependencies": { "@jest/get-type": "30.0.1", "chalk": "^4.1.2", "jest-diff": "30.0.5", "pretty-format": "30.0.5" } }, "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ=="], "jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], - "jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], + "jest-mock": ["jest-mock@30.0.5", "", { "dependencies": { "@jest/types": "30.0.5", "@types/node": "*", "jest-util": "30.0.5" } }, "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ=="], "jest-pnp-resolver": ["jest-pnp-resolver@1.2.3", "", { "peerDependencies": { "jest-resolve": "*" }, "optionalPeers": ["jest-resolve"] }, "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w=="], - "jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + "jest-regex-util": ["jest-regex-util@30.0.1", "", {}, "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA=="], "jest-resolve": ["jest-resolve@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" } }, "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA=="], @@ -1549,7 +1581,7 @@ "jest-snapshot": ["jest-snapshot@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", "@jest/expect-utils": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", "expect": "^29.7.0", "graceful-fs": "^4.2.9", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "natural-compare": "^1.4.0", "pretty-format": "^29.7.0", "semver": "^7.5.3" } }, "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw=="], - "jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + "jest-util": ["jest-util@30.0.5", "", { "dependencies": { "@jest/types": "30.0.5", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g=="], "jest-validate": ["jest-validate@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "leven": "^3.1.0", "pretty-format": "^29.7.0" } }, "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw=="], @@ -1671,7 +1703,7 @@ "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], - "memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="], + "memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="], "meow": ["meow@12.1.1", "", {}, "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw=="], @@ -1747,6 +1779,8 @@ "nocache": ["nocache@3.0.4", "", {}, "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw=="], + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], @@ -1847,13 +1881,15 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], "prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="], - "pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "pretty-format": ["pretty-format@30.0.5", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw=="], "promise": ["promise@8.3.0", "", { "dependencies": { "asap": "~2.0.6" } }, "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg=="], @@ -1895,6 +1931,8 @@ "react-devtools-core": ["react-devtools-core@6.1.5", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA=="], + "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="], + "react-is": ["react-is@19.1.1", "", {}, "sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA=="], "react-native": ["react-native@0.80.1", "", { "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.80.1", "@react-native/codegen": "0.80.1", "@react-native/community-cli-plugin": "0.80.1", "@react-native/gradle-plugin": "0.80.1", "@react-native/js-polyfills": "0.80.1", "@react-native/normalize-colors": "0.80.1", "@react-native/virtualized-lists": "0.80.1", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.28.1", "base64-js": "^1.5.1", "chalk": "^4.0.0", "commander": "^12.0.0", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "invariant": "^2.2.4", "jest-environment-node": "^29.7.0", "memoize-one": "^5.0.0", "metro-runtime": "^0.82.2", "metro-source-map": "^0.82.2", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.1.1", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.26.0", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^19.1.0", "react": "^19.1.0" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js" } }, "sha512-cIiJiPItdC2+Z9n30FmE2ef1y4522kgmOjMIoDtlD16jrOMNTUdB2u+CylLTy3REkWkWTS6w8Ub7skUthkeo5w=="], @@ -1911,6 +1949,8 @@ "react-native-toast-message": ["react-native-toast-message@2.3.3", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-4IIUHwUPvKHu4gjD0Vj2aGQzqPATiblL1ey8tOqsxOWRPGGu52iIbL8M/mCz4uyqecvPdIcMY38AfwRuUADfQQ=="], + "react-native-web": ["react-native-web@0.21.0", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", "fbjs": "^3.0.4", "inline-style-prefixer": "^7.0.1", "memoize-one": "^6.0.0", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "styleq": "^0.1.3" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-S0mtV7wMPeet1kCRnrEmo1bTUJeFsKebleCbRwbBRBUg/BWS64bfsnnm+ArC+QtjlbZFSZmtvv8imzOIuOOa3Q=="], + "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="], "react-test-renderer": ["react-test-renderer@19.1.0", "", { "dependencies": { "react-is": "^19.1.0", "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw=="], @@ -2011,6 +2051,8 @@ "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -2105,6 +2147,8 @@ "stubborn-fs": ["stubborn-fs@1.2.5", "", {}, "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g=="], + "styleq": ["styleq@0.1.3", "", {}, "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA=="], + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], @@ -2133,6 +2177,8 @@ "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + "trim-newlines": ["trim-newlines@3.0.1", "", {}, "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw=="], "ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], @@ -2165,6 +2211,8 @@ "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + "ua-parser-js": ["ua-parser-js@1.0.40", "", { "bin": { "ua-parser-js": "script/cli.js" } }, "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew=="], + "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], @@ -2223,8 +2271,12 @@ "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="], + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + "whatwg-fetch": ["whatwg-fetch@3.6.20", "", {}, "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="], + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + "when-exit": ["when-exit@2.1.4", "", {}, "sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -2253,7 +2305,7 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + "write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="], "ws": ["ws@6.2.3", "", { "dependencies": { "async-limiter": "~1.0.0" } }, "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA=="], @@ -2315,10 +2367,40 @@ "@istanbuljs/load-nyc-config/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], + "@jest/console/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "@jest/core/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], + "@jest/core/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + "@jest/core/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + + "@jest/core/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "@jest/core/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "@jest/core/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "@jest/environment/jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], + + "@jest/expect/expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], + + "@jest/fake-timers/jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], + + "@jest/fake-timers/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "@jest/globals/jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], + + "@jest/reporters/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], + "@jest/reporters/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "@jest/reporters/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "@jest/test-sequencer/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + + "@jest/transform/@jest/types": ["@jest/types@30.0.5", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ=="], + "@nicolo-ribaudo/eslint-scope-5-internals/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], @@ -2385,8 +2467,6 @@ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], - "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "boxen/camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], @@ -2425,6 +2505,8 @@ "cosmiconfig/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], + "create-jest/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + "decamelize-keys/map-obj": ["map-obj@1.0.1", "", {}, "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="], "del/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], @@ -2451,8 +2533,12 @@ "eslint-plugin-jest/@typescript-eslint/utils": ["@typescript-eslint/utils@5.62.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ=="], + "expect/jest-message-util": ["jest-message-util@30.0.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@jest/types": "30.0.5", "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "micromatch": "^4.0.8", "pretty-format": "30.0.5", "slash": "^3.0.0", "stack-utils": "^2.0.6" } }, "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "fbjs/promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="], + "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "finalhandler/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], @@ -2495,23 +2581,101 @@ "jest-changed-files/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "jest-changed-files/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + "jest-circus/dedent": ["dedent@1.6.0", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA=="], + "jest-circus/jest-matcher-utils": ["jest-matcher-utils@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g=="], + + "jest-circus/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "jest-circus/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "jest-cli/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "jest-config/babel-jest": ["babel-jest@29.7.0", "", { "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, "peerDependencies": { "@babel/core": "^7.8.0" } }, "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg=="], + "jest-config/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "jest-config/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "jest-config/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "jest-config/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "jest-config/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "jest-diff/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "jest-each/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "jest-each/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "jest-environment-node/jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], + + "jest-environment-node/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "jest-haste-map/@jest/types": ["@jest/types@30.0.5", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ=="], + + "jest-haste-map/jest-worker": ["jest-worker@30.0.5", "", { "dependencies": { "@types/node": "*", "@ungap/structured-clone": "^1.3.0", "jest-util": "30.0.5", "merge-stream": "^2.0.0", "supports-color": "^8.1.1" } }, "sha512-ojRXsWzEP16NdUuBw/4H/zkZdHOa7MMYCk4E430l+8fELeLg/mqmMlRhjL7UNZvQrDmnovWZV4DxX03fZF48fQ=="], + + "jest-leak-detector/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "jest-matcher-utils/jest-diff": ["jest-diff@30.0.5", "", { "dependencies": { "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.0.1", "chalk": "^4.1.2", "pretty-format": "30.0.5" } }, "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A=="], + + "jest-message-util/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "jest-mock/@jest/types": ["@jest/types@30.0.5", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ=="], + + "jest-resolve/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + + "jest-resolve/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + "jest-resolve/resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + "jest-resolve-dependencies/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "jest-runner/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], + + "jest-runner/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + + "jest-runner/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "jest-runtime/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], + "jest-runtime/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "jest-runtime/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + + "jest-runtime/jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], + + "jest-runtime/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "jest-runtime/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + "jest-runtime/strip-bom": ["strip-bom@4.0.0", "", {}, "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="], + "jest-snapshot/@jest/expect-utils": ["@jest/expect-utils@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3" } }, "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA=="], + + "jest-snapshot/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], + + "jest-snapshot/expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], + + "jest-snapshot/jest-matcher-utils": ["jest-matcher-utils@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g=="], + + "jest-snapshot/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "jest-snapshot/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "jest-snapshot/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - "jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + "jest-util/@jest/types": ["@jest/types@30.0.5", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ=="], - "jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "jest-validate/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "jest-watcher/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "jest-worker/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], "jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], @@ -2551,6 +2715,8 @@ "pkg-dir/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], + "pretty-format/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], "pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -2565,10 +2731,18 @@ "react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + "react-native/@react-native/normalize-colors": ["@react-native/normalize-colors@0.80.1", "", {}, "sha512-YP12bjz0bzo2lFxZDOPkRJSOkcqAzXCQQIV1wd7lzCTXE0NJNwoaeNBobJvcPhiODEWUYCXPANrZveFhtFu5vw=="], + + "react-native/babel-jest": ["babel-jest@29.7.0", "", { "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, "peerDependencies": { "@babel/core": "^7.8.0" } }, "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg=="], + "react-native/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], "react-native/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "react-native/memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="], + + "react-native/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "react-native/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], "read-pkg/parse-json": ["parse-json@7.1.1", "", { "dependencies": { "@babel/code-frame": "^7.21.4", "error-ex": "^1.3.2", "json-parse-even-better-errors": "^3.0.0", "lines-and-columns": "^2.0.3", "type-fest": "^3.8.0" } }, "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw=="], @@ -2637,8 +2811,6 @@ "windows-release/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], - "write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "@commitlint/cli/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "@commitlint/cli/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], @@ -2687,6 +2859,52 @@ "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "@jest/console/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "@jest/console/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "@jest/core/@jest/transform/babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], + + "@jest/core/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + + "@jest/core/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "@jest/core/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "@jest/core/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "@jest/environment/jest-mock/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "@jest/expect/expect/@jest/expect-utils": ["@jest/expect-utils@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3" } }, "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA=="], + + "@jest/expect/expect/jest-matcher-utils": ["jest-matcher-utils@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g=="], + + "@jest/expect/expect/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "@jest/fake-timers/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "@jest/fake-timers/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "@jest/globals/jest-mock/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "@jest/reporters/@jest/transform/babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], + + "@jest/reporters/@jest/transform/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + + "@jest/reporters/@jest/transform/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "@jest/reporters/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + + "@jest/reporters/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "@jest/reporters/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "@jest/test-sequencer/jest-haste-map/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "@jest/test-sequencer/jest-haste-map/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "@jest/transform/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], + "@nicolo-ribaudo/eslint-scope-5-internals/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], "@react-native-community/cli-clean/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], @@ -2845,6 +3063,10 @@ "conventional-recommended-bump/git-semver-tags/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "create-jest/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "create-jest/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "eslint-config-expo/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.38.0", "", { "dependencies": { "@typescript-eslint/types": "8.38.0", "@typescript-eslint/visitor-keys": "8.38.0" } }, "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ=="], "eslint-config-expo/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.38.0", "", { "dependencies": { "@typescript-eslint/types": "8.38.0", "@typescript-eslint/typescript-estree": "8.38.0", "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg=="], @@ -2879,6 +3101,8 @@ "eslint-plugin-jest/@typescript-eslint/utils/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "expect/jest-message-util/@jest/types": ["@jest/types@30.0.5", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ=="], + "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], @@ -2933,6 +3157,120 @@ "jest-changed-files/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "jest-changed-files/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-changed-files/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-circus/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-circus/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-circus/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-circus/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "jest-cli/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-cli/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-config/babel-jest/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], + + "jest-config/babel-jest/babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], + + "jest-config/babel-jest/babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="], + + "jest-config/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-config/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-config/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "jest-diff/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-diff/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "jest-each/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-each/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-each/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-each/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "jest-environment-node/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-environment-node/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-haste-map/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], + + "jest-haste-map/jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + + "jest-leak-detector/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-leak-detector/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "jest-message-util/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "jest-mock/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], + + "jest-resolve/jest-haste-map/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "jest-resolve/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-resolve/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-runner/@jest/transform/babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], + + "jest-runner/@jest/transform/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "jest-runner/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + + "jest-runner/jest-haste-map/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "jest-runner/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-runner/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-runtime/@jest/transform/babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], + + "jest-runtime/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + + "jest-runtime/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-runtime/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-snapshot/@jest/transform/babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], + + "jest-snapshot/@jest/transform/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + + "jest-snapshot/@jest/transform/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "jest-snapshot/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + + "jest-snapshot/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-snapshot/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-snapshot/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-snapshot/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "jest-util/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], + + "jest-validate/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-validate/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "jest-watcher/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-watcher/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "jest-worker/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "jest-worker/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "logkitty/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="], @@ -2959,6 +3297,18 @@ "pkg-dir/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + "pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], + + "react-native/babel-jest/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], + + "react-native/babel-jest/babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], + + "react-native/babel-jest/babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="], + + "react-native/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "react-native/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "read-pkg-up/find-up/locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="], "read-pkg-up/find-up/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="], @@ -3019,6 +3369,34 @@ "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "@jest/core/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], + + "@jest/core/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "@jest/environment/jest-mock/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "@jest/environment/jest-mock/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "@jest/expect/expect/jest-matcher-utils/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "@jest/expect/expect/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "@jest/expect/expect/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "@jest/globals/jest-mock/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "@jest/globals/jest-mock/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "@jest/reporters/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], + + "@jest/reporters/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "@jest/test-sequencer/jest-haste-map/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "@jest/test-sequencer/jest-haste-map/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "@jest/transform/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], + "@react-native-community/cli-clean/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], "@react-native-community/cli-config-apple/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], @@ -3079,6 +3457,8 @@ "eslint-plugin-jest/@typescript-eslint/utils/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + "expect/jest-message-util/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], + "inquirer/ora/cli-cursor/restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="], "is-git-dirty/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], @@ -3087,6 +3467,32 @@ "jest-changed-files/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "jest-config/babel-jest/@jest/transform/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + + "jest-config/babel-jest/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + + "jest-config/babel-jest/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], + + "jest-config/babel-jest/babel-preset-jest/babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@29.6.3", "", { "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" } }, "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg=="], + + "jest-haste-map/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], + + "jest-mock/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], + + "jest-runner/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], + + "jest-runner/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "jest-runtime/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], + + "jest-runtime/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "jest-snapshot/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], + + "jest-snapshot/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "jest-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], + "logkitty/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], "logkitty/yargs/yargs-parser/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], @@ -3097,6 +3503,18 @@ "pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "react-native/babel-jest/@jest/transform/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + + "react-native/babel-jest/@jest/transform/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + + "react-native/babel-jest/@jest/transform/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + + "react-native/babel-jest/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + + "react-native/babel-jest/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], + + "react-native/babel-jest/babel-preset-jest/babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@29.6.3", "", { "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" } }, "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg=="], + "read-pkg-up/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], "slice-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], @@ -3127,6 +3545,10 @@ "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + "@jest/expect/expect/jest-matcher-utils/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "@jest/expect/expect/jest-matcher-utils/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "@react-native-community/cli-doctor/ora/cli-cursor/restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], "@react-native-community/cli-doctor/ora/cli-cursor/restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], @@ -3151,14 +3573,28 @@ "eslint-plugin-jest/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + "expect/jest-message-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], + "inquirer/ora/cli-cursor/restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], "inquirer/ora/cli-cursor/restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "jest-config/babel-jest/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "jest-snapshot/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "logkitty/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + "react-native/babel-jest/@jest/transform/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "react-native/babel-jest/@jest/transform/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "react-native/babel-jest/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "react-native/babel-jest/babel-plugin-istanbul/istanbul-lib-instrument/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "read-pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="], "@commitlint/parse/conventional-commits-parser/meow/normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..2b0c481 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,10 @@ +/** @type {import('jest').Config} */ +module.exports = { + projects: [ + '/apps/*/jest.config.js', // This will find all jest.config.js files in app directories + ], + // Common settings for all projects + transformIgnorePatterns: [ + 'node_modules/(?!(react-native|@react-native|@react-native-community|@callstack/react-native-sandbox|react-native-toast-message|react-native-reanimated)/)', + ], +} diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 0000000..3dfa7c6 --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,8 @@ +/* eslint no-undef: "off" */ + +// Common mocks for all apps +jest.mock('@callstack/react-native-sandbox', () => 'View') +jest.mock('react-native-toast-message', () => 'Text') + +// Setup react-native-reanimated for testing +require('react-native-reanimated').setUpTests() diff --git a/package.json b/package.json index b2002b3..7c087e1 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,13 @@ "lint": "eslint . && bun --filter=@callstack/react-native-sandbox lint", "format": "eslint . --fix && bun --filter=@callstack/react-native-sandbox format", "typecheck": "bun --filter=@callstack/react-native-sandbox typecheck", - "test": "jest" + "test": "bun run jest && bun --filter=@callstack/react-native-sandbox xctest" }, "devDependencies": { "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", + "@babel/preset-react": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", "@babel/runtime": "^7.25.0", "@commitlint/config-conventional": "^17.0.2", "@evilmartians/lefthook": "^1.5.0", @@ -25,9 +27,12 @@ "@react-native/typescript-config": "0.80.1", "@release-it-plugins/workspaces": "^4.2.0", "@release-it/conventional-changelog": "^8.0.1", + "@types/jest": "^30.0.0", "@types/react": "^19.1.9", + "@types/react-test-renderer": "^19.1.0", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", + "babel-jest": "^30.0.5", "commitlint": "^17.0.2", "eslint": "^9.31.0", "eslint-config-expo": "^9.2.0", @@ -38,6 +43,7 @@ "jest": "^29.7.0", "prettier": "^3.6.2", "react-native-builder-bob": "^0.23.2", + "react-native-web": "^0.21.0", "react-test-renderer": "19.1.0", "release-it": "^17.6.0", "typescript": "^5.2.2" From 328c5ff4e337a082d99fa62e65dc1fcf566df9a0 Mon Sep 17 00:00:00 2001 From: Aliaksandr Babrykovich Date: Fri, 8 Aug 2025 00:41:38 +0200 Subject: [PATCH 07/15] fix: update bun.lock --- bun.lock | 170 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 114 insertions(+), 56 deletions(-) diff --git a/bun.lock b/bun.lock index bf5b058..6169bd2 100644 --- a/bun.lock +++ b/bun.lock @@ -3,9 +3,6 @@ "workspaces": { "": { "name": "multi-instance-monorepo", - "dependencies": { - "@types/jest": "^30.0.0", - }, "devDependencies": { "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", @@ -20,6 +17,7 @@ "@react-native/typescript-config": "0.80.1", "@release-it-plugins/workspaces": "^4.2.0", "@release-it/conventional-changelog": "^8.0.1", + "@types/jest": "^30.0.0", "@types/react": "^19.1.9", "@types/react-test-renderer": "^19.1.0", "@typescript-eslint/eslint-plugin": "^7.3.1", @@ -543,7 +541,7 @@ "@jest/reporters": ["@jest/reporters@29.7.0", "", { "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", "v8-to-istanbul": "^9.0.1" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "optionalPeers": ["node-notifier"] }, "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg=="], - "@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + "@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], "@jest/source-map": ["@jest/source-map@29.6.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", "graceful-fs": "^4.2.9" } }, "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw=="], @@ -677,7 +675,7 @@ "@sideway/pinpoint": ["@sideway/pinpoint@2.0.0", "", {}, "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="], - "@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@2.3.0", "", {}, "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg=="], @@ -835,7 +833,7 @@ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], @@ -1547,7 +1545,7 @@ "jest-config": ["jest-config@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", "jest-circus": "^29.7.0", "jest-environment-node": "^29.7.0", "jest-get-type": "^29.6.3", "jest-regex-util": "^29.6.3", "jest-resolve": "^29.7.0", "jest-runner": "^29.7.0", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, "peerDependencies": { "@types/node": "*", "ts-node": ">=9.0.0" }, "optionalPeers": ["@types/node", "ts-node"] }, "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ=="], - "jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="], + "jest-diff": ["jest-diff@30.0.5", "", { "dependencies": { "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.0.1", "chalk": "^4.1.2", "pretty-format": "30.0.5" } }, "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A=="], "jest-docblock": ["jest-docblock@29.7.0", "", { "dependencies": { "detect-newline": "^3.0.0" } }, "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g=="], @@ -1563,7 +1561,7 @@ "jest-matcher-utils": ["jest-matcher-utils@30.0.5", "", { "dependencies": { "@jest/get-type": "30.0.1", "chalk": "^4.1.2", "jest-diff": "30.0.5", "pretty-format": "30.0.5" } }, "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ=="], - "jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "jest-message-util": ["jest-message-util@30.0.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@jest/types": "30.0.5", "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "micromatch": "^4.0.8", "pretty-format": "30.0.5", "slash": "^3.0.0", "stack-utils": "^2.0.6" } }, "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA=="], "jest-mock": ["jest-mock@30.0.5", "", { "dependencies": { "@jest/types": "30.0.5", "@types/node": "*", "jest-util": "30.0.5" } }, "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ=="], @@ -2367,6 +2365,8 @@ "@istanbuljs/load-nyc-config/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], + "@jest/console/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "@jest/console/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], "@jest/core/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], @@ -2375,6 +2375,8 @@ "@jest/core/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + "@jest/core/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "@jest/core/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], "@jest/core/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], @@ -2385,6 +2387,8 @@ "@jest/expect/expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], + "@jest/fake-timers/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "@jest/fake-timers/jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], "@jest/fake-timers/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], @@ -2395,12 +2399,16 @@ "@jest/reporters/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "@jest/reporters/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "@jest/reporters/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], "@jest/test-sequencer/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], "@jest/transform/@jest/types": ["@jest/types@30.0.5", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ=="], + "@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + "@nicolo-ribaudo/eslint-scope-5-internals/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], @@ -2483,6 +2491,8 @@ "camelcase-keys/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], + "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "chrome-launcher/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], "chromium-edge-launcher/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], @@ -2533,8 +2543,6 @@ "eslint-plugin-jest/@typescript-eslint/utils": ["@typescript-eslint/utils@5.62.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ=="], - "expect/jest-message-util": ["jest-message-util@30.0.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@jest/types": "30.0.5", "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "micromatch": "^4.0.8", "pretty-format": "30.0.5", "slash": "^3.0.0", "stack-utils": "^2.0.6" } }, "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA=="], - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fbjs/promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="], @@ -2587,6 +2595,8 @@ "jest-circus/jest-matcher-utils": ["jest-matcher-utils@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g=="], + "jest-circus/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "jest-circus/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], "jest-circus/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], @@ -2605,8 +2615,6 @@ "jest-config/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], - "jest-diff/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], - "jest-each/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], "jest-each/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], @@ -2621,9 +2629,7 @@ "jest-leak-detector/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], - "jest-matcher-utils/jest-diff": ["jest-diff@30.0.5", "", { "dependencies": { "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.0.1", "chalk": "^4.1.2", "pretty-format": "30.0.5" } }, "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A=="], - - "jest-message-util/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "jest-message-util/@jest/types": ["@jest/types@30.0.5", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ=="], "jest-mock/@jest/types": ["@jest/types@30.0.5", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ=="], @@ -2639,6 +2645,8 @@ "jest-runner/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + "jest-runner/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "jest-runner/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], "jest-runtime/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], @@ -2647,6 +2655,8 @@ "jest-runtime/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], + "jest-runtime/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "jest-runtime/jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], "jest-runtime/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], @@ -2661,8 +2671,12 @@ "jest-snapshot/expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], + "jest-snapshot/jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="], + "jest-snapshot/jest-matcher-utils": ["jest-matcher-utils@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g=="], + "jest-snapshot/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "jest-snapshot/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], "jest-snapshot/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], @@ -2715,10 +2729,6 @@ "pkg-dir/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - "pretty-format/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], - - "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - "pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], @@ -2811,6 +2821,8 @@ "windows-release/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "@commitlint/cli/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "@commitlint/cli/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], @@ -2859,6 +2871,8 @@ "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "@jest/console/jest-message-util/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "@jest/console/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "@jest/console/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -2869,7 +2883,7 @@ "@jest/core/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "@jest/core/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "@jest/core/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "@jest/core/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -2879,8 +2893,12 @@ "@jest/expect/expect/jest-matcher-utils": ["jest-matcher-utils@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g=="], + "@jest/expect/expect/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "@jest/expect/expect/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + "@jest/fake-timers/jest-message-util/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "@jest/fake-timers/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "@jest/fake-timers/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -2895,6 +2913,8 @@ "@jest/reporters/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + "@jest/reporters/jest-message-util/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "@jest/reporters/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "@jest/reporters/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -2903,7 +2923,7 @@ "@jest/test-sequencer/jest-haste-map/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], - "@jest/transform/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], + "@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], "@nicolo-ribaudo/eslint-scope-5-internals/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], @@ -2989,6 +3009,8 @@ "@react-native-community/cli-server-api/pretty-format/@jest/types": ["@jest/types@26.6.2", "", { "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ=="], + "@react-native-community/cli-server-api/pretty-format/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "@react-native-community/cli-server-api/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], "@react-native-community/cli-tools/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], @@ -3053,6 +3075,8 @@ "chromium-edge-launcher/is-wsl/is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "configstore/dot-prop/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], @@ -3101,8 +3125,6 @@ "eslint-plugin-jest/@typescript-eslint/utils/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - "expect/jest-message-util/@jest/types": ["@jest/types@30.0.5", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ=="], - "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], @@ -3161,11 +3183,13 @@ "jest-changed-files/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "jest-circus/jest-matcher-utils/jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="], + "jest-circus/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "jest-circus/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "jest-circus/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "jest-circus/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "jest-circus/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -3181,19 +3205,15 @@ "jest-config/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "jest-config/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "jest-config/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "jest-config/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - "jest-diff/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - - "jest-diff/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - "jest-each/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "jest-each/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "jest-each/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "jest-each/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "jest-each/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -3201,20 +3221,12 @@ "jest-environment-node/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "jest-haste-map/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], - "jest-haste-map/jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], - "jest-leak-detector/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "jest-leak-detector/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "jest-leak-detector/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - "jest-message-util/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - - "jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - - "jest-mock/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], - "jest-resolve/jest-haste-map/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], "jest-resolve/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], @@ -3229,6 +3241,8 @@ "jest-runner/jest-haste-map/jest-regex-util": ["jest-regex-util@29.6.3", "", {}, "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg=="], + "jest-runner/jest-message-util/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "jest-runner/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "jest-runner/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -3237,6 +3251,8 @@ "jest-runtime/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], + "jest-runtime/jest-message-util/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "jest-runtime/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "jest-runtime/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -3253,13 +3269,11 @@ "jest-snapshot/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "jest-snapshot/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "jest-snapshot/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "jest-snapshot/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - "jest-util/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], - - "jest-validate/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "jest-validate/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "jest-validate/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -3297,15 +3311,13 @@ "pkg-dir/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - "pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], - "react-native/babel-jest/@jest/transform": ["@jest/transform@29.7.0", "", { "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" } }, "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw=="], "react-native/babel-jest/babel-plugin-istanbul": ["babel-plugin-istanbul@6.1.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" } }, "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA=="], "react-native/babel-jest/babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="], - "react-native/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "react-native/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "react-native/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -3369,20 +3381,34 @@ "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "@jest/console/jest-message-util/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "@jest/console/jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "@jest/core/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], "@jest/core/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "@jest/core/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "@jest/environment/jest-mock/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "@jest/environment/jest-mock/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "@jest/expect/expect/jest-matcher-utils/jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="], + "@jest/expect/expect/jest-matcher-utils/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "@jest/expect/expect/jest-message-util/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "@jest/expect/expect/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "@jest/expect/expect/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "@jest/fake-timers/jest-message-util/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "@jest/fake-timers/jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "@jest/globals/jest-mock/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "@jest/globals/jest-mock/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -3391,12 +3417,14 @@ "@jest/reporters/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "@jest/reporters/jest-message-util/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "@jest/reporters/jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "@jest/test-sequencer/jest-haste-map/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "@jest/test-sequencer/jest-haste-map/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "@jest/transform/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], - "@react-native-community/cli-clean/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], "@react-native-community/cli-config-apple/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], @@ -3457,8 +3485,6 @@ "eslint-plugin-jest/@typescript-eslint/utils/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], - "expect/jest-message-util/@jest/types/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], - "inquirer/ora/cli-cursor/restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="], "is-git-dirty/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], @@ -3467,6 +3493,8 @@ "jest-changed-files/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "jest-circus/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "jest-config/babel-jest/@jest/transform/jest-haste-map": ["jest-haste-map@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA=="], "jest-config/babel-jest/@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], @@ -3475,23 +3503,35 @@ "jest-config/babel-jest/babel-preset-jest/babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@29.6.3", "", { "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" } }, "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg=="], - "jest-haste-map/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], + "jest-config/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], - "jest-mock/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], + "jest-each/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + + "jest-leak-detector/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], "jest-runner/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], "jest-runner/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "jest-runner/jest-message-util/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "jest-runner/jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "jest-runtime/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], "jest-runtime/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "jest-runtime/jest-message-util/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "jest-runtime/jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "jest-snapshot/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="], "jest-snapshot/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "jest-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], + "jest-snapshot/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + + "jest-validate/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], "logkitty/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], @@ -3515,6 +3555,8 @@ "react-native/babel-jest/babel-preset-jest/babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@29.6.3", "", { "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" } }, "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg=="], + "react-native/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "read-pkg-up/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], "slice-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], @@ -3545,10 +3587,20 @@ "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - "@jest/expect/expect/jest-matcher-utils/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "@jest/console/jest-message-util/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + + "@jest/expect/expect/jest-matcher-utils/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "@jest/expect/expect/jest-matcher-utils/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "@jest/expect/expect/jest-message-util/pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "@jest/expect/expect/jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "@jest/fake-timers/jest-message-util/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + + "@jest/reporters/jest-message-util/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "@react-native-community/cli-doctor/ora/cli-cursor/restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], "@react-native-community/cli-doctor/ora/cli-cursor/restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], @@ -3573,14 +3625,16 @@ "eslint-plugin-jest/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - "expect/jest-message-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.34.38", "", {}, "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA=="], - "inquirer/ora/cli-cursor/restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], "inquirer/ora/cli-cursor/restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "jest-config/babel-jest/@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "jest-runner/jest-message-util/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + + "jest-runtime/jest-message-util/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "jest-snapshot/@jest/transform/babel-plugin-istanbul/istanbul-lib-instrument/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "logkitty/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], @@ -3613,6 +3667,10 @@ "@commitlint/read/git-raw-commits/meow/read-pkg-up/read-pkg/type-fest": ["type-fest@0.6.0", "", {}, "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="], + "@jest/expect/expect/jest-matcher-utils/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + + "@jest/expect/expect/jest-message-util/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "@react-native-community/cli-doctor/ora/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], "@react-native-community/cli-tools/ora/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], From 11faa518723209bbb06174e403340fd05d9ca71a Mon Sep 17 00:00:00 2001 From: Aliaksandr Babrykovich Date: Fri, 8 Aug 2025 00:45:25 +0200 Subject: [PATCH 08/15] chore: split lint and test workflows --- .github/workflows/check.yml | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 3d1afa4..d505a09 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -48,15 +48,39 @@ jobs: - name: Run ESLint run: bun run lint - - name: Run Test - run: bun run test - - name: Test pack working-directory: packages/react-native-sandbox run: npm pack + test: + runs-on: macos-15 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install asdf + uses: asdf-vm/actions/setup@v4 + + - name: Tools cache + id: asdf-cache + uses: actions/cache@v4 + with: + path: ~/.asdf/ + key: ${{ runner.os }}-${{ hashFiles('**/.tool-versions') }} + + - name: Install tools from .tool-versions + if: steps.asdf-cache.outputs.cache-hit != 'true' + uses: asdf-vm/actions/install@v4 + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Run Test + run: bun run test + example-compilation: runs-on: macos-15 + needs: [lint, test] strategy: matrix: example: [demo] # side-by-side, recursive, fs-experiment From 3a5905ed429f09189327ca27cba9aa0bc0b0debe Mon Sep 17 00:00:00 2001 From: Aliaksandr Babrykovich Date: Fri, 8 Aug 2025 12:33:19 +0200 Subject: [PATCH 09/15] feat: add sandbox2sandboxi communication security with allowedOrigins - Add allowedOrigins property to SandboxReactNativeDelegate for origin control - Implement origin-based message filtering in SandboxRegistry - Add p2p-chat: Advanced P2P communication with dynamic instances - Add p2p-counter: Direct sandbox communication demo with origin permissions --- README.md | 8 +- apps/p2p-chat/src/components/ChatCarousel.tsx | 12 +- .../src/services/FriendshipManager.ts | 7 - apps/p2p-chat/src/services/MessageHandler.ts | 22 +- apps/p2p-chat/src/utils/chatHelpers.ts | 8 + apps/p2p-counter/App.tsx | 138 + apps/p2p-counter/README.md | 14 + apps/p2p-counter/SandboxApp.tsx | 106 + apps/p2p-counter/__tests__/App.test.tsx | 14 + apps/p2p-counter/android/app/build.gradle | 119 + apps/p2p-counter/android/app/debug.keystore | Bin 0 -> 2257 bytes .../android/app/proguard-rules.pro | 10 + .../android/app/src/debug/AndroidManifest.xml | 9 + .../android/app/src/main/AndroidManifest.xml | 27 + .../main/java/com/p2pcounter/MainActivity.kt | 22 + .../java/com/p2pcounter/MainApplication.kt | 44 + .../res/drawable/rn_edit_text_material.xml | 37 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3056 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2096 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2858 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4569 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7098 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6464 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10676 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9250 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15523 bytes .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/styles.xml | 9 + apps/p2p-counter/android/build.gradle | 21 + apps/p2p-counter/android/gradle.properties | 39 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43705 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + apps/p2p-counter/android/gradlew | 251 ++ apps/p2p-counter/android/gradlew.bat | 94 + apps/p2p-counter/android/settings.gradle | 6 + apps/p2p-counter/app.json | 4 + apps/p2p-counter/babel.config.js | 4 + apps/p2p-counter/docs/README.md | 100 + apps/p2p-counter/docs/screenshot_1.png | Bin 0 -> 148966 bytes apps/p2p-counter/docs/screenshot_2.png | Bin 0 -> 159399 bytes apps/p2p-counter/index.js | 9 + apps/p2p-counter/ios/.lldbinit | 2 + apps/p2p-counter/ios/.xcode.env | 11 + apps/p2p-counter/ios/AppDelegate.swift | 54 + .../ios/P2PCounter.xcodeproj/project.pbxproj | 500 ++++ .../xcschemes/MultiInstancePOC.xcscheme | 79 + .../contents.xcworkspacedata | 10 + .../AppIcon.appiconset/Contents.json | 53 + .../P2PCounter/Images.xcassets/Contents.json | 6 + apps/p2p-counter/ios/P2PCounter/Info.plist | 53 + .../ios/P2PCounter/LaunchScreen.storyboard | 53 + .../ios/P2PCounter/PrivacyInfo.xcprivacy | 37 + apps/p2p-counter/ios/Podfile | 36 + apps/p2p-counter/ios/Podfile.lock | 2508 +++++++++++++++++ apps/p2p-counter/jest.config.js | 7 + apps/p2p-counter/metro.config.js | 24 + apps/p2p-counter/package.json | 36 + apps/p2p-counter/react-native.config.js | 6 + apps/p2p-counter/rspack.config.mjs | 29 + apps/p2p-counter/sandbox.js | 9 + bun.lock | 464 ++- packages/react-native-sandbox/README.md | 18 +- .../ios/SandboxReactNativeDelegate.h | 6 + .../ios/SandboxReactNativeDelegate.mm | 40 +- .../SandboxReactNativeViewComponentView.mm | 12 + .../ios/SandboxRegistry.h | 31 +- .../ios/SandboxRegistry.mm | 66 +- .../specs/NativeSandboxReactNativeView.ts | 3 + packages/react-native-sandbox/src/index.tsx | 10 +- packages/react-native-sandbox/tests/README.md | 112 - .../tests/SandboxRegistryTests.mm | 149 +- 72 files changed, 5388 insertions(+), 180 deletions(-) create mode 100644 apps/p2p-counter/App.tsx create mode 100644 apps/p2p-counter/README.md create mode 100644 apps/p2p-counter/SandboxApp.tsx create mode 100644 apps/p2p-counter/__tests__/App.test.tsx create mode 100644 apps/p2p-counter/android/app/build.gradle create mode 100644 apps/p2p-counter/android/app/debug.keystore create mode 100644 apps/p2p-counter/android/app/proguard-rules.pro create mode 100644 apps/p2p-counter/android/app/src/debug/AndroidManifest.xml create mode 100644 apps/p2p-counter/android/app/src/main/AndroidManifest.xml create mode 100644 apps/p2p-counter/android/app/src/main/java/com/p2pcounter/MainActivity.kt create mode 100644 apps/p2p-counter/android/app/src/main/java/com/p2pcounter/MainApplication.kt create mode 100644 apps/p2p-counter/android/app/src/main/res/drawable/rn_edit_text_material.xml create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 apps/p2p-counter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 apps/p2p-counter/android/app/src/main/res/values/strings.xml create mode 100644 apps/p2p-counter/android/app/src/main/res/values/styles.xml create mode 100644 apps/p2p-counter/android/build.gradle create mode 100644 apps/p2p-counter/android/gradle.properties create mode 100644 apps/p2p-counter/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 apps/p2p-counter/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 apps/p2p-counter/android/gradlew create mode 100644 apps/p2p-counter/android/gradlew.bat create mode 100644 apps/p2p-counter/android/settings.gradle create mode 100644 apps/p2p-counter/app.json create mode 100644 apps/p2p-counter/babel.config.js create mode 100644 apps/p2p-counter/docs/README.md create mode 100644 apps/p2p-counter/docs/screenshot_1.png create mode 100644 apps/p2p-counter/docs/screenshot_2.png create mode 100644 apps/p2p-counter/index.js create mode 100644 apps/p2p-counter/ios/.lldbinit create mode 100644 apps/p2p-counter/ios/.xcode.env create mode 100644 apps/p2p-counter/ios/AppDelegate.swift create mode 100644 apps/p2p-counter/ios/P2PCounter.xcodeproj/project.pbxproj create mode 100644 apps/p2p-counter/ios/P2PCounter.xcodeproj/xcshareddata/xcschemes/MultiInstancePOC.xcscheme create mode 100644 apps/p2p-counter/ios/P2PCounter.xcworkspace/contents.xcworkspacedata create mode 100644 apps/p2p-counter/ios/P2PCounter/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 apps/p2p-counter/ios/P2PCounter/Images.xcassets/Contents.json create mode 100644 apps/p2p-counter/ios/P2PCounter/Info.plist create mode 100644 apps/p2p-counter/ios/P2PCounter/LaunchScreen.storyboard create mode 100644 apps/p2p-counter/ios/P2PCounter/PrivacyInfo.xcprivacy create mode 100644 apps/p2p-counter/ios/Podfile create mode 100644 apps/p2p-counter/ios/Podfile.lock create mode 100644 apps/p2p-counter/jest.config.js create mode 100644 apps/p2p-counter/metro.config.js create mode 100644 apps/p2p-counter/package.json create mode 100644 apps/p2p-counter/react-native.config.js create mode 100644 apps/p2p-counter/rspack.config.mjs create mode 100644 apps/p2p-counter/sandbox.js delete mode 100644 packages/react-native-sandbox/tests/README.md diff --git a/README.md b/README.md index 29c7001..550850c 100644 --- a/README.md +++ b/README.md @@ -142,9 +142,13 @@ AppRegistry.registerComponent("SandboxApp", () => App); Full examples: +- [`apps/demo`](./apps/demo/README.md): Security demo. - [`apps/side-by-side`](./apps/side-by-side/README.md): An example application with two sandbox instances. - [`apps/recursive`](./apps/recursive/README.md): An example application with few nested sandbox instances. -- [`apps/demo`](./apps/demo/README.md): Security demo. +- [`apps/p2p-counter`](./apps/p2p-counter/README.md): Direct sandbox-to-sandbox communication demo. + + + ## ๐Ÿ“š API Reference @@ -155,7 +159,7 @@ For comprehensive API documentation, installation instructions, and advanced usa We're actively working on expanding the capabilities of `react-native-sandbox`. Here's what's planned: - [ ] **Android Support** - Full cross-platform compatibility -- [ ] **Inter-Sandbox Communication** - Secure direct communication between sandbox instances +- [x] **Inter-Sandbox Communication** - Secure direct communication between sandbox instances - [ ] **[RE.Pack](https://github.com/callstack/repack) Integration** - Advanced bundling and module federation - Hot-reloading for sandbox instances in development - Dynamic bundle fetching from remote sources diff --git a/apps/p2p-chat/src/components/ChatCarousel.tsx b/apps/p2p-chat/src/components/ChatCarousel.tsx index 1b936c3..9c96a00 100644 --- a/apps/p2p-chat/src/components/ChatCarousel.tsx +++ b/apps/p2p-chat/src/components/ChatCarousel.tsx @@ -35,8 +35,15 @@ const ChatInstanceView: React.FC = ({ chatInstances, onRemoveChatInstance, }) => { - const {getTargetOptions, getPotentialFriends, getPendingRequests} = - getChatHelpers(chatInstances, friendshipManager) + const { + getTargetOptions, + getPotentialFriends, + getPendingRequests, + getFriends, + } = getChatHelpers(chatInstances, friendshipManager) + + // Get list of friend IDs for allowedOrigins + const allowedOrigins = getFriends(chat.id).map(friend => friend.id) return ( @@ -88,6 +95,7 @@ const ChatInstanceView: React.FC = ({ }} onError={messageHandler.handleChatError(chat.userName)} onMessage={messageHandler.handleChatMessage(chat.id)} + allowedOrigins={allowedOrigins} style={carouselStyles.sandbox} /> diff --git a/apps/p2p-chat/src/services/FriendshipManager.ts b/apps/p2p-chat/src/services/FriendshipManager.ts index 9daab45..d61e6a0 100644 --- a/apps/p2p-chat/src/services/FriendshipManager.ts +++ b/apps/p2p-chat/src/services/FriendshipManager.ts @@ -50,13 +50,6 @@ export class FriendshipManager { return request } - canMessage(from: string, to: string): boolean { - const friendshipKey = this.createFriendshipKey(from, to) - const canSend = this.friendships.has(friendshipKey) - console.log(`[FriendshipManager] Can ${from} message ${to}? ${canSend}`) - return canSend - } - getFriends(userId: string): string[] { const friends: string[] = [] for (const friendship of this.friendships) { diff --git a/apps/p2p-chat/src/services/MessageHandler.ts b/apps/p2p-chat/src/services/MessageHandler.ts index 58e3842..cc9416a 100644 --- a/apps/p2p-chat/src/services/MessageHandler.ts +++ b/apps/p2p-chat/src/services/MessageHandler.ts @@ -17,6 +17,16 @@ export class MessageHandler { handleChatError = (chatId: string) => (error: any) => { console.log(`[${chatId}] Error:`, error) + + // Send error message to sandbox for display in chat history + const errorMessage = { + type: 'message_error', + errorText: error.message || 'An error occurred', + reason: error.name || 'unknown_error', + timestamp: Date.now(), + } + + this.sendToSandbox(chatId, errorMessage) } handleChatMessage = (chatId: string) => (data: any) => { @@ -152,17 +162,6 @@ export class MessageHandler { return } - if (!this.friendshipManager.canMessage(senderId, target)) { - const errorMessage = { - type: 'message_error', - errorText: `Cannot send message to ${target}: not friends`, - reason: 'not_friends', - timestamp: Date.now(), - } - this.sendToSandbox(senderId, errorMessage) - return - } - const forwardedMessage = { type: 'chat_message', messageId, @@ -172,6 +171,7 @@ export class MessageHandler { timestamp, } + // Message will be blocked at native level if not allowed this.sendToSandbox(target, forwardedMessage) } diff --git a/apps/p2p-chat/src/utils/chatHelpers.ts b/apps/p2p-chat/src/utils/chatHelpers.ts index 4235440..07d4e13 100644 --- a/apps/p2p-chat/src/utils/chatHelpers.ts +++ b/apps/p2p-chat/src/utils/chatHelpers.ts @@ -23,10 +23,18 @@ export const getChatHelpers = ( return friendshipManager.getPendingRequestsFor(chatId) } + const getFriends = (chatId: string) => { + return chatInstances + .filter(chat => chat.id !== chatId) // Exclude self + .filter(chat => friendshipManager.areFriends(chatId, chat.id)) // Only include friends + .map(chat => ({id: chat.id, name: chat.userName})) + } + return { getTargetOptions, getPotentialFriends, getPendingRequests, + getFriends, } } diff --git a/apps/p2p-counter/App.tsx b/apps/p2p-counter/App.tsx new file mode 100644 index 0000000..314e914 --- /dev/null +++ b/apps/p2p-counter/App.tsx @@ -0,0 +1,138 @@ +import SandboxReactNativeView, { + SandboxReactNativeViewRef, +} from '@callstack/react-native-sandbox' +import React, {useRef, useState} from 'react' +import {SafeAreaView, StyleSheet, Switch, Text, View} from 'react-native' +import Toast from 'react-native-toast-message' + +interface CounterSandboxProps { + sourceName: string + backgroundColor: string + targetOrigin: string + isCommunicationEnabled: boolean + onCommunicationToggle: (enabled: boolean) => void + communicationLabel: string +} + +function CounterSandboxView({ + sourceName, + backgroundColor, + targetOrigin, + isCommunicationEnabled, + onCommunicationToggle, + communicationLabel, +}: CounterSandboxProps) { + const sandboxRef = useRef(null) + + const handleMessage = (msg: any) => { + console.log(`Got message from ${sourceName}`, msg) + + Toast.show({ + type: 'info', + text1: sourceName, + text2: JSON.stringify(msg), + }) + } + + const handleError = (error: any) => { + const isFatal = error.isFatal + const message = `Got ${isFatal ? 'fatal' : 'non-fatal'} error from ${sourceName}` + console.warn(message, error) + Toast.show({ + type: 'error', + text1: message, + text2: `${error.name}: ${error.message}`, + }) + return false + } + + return ( + + + {communicationLabel} + + + + + ) +} + +export default function App() { + const [allowAtoB, setAllowAtoB] = useState(false) + const [allowBtoA, setAllowBtoA] = useState(false) + + return ( + + + Sandbox 2 sandbox direct communication + + + + + + + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + header: { + padding: 20, + alignItems: 'center', + }, + sandboxContainer: { + flex: 1, + flexDirection: 'row', + }, + control: { + alignItems: 'center', + margin: 5, + }, + communicationControl: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + padding: 10, + margin: 5, + }, + sandboxView: { + flex: 1, + padding: 20, + }, +}) diff --git a/apps/p2p-counter/README.md b/apps/p2p-counter/README.md new file mode 100644 index 0000000..01ddeb2 --- /dev/null +++ b/apps/p2p-counter/README.md @@ -0,0 +1,14 @@ +# P2P Counter Demo + +![Platform: iOS](https://img.shields.io/badge/platform-iOS-blue.svg) + +This example demonstrates direct peer-to-peer communication between two isolated React Native sandboxes using `react-native-sandbox`. It features two side-by-side sandbox instances (A and B), each containing a counter app with "This" and "That" counters. The demo showcases controlled communication where sandboxes can send increment events directly to each other based on configurable origin permissions. + +Users can toggle communication switches to enable/disable message flow between sandboxes, demonstrating the library's ability to provide isolated environments while allowing controlled inter-sandbox communication. The "Increment This" button works locally, while "Increment That" sends messages directly to the other sandbox's counter. + +## Screenshot + +

+ + +
diff --git a/apps/p2p-counter/SandboxApp.tsx b/apps/p2p-counter/SandboxApp.tsx new file mode 100644 index 0000000..1ffb2dd --- /dev/null +++ b/apps/p2p-counter/SandboxApp.tsx @@ -0,0 +1,106 @@ +import React, {useCallback, useEffect, useState} from 'react' +import {Button, StyleSheet, Text, View} from 'react-native' + +type AppProps = { + sourceName: string + backgroundColor: string + targetOrigin: string +} + +function App({sourceName, backgroundColor, targetOrigin}: AppProps) { + const [counterThis, setCounterThis] = useState(0) + + const incrementThis = () => { + setCounterThis(prev => prev + 1) + // No message sent to host or targetOrigin + } + + const incrementThat = () => { + // Send message directly to targetOrigin + if (typeof globalThis.postMessage === 'function') { + globalThis.postMessage( + { + type: 'counter_update', + value: 1, // Increment by 1 + source: sourceName, + counter: 'that', + }, + targetOrigin + ) + } + } + + const onMessage = useCallback((payload: any) => { + if (payload.type === 'counter_update') { + setCounterThis(prev => prev + 1) + } + }, []) + + // Set up message handler + useEffect(() => { + if (typeof globalThis.setOnMessage === 'function') { + globalThis.setOnMessage(onMessage) + } + }, [onMessage]) + + return ( + + Sandbox {sourceName} + + + + {counterThis} +