From a72b6b35a974019e4496b44d3b9339a37e12b6c8 Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj <31964049+isekovanic@users.noreply.github.com> Date: Wed, 26 Aug 2026 10:36:04 +0200 Subject: [PATCH 1/2] refactor: rn 0.87 compatibility (#3786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 🎯 Goal Support React Native 0.87. 0.87 makes the Strict TypeScript API the default and drops several types from the root `react-native` export, which breaks our type-check. Its Babel preset also miscompiles one of our shipped files. The SDK now builds, type-checks and runs on **both 0.86 and 0.87**. Peer range (`>= 0.76.0`) naturally remains unchanged, no public API removed. ## πŸ›  Implementation details **Strict TypeScript API** - new `src/types/react-native-compat.ts` exporting `ViewRef`/`TextInputRef`/`ScrollViewRef`/`FlatListRef` etc. as `React.ComponentRef`. The "fix" is in the semantics, as it resolves to the old class-instance types on `<= 0.86` and the new ones on `0.87`, so we keep the `0.76` floor. Using RN's own `*Instance` names would have dropped everything below `0.87`. Same file redeclares `KeyboardEventListener`, `ViewToken` and `ViewabilityConfig`, which 0.87 no longer exports. **API shape changes** - `useColorScheme()` and `findNodeHandle()` have become `null`able, `AppState.currentState` widened to `string | null | undefined`, `FlatList`'s `data` and `ListFooterComponent` have stopped accepting `null`, style objects have become `Readonly`, `NativeEventEmitter` has become generic. `onAccessibilityAction` moved to `ViewProps`. **Type leaks** - explicit annotations on `Input`, `useAnimatedGalleryStyle`, `SafeAreaViewWrapper` and `ImageGalleryFooter`, plus a `ThemeStyle` union, to stop RN-internal names leaking into `lib/typescript`. **`SqliteClient` runtime fix** - statics were arrow class fields calling `this.foo()`. `0.87`'s Babel preset hoists that to a module-level `var _this = this`, so `lib/commonjs` shipped calls against the wrong object and threw. Now references the class explicitly. **Build config** - both wrappers' `tsconfig.json` opt into legacy deep-import types (the codegen spec must keep the deep `codegenNativeComponent` import; the root export only exists from 0.80). Android: Java 1.8 => 17, Kotlin 1.7 => 2.2, `minSdk` 21 => 24, `compileSdk` fallback 31 => 34. `compileSdk` is 34 rather than 37 on purpose, as `0.87`'s `react-android` declares `minCompileSdk=34` and a library's `compileSdk` caps what *we* may call, it isn't a floor on integrators. It's only reached when the app doesn't set `rootProject.ext.compileSdkVersion`, which basically any app always does. **Jest** - two `moduleNameMapper` shims, both third-party: For our integration tests to work I had to do some mental gymnastics to get everything off the ground. - `react-native-svg` (<= `15.15.5`) reads `Touchable.Mixin` off the RN root. `0.87` keeps that export alive for exactly this call site but installs it late and non-enumerably, so Babel's ESM interop drops it under Jest. - reanimated `4.6.0`'s `initializeReanimatedModule()` calls `setCSSEventHandler()` unconditionally, which throws on the JS only backend Jest uses. `moduleNameMapper` rather than `jest.mock('react-native', …)` because it's resolver-level, so it also covers the separate registry Jest builds for automocks and it doesn't shadow per suite RN mocks. **Example apps** - SampleApp gets the AGP 9 proguard fix (`proguard-android-optimize.txt`; AGP 9 rejects `proguard-android.txt` outright and since it's evaluated at configuration time it breaks the *debug* build too), AGP 9 opt-outs, SDK 37/36, and 13 strict-API fixes in its own code. ExpoMessaging deliberately stays on Expo 57 / RN 0.86 for now, while Expo `58` remains canary only and holding it back doubles as backwards-compat coverage for now. Once 58 ships we can likely bump that too pretty easily. **Native source** - no changes. None of `0.87`'s removed native APIs are used, headers already use the namespaced `` form, and `StreamShimmerViewComponentView` already sets `_props = defaultProps` in `initWithFrame` β€” the thing `0.87` now asserts on. ## 🎨 UI Changes ## πŸ§ͺ Testing ## β˜‘οΈ Checklist - [x] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [x] PR targets the `develop` branch - [ ] Documentation is updated - [x] New code is tested in main example apps, including all possible scenarios - [x] SampleApp iOS and Android - [ ] Expo iOS and Android --- examples/ExpoMessaging/tsconfig.json | 10 +- examples/SampleApp/android/app/build.gradle | 5 +- examples/SampleApp/android/build.gradle | 6 +- examples/SampleApp/android/gradle.properties | 5 + .../gradle/wrapper/gradle-wrapper.properties | 2 +- examples/SampleApp/ios/Podfile.lock | 1216 +++++++---------- .../ios/SampleApp.xcodeproj/project.pbxproj | 2 + examples/SampleApp/package.json | 27 +- .../SampleApp/src/hooks/useStreamChatTheme.ts | 3 +- .../src/screens/ChannelListScreen.tsx | 3 +- .../src/screens/NewDirectMessagingScreen.tsx | 5 +- .../NewGroupChannelAddMemberScreen.tsx | 4 +- .../useScreenReaderComposerFocusEffect.tsx | 7 +- package.json | 2 +- package/__mocks__/SvgTouchableMixin.js | 39 + package/__mocks__/reanimatedModuleInstance.js | 36 + package/expo-package/android/build.gradle | 6 +- .../expo-package/android/gradle.properties | 11 +- package/expo-package/tsconfig.json | 10 + package/jest.config.js | 10 + package/native-package/android/build.gradle | 6 +- .../native-package/android/gradle.properties | 13 +- package/native-package/package.json | 2 +- package/native-package/tsconfig.json | 10 + package/package.json | 14 +- .../hooks/useAccessibilityActivateAction.ts | 8 +- .../a11y/hooks/useSetAccessibilityFocus.ts | 2 +- package/src/components/Attachment/Gallery.tsx | 3 +- .../AutoCompleteInput/AutoCompleteInput.tsx | 9 +- .../__tests__/AutoCompleteInput.test.tsx | 5 +- .../components/members/ChannelMemberList.tsx | 2 +- .../navigation-section/MediaItem.tsx | 9 +- .../ChannelList/ChannelListView.tsx | 2 +- package/src/components/Chat/Chat.tsx | 7 +- .../components/ImageGalleryFooter.tsx | 18 +- .../hooks/useAnimatedGalleryStyle.tsx | 11 +- .../KeyboardCompatibleView.tsx | 6 +- package/src/components/Message/Message.tsx | 7 +- .../MessageItemView/MessageContent.tsx | 8 +- .../__tests__/MessageAuthor.test.tsx | 8 +- .../__tests__/MessagePinnedHeader.test.tsx | 8 +- .../__tests__/MessageReplies.test.tsx | 10 +- .../MessageItemView/utils/renderText.tsx | 6 +- .../Message/MessageOverlayWrapper.tsx | 3 +- .../utils/__tests__/measureInWindow.test.ts | 8 +- .../Message/utils/measureInWindow.ts | 7 +- .../AttachmentUploadPreviewList.tsx | 3 +- .../MessageList/MessageFlashList.tsx | 189 +-- .../components/MessageList/MessageList.tsx | 183 +-- .../useScrollToBottomAccessibilityAction.ts | 20 +- .../MessageMenu/MessageUserReactions.tsx | 4 +- .../MessageUserReactionsAvatar.test.tsx | 6 +- .../Poll/components/CreatePollOptions.tsx | 4 +- .../Poll/components/MultipleVotesSettings.tsx | 3 +- .../src/components/ThreadList/ThreadList.tsx | 2 +- .../UIComponents/PortalWhileClosingView.tsx | 3 +- .../UIComponents/SafeAreaViewWrapper.tsx | 14 +- .../components/UIComponents/SearchInput.tsx | 5 +- .../StreamBottomSheetModalFlatList.tsx | 4 +- .../components/UIComponents/SvgAwareImage.tsx | 2 +- package/src/components/ui/Input/Input.tsx | 7 +- .../messageContext/MessageContext.tsx | 6 +- .../MessageInputContext.tsx | 7 +- .../contexts/themeContext/ThemeContext.tsx | 18 +- package/src/hooks/useAppStateListener.ts | 7 +- package/src/hooks/usePrunableMessageList.ts | 3 +- package/src/index.ts | 1 + package/src/nativeMultipartUpload.ts | 7 +- package/src/store/SqliteClient.ts | 75 +- package/src/types/react-native-compat.ts | 83 ++ yarn.lock | 961 ++++++++----- 71 files changed, 1851 insertions(+), 1367 deletions(-) create mode 100644 package/__mocks__/SvgTouchableMixin.js create mode 100644 package/__mocks__/reanimatedModuleInstance.js create mode 100644 package/src/types/react-native-compat.ts diff --git a/examples/ExpoMessaging/tsconfig.json b/examples/ExpoMessaging/tsconfig.json index ecae322043..be14f163f0 100644 --- a/examples/ExpoMessaging/tsconfig.json +++ b/examples/ExpoMessaging/tsconfig.json @@ -2,7 +2,15 @@ "extends": ["expo/tsconfig.base", "@stream-io/typescript-config/base.json"], "compilerOptions": { "paths": { - "@/*": ["./*"] + "@/*": ["./*"], + // This workspace keeps its own React Native (Expo 57 -> RN 0.86) while the SDK workspace is on + // RN 0.87, and `nmHoistingLimits: workspaces` means both copies exist. Without this, the SDK's + // declarations resolve React Native relative to themselves, so `ColorValue` from the SDK's + // theme and `ColorValue` in this app's `ViewStyle` are two different nominal types. Pinning + // the whole program to this workspace's copy models what a real consumer sees (exactly one + // react-native) and, usefully, type-checks the SDK against RN 0.86. + "react-native": ["./node_modules/react-native"], + "react-native/*": ["./node_modules/react-native/*"] } }, "include": ["**/*.ts", "**/*.tsx"] diff --git a/examples/SampleApp/android/app/build.gradle b/examples/SampleApp/android/app/build.gradle index 0f4cd46263..edbecd0bae 100644 --- a/examples/SampleApp/android/app/build.gradle +++ b/examples/SampleApp/android/app/build.gradle @@ -120,7 +120,10 @@ android { // see https://reactnative.dev/docs/signed-apk-android. signingConfig signingConfigs.debug minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + // AGP 9 (React Native 0.87) rejects "proguard-android.txt" because it bundles + // -dontoptimize, which blocks most R8 optimisations. The optimizing variant is the + // supported replacement. + proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" } } } diff --git a/examples/SampleApp/android/build.gradle b/examples/SampleApp/android/build.gradle index 6af6ee669e..860fda2cf8 100644 --- a/examples/SampleApp/android/build.gradle +++ b/examples/SampleApp/android/build.gradle @@ -3,12 +3,12 @@ import org.apache.tools.ant.taskdefs.condition.Os buildscript { ext { - buildToolsVersion = "36.0.0" + buildToolsVersion = "37.0.0" minSdkVersion = 24 - compileSdkVersion = 36 + compileSdkVersion = 37 targetSdkVersion = 36 ndkVersion = "27.1.12297006" - kotlinVersion = "2.1.20" + kotlinVersion = "2.2.0" androidXCore = "1.0.2" } repositories { diff --git a/examples/SampleApp/android/gradle.properties b/examples/SampleApp/android/gradle.properties index 5923c56006..ed75031faa 100644 --- a/examples/SampleApp/android/gradle.properties +++ b/examples/SampleApp/android/gradle.properties @@ -43,6 +43,11 @@ hermesEnabled=true # Note: Only works with ReactActivity and should not be used with custom Activity. edgeToEdgeEnabled=true +# Opt out of the built-in Kotlin support and the new DSL that ship with AGP 9, which React Native +# 0.87 adopts in a transitional configuration. Starting with AGP 10.x these opt-outs are removed. +android.builtInKotlin=false +android.newDsl=false + # disables the check for multiple instances for gesture handler # this is needed for react-native-gesture-handler to be both a devDep of core and be a dep on the expo sample app disableMultipleInstancesCheck=true diff --git a/examples/SampleApp/android/gradle/wrapper/gradle-wrapper.properties b/examples/SampleApp/android/gradle/wrapper/gradle-wrapper.properties index 37f78a6af8..c61a118f7d 100644 --- a/examples/SampleApp/android/gradle/wrapper/gradle-wrapper.properties +++ b/examples/SampleApp/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/examples/SampleApp/ios/Podfile.lock b/examples/SampleApp/ios/Podfile.lock index e86287ae53..d223b78687 100644 --- a/examples/SampleApp/ios/Podfile.lock +++ b/examples/SampleApp/ios/Podfile.lock @@ -3,6 +3,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -17,11 +18,17 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - FBLazyVector (0.86.2) + - boost (1.84.0): + - ReactNativeDependencies + - DoubleConversion (1.1.6): + - ReactNativeDependencies + - fast_float (8.0.0): + - ReactNativeDependencies + - FBLazyVector (0.87.0): + - React-Core-prebuilt - Firebase/Analytics (12.10.0): - Firebase/Core - Firebase/AppDistribution (12.10.0): @@ -102,6 +109,10 @@ PODS: - GoogleUtilities/UserDefaults (~> 8.1) - nanopb (~> 3.30910.0) - PromisesSwift (~> 2.1) + - fmt (12.1.0): + - ReactNativeDependencies + - glog (0.3.5): + - ReactNativeDependencies - GoogleAdsOnDeviceConversion (3.3.0): - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Logger (~> 8.1) @@ -188,6 +199,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-callinvoker - React-Core - React-Core-prebuilt @@ -203,7 +215,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -212,6 +223,7 @@ PODS: - NitroModules - RCTRequired - RCTTypeSafety + - React-bridging - React-callinvoker - React-Core - React-Core-prebuilt @@ -227,14 +239,14 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - op-sqlite (17.1.2): + - op-sqlite (18.1.1): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -249,341 +261,118 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - PromisesObjC (2.4.1) - PromisesSwift (2.4.1): - PromisesObjC (= 2.4.1) - - RCTDeprecation (0.86.2) - - RCTRequired (0.86.2) - - RCTSwiftUI (0.86.2) - - RCTSwiftUIWrapper (0.86.2): - - RCTSwiftUI - - RCTTypeSafety (0.86.2): - - FBLazyVector (= 0.86.2) - - RCTRequired (= 0.86.2) - - React-Core (= 0.86.2) - - React (0.86.2): - - React-Core (= 0.86.2) - - React-Core/DevSupport (= 0.86.2) - - React-Core/RCTWebSocket (= 0.86.2) - - React-RCTActionSheet (= 0.86.2) - - React-RCTAnimation (= 0.86.2) - - React-RCTBlob (= 0.86.2) - - React-RCTImage (= 0.86.2) - - React-RCTLinking (= 0.86.2) - - React-RCTNetwork (= 0.86.2) - - React-RCTSettings (= 0.86.2) - - React-RCTText (= 0.86.2) - - React-RCTVibration (= 0.86.2) - - React-callinvoker (0.86.2) - - React-Core (0.86.2): - - hermes-engine - - RCTDeprecation - - React-Core-prebuilt - - React-Core/Default (= 0.86.2) - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils + - RCT-Folly (2024.11.18.00): + - RCT-Folly/Default (= 2024.11.18.00) - ReactNativeDependencies - - Yoga - - React-Core-prebuilt (0.86.2): + - RCT-Folly/Default (2024.11.18.00): - ReactNativeDependencies - - React-Core/CoreModulesHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - RCTDeprecation (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/Default (0.86.2): - - hermes-engine - - RCTDeprecation + - RCTRequired (0.87.0): - React-Core-prebuilt - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/DevSupport (0.86.2): - - hermes-engine - - RCTDeprecation + - RCTSwiftUI (0.87.0) + - RCTSwiftUIWrapper (0.87.0): + - RCTSwiftUI + - RCTTypeSafety (0.87.0): + - FBLazyVector (= 0.87.0) + - RCTRequired (= 0.87.0) + - React-Core (= 0.87.0) + - React (0.87.0): + - React-Core (= 0.87.0) + - React-Core/DevSupport (= 0.87.0) + - React-Core/RCTWebSocket (= 0.87.0) + - React-RCTActionSheet (= 0.87.0) + - React-RCTAnimation (= 0.87.0) + - React-RCTBlob (= 0.87.0) + - React-RCTImage (= 0.87.0) + - React-RCTLinking (= 0.87.0) + - React-RCTNetwork (= 0.87.0) + - React-RCTSettings (= 0.87.0) + - React-RCTText (= 0.87.0) + - React-RCTVibration (= 0.87.0) + - React-bridging (0.87.0): + - React-callinvoker - React-Core-prebuilt - - React-Core/Default (= 0.86.2) - - React-Core/RCTWebSocket (= 0.86.2) - - React-cxxreact - - React-featureflags - - React-hermes - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils + - React-timing - ReactNativeDependencies - - Yoga - - React-Core/RCTActionSheetHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - React-callinvoker (0.87.0) + - React-Core (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils + - React-Core/Default (= 0.87.0) + - React-Core-prebuilt (0.87.0): - ReactNativeDependencies - - Yoga - - React-Core/RCTAnimationHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - React-Core/CoreModulesHeaders (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/RCTBlobHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - React-Core/Default (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/RCTImageHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - React-Core/DevSupport (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/RCTLinkingHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - React-Core/RCTActionSheetHeaders (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/RCTNetworkHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - React-Core/RCTAnimationHeaders (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/RCTSettingsHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - React-Core/RCTBlobHeaders (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/RCTTextHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - React-Core/RCTImageHeaders (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/RCTVibrationHeaders (0.86.2): - - hermes-engine - - RCTDeprecation + - React-Core/RCTLinkingHeaders (0.87.0): - React-Core-prebuilt - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-Core/RCTWebSocket (0.86.2): - - hermes-engine - - RCTDeprecation + - React-Core/RCTNetworkHeaders (0.87.0): - React-Core-prebuilt - - React-Core/Default (= 0.86.2) - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-CoreModules (0.86.2): - - RCTTypeSafety (= 0.86.2) + - React-Core/RCTSettingsHeaders (0.87.0): + - React-Core-prebuilt + - React-Core/RCTTextHeaders (0.87.0): + - React-Core-prebuilt + - React-Core/RCTVibrationHeaders (0.87.0): + - React-Core-prebuilt + - React-Core/RCTWebSocket (0.87.0): + - React-Core-prebuilt + - React-CoreModules (0.87.0): + - RCTTypeSafety (= 0.87.0) - React-Core-prebuilt - - React-Core/CoreModulesHeaders (= 0.86.2) + - React-Core/CoreModulesHeaders (= 0.87.0) - React-debug - React-featureflags - - React-jsi (= 0.86.2) + - React-jsi (= 0.87.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - React-NativeModulesApple - React-RCTBlob - React-RCTFBReactNativeSpec - - React-RCTImage (= 0.86.2) + - React-RCTImage (= 0.87.0) - React-runtimeexecutor - React-utils - ReactCommon - ReactNativeDependencies - - React-cxxreact (0.86.2): + - React-cxxreact (0.87.0): - hermes-engine - - React-callinvoker (= 0.86.2) + - React-callinvoker (= 0.87.0) - React-Core-prebuilt - - React-debug (= 0.86.2) - - React-jsi (= 0.86.2) + - React-debug (= 0.87.0) + - React-jserrorhandler (= 0.87.0) + - React-jsi (= 0.87.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - - React-logger (= 0.86.2) - - React-perflogger (= 0.86.2) + - React-logger (= 0.87.0) + - React-perflogger (= 0.87.0) - React-runtimeexecutor - - React-timing (= 0.86.2) + - React-timing (= 0.87.0) - React-utils - ReactNativeDependencies - - React-debug (0.86.2): - - React-debug/redbox (= 0.86.2) - - React-debug/redbox (0.86.2) - - React-defaultsnativemodule (0.86.2): + - React-cxxstableapi (0.87.0) + - React-debug (0.87.0): + - React-debug/redbox (= 0.87.0) + - React-debug/redbox (0.87.0) + - React-defaultsnativemodule (0.87.0): - hermes-engine - React-Core-prebuilt - React-domnativemodule @@ -601,8 +390,9 @@ PODS: - React-webperformancenativemodule - ReactNativeDependencies - Yoga - - React-domnativemodule (0.86.2): + - React-domnativemodule (0.87.0): - hermes-engine + - React-bridging - React-Core-prebuilt - React-Fabric - React-Fabric/bridging @@ -615,33 +405,34 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-Fabric (0.86.2): + - React-Fabric (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact - React-debug - - React-Fabric/animated (= 0.86.2) - - React-Fabric/animationbackend (= 0.86.2) - - React-Fabric/animations (= 0.86.2) - - React-Fabric/attributedstring (= 0.86.2) - - React-Fabric/bridging (= 0.86.2) - - React-Fabric/componentregistry (= 0.86.2) - - React-Fabric/componentregistrynative (= 0.86.2) - - React-Fabric/components (= 0.86.2) - - React-Fabric/consistency (= 0.86.2) - - React-Fabric/core (= 0.86.2) - - React-Fabric/dom (= 0.86.2) - - React-Fabric/imagemanager (= 0.86.2) - - React-Fabric/leakchecker (= 0.86.2) - - React-Fabric/mounting (= 0.86.2) - - React-Fabric/observers (= 0.86.2) - - React-Fabric/scheduler (= 0.86.2) - - React-Fabric/telemetry (= 0.86.2) - - React-Fabric/uimanager (= 0.86.2) - - React-Fabric/viewtransition (= 0.86.2) + - React-Fabric/animated (= 0.87.0) + - React-Fabric/animationbackend (= 0.87.0) + - React-Fabric/animations (= 0.87.0) + - React-Fabric/attributedstring (= 0.87.0) + - React-Fabric/bridging (= 0.87.0) + - React-Fabric/componentregistry (= 0.87.0) + - React-Fabric/componentregistrynative (= 0.87.0) + - React-Fabric/components (= 0.87.0) + - React-Fabric/consistency (= 0.87.0) + - React-Fabric/core (= 0.87.0) + - React-Fabric/dom (= 0.87.0) + - React-Fabric/imagemanager (= 0.87.0) + - React-Fabric/leakchecker (= 0.87.0) + - React-Fabric/mounting (= 0.87.0) + - React-Fabric/observers (= 0.87.0) + - React-Fabric/scheduler (= 0.87.0) + - React-Fabric/telemetry (= 0.87.0) + - React-Fabric/uimanager (= 0.87.0) + - React-Fabric/viewtransition (= 0.87.0) - React-featureflags - React-graphics - React-jsi @@ -653,10 +444,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/animated (0.86.2): + - React-Fabric/animated (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -673,10 +465,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/animationbackend (0.86.2): + - React-Fabric/animationbackend (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -692,10 +485,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/animations (0.86.2): + - React-Fabric/animations (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -711,10 +505,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/attributedstring (0.86.2): + - React-Fabric/attributedstring (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -730,10 +525,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/bridging (0.86.2): + - React-Fabric/bridging (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -749,10 +545,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/componentregistry (0.86.2): + - React-Fabric/componentregistry (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -768,10 +565,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/componentregistrynative (0.86.2): + - React-Fabric/componentregistrynative (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -787,18 +585,19 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/components (0.86.2): + - React-Fabric/components (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact - React-debug - - React-Fabric/components/legacyviewmanagerinterop (= 0.86.2) - - React-Fabric/components/root (= 0.86.2) - - React-Fabric/components/scrollview (= 0.86.2) - - React-Fabric/components/view (= 0.86.2) + - React-Fabric/components/legacyviewmanagerinterop (= 0.87.0) + - React-Fabric/components/root (= 0.87.0) + - React-Fabric/components/scrollview (= 0.87.0) + - React-Fabric/components/view (= 0.87.0) - React-featureflags - React-graphics - React-jsi @@ -810,10 +609,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/components/legacyviewmanagerinterop (0.86.2): + - React-Fabric/components/legacyviewmanagerinterop (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -829,10 +629,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/components/root (0.86.2): + - React-Fabric/components/root (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -848,10 +649,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/components/scrollview (0.86.2): + - React-Fabric/components/scrollview (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -867,10 +669,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/components/view (0.86.2): + - React-Fabric/components/view (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -888,10 +691,11 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-Fabric/consistency (0.86.2): + - React-Fabric/consistency (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -907,10 +711,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/core (0.86.2): + - React-Fabric/core (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -926,10 +731,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/dom (0.86.2): + - React-Fabric/dom (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -945,10 +751,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/imagemanager (0.86.2): + - React-Fabric/imagemanager (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -964,10 +771,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/leakchecker (0.86.2): + - React-Fabric/leakchecker (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -983,10 +791,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/mounting (0.86.2): + - React-Fabric/mounting (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -1003,17 +812,18 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/observers (0.86.2): + - React-Fabric/observers (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact - React-debug - - React-Fabric/observers/events (= 0.86.2) - - React-Fabric/observers/intersection (= 0.86.2) - - React-Fabric/observers/mutation (= 0.86.2) + - React-Fabric/observers/events (= 0.87.0) + - React-Fabric/observers/intersection (= 0.87.0) + - React-Fabric/observers/mutation (= 0.87.0) - React-featureflags - React-graphics - React-jsi @@ -1025,10 +835,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/observers/events (0.86.2): + - React-Fabric/observers/events (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -1044,10 +855,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/observers/intersection (0.86.2): + - React-Fabric/observers/intersection (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -1063,10 +875,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/observers/mutation (0.86.2): + - React-Fabric/observers/mutation (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -1082,10 +895,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/scheduler (0.86.2): + - React-Fabric/scheduler (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -1106,10 +920,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/telemetry (0.86.2): + - React-Fabric/telemetry (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -1125,15 +940,16 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/uimanager (0.86.2): + - React-Fabric/uimanager (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact - React-debug - - React-Fabric/uimanager/consistency (= 0.86.2) + - React-Fabric/uimanager/consistency (= 0.87.0) - React-featureflags - React-graphics - React-jsi @@ -1146,10 +962,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/uimanager/consistency (0.86.2): + - React-Fabric/uimanager/consistency (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -1166,10 +983,11 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-Fabric/viewtransition (0.86.2): + - React-Fabric/viewtransition (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-cxxreact @@ -1185,7 +1003,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-FabricComponents (0.86.2): + - React-FabricComponents (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1194,8 +1012,8 @@ PODS: - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components (= 0.86.2) - - React-FabricComponents/textlayoutmanager (= 0.86.2) + - React-FabricComponents/components (= 0.87.0) + - React-FabricComponents/textlayoutmanager (= 0.87.0) - React-featureflags - React-graphics - React-jsi @@ -1208,7 +1026,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components (0.86.2): + - React-FabricComponents/components (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1217,17 +1035,17 @@ PODS: - React-cxxreact - React-debug - React-Fabric - - React-FabricComponents/components/inputaccessory (= 0.86.2) - - React-FabricComponents/components/iostextinput (= 0.86.2) - - React-FabricComponents/components/modal (= 0.86.2) - - React-FabricComponents/components/rncore (= 0.86.2) - - React-FabricComponents/components/safeareaview (= 0.86.2) - - React-FabricComponents/components/scrollview (= 0.86.2) - - React-FabricComponents/components/switch (= 0.86.2) - - React-FabricComponents/components/text (= 0.86.2) - - React-FabricComponents/components/textinput (= 0.86.2) - - React-FabricComponents/components/unimplementedview (= 0.86.2) - - React-FabricComponents/components/virtualview (= 0.86.2) + - React-FabricComponents/components/inputaccessory (= 0.87.0) + - React-FabricComponents/components/iostextinput (= 0.87.0) + - React-FabricComponents/components/modal (= 0.87.0) + - React-FabricComponents/components/rncore (= 0.87.0) + - React-FabricComponents/components/safeareaview (= 0.87.0) + - React-FabricComponents/components/scrollview (= 0.87.0) + - React-FabricComponents/components/switch (= 0.87.0) + - React-FabricComponents/components/text (= 0.87.0) + - React-FabricComponents/components/textinput (= 0.87.0) + - React-FabricComponents/components/unimplementedview (= 0.87.0) + - React-FabricComponents/components/virtualview (= 0.87.0) - React-featureflags - React-graphics - React-jsi @@ -1240,7 +1058,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/inputaccessory (0.86.2): + - React-FabricComponents/components/inputaccessory (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1261,7 +1079,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/iostextinput (0.86.2): + - React-FabricComponents/components/iostextinput (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1282,7 +1100,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/modal (0.86.2): + - React-FabricComponents/components/modal (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1303,7 +1121,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/rncore (0.86.2): + - React-FabricComponents/components/rncore (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1324,7 +1142,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/safeareaview (0.86.2): + - React-FabricComponents/components/safeareaview (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1345,7 +1163,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/scrollview (0.86.2): + - React-FabricComponents/components/scrollview (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1366,7 +1184,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/switch (0.86.2): + - React-FabricComponents/components/switch (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1387,7 +1205,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/text (0.86.2): + - React-FabricComponents/components/text (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1408,7 +1226,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/textinput (0.86.2): + - React-FabricComponents/components/textinput (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1429,7 +1247,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/unimplementedview (0.86.2): + - React-FabricComponents/components/unimplementedview (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1450,7 +1268,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/components/virtualview (0.86.2): + - React-FabricComponents/components/virtualview (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1471,7 +1289,7 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricComponents/textlayoutmanager (0.86.2): + - React-FabricComponents/textlayoutmanager (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -1492,27 +1310,27 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-FabricImage (0.86.2): + - React-FabricImage (0.87.0): - hermes-engine - - RCTRequired (= 0.86.2) - - RCTTypeSafety (= 0.86.2) + - RCTRequired (= 0.87.0) + - RCTTypeSafety (= 0.87.0) - React-Core-prebuilt - React-Fabric - React-featureflags - React-graphics - React-ImageManager - React-jsi - - React-jsiexecutor (= 0.86.2) + - React-jsiexecutor (= 0.87.0) - React-logger - React-rendererdebug - React-utils - ReactCommon - ReactNativeDependencies - Yoga - - React-featureflags (0.86.2): + - React-featureflags (0.87.0): - React-Core-prebuilt - ReactNativeDependencies - - React-featureflagsnativemodule (0.86.2): + - React-featureflagsnativemodule (0.87.0): - hermes-engine - React-Core-prebuilt - React-featureflags @@ -1521,7 +1339,7 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-graphics (0.86.2): + - React-graphics (0.87.0): - hermes-engine - React-Core-prebuilt - React-featureflags @@ -1530,22 +1348,23 @@ PODS: - React-rendererdebug - React-utils - ReactNativeDependencies - - React-hermes (0.86.2): + - React-hermes (0.87.0): - hermes-engine - React-Core-prebuilt - - React-cxxreact (= 0.86.2) + - React-cxxreact (= 0.87.0) - React-jsi - - React-jsiexecutor (= 0.86.2) + - React-jsiexecutor (= 0.87.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - React-jsitooling - React-oscompat - - React-perflogger (= 0.86.2) + - React-perflogger (= 0.87.0) - React-runtimeexecutor - ReactNativeDependencies - - React-idlecallbacksnativemodule (0.86.2): + - React-idlecallbacksnativemodule (0.87.0): - hermes-engine + - React-bridging - React-Core-prebuilt - React-jsi - React-jsiexecutor @@ -1554,7 +1373,7 @@ PODS: - React-runtimescheduler - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-ImageManager (0.86.2): + - React-ImageManager (0.87.0): - React-Core-prebuilt - React-Core/Default - React-debug @@ -1563,8 +1382,9 @@ PODS: - React-rendererdebug - React-utils - ReactNativeDependencies - - React-intersectionobservernativemodule (0.86.2): + - React-intersectionobservernativemodule (0.87.0): - hermes-engine + - React-bridging - React-Core-prebuilt - React-cxxreact - React-Fabric @@ -1578,20 +1398,19 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-jserrorhandler (0.86.2): + - React-jserrorhandler (0.87.0): - hermes-engine + - React-bridging - React-Core-prebuilt - - React-cxxreact - React-debug - React-featureflags - React-jsi - - ReactCommon/turbomodule/bridging - ReactNativeDependencies - - React-jsi (0.86.2): + - React-jsi (0.87.0): - hermes-engine - React-Core-prebuilt - ReactNativeDependencies - - React-jsiexecutor (0.86.2): + - React-jsiexecutor (0.87.0): - hermes-engine - React-Core-prebuilt - React-cxxreact @@ -1606,7 +1425,7 @@ PODS: - React-runtimeexecutor - React-utils - ReactNativeDependencies - - React-jsinspector (0.86.2): + - React-jsinspector (0.87.0): - hermes-engine - React-Core-prebuilt - React-featureflags @@ -1615,18 +1434,18 @@ PODS: - React-jsinspectornetwork - React-jsinspectortracing - React-oscompat - - React-perflogger (= 0.86.2) + - React-perflogger (= 0.87.0) - React-runtimeexecutor - React-utils - ReactNativeDependencies - - React-jsinspectorcdp (0.86.2): + - React-jsinspectorcdp (0.87.0): - React-Core-prebuilt - ReactNativeDependencies - - React-jsinspectornetwork (0.86.2): + - React-jsinspectornetwork (0.87.0): - React-Core-prebuilt - React-jsinspectorcdp - ReactNativeDependencies - - React-jsinspectortracing (0.86.2): + - React-jsinspectortracing (0.87.0): - hermes-engine - React-Core-prebuilt - React-jsi @@ -1635,28 +1454,28 @@ PODS: - React-timing - React-utils - ReactNativeDependencies - - React-jsitooling (0.86.2): + - React-jsitooling (0.87.0): - hermes-engine - React-Core-prebuilt - - React-cxxreact (= 0.86.2) + - React-cxxreact (= 0.87.0) - React-debug - - React-jsi (= 0.86.2) + - React-jsi (= 0.87.0) - React-jsinspector - React-jsinspectorcdp - React-jsinspectortracing - React-runtimeexecutor - React-utils - ReactNativeDependencies - - React-jsitracing (0.86.2): + - React-jsitracing (0.87.0): - React-jsi - - React-logger (0.86.2): + - React-logger (0.87.0): - React-Core-prebuilt - ReactNativeDependencies - - React-Mapbuffer (0.86.2): + - React-Mapbuffer (0.87.0): - React-Core-prebuilt - React-debug - ReactNativeDependencies - - React-microtasksnativemodule (0.86.2): + - React-microtasksnativemodule (0.87.0): - hermes-engine - React-Core-prebuilt - React-jsi @@ -1664,8 +1483,9 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-mutationobservernativemodule (0.86.2): + - React-mutationobservernativemodule (0.87.0): - hermes-engine + - React-bridging - React-Core-prebuilt - React-cxxreact - React-Fabric @@ -1683,6 +1503,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1697,7 +1518,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1705,6 +1525,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1719,7 +1540,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1727,6 +1547,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1741,7 +1562,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1749,6 +1569,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1763,7 +1584,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1771,6 +1591,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1785,7 +1606,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1793,6 +1613,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1807,7 +1628,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1817,6 +1637,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1831,7 +1652,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1839,6 +1659,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1854,7 +1675,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1862,6 +1682,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1876,7 +1697,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1884,6 +1704,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1900,7 +1721,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1908,6 +1728,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1922,7 +1743,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1930,6 +1750,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1945,7 +1766,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1953,6 +1773,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1968,7 +1789,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1976,6 +1796,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -1990,7 +1811,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -1998,6 +1818,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2013,12 +1834,12 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-NativeModulesApple (0.86.2): + - React-NativeModulesApple (0.87.0): - hermes-engine + - React-bridging - React-callinvoker - React-Core - React-Core-prebuilt @@ -2029,21 +1850,20 @@ PODS: - React-jsinspector - React-jsinspectorcdp - React-runtimeexecutor - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - React-networking (0.86.2): + - React-networking (0.87.0): - React-Core-prebuilt - React-jsinspectornetwork - React-jsinspectortracing - React-performancetimeline - React-timing - ReactNativeDependencies - - React-oscompat (0.86.2) - - React-perflogger (0.86.2): + - React-oscompat (0.87.0) + - React-perflogger (0.87.0): - React-Core-prebuilt - ReactNativeDependencies - - React-performancecdpmetrics (0.86.2): + - React-performancecdpmetrics (0.87.0): - hermes-engine - React-Core-prebuilt - React-jsi @@ -2051,7 +1871,7 @@ PODS: - React-runtimeexecutor - React-timing - ReactNativeDependencies - - React-performancetimeline (0.86.2): + - React-performancetimeline (0.87.0): - React-Core-prebuilt - React-featureflags - React-jsinspector @@ -2059,9 +1879,18 @@ PODS: - React-perflogger - React-timing - ReactNativeDependencies - - React-RCTActionSheet (0.86.2): - - React-Core/RCTActionSheetHeaders (= 0.86.2) - - React-RCTAnimation (0.86.2): + - React-RCTActionSheet (0.87.0): + - React-Core/RCTActionSheetHeaders (= 0.87.0) + - React-RCTAnimatedModuleProvider (0.87.0): + - hermes-engine + - React-Core + - React-Core-prebuilt + - React-Fabric/animated + - React-featureflags + - ReactCommon + - ReactNativeDependencies + - Yoga + - React-RCTAnimation (0.87.0): - RCTTypeSafety - React-Core-prebuilt - React-Core/RCTAnimationHeaders @@ -2072,7 +1901,7 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon - ReactNativeDependencies - - React-RCTAppDelegate (0.86.2): + - React-RCTAppDelegate (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety @@ -2087,6 +1916,7 @@ PODS: - React-hermes - React-jsitooling - React-NativeModulesApple + - React-RCTAnimatedModuleProvider - React-RCTFabric - React-RCTFBReactNativeSpec - React-RCTImage @@ -2100,7 +1930,7 @@ PODS: - React-utils - ReactCommon - ReactNativeDependencies - - React-RCTBlob (0.86.2): + - React-RCTBlob (0.87.0): - hermes-engine - React-Core-prebuilt - React-Core/RCTBlobHeaders @@ -2113,52 +1943,25 @@ PODS: - React-RCTNetwork - ReactCommon - ReactNativeDependencies - - React-RCTFabric (0.86.2): - - hermes-engine - - RCTSwiftUIWrapper - - React-Core + - React-RCTFabric (0.87.0): - React-Core-prebuilt - - React-debug - - React-Fabric - - React-FabricComponents - - React-FabricImage - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectortracing - - React-networking - - React-performancecdpmetrics - - React-performancetimeline - - React-RCTAnimation - - React-RCTFBReactNativeSpec - - React-RCTImage - - React-RCTText - - React-rendererconsistency - - React-renderercss - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactNativeDependencies - - Yoga - - React-RCTFBReactNativeSpec (0.86.2): + - React-RCTFBReactNativeSpec (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-jsi - React-NativeModulesApple - - React-RCTFBReactNativeSpec/components (= 0.86.2) + - React-RCTFBReactNativeSpec/components (= 0.87.0) - ReactCommon - ReactNativeDependencies - - React-RCTFBReactNativeSpec/components (0.86.2): + - React-RCTFBReactNativeSpec/components (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2172,7 +1975,7 @@ PODS: - ReactCommon - ReactNativeDependencies - Yoga - - React-RCTImage (0.86.2): + - React-RCTImage (0.87.0): - RCTTypeSafety - React-Core-prebuilt - React-Core/RCTImageHeaders @@ -2182,14 +1985,14 @@ PODS: - React-RCTNetwork - ReactCommon - ReactNativeDependencies - - React-RCTLinking (0.86.2): - - React-Core/RCTLinkingHeaders (= 0.86.2) - - React-jsi (= 0.86.2) + - React-RCTLinking (0.87.0): + - React-Core/RCTLinkingHeaders (= 0.87.0) + - React-jsi (= 0.87.0) - React-NativeModulesApple - React-RCTFBReactNativeSpec - ReactCommon - - ReactCommon/turbomodule/core (= 0.86.2) - - React-RCTNetwork (0.86.2): + - ReactCommon/turbomodule/core (= 0.87.0) + - React-RCTNetwork (0.87.0): - RCTTypeSafety - React-Core-prebuilt - React-Core/RCTNetworkHeaders @@ -2203,23 +2006,9 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon - ReactNativeDependencies - - React-RCTRuntime (0.86.2): - - hermes-engine - - React-Core + - React-RCTRuntime (0.87.0): - React-Core-prebuilt - - React-debug - - React-jsi - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectortracing - - React-jsitooling - - React-RuntimeApple - - React-RuntimeCore - - React-runtimeexecutor - - React-RuntimeHermes - - React-utils - - ReactNativeDependencies - - React-RCTSettings (0.86.2): + - React-RCTSettings (0.87.0): - RCTTypeSafety - React-Core-prebuilt - React-Core/RCTSettingsHeaders @@ -2228,10 +2017,10 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon - ReactNativeDependencies - - React-RCTText (0.86.2): - - React-Core/RCTTextHeaders (= 0.86.2) + - React-RCTText (0.87.0): + - React-Core/RCTTextHeaders (= 0.87.0) - Yoga - - React-RCTVibration (0.86.2): + - React-RCTVibration (0.87.0): - React-Core-prebuilt - React-Core/RCTVibrationHeaders - React-jsi @@ -2239,15 +2028,15 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon - ReactNativeDependencies - - React-rendererconsistency (0.86.2) - - React-renderercss (0.86.2): + - React-rendererconsistency (0.87.0) + - React-renderercss (0.87.0): - React-debug - React-utils - - React-rendererdebug (0.86.2): + - React-rendererdebug (0.87.0): - React-Core-prebuilt - React-debug - ReactNativeDependencies - - React-RuntimeApple (0.86.2): + - React-RuntimeApple (0.87.0): - hermes-engine - React-callinvoker - React-Core-prebuilt @@ -2270,7 +2059,7 @@ PODS: - React-runtimescheduler - React-utils - ReactNativeDependencies - - React-RuntimeCore (0.86.2): + - React-RuntimeCore (0.87.0): - hermes-engine - React-Core-prebuilt - React-cxxreact @@ -2286,14 +2075,14 @@ PODS: - React-runtimescheduler - React-utils - ReactNativeDependencies - - React-runtimeexecutor (0.86.2): + - React-runtimeexecutor (0.87.0): - React-Core-prebuilt - React-debug - React-featureflags - - React-jsi (= 0.86.2) + - React-jsi (= 0.87.0) - React-utils - ReactNativeDependencies - - React-RuntimeHermes (0.86.2): + - React-RuntimeHermes (0.87.0): - hermes-engine - React-Core-prebuilt - React-featureflags @@ -2308,13 +2097,14 @@ PODS: - React-runtimeexecutor - React-utils - ReactNativeDependencies - - React-runtimescheduler (0.86.2): + - React-runtimescheduler (0.87.0): - hermes-engine - React-callinvoker - React-Core-prebuilt - React-cxxreact - React-debug - React-featureflags + - React-jserrorhandler - React-jsi - React-jsinspectortracing - React-performancetimeline @@ -2324,15 +2114,15 @@ PODS: - React-timing - React-utils - ReactNativeDependencies - - React-timing (0.86.2): + - React-timing (0.87.0): - React-debug - - React-utils (0.86.2): + - React-utils (0.87.0): - hermes-engine - React-Core-prebuilt - React-debug - - React-jsi (= 0.86.2) + - React-jsi (= 0.87.0) - ReactNativeDependencies - - React-viewtransitionnativemodule (0.86.2): + - React-viewtransitionnativemodule (0.87.0): - hermes-engine - React-Core-prebuilt - React-Fabric @@ -2344,8 +2134,9 @@ PODS: - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - React-webperformancenativemodule (0.86.2): + - React-webperformancenativemodule (0.87.0): - hermes-engine + - React-bridging - React-Core-prebuilt - React-cxxreact - React-jsi @@ -2355,12 +2146,13 @@ PODS: - React-runtimeexecutor - ReactCommon/turbomodule/core - ReactNativeDependencies - - ReactAppDependencyProvider (0.86.2): + - ReactAppDependencyProvider (0.87.0): - ReactCodegen - - ReactCodegen (0.86.2): + - ReactCodegen (0.87.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2374,50 +2166,40 @@ PODS: - React-RCTAppDelegate - React-rendererdebug - React-utils - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - ReactCommon (0.86.2): - - React-Core-prebuilt - - ReactCommon/turbomodule (= 0.86.2) - - ReactNativeDependencies - - ReactCommon/turbomodule (0.86.2): - - hermes-engine - - React-callinvoker (= 0.86.2) + - ReactCommon (0.87.0): - React-Core-prebuilt - - React-cxxreact (= 0.86.2) - - React-jsi (= 0.86.2) - - React-logger (= 0.86.2) - - React-perflogger (= 0.86.2) - - ReactCommon/turbomodule/bridging (= 0.86.2) - - ReactCommon/turbomodule/core (= 0.86.2) + - ReactCommon/turbomodule (= 0.87.0) - ReactNativeDependencies - - ReactCommon/turbomodule/bridging (0.86.2): + - ReactCommon/turbomodule (0.87.0): - hermes-engine - - React-callinvoker (= 0.86.2) + - React-callinvoker (= 0.87.0) - React-Core-prebuilt - - React-cxxreact (= 0.86.2) - - React-jsi (= 0.86.2) - - React-logger (= 0.86.2) - - React-perflogger (= 0.86.2) + - React-jsi (= 0.87.0) + - React-logger (= 0.87.0) + - React-perflogger (= 0.87.0) + - ReactCommon/turbomodule/core (= 0.87.0) - ReactNativeDependencies - - ReactCommon/turbomodule/core (0.86.2): + - ReactCommon/turbomodule/core (0.87.0): - hermes-engine - - React-callinvoker (= 0.86.2) + - React-bridging + - React-callinvoker (= 0.87.0) - React-Core-prebuilt - - React-cxxreact (= 0.86.2) - - React-debug (= 0.86.2) - - React-featureflags (= 0.86.2) - - React-jsi (= 0.86.2) - - React-logger (= 0.86.2) - - React-perflogger (= 0.86.2) - - React-utils (= 0.86.2) + - React-cxxreact (= 0.87.0) + - React-debug (= 0.87.0) + - React-featureflags (= 0.87.0) + - React-jsi (= 0.87.0) + - React-logger (= 0.87.0) + - React-perflogger (= 0.87.0) + - React-utils (= 0.87.0) - ReactNativeDependencies - - ReactNativeDependencies (0.86.2) + - ReactNativeDependencies (0.87.0) - RNCClipboard (1.16.3): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2432,7 +2214,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -2442,6 +2223,7 @@ PODS: - libavif/libdav1d (~> 0.11.1) - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2456,7 +2238,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - SDWebImage (>= 5.19.1) @@ -2469,6 +2250,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2483,7 +2265,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -2493,6 +2274,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2507,7 +2289,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - RNFBApp @@ -2516,6 +2297,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2531,7 +2313,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -2544,6 +2325,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2558,14 +2340,14 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNReanimated (4.5.2): + - RNReanimated (4.6.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2581,18 +2363,18 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - RNReanimated/apple (= 4.5.2) - - RNReanimated/common (= 4.5.2) - - RNReanimated/view (= 4.5.2) + - RNReanimated/apple (= 4.6.0) + - RNReanimated/common (= 4.6.0) + - RNReanimated/view (= 4.6.0) - RNWorklets - Yoga - - RNReanimated/apple (4.5.2): + - RNReanimated/apple (4.6.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2608,15 +2390,15 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - RNWorklets - Yoga - - RNReanimated/common (4.5.2): + - RNReanimated/common (4.6.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2632,15 +2414,15 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - RNWorklets - Yoga - - RNReanimated/view (4.5.2): + - RNReanimated/view (4.6.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2656,15 +2438,15 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - RNWorklets - Yoga - - RNScreens (4.26.2): + - RNScreens (4.27.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2680,15 +2462,15 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - RNScreens/common (= 4.26.2) + - RNScreens/common (= 4.27.0) - Yoga - - RNScreens/common (4.26.2): + - RNScreens/common (4.27.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2704,7 +2486,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -2712,6 +2493,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2726,7 +2508,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -2734,6 +2515,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2748,7 +2530,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - RNSVG/common (= 15.15.5) @@ -2757,6 +2538,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2771,14 +2553,14 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNWorklets (0.11.1): + - RNWorklets (0.12.1): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2794,16 +2576,16 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - - RNWorklets/apple (= 0.11.1) - - RNWorklets/common (= 0.11.1) + - RNWorklets/apple (= 0.12.1) + - RNWorklets/common (= 0.12.1) - Yoga - - RNWorklets/apple (0.11.1): + - RNWorklets/apple (0.12.1): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2819,14 +2601,14 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - RNWorklets/common (0.11.1): + - RNWorklets/common (0.12.1): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2842,7 +2624,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -2857,10 +2638,13 @@ PODS: - SDWebImageWebPCoder (0.15.0): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.17) - - stream-chat-react-native (9.7.6): + - SocketRocket (0.7.1): + - ReactNativeDependencies + - stream-chat-react-native (9.8.0): - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2875,7 +2659,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga @@ -2883,6 +2666,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2897,7 +2681,6 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Teleport/common (= 1.1.12) @@ -2906,6 +2689,7 @@ PODS: - hermes-engine - RCTRequired - RCTTypeSafety + - React-bridging - React-Core - React-Core-prebuilt - React-debug @@ -2920,35 +2704,43 @@ PODS: - React-rendererdebug - React-utils - ReactCodegen - - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - ReactNativeDependencies - Yoga - - Yoga (0.0.0) + - Yoga (0.0.0): + - React-Core-prebuilt DEPENDENCIES: - "AsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) + - boost (from `build/rndeps-facades/boost`) + - DoubleConversion (from `build/rndeps-facades/DoubleConversion`) + - fast_float (from `build/rndeps-facades/fast_float`) + - FBLazyVector (from `build/rncore-facades/FBLazyVector`) - Firebase/Analytics (= 12.10.0) - Firebase/AppDistribution (= 12.10.0) - Firebase/Crashlytics (= 12.10.0) - FirebaseCoreExtension (= 12.10.0) + - fmt (from `build/rndeps-facades/fmt`) + - glog (from `build/rndeps-facades/glog`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - NitroModules (from `../node_modules/react-native-nitro-modules`) - NitroSound (from `../node_modules/react-native-nitro-sound`) - "op-sqlite (from `../node_modules/@op-engineering/op-sqlite`)" - - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - - RCTRequired (from `../node_modules/react-native/Libraries/Required`) + - RCT-Folly (from `build/rndeps-facades/RCT-Folly`) + - RCTDeprecation (from `build/rncore-facades/RCTDeprecation`) + - RCTRequired (from `build/rncore-facades/RCTRequired`) - RCTSwiftUI (from `../node_modules/react-native/ReactApple/RCTSwiftUI`) - RCTSwiftUIWrapper (from `../node_modules/react-native/ReactApple/RCTSwiftUIWrapper`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - React (from `../node_modules/react-native/`) + - React-bridging (from `../node_modules/react-native/ReactCommon/react/bridging`) - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) - - React-Core (from `../node_modules/react-native/`) + - React-Core (from `build/rncore-facades/React-Core`) - React-Core-prebuilt (from `../node_modules/react-native/React-Core-prebuilt.podspec`) - - React-Core/RCTWebSocket (from `../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `build/rncore-facades/React-Core`) - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) + - React-cxxstableapi (from `../node_modules/react-native/ReactCommon/react/cxxstableapi`) - 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`) @@ -2992,15 +2784,16 @@ DEPENDENCIES: - React-performancecdpmetrics (from `../node_modules/react-native/ReactCommon/react/performance/cdpmetrics`) - React-performancetimeline (from `../node_modules/react-native/ReactCommon/react/performance/timeline`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimatedModuleProvider (from `../node_modules/react-native/ReactApple/RCTAnimatedModuleProvider`) - 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-RCTFabric (from `build/rncore-facades/React-RCTFabric`) - 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-RCTRuntime (from `build/rncore-facades/React-RCTRuntime`) - 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`) @@ -3032,9 +2825,10 @@ DEPENDENCIES: - RNShare (from `../node_modules/react-native-share`) - RNSVG (from `../node_modules/react-native-svg`) - RNWorklets (from `../node_modules/react-native-worklets`) + - SocketRocket (from `build/rndeps-facades/SocketRocket`) - stream-chat-react-native (from `../node_modules/stream-chat-react-native`) - Teleport (from `../node_modules/react-native-teleport`) - - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) + - Yoga (from `build/rncore-facades/Yoga`) SPEC REPOS: trunk: @@ -3067,8 +2861,18 @@ SPEC REPOS: EXTERNAL SOURCES: AsyncStorage: :path: "../node_modules/@react-native-async-storage/async-storage" + boost: + :path: build/rndeps-facades/boost + DoubleConversion: + :path: build/rndeps-facades/DoubleConversion + fast_float: + :path: build/rndeps-facades/fast_float FBLazyVector: - :path: "../node_modules/react-native/Libraries/FBLazyVector" + :path: build/rncore-facades/FBLazyVector + fmt: + :path: build/rndeps-facades/fmt + glog: + :path: build/rndeps-facades/glog hermes-engine: :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" :tag: hermes-v250829098.0.16 @@ -3078,10 +2882,12 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-nitro-sound" op-sqlite: :path: "../node_modules/@op-engineering/op-sqlite" + RCT-Folly: + :path: build/rndeps-facades/RCT-Folly RCTDeprecation: - :path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" + :path: build/rncore-facades/RCTDeprecation RCTRequired: - :path: "../node_modules/react-native/Libraries/Required" + :path: build/rncore-facades/RCTRequired RCTSwiftUI: :path: "../node_modules/react-native/ReactApple/RCTSwiftUI" RCTSwiftUIWrapper: @@ -3090,16 +2896,20 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/Libraries/TypeSafety" React: :path: "../node_modules/react-native/" + React-bridging: + :path: "../node_modules/react-native/ReactCommon/react/bridging" React-callinvoker: :path: "../node_modules/react-native/ReactCommon/callinvoker" React-Core: - :path: "../node_modules/react-native/" + :path: build/rncore-facades/React-Core React-Core-prebuilt: :podspec: "../node_modules/react-native/React-Core-prebuilt.podspec" React-CoreModules: :path: "../node_modules/react-native/React/CoreModules" React-cxxreact: :path: "../node_modules/react-native/ReactCommon/cxxreact" + React-cxxstableapi: + :path: "../node_modules/react-native/ReactCommon/react/cxxstableapi" React-debug: :path: "../node_modules/react-native/ReactCommon/react/debug" React-defaultsnativemodule: @@ -3186,6 +2996,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/react/performance/timeline" React-RCTActionSheet: :path: "../node_modules/react-native/Libraries/ActionSheetIOS" + React-RCTAnimatedModuleProvider: + :path: "../node_modules/react-native/ReactApple/RCTAnimatedModuleProvider" React-RCTAnimation: :path: "../node_modules/react-native/Libraries/NativeAnimation" React-RCTAppDelegate: @@ -3193,7 +3005,7 @@ EXTERNAL SOURCES: React-RCTBlob: :path: "../node_modules/react-native/Libraries/Blob" React-RCTFabric: - :path: "../node_modules/react-native/React" + :path: build/rncore-facades/React-RCTFabric React-RCTFBReactNativeSpec: :path: "../node_modules/react-native/React" React-RCTImage: @@ -3203,7 +3015,7 @@ EXTERNAL SOURCES: React-RCTNetwork: :path: "../node_modules/react-native/Libraries/Network" React-RCTRuntime: - :path: "../node_modules/react-native/React/Runtime" + :path: build/rncore-facades/React-RCTRuntime React-RCTSettings: :path: "../node_modules/react-native/Libraries/Settings" React-RCTText: @@ -3266,16 +3078,21 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-svg" RNWorklets: :path: "../node_modules/react-native-worklets" + SocketRocket: + :path: build/rndeps-facades/SocketRocket stream-chat-react-native: :path: "../node_modules/stream-chat-react-native" Teleport: :path: "../node_modules/react-native-teleport" Yoga: - :path: "../node_modules/react-native/ReactCommon/yoga" + :path: build/rncore-facades/Yoga SPEC CHECKSUMS: - AsyncStorage: d721bb185bb20868b581693f0e77bb2ac13c4f12 - FBLazyVector: 3c3be9a019176b5699455f7f66c5444b78a411c6 + AsyncStorage: 53aec912b041c2a6da932333454c438875ec799c + boost: 8502bfdc46e2804408af2e163049ece181009af4 + DoubleConversion: 501a575c40c0b50c89685762dd3e415e6bb26fb0 + fast_float: 7c925d1cebf8328d4018d061f49377e9f887e3b4 + FBLazyVector: 732df79fea96f10cc14477969cea05707026d2fd Firebase: 99f203d3a114c6ba591f3b32263a9626e450af65 FirebaseAnalytics: ef2970f65f3a2807e8f47a49cf3b8719d53e9fce FirebaseAppDistribution: 95d3febe2962bf26e445b809c9726e9131c8f859 @@ -3287,121 +3104,128 @@ SPEC CHECKSUMS: FirebaseMessaging: ed18fb50634e6e85b5d3e77e628c21e2c928cc53 FirebaseRemoteConfigInterop: a169873f4093b241eb5e27a4fbad91af36d64b8d FirebaseSessions: dba7635960740ad77cc2f3387d90ff22a7942f82 + fmt: ba0886e864ebb21d14c98b2e12e164b8002f5b93 + glog: 3cf7cb46f743c4bc758ae3e6d9c15bd1b4cfd6d4 GoogleAdsOnDeviceConversion: b25c8d714e457fe2ceae3d3bdc102b6cf9677afb GoogleAppMeasurement: 57270ccc2b77472d7e85c4cbe45972564eff78bb GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 766ace00c6b10d8148408f329d10c4f051931850 - hermes-engine: 3730f5b467f988fa954ded67cbea8a9ba32d854c + hermes-engine: 6a26e94b4a974cbadc751eed9caa12624c38f226 libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 - NitroModules: e0ac5f9a04e23cb2f378b51810ebc07ed63aeae9 - NitroSound: a18e2d59d0d60c291586e622ce4752c53da73086 - op-sqlite: d8d5eae2bddb0b55d6f48cf7ac356b63d26cb4f0 + NitroModules: 8cad24cfdb4fb7c561bc68d76b8e86c096a4e598 + NitroSound: bc4220718f728f1f83093166dc1b5487cfba973f + op-sqlite: 6c653c1e122b7b777bc0892839fd09a4bb0e264f PromisesObjC: 752c3227f599e3467650e47ea36f433eeb10c273 PromisesSwift: 217dea0fd5d2ad65222a109c48698add13cc1c5b - RCTDeprecation: bccb6545c26db881ecddfd83a3f9ea82aba1605f - RCTRequired: b2f74764d596fc0051f00fee94b49bf41a6f7f5a - RCTSwiftUI: c6d6a31b849b9dfa64c33b55dc91ac15dd55774c - RCTSwiftUIWrapper: bdff268d65d662a79b1e571e841e1512275f9319 - RCTTypeSafety: 159e394bdab42023fbdd8fa022dfdc5577a8b9ad - React: 4b2532a459d15e1adf6c22d3e399e5c85a94220f - React-callinvoker: 0b8ce4057e02a0bd15cf0532596e8eb8c0392e92 - React-Core: 5af045531a540ba3f65f07de1e3f585ddfb27948 - React-Core-prebuilt: 405cf395d66cf694faf9aed3483a21b5515cec85 - React-CoreModules: 99b194a721de84ccfc1be149a0de52647dc38c0e - React-cxxreact: b7e8e254074fd8111d147202b391ccf7816946a6 - React-debug: 3281bfefe5ece9a9d8b28bec3f871db229f9d8d8 - React-defaultsnativemodule: 1bd0a6e02f816f0c939baeaf2b7a9e799327274e - React-domnativemodule: f7d8ab3fbd37453301d69a866bc21456dc8c9bd8 - React-Fabric: 9bb75bdf09a445e74d49dc2152cd2176d1379432 - React-FabricComponents: f5c87bd4421ed262c25f57e8dea560221074e065 - React-FabricImage: 39be2035c85a1004004c3dc62ddc3f17073fe105 - React-featureflags: de5b3e964937f9dfbc05b98e51f3f4d0472789f4 - React-featureflagsnativemodule: 6bebf75aeddd8799107a13f1db99f1a0ce2a0977 - React-graphics: 9d482a1031375b90704be420d445879bed3132f7 - React-hermes: 6fd579ceeb680830fc508697acdd5e8cd8dbf29f - React-idlecallbacksnativemodule: b5dacdd51c63ab15ed8a9bb1c875c6f0e92160d4 - React-ImageManager: 1e86a5fbe7c49cc39f4bfb49f02df410d53dd96c - React-intersectionobservernativemodule: 13011eeac1a10a76e63888b1baab1ca2839fed39 - React-jserrorhandler: 8c7e83f8259120207965c61e430733218d8fb58e - React-jsi: 7b14065a285f91b3dbe5448371ff575bbb523d59 - React-jsiexecutor: 10b4ba40ec9fd571370dfee20251201f57712e79 - React-jsinspector: fb62fdf5baea797b121e5566492536e3fab928ff - React-jsinspectorcdp: 2a0a882d722a17cea6bebcce23464156c470f384 - React-jsinspectornetwork: b1dc2b219dd1c9bdbf13dccc8bfe313c39cf6cd7 - React-jsinspectortracing: c57b2e67ee3b56f98d2b507d3a29c1b5d44049e6 - React-jsitooling: 9fc594ea389d97893904f8ddafe1bec63313321b - React-jsitracing: ecbbc6d7f3de28197849da27f0e273e34a55626d - React-logger: 35ab012027cc552057f7ca5d031c6721f896e6bc - React-Mapbuffer: 75518c85e76e5117a6f7cac4e0bec4ba0a45723f - React-microtasksnativemodule: 9d2adb05be26b36bff9e3f24b0cc5bf0a0511c65 - React-mutationobservernativemodule: e450c29376a09a0f6dd23170096f3e46f1ba6d57 - react-native-blob-util: 4bbe44ed5eff3697228a3645c3761c90b7160f90 - react-native-blur: 2c15b7e68450a2aa01a064f99d6045b86ee42fe0 - react-native-cameraroll: d7e55dae97acb52c4ce88a32deb02841fa15de12 - react-native-document-picker: d74eb20980217096d3794a63ee5105b5f70772e3 - react-native-geolocation: aba0b4dbf0f9a84c328059efd93af22e9e6190d7 - react-native-image-picker: 09cea417bef3ee25008ad21d4426f9a1fc54ad84 - react-native-maps: f6b0ae1b6a4219d835846c2d817abbc2d95bdf50 - react-native-netinfo: a05f9b897e76ad24b53f615fed1cbb731932363d - react-native-safe-area-context: 0bbcdfba5c4a1b3203276f1ed9128e3239fdf199 - react-native-video: 950a8bb07646654716ebf0dd78acf16f95fa3b6c - React-NativeModulesApple: 167e532fb9bae30f3c66636b8ee0092bab263667 - React-networking: e382e6f2f815e801a34a630e708a551c7feb2090 - React-oscompat: db6675ddaef3bddd1b41533627f3d26ec710c05f - React-perflogger: c34660c72849d23319f5e544c97e6c6ed687b186 - React-performancecdpmetrics: c852458c139c8c2a15284ddb7d4927d907b38731 - React-performancetimeline: cf6cd525e8d8a1c625985d54d9ee68296073f281 - React-RCTActionSheet: 7d18777c531c516ab9f86342583057b15fb7fd7d - React-RCTAnimation: f2505067d2d83268f5619192f8f5ff14b2606c7f - React-RCTAppDelegate: 7bfa4d752f45a0b9195b8da0f188f8904806a86f - React-RCTBlob: 7b4d25eca2a6ecd83766d76790879774b73b4cd5 - React-RCTFabric: 2f4e25dd2a256cbba5d95d8ec2c03a876fddd8ec - React-RCTFBReactNativeSpec: 1f8c18b3344dee44d60d750958a0d0f94fcf4f3d - React-RCTImage: 9123b010921479b5bcd3771a4a4d4e788227a427 - React-RCTLinking: 358d42629d7012c5d75060fd3e25023b72ebae31 - React-RCTNetwork: 32f5274abd61e937bdcbfd85810ae784d3f0e38a - React-RCTRuntime: 606364977ba254a416e85ef7a9f6e08b89818a32 - React-RCTSettings: 86aa6e6a3a335d989e3fcd1bd98b9ea6331adfa9 - React-RCTText: 59376611225f3c6fdc75d57571d7c345bdd0be1e - React-RCTVibration: 3c7d17d3373c114220be9ac3b99df1646009b8fe - React-rendererconsistency: 3238990615931ff327b5f1d98852cd6603dc5d10 - React-renderercss: a0142ecb1e580926514c05c70205e825230005d0 - React-rendererdebug: d475dc238a8f39c248d328605bc48268b2111625 - React-RuntimeApple: 2aab0db6b710869c4e995e0b9054edbe01c1f182 - React-RuntimeCore: 701d4a24f5a4a5e990b1cafea443e7bffb707de8 - React-runtimeexecutor: 9a0a090bf8183a45ddda95be72ba0d2d7bc5a6d4 - React-RuntimeHermes: c0a3a63d306daf45646862ce09afa00d8e654873 - React-runtimescheduler: 2af097dd7630e559e7f0a740d875857c4a02f90d - React-timing: 329b5e88f59491a5e893f6c549bd10883ef30e5a - React-utils: 25a0beadc5eacbffeb965c7024826b1233dbedc6 - React-viewtransitionnativemodule: 8f1d4895e081e0b2f0a98069c098eb40be032138 - React-webperformancenativemodule: 5bddf6c7eecda8a3ce48b82609008965cc9d94c2 - ReactAppDependencyProvider: 0e13d430eadac8a2ef18515a860d5c59df05b475 - ReactCodegen: b4e2c5f0d9415b8fe2faf163fca6f40c62f9666a - ReactCommon: 9002f006f571256348994183f7d4387aa7cf84e8 - ReactNativeDependencies: 2bd6854ade79bf1b60586d1ab7813a389df1b6bf - RNCClipboard: 7a7d4557bfd3370b35c99dfecd92ae7b9fc4948a - RNFastImage: 14580cef91660b889645fb9e87f58a53621db993 - RNFBApp: ab502c911e5203f22a56998b7ed22e5ecf436da4 - RNFBMessaging: 83417183855f6324900a28a0a007ff4be2f9c08c - RNGestureHandler: f07022ab7ca42692c1cd3364f4aab2eebe66dc0d + RCT-Folly: 82921e585c0400eda46fac1068297aa9dfb035e4 + RCTDeprecation: 6d66040b74f4be203495adda55b49c1a25190291 + RCTRequired: ebf172e0b04196922ba4640458f9926c1bb34012 + RCTSwiftUI: ac8537fe21f096259c91bd663655ff52ec864cb0 + RCTSwiftUIWrapper: 64406e4d42af802b3baf2fbaa0bd8ad75bc0cc9b + RCTTypeSafety: 80eac4783ed394cdffb5981d2c99b73a1bc519ca + React: 41924a5bad9466fe869b4511650b7d6e0cf02c84 + React-bridging: 02db3b5ad985a0ff729c20c74349a29b511507a0 + React-callinvoker: 2703817f9ad45a19f839421aa77a3504b2a76f0d + React-Core: 7b2c3d5cc62d50659ba1c0c86b9112a3b44c1219 + React-Core-prebuilt: 508793fe2a734c137e80b894998702a6fe7caa94 + React-CoreModules: f940b6c1451d74b19d340aa52612eb7e9a306d95 + React-cxxreact: cf37497363d74b6a766fb06e8feb96dc8156f471 + React-cxxstableapi: 912021758ac453cfa5d83a6c525712dda4782847 + React-debug: 1925c8216471fb347ba613f89b791887131d4968 + React-defaultsnativemodule: f05dc714a4406f74c0d9889e86f677da73f7fae2 + React-domnativemodule: 5d2b0fc8c1ff9ac7ecb3ec3b80ce1d5b428ea56c + React-Fabric: c606cc2c9c17fd2a4324a5f7f8580270bc2e41ac + React-FabricComponents: b91d325fa8b293bf8fccf05502c87f5adc296cb6 + React-FabricImage: cdb9f5bba3be493e9bc46292a20edce22eb0779f + React-featureflags: 90fda5d3e4330160828e682d1a6dfc78b7a36f33 + React-featureflagsnativemodule: c51aa918034fd0739117baaf00434d0950b39673 + React-graphics: fa1172e102ce117e535a194f9876199e6e7f8fdf + React-hermes: 8cbf3f3ca711b7ab1724fe25ec4f2a0d86c9abe6 + React-idlecallbacksnativemodule: 845fd7655c5187c68f51affafb26ed5398978bdc + React-ImageManager: ad49eff074b600bc772b2a2ead00feaa7f1f0581 + React-intersectionobservernativemodule: 6af306d14be72655ad624648f757ebd41930aa32 + React-jserrorhandler: 863cda77bda575f0a7f97a18085e40a3f385aa89 + React-jsi: 14ad6fae949e154da5b81d27692298ebd49a8a34 + React-jsiexecutor: 9e59ba557dbe7197ec5c87e6685a2082964ef14c + React-jsinspector: 980659da8ce50cd04b91d50e668bc8144ca4bee3 + React-jsinspectorcdp: a95858a321a9f0123635920971ee203756f84a4b + React-jsinspectornetwork: dd95a42f61b70a10e4d2821c4ac6c4e82654d614 + React-jsinspectortracing: 8ebf04f02ea5e061aa338eab0bd8ee35b437c68b + React-jsitooling: 0934cf307ef95267f182b40d258f10f9d706abaf + React-jsitracing: 2ce502324a7718d1be54823589fbfd0f894dfd64 + React-logger: f280101234b7e8df003e99ff4d359dc57de77117 + React-Mapbuffer: 890e1f677fd1e49f9114440fb17de61b53eb8db1 + React-microtasksnativemodule: 5adac537604494606f0d2121a3c966356eb42cbb + React-mutationobservernativemodule: 44fd38a55451f25ea5a2c74e74f18bf08f94d40a + react-native-blob-util: df652a6c82b0ba13c29a25d265f09304118cb506 + react-native-blur: e53fde35176cb11b6db241d2e87d87cb21dcd144 + react-native-cameraroll: 49fe4bef0279af8d7987d34f1c9e5d481b159fdc + react-native-document-picker: 2564519c256c174ae70d9403edf4d878c5b985ac + react-native-geolocation: 1202caa7bc8dc4149458369179e45d42172b506a + react-native-image-picker: fb55a573ca2d0c23789fa88fb07b878d591559d8 + react-native-maps: 77281baf36154aa6b4ed6ae671d9b5f1c5bea21f + react-native-netinfo: afde752bbbafa8accf42ec4e8c82a91f3624922f + react-native-safe-area-context: caf82477cea4a21a673f089a246f35bc476d915e + react-native-video: 554f0512f51695e922d53eeb92f0a0e9855bb5f8 + React-NativeModulesApple: 22b2f3e1538ceb1aab1ab182ccb0666ea2ec36ad + React-networking: b007a80590c11f2ce62a4967386c594a434a83cb + React-oscompat: 72573ceb65b6c6c6bc34e51e0df8f44c91081a6d + React-perflogger: 885850f7ee01af5e47cfb7e51eb9f3e10ab0c9da + React-performancecdpmetrics: ba5a94088f6a1ae13bae2ac74fd6ee210c938af5 + React-performancetimeline: 0aa93dd28bff8b1259916e23f57939cbe8974da5 + React-RCTActionSheet: a88d0f7eb5738ec418a2ff1984439a6924662b2c + React-RCTAnimatedModuleProvider: 7af9d8aaac99a3442b3d795fb04056e8d780b974 + React-RCTAnimation: 5314c78f58d3018eb47026ef26f116d9e126d5ed + React-RCTAppDelegate: d048873b1e0145204137ebd14e5d3d8577a76672 + React-RCTBlob: ba1b169851e00249af27f946cb4ae27eba65f133 + React-RCTFabric: f8302793314697ae59e037fd16578979f9a72d44 + React-RCTFBReactNativeSpec: e6c6473f8ab6eb646c4dd4353956e9b1768e99b8 + React-RCTImage: e159826f6c49e8bc282a73410c68de2e83456b98 + React-RCTLinking: 7adcbc80b056e382a5346d9cda1509c97a9f6f69 + React-RCTNetwork: 4106de28a9c4cc6f4664a5829f2b56e8d67d45b4 + React-RCTRuntime: f2eeab7e701c16153dd86f5b78b9aa6e613dc3b0 + React-RCTSettings: 0ce8009ed2db69ea9f1900c6578831c23fa88c77 + React-RCTText: 901788b3060e912a6893e24cdbdc0f504895d215 + React-RCTVibration: 8ace21ea22b677d4bef389d43d7b2f0b87ec147f + React-rendererconsistency: 3da45e68ab9f4d23f9c25a015968cd8452f3bc0b + React-renderercss: c81b495b42c27d68646557bebbe0838887bd563a + React-rendererdebug: 7f6ccdf375c63722d6514e93dec8aa40d28bb5e3 + React-RuntimeApple: 2cd58a69b481765c998fd7effd089bec71801f11 + React-RuntimeCore: f47d64cdd380dfb708e4d116a3937bb2ce95cb58 + React-runtimeexecutor: 207d060a187422b6d27107a20028da167d0c3d37 + React-RuntimeHermes: e0211d52adae396b696e7e2ebce67b0d10736108 + React-runtimescheduler: b19728d726691ce0049e571fa2823053097bff7f + React-timing: 069339825629b0a604e9833dfb58e7eb06680a4a + React-utils: edc3dcabc91cce412b2f540ce061a8a811baa4b0 + React-viewtransitionnativemodule: 9e3d0ecfe85e264d6a9b77c1cc3284a6606a8102 + React-webperformancenativemodule: 9f363c62029fb104edd9c488226936c64d842114 + ReactAppDependencyProvider: ed5609dc8e30eeade35ba8e43128781ee8fdf4de + ReactCodegen: 4c799b1a7c20e188310302f1df58a8c1305a21e5 + ReactCommon: 5f5a302748322015f2e3334869cdd9b4d0395846 + ReactNativeDependencies: ec5530432d8191b38e278117d498aaff05ea9d6f + RNCClipboard: 3d8a353cc2bbea95585575267634a48e9fcbf7c8 + RNFastImage: 14da4de6653acd7a99bb031501b99b40b7e0b6b7 + RNFBApp: b86621e66e9c262a5cbf408d3f7d9f3c86a4da3b + RNFBMessaging: 34893120f5cc959183d030390a82ce85957f1b65 + RNGestureHandler: ebb16781e4f43ea10056490a0eb3d0b51d30b909 RNNotifee: 5e3b271e8ea7456a36eec994085543c9adca9168 - RNReactNativeHapticFeedback: 9dc72312c12cb53ee240b5b7aae1e167f3d940a6 - RNReanimated: 115090d709c1cf268b897c57bb16963bb920f54a - RNScreens: 5b4b260e28d8263d97fddcb53e59649515797bcc - RNShare: 26c9524aee8cc3eedbab6d6b98cacce2f5247893 - RNSVG: 394cfd0518613b144c65ffe69045f827b78a7a7c - RNWorklets: f0734080219ea54d479c179da498c9b24611faed + RNReactNativeHapticFeedback: d4c13fca796d9d6eb967a73ce7c906c5d3930fcf + RNReanimated: 7e68cf425d1c4a54c2dc4baee0ac9d8d347fd289 + RNScreens: 9964f9126ccea33c2c18cfe3dc0398ff7c917056 + RNShare: 762aa7c1ca8060890f11e54aa29f6fd4e6312730 + RNSVG: 4da2e0a724c48a841e0dbfbdc7db39286c050b61 + RNWorklets: a140b8a8c63ab3f1ac247f5c6e8ae192bf70b0a0 SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57 SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377 - stream-chat-react-native: ea3499916019c499ecb745be61e7ecf82f0fd999 - Teleport: c56b30b08bd20d10da1efb0f21c6bc50c269ee6a - Yoga: 542a30dafe5b0f5f1d9f185ea7b2a3811ac54801 + SocketRocket: 37aec555668fb852ec12e3c0de59a86ca58f0871 + stream-chat-react-native: de5922b681460d780a4d47c58b2b024622954aa7 + Teleport: c42718cf1f14dd53b250c0fa29b9235a42f861ec + Yoga: d1c536142c5ff8ec8cd856ab2a7c227a1d875c8e PODFILE CHECKSUM: ac9682db83e42d63d399a8de4586161e86b3e751 diff --git a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj index a56502bb99..94d556b113 100644 --- a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj +++ b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj @@ -685,6 +685,7 @@ "${PODS_ROOT}/React-graphics/react/renderer/graphics/platform/ios", "${PODS_ROOT}/React-featureflags", "${PODS_ROOT}/React-renderercss", + "${PODS_ROOT}/React-bridging", ); IPHONEOS_DEPLOYMENT_TARGET = 15.1; LD_RUNPATH_SEARCH_PATHS = ( @@ -841,6 +842,7 @@ "${PODS_ROOT}/React-graphics/react/renderer/graphics/platform/ios", "${PODS_ROOT}/React-featureflags", "${PODS_ROOT}/React-renderercss", + "${PODS_ROOT}/React-bridging", ); IPHONEOS_DEPLOYMENT_TARGET = 15.1; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/examples/SampleApp/package.json b/examples/SampleApp/package.json index 4de4c0f3c2..4ff13d0ace 100644 --- a/examples/SampleApp/package.json +++ b/examples/SampleApp/package.json @@ -28,7 +28,7 @@ "@emoji-mart/data": "^1.2.1", "@gorhom/bottom-sheet": "^5.2.14", "@notifee/react-native": "^9.1.8", - "@op-engineering/op-sqlite": "^17.1.2", + "@op-engineering/op-sqlite": "^18.1.0", "@react-native-async-storage/async-storage": "^3.1.1", "@react-native-camera-roll/camera-roll": "^7.10.2", "@react-native-clipboard/clipboard": "^1.16.3", @@ -48,7 +48,7 @@ "emoji-mart": "^5.6.0", "lodash.mergewith": "^4.6.2", "react": "19.2.3", - "react-native": "0.86.2", + "react-native": "0.87.0", "react-native-blob-util": "^0.24.10", "react-native-gesture-handler": "^3.1.0", "react-native-haptic-feedback": "^3.0.0", @@ -56,14 +56,14 @@ "react-native-maps": "^1.29.0", "react-native-nitro-modules": "0.36.5", "react-native-nitro-sound": "0.2.17", - "react-native-reanimated": "4.5.2", + "react-native-reanimated": "4.6.0", "react-native-safe-area-context": "^5.8.0", - "react-native-screens": "^4.26.2", + "react-native-screens": "^4.27.0", "react-native-share": "^12.3.1", "react-native-svg": "^15.15.5", "react-native-teleport": "^1.1.12", "react-native-video": "^6.19.2", - "react-native-worklets": "^0.11.1", + "react-native-worklets": "^0.12.1", "stream-chat": "^9.51.0", "stream-chat-react-native": "workspace:^", "stream-chat-react-native-core": "workspace:^" @@ -71,19 +71,18 @@ "reanimated": { "staticFeatureFlags": { "USE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS": true, - "USE_SYNCHRONIZABLE_FOR_MUTABLES": true, - "FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS": false + "USE_SYNCHRONIZABLE_FOR_MUTABLES": true } }, "devDependencies": { "@babel/core": "^7.29.0", "@babel/runtime": "^7.29.2", - "@react-native-community/cli": "20.1.0", - "@react-native-community/cli-platform-android": "20.1.0", - "@react-native-community/cli-platform-ios": "20.1.0", - "@react-native/babel-preset": "0.86.2", - "@react-native/metro-config": "0.86.2", - "@react-native/typescript-config": "0.86.2", + "@react-native-community/cli": "20.2.0", + "@react-native-community/cli-platform-android": "20.2.0", + "@react-native-community/cli-platform-ios": "20.2.0", + "@react-native/babel-preset": "0.87.0", + "@react-native/metro-config": "0.87.0", + "@react-native/typescript-config": "0.87.0", "@rnx-kit/metro-config": "^2.1.0", "@stream-io/typescript-config": "workspace:^", "@types/lodash.mergewith": "^4.6.9", @@ -92,6 +91,6 @@ "typescript": "6.0.3" }, "engines": { - "node": ">=20.19.4" + "node": ">=22.13.0" } } diff --git a/examples/SampleApp/src/hooks/useStreamChatTheme.ts b/examples/SampleApp/src/hooks/useStreamChatTheme.ts index 0016c73343..453702b218 100644 --- a/examples/SampleApp/src/hooks/useStreamChatTheme.ts +++ b/examples/SampleApp/src/hooks/useStreamChatTheme.ts @@ -4,7 +4,8 @@ import { ColorSchemeName, useColorScheme } from 'react-native'; import type { DeepPartial, Theme } from 'stream-chat-react-native'; const getChatStyle = ( - colorScheme: ColorSchemeName, + // React Native 0.87 types `useColorScheme()` as `ColorSchemeName | null`. + colorScheme: ColorSchemeName | null, ): DeepPartial & { colors: Record } => ({ colors: colorScheme === 'dark' diff --git a/examples/SampleApp/src/screens/ChannelListScreen.tsx b/examples/SampleApp/src/screens/ChannelListScreen.tsx index 0cc490bee9..9258afcb6d 100644 --- a/examples/SampleApp/src/screens/ChannelListScreen.tsx +++ b/examples/SampleApp/src/screens/ChannelListScreen.tsx @@ -18,6 +18,7 @@ import { useStableCallback, ChannelActionItem, GetChannelActionItems, + TextInputRef, } from 'stream-chat-react-native'; import { ChatScreenHeader } from '../components/ChatScreenHeader'; @@ -83,7 +84,7 @@ export const ChannelListScreen: React.FC = () => { const { black, grey, grey_gainsboro, grey_whisper, white, white_snow } = useLegacyColors(); const { setChannel } = useStreamChatContext(); - const searchInputRef = useRef(null); + const searchInputRef = useRef(null); const scrollRef = useRef | null>(null); const [searchInputText, setSearchInputText] = useState(''); diff --git a/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx b/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx index 42a76ec4a5..68cca68708 100644 --- a/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx +++ b/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx @@ -14,6 +14,7 @@ import { UserAdd, WithComponents, useTheme, + TextInputRef, } from 'stream-chat-react-native'; import { NewDirectMessagingSendButton } from '../components/NewDirectMessagingSendButton'; @@ -136,8 +137,8 @@ export const NewDirectMessagingScreen: React.FC = toggleUser, } = useUserSearchContext(); - const messageInputRef = useRef(null); - const searchInputRef = useRef(null); + const messageInputRef = useRef(null); + const searchInputRef = useRef(null); const currentChannel = useRef(undefined); const isDraft = useRef(true); const initialUserIdRef = useRef(undefined); diff --git a/examples/SampleApp/src/screens/NewGroupChannelAddMemberScreen.tsx b/examples/SampleApp/src/screens/NewGroupChannelAddMemberScreen.tsx index 641fd6821b..04611b9016 100644 --- a/examples/SampleApp/src/screens/NewGroupChannelAddMemberScreen.tsx +++ b/examples/SampleApp/src/screens/NewGroupChannelAddMemberScreen.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useRef } from 'react'; import { FlatList, StyleSheet, TextInput, TouchableOpacity, View } from 'react-native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; -import { Search, useTheme } from 'stream-chat-react-native'; +import { Search, TextInputRef, useTheme } from 'stream-chat-react-native'; import { ScreenHeader } from '../components/ScreenHeader'; import { UserGridItem } from '../components/UserSearch/UserGridItem'; @@ -76,7 +76,7 @@ type Props = { export const NewGroupChannelAddMemberScreen: React.FC = ({ navigation }) => { const { chatClient } = useAppContext(); - const searchInputRef = useRef(null); + const searchInputRef = useRef(null); const { theme: { semantics }, diff --git a/examples/SampleApp/src/utils/useScreenReaderComposerFocusEffect.tsx b/examples/SampleApp/src/utils/useScreenReaderComposerFocusEffect.tsx index 78fc917bbf..c176366bde 100644 --- a/examples/SampleApp/src/utils/useScreenReaderComposerFocusEffect.tsx +++ b/examples/SampleApp/src/utils/useScreenReaderComposerFocusEffect.tsx @@ -1,9 +1,8 @@ import { useCallback, useRef } from 'react'; -import { TextInput } from 'react-native'; import { ParamListBase, useFocusEffect, useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; -import { useSetAccessibilityFocus } from 'stream-chat-react-native'; +import { TextInputRef, useSetAccessibilityFocus } from 'stream-chat-react-native'; /** * Lands the screen-reader cursor on the message composer input when this screen @@ -18,11 +17,11 @@ import { useSetAccessibilityFocus } from 'stream-chat-react-native'; * OS's own focus pass and is dropped). `transitionEnd` fires on push and pop reveal. */ export const useScreenReaderComposerFocusEffect = () => { - const inputRef = useRef(null); + const inputRef = useRef(null); const setAccessibilityFocus = useSetAccessibilityFocus(); const navigation = useNavigation>(); - const setInputRef = useCallback((ref: TextInput | null) => { + const setInputRef = useCallback((ref: TextInputRef | null) => { inputRef.current = ref; }, []); diff --git a/package.json b/package.json index 101c1df6cd..79f28d9e77 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@types/react": "^19.2.0" }, "engines": { - "node": ">=20.19.4" + "node": ">=22.13.0" }, "dependenciesMeta": { "@swc/core": { diff --git a/package/__mocks__/SvgTouchableMixin.js b/package/__mocks__/SvgTouchableMixin.js new file mode 100644 index 0000000000..86eebd896f --- /dev/null +++ b/package/__mocks__/SvgTouchableMixin.js @@ -0,0 +1,39 @@ +/* global require, module, __dirname, Object */ + +/** + * Test-environment shim for `react-native-svg`'s `SvgTouchableMixin`. + * + * `react-native-svg` (<= 15.15.5) reads `Touchable.Mixin` off the `react-native` root. React Native + * 0.87 dropped `Touchable` from the public API but deliberately keeps the runtime export alive for + * exactly that call site - except it now installs it *after* the main export object, via + * `Object.defineProperty(module.exports, 'Touchable', ...)`. That definition is both late and + * non-enumerable, so it is lost through Babel's `_interopRequireWildcard`, which builds the + * namespace object that `import { Touchable } from 'react-native'` reads by copying enumerable own + * properties. Metro is unaffected - this is purely a Jest interop artefact. + * + * This is wired up through `moduleNameMapper` rather than `jest.mock()` on purpose. Mapping happens + * in the resolver, so it also applies inside the separate module registry Jest uses to build + * automocks (which is where four of the `ChannelDetails` suites hit this). A `jest.mock()` on + * `react-native` in the shared setup would work too, but it would shadow the per-suite + * `react-native` mocks some tests install, because Jest caches the first mock instance per module. + * + * The real mixin is returned unchanged, so SVG touch behaviour under test is untouched. + * + * Remove once `react-native-svg` stops depending on the `Touchable` mixin. + */ + +const ReactNative = require('react-native'); + +if (!ReactNative.Touchable) { + Object.defineProperty(ReactNative, 'Touchable', { + configurable: true, + enumerable: true, + get: () => require('react-native/Libraries/Components/Touchable/Touchable').default, + }); +} + +// Required by its full path (with extension) so the `SvgTouchableMixin$` mapper does not match it +// again and send us back here. +module.exports = require( + `${__dirname}/../node_modules/react-native-svg/src/lib/SvgTouchableMixin.ts`, +); diff --git a/package/__mocks__/reanimatedModuleInstance.js b/package/__mocks__/reanimatedModuleInstance.js new file mode 100644 index 0000000000..fdb3133125 --- /dev/null +++ b/package/__mocks__/reanimatedModuleInstance.js @@ -0,0 +1,36 @@ +/* global require, module, __dirname */ + +/** + * Test-environment shim for Reanimated's `ReanimatedModule` singleton. + * + * Reanimated regression, introduced between 4.5.2 and 4.6.0 and STILL PRESENT IN 4.6.0 STABLE + * (re-verified 2026-08-25): `initializeReanimatedModule()` calls `setCSSEventHandler()` + * unconditionally at import time. On the JS-only `JSReanimated` backend + * that Jest resolves to, that method *throws* (`[Reanimated] setCSSEventHandler is not available in + * JSReanimated.`), whereas the native backend's stub is a harmless no-op. The result is that merely + * requiring Reanimated's own official mock blows up, taking the whole suite with it. + * + * Wired through `moduleNameMapper` rather than `jest.mock()` for two reasons: mapping happens in the + * resolver, so it applies inside the separate module registry Jest uses to build automocks (four + * `ChannelDetails` suites hit this), and it survives suites that register their own + * `react-native-reanimated` mock, whose factories win over anything in the shared setup. + * + * Device and simulator builds use the native backend and are unaffected. + * + * TODO: remove once Reanimated fixes this upstream - `JSReanimated.setCSSEventHandler` should be a + * no-op like the native backend's stub in `NativeReanimated.ts`. Re-check on each Reanimated bump: + * grep `setCSSEventHandler` in `src/initializers.native.ts` and `src/ReanimatedModule/js-reanimated/ + * JSReanimated.ts`. https://github.com/software-mansion/react-native-reanimated + */ + +// Required by its full path (with the platform suffix and extension) so the +// `reanimatedModuleInstance$` mapper does not match it again and recurse back into this file. +const actual = require( + `${__dirname}/../node_modules/react-native-reanimated/src/ReanimatedModule/reanimatedModuleInstance.native.ts`, +); + +if (actual && actual.ReanimatedModule) { + actual.ReanimatedModule.setCSSEventHandler = () => {}; +} + +module.exports = actual; diff --git a/package/expo-package/android/build.gradle b/package/expo-package/android/build.gradle index 63e6799460..e5d534cfc2 100644 --- a/package/expo-package/android/build.gradle +++ b/package/expo-package/android/build.gradle @@ -63,8 +63,10 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + // Must match the Kotlin jvmTarget below. AGP 9 (React Native 0.87) fails the build on an + // inconsistent JVM-target between the Java and Kotlin compilation tasks. + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { diff --git a/package/expo-package/android/gradle.properties b/package/expo-package/android/gradle.properties index ff43bf1f8d..23bcd2af7f 100644 --- a/package/expo-package/android/gradle.properties +++ b/package/expo-package/android/gradle.properties @@ -1,4 +1,7 @@ -StreamChatExpo_kotlinVersion=1.7.0 -StreamChatExpo_minSdkVersion=21 -StreamChatExpo_targetSdkVersion=31 -StreamChatExpo_compileSdkVersion=31 +# Fallbacks used only when the consuming app's root project does not define these (an app created +# from the React Native template always does). Kept in step with React Native 0.87's requirements: +# libraries must build against compileSdk >= 34 (minCompileSdk), and Kotlin >= 2.0 is required. +StreamChatExpo_kotlinVersion=2.2.0 +StreamChatExpo_minSdkVersion=24 +StreamChatExpo_targetSdkVersion=36 +StreamChatExpo_compileSdkVersion=34 diff --git a/package/expo-package/tsconfig.json b/package/expo-package/tsconfig.json index f8b97a6352..fb63725780 100644 --- a/package/expo-package/tsconfig.json +++ b/package/expo-package/tsconfig.json @@ -1,6 +1,16 @@ { "extends": "@stream-io/typescript-config/library.json", "compilerOptions": { + // React Native 0.87's Strict TypeScript API removes types for `react-native/Libraries/*` deep + // imports. `src/native/StreamShimmerViewNativeComponent.ts` must keep importing + // `codegenNativeComponent` from its deep path at *runtime*: the root re-export only exists from + // React Native 0.80, and this package supports `>=0.76`. Opt this package (and only this + // package - the core SDK is fully migrated to the Strict API) back into the legacy deep-import + // types. Build-time only; it changes no emitted code and no public type. + // TODO: drop this once the wrappers' minimum React Native is >= 0.80, and switch the import to + // `import { codegenNativeComponent } from 'react-native'`. The opt-out itself disappears after + // React Native 0.88. + "customConditions": ["react-native", "react-native-legacy-deep-imports"], // The optional-dependency shims dynamically `require()` peer deps that are // not installed here (so their values are untyped) and guard them behind // runtime checks TS can't narrow across closures. The strict-`any`/null/ diff --git a/package/jest.config.js b/package/jest.config.js index 2adb1e9ce8..1ac5fcc627 100644 --- a/package/jest.config.js +++ b/package/jest.config.js @@ -6,6 +6,16 @@ module.exports = { ...(process.env.CI ? { maxWorkers: 2 } : {}), moduleNameMapper: { 'mock-builders(.*)$': '/src/mock-builders$1', + // See __mocks__/SvgTouchableMixin.js - react-native-svg reads `Touchable.Mixin` off the + // react-native root, and RN 0.87's late, non-enumerable re-export of it is lost through + // Babel's ESM interop under Jest. Mapped (rather than jest.mock'd) so it also applies in the + // separate registry Jest uses to build automocks. + SvgTouchableMixin$: '/__mocks__/SvgTouchableMixin.js', + // See __mocks__/reanimatedModuleInstance.js - Reanimated (>=4.6.0, incl. stable) throws from + // `setCSSEventHandler` on the JS-only backend Jest uses, which breaks importing its own mock. + // Mapped (rather than jest.mock'd) so it applies in the automock registry too, and so it is not + // overridden by suites that register their own react-native-reanimated mock. + reanimatedModuleInstance$: '/__mocks__/reanimatedModuleInstance.js', }, preset: '@react-native/jest-preset', setupFiles: ['./node_modules/react-native-gesture-handler/jestSetup.js'], diff --git a/package/native-package/android/build.gradle b/package/native-package/android/build.gradle index a7e6e30000..0d699c4ebf 100644 --- a/package/native-package/android/build.gradle +++ b/package/native-package/android/build.gradle @@ -73,8 +73,10 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + // Must match the Kotlin jvmTarget below. AGP 9 (React Native 0.87) fails the build on an + // inconsistent JVM-target between the Java and Kotlin compilation tasks. + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { diff --git a/package/native-package/android/gradle.properties b/package/native-package/android/gradle.properties index e684c75fff..19b45d7cae 100644 --- a/package/native-package/android/gradle.properties +++ b/package/native-package/android/gradle.properties @@ -1,5 +1,8 @@ -ImageResizer_kotlinVersion=1.7.0 -ImageResizer_minSdkVersion=21 -ImageResizer_targetSdkVersion=31 -ImageResizer_compileSdkVersion=31 -ImageResizer_ndkversion=21.4.7075529 +# Fallbacks used only when the consuming app's root project does not define these (an app created +# from the React Native template always does). Kept in step with React Native 0.87's requirements: +# libraries must build against compileSdk >= 34 (minCompileSdk), and Kotlin >= 2.0 is required. +ImageResizer_kotlinVersion=2.2.0 +ImageResizer_minSdkVersion=24 +ImageResizer_targetSdkVersion=36 +ImageResizer_compileSdkVersion=34 +ImageResizer_ndkversion=27.1.12297006 diff --git a/package/native-package/package.json b/package/native-package/package.json index fdc57ad92a..82be137fa9 100644 --- a/package/native-package/package.json +++ b/package/native-package/package.json @@ -84,7 +84,7 @@ }, "devDependencies": { "@stream-io/typescript-config": "workspace:^", - "react-native": "0.86.0", + "react-native": "0.87.0", "typescript": "6.0.3" }, "codegenConfig": { diff --git a/package/native-package/tsconfig.json b/package/native-package/tsconfig.json index f8b97a6352..fb63725780 100644 --- a/package/native-package/tsconfig.json +++ b/package/native-package/tsconfig.json @@ -1,6 +1,16 @@ { "extends": "@stream-io/typescript-config/library.json", "compilerOptions": { + // React Native 0.87's Strict TypeScript API removes types for `react-native/Libraries/*` deep + // imports. `src/native/StreamShimmerViewNativeComponent.ts` must keep importing + // `codegenNativeComponent` from its deep path at *runtime*: the root re-export only exists from + // React Native 0.80, and this package supports `>=0.76`. Opt this package (and only this + // package - the core SDK is fully migrated to the Strict API) back into the legacy deep-import + // types. Build-time only; it changes no emitted code and no public type. + // TODO: drop this once the wrappers' minimum React Native is >= 0.80, and switch the import to + // `import { codegenNativeComponent } from 'react-native'`. The opt-out itself disappears after + // React Native 0.88. + "customConditions": ["react-native", "react-native-legacy-deep-imports"], // The optional-dependency shims dynamically `require()` peer deps that are // not installed here (so their values are untyped) and guard them behind // runtime checks TS can't narrow across closures. The strict-`any`/null/ diff --git a/package/package.json b/package/package.json index b9759e7c87..46ec247b00 100644 --- a/package/package.json +++ b/package/package.json @@ -111,10 +111,10 @@ "devDependencies": { "@babel/core": "^7.29.0", "@babel/runtime": "^7.29.2", - "@op-engineering/op-sqlite": "^17.1.2", + "@op-engineering/op-sqlite": "^18.1.0", "@react-native-community/netinfo": "^12.0.1", - "@react-native/babel-preset": "0.86.0", - "@react-native/jest-preset": "0.86.0", + "@react-native/babel-preset": "0.87.0", + "@react-native/jest-preset": "0.87.0", "@shopify/flash-list": "^2.3.2", "@stream-io/typescript-config": "workspace:^", "@testing-library/jest-native": "^5.4.3", @@ -139,14 +139,14 @@ "jest": "^30.4.2", "moment-timezone": "^0.6.0", "react": "19.2.3", - "react-native": "0.86.0", + "react-native": "0.87.0", "react-native-builder-bob": "0.40.11", "react-native-gesture-handler": "^3.1.0", - "react-native-reanimated": "^4.5.2", - "react-native-safe-area-context": "^5.8.0", + "react-native-reanimated": "^4.6.0", + "react-native-safe-area-context": "^5.8.1", "react-native-svg": "15.15.5", "react-native-teleport": "^1.1.12", - "react-native-worklets": "^0.11.1", + "react-native-worklets": "^0.12.1", "react-test-renderer": "19.2.3", "rimraf": "^6.0.1", "typescript": "6.0.3", diff --git a/package/src/a11y/hooks/useAccessibilityActivateAction.ts b/package/src/a11y/hooks/useAccessibilityActivateAction.ts index 1fce1c7822..df288cdc27 100644 --- a/package/src/a11y/hooks/useAccessibilityActivateAction.ts +++ b/package/src/a11y/hooks/useAccessibilityActivateAction.ts @@ -1,4 +1,4 @@ -import type { AccessibilityActionEvent, AccessibilityProps } from 'react-native'; +import type { AccessibilityActionEvent, ViewProps } from 'react-native'; import { useAccessibilityContext } from '../../contexts/accessibilityContext/AccessibilityContext'; @@ -9,12 +9,12 @@ export type UseAccessibilityActivateActionProps = { export type UseAccessibilityActivateActionResult = | { - accessibilityActions?: AccessibilityProps['accessibilityActions']; - onAccessibilityAction?: AccessibilityProps['onAccessibilityAction']; + accessibilityActions?: ViewProps['accessibilityActions']; + onAccessibilityAction?: ViewProps['onAccessibilityAction']; } | undefined; -const accessibilityActivateActions: NonNullable = [ +const accessibilityActivateActions: NonNullable = [ { name: 'activate' }, ]; diff --git a/package/src/a11y/hooks/useSetAccessibilityFocus.ts b/package/src/a11y/hooks/useSetAccessibilityFocus.ts index 11d0ac6e7b..1a10dbded2 100644 --- a/package/src/a11y/hooks/useSetAccessibilityFocus.ts +++ b/package/src/a11y/hooks/useSetAccessibilityFocus.ts @@ -19,7 +19,7 @@ const resolveNode = (target: AccessibilityFocusTarget): number | null => { return target; } const current = target.current; - return current == null ? null : findNodeHandle(current as FindNodeHandleArg); + return current == null ? null : (findNodeHandle(current as FindNodeHandleArg) ?? null); }; /** diff --git a/package/src/components/Attachment/Gallery.tsx b/package/src/components/Attachment/Gallery.tsx index b675a24725..f8ec0a18ba 100644 --- a/package/src/components/Attachment/Gallery.tsx +++ b/package/src/components/Attachment/Gallery.tsx @@ -38,6 +38,7 @@ import { useTheme } from '../../contexts/themeContext/ThemeContext'; import { isVideoPlayerAvailable } from '../../native'; import { primitives } from '../../theme'; +import type { ViewRef } from '../../types/react-native-compat'; import { FileTypes } from '../../types/types'; import { getUrlWithoutParams } from '../../utils/utils'; @@ -242,7 +243,7 @@ const GalleryThumbnail = ({ isVideo ? 'a11y/Gallery Video' : 'a11y/Gallery Image', ); const thumbnailAccessibilityHint = useA11yLabel('a11y/Double tap to open'); - const thumbnailRef = useRef(null); + const thumbnailRef = useRef(null); const openImageViewer = () => { if (!message) { return; diff --git a/package/src/components/AutoCompleteInput/AutoCompleteInput.tsx b/package/src/components/AutoCompleteInput/AutoCompleteInput.tsx index 764ee5851d..9b95a1be3f 100644 --- a/package/src/components/AutoCompleteInput/AutoCompleteInput.tsx +++ b/package/src/components/AutoCompleteInput/AutoCompleteInput.tsx @@ -27,18 +27,19 @@ import { import { useStableCallback } from '../../hooks'; import { useStateStore } from '../../hooks/useStateStore'; +import type { TextInputRef } from '../../types/react-native-compat'; import { useCooldownRemaining } from '../MessageInput/hooks/useCooldownRemaining'; export type TextInputOverrideComponent = | typeof RNTextInput | React.ComponentClass - | React.ForwardRefExoticComponent>; + | React.ForwardRefExoticComponent>; type AnimatedTextInputRendererProps = TextInputProps & { TextInputComponent: TextInputOverrideComponent; }; -const TextInputRenderer = React.forwardRef( +const TextInputRenderer = React.forwardRef( ({ TextInputComponent: Component, ...props }, ref) => , ); @@ -165,7 +166,7 @@ const AutoCompleteInputWithContext = (props: AutoCompleteInputPropsWithContext) } }, [text]); - const nativeInputRef = useRef(null); + const nativeInputRef = useRef(null); const clearState = useCallback(() => { setLocalText(''); @@ -186,7 +187,7 @@ const AutoCompleteInputWithContext = (props: AutoCompleteInputPropsWithContext) }); const setExtendedInputRef = useCallback( - (ref: RNTextInput | null) => { + (ref: TextInputRef | null) => { nativeInputRef.current = ref; if (!ref) { setRef(setInputBoxRef, null); diff --git a/package/src/components/AutoCompleteInput/__tests__/AutoCompleteInput.test.tsx b/package/src/components/AutoCompleteInput/__tests__/AutoCompleteInput.test.tsx index 442b37c8d4..428901a378 100644 --- a/package/src/components/AutoCompleteInput/__tests__/AutoCompleteInput.test.tsx +++ b/package/src/components/AutoCompleteInput/__tests__/AutoCompleteInput.test.tsx @@ -1,13 +1,12 @@ import React from 'react'; -import type { TextInput } from 'react-native'; - import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react-native'; import type { Channel as ChannelType, StreamChat } from 'stream-chat'; import { OverlayProvider } from '../../../contexts'; import type { InputBoxRef } from '../../../contexts/messageInputContext/MessageInputContext'; import { initiateClientWithChannels } from '../../../mock-builders/api/initiateClientWithChannels'; +import type { TextInputRef } from '../../../types/react-native-compat'; import type { ChannelProps } from '../../Channel/Channel'; import { Channel } from '../../Channel/Channel'; import { Chat } from '../../Chat/Chat'; @@ -136,7 +135,7 @@ describe('AutoCompleteInput', () => { const text = 'hello'; const channelProps = { channel, - setInputRef: (ref: TextInput | null) => { + setInputRef: (ref: TextInputRef | null) => { inputRef = ref as InputBoxRef | null; }, }; diff --git a/package/src/components/ChannelDetails/components/members/ChannelMemberList.tsx b/package/src/components/ChannelDetails/components/members/ChannelMemberList.tsx index eb952e0145..eea1aa8b82 100644 --- a/package/src/components/ChannelDetails/components/members/ChannelMemberList.tsx +++ b/package/src/components/ChannelDetails/components/members/ChannelMemberList.tsx @@ -149,7 +149,7 @@ const ChannelMemberListContent = ({ keyExtractor={keyExtractor} ListEmptyComponent={emptyState} ListFooterComponent={ - loading && members && members.length > 0 ? : null + loading && members && members.length > 0 ? : undefined } onEndReached={loadMore} onEndReachedThreshold={0.2} diff --git a/package/src/components/ChannelDetails/components/navigation-section/MediaItem.tsx b/package/src/components/ChannelDetails/components/navigation-section/MediaItem.tsx index 53184ccb90..0ecda270a6 100644 --- a/package/src/components/ChannelDetails/components/navigation-section/MediaItem.tsx +++ b/package/src/components/ChannelDetails/components/navigation-section/MediaItem.tsx @@ -7,6 +7,7 @@ import { useChatConfigContext } from '../../../../contexts/chatConfigContext/Cha import { useComponentsContext } from '../../../../contexts/componentsContext/ComponentsContext'; import { useTheme } from '../../../../contexts/themeContext/ThemeContext'; import { primitives } from '../../../../theme'; +import type { ViewRef } from '../../../../types/react-native-compat'; import { FileTypes } from '../../../../types/types'; import { getResizedImageUrl } from '../../../../utils/getResizedImageUrl'; import { getUrlOfImageAttachment } from '../../../../utils/getUrlOfImageAttachment'; @@ -49,7 +50,7 @@ export const MediaItem = (props: MediaItemProps) => { } = useTheme(); const { resizableCDNHosts } = useChatConfigContext(); const styles = useStyles(); - const containerRef = useRef(null); + const containerRef = useRef(null); const isVideo = attachment.type === FileTypes.Video; const url = attachment.thumb_url || getUrlOfImageAttachment(attachment); @@ -65,7 +66,11 @@ export const MediaItem = (props: MediaItemProps) => { onPress={ onPress ? () => - onPress({ attachment, message, requesterNode: findNodeHandle(containerRef.current) }) + onPress({ + attachment, + message, + requesterNode: findNodeHandle(containerRef.current) ?? null, + }) : undefined } ref={containerRef} diff --git a/package/src/components/ChannelList/ChannelListView.tsx b/package/src/components/ChannelList/ChannelListView.tsx index e846c6cebc..14dbf63ea3 100644 --- a/package/src/components/ChannelList/ChannelListView.tsx +++ b/package/src/components/ChannelList/ChannelListView.tsx @@ -133,7 +133,7 @@ const ChannelListViewWithContext = (props: ChannelListViewPropsWithContext) => { <> & * * @overrideType object */ - style?: DeepPartial; + style?: ThemeStyle; }; const selector = (nextValue: OfflineDBState) => @@ -366,5 +365,5 @@ const ChatWithContext = (props: PropsWithChildren) => { export const Chat = (props: PropsWithChildren) => { const { theme } = useTheme(); - return } {...props} />; + return ; }; diff --git a/package/src/components/ImageGallery/components/ImageGalleryFooter.tsx b/package/src/components/ImageGallery/components/ImageGalleryFooter.tsx index c9cdb5feed..daa68a97ca 100644 --- a/package/src/components/ImageGallery/components/ImageGalleryFooter.tsx +++ b/package/src/components/ImageGallery/components/ImageGalleryFooter.tsx @@ -17,9 +17,21 @@ import { FileTypes } from '../../../types/types'; import { Button } from '../../ui/Button/Button'; import { SafeAreaView } from '../../UIComponents/SafeAreaViewWrapper'; -const ReanimatedSafeAreaView = Animated.createAnimatedComponent - ? Animated.createAnimatedComponent(SafeAreaView) - : SafeAreaView; +// Never called - it exists only so `ReturnType` below can name the animated component's type. +// Spelling it as `ReturnType>` +// directly resolves against the wrong overload (the `FlatList` one). +// eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in type position only +const createAnimatedSafeAreaViewType = () => Animated.createAnimatedComponent(SafeAreaView); + +/** + * `createAnimatedComponent` is guarded because a stripped-down Reanimated mock may not provide it. + * The result is asserted back to the animated component type on purpose: written as a bare ternary + * this types as the *union* of the animated and the plain component, so its `style` prop becomes + * their intersection - which from React Native 0.87 no longer accepts an animated style. + */ +const ReanimatedSafeAreaView = ( + Animated.createAnimatedComponent ? Animated.createAnimatedComponent(SafeAreaView) : SafeAreaView +) as ReturnType; export type ImageGalleryFooterCustomComponent = ({ openGridView, diff --git a/package/src/components/ImageGallery/hooks/useAnimatedGalleryStyle.tsx b/package/src/components/ImageGallery/hooks/useAnimatedGalleryStyle.tsx index 95ccc19620..743eb35636 100644 --- a/package/src/components/ImageGallery/hooks/useAnimatedGalleryStyle.tsx +++ b/package/src/components/ImageGallery/hooks/useAnimatedGalleryStyle.tsx @@ -15,6 +15,13 @@ type Props = { const oneEighth = 1 / 8; +/** + * Named via `ReturnType` because Reanimated does not export `AnimatedStyleHandle` from its root. + * Without an explicit annotation the inferred return type inlines React Native's internal style + * helper types, which cannot be written into the emitted declarations (TS2883). + */ +type GalleryAnimatedStyle = ReturnType>; + export const useAnimatedGalleryStyle = ({ currentIndexShared, index, @@ -23,7 +30,7 @@ export const useAnimatedGalleryStyle = ({ screenHeight, translateX, translateY, -}: Props) => { +}: Props): [GalleryAnimatedStyle, GalleryAnimatedStyle] => { const { vw } = useViewport(); const screenWidth = vw(100); @@ -64,7 +71,7 @@ export const useAnimatedGalleryStyle = ({ }; }, []); - const animatedStyles = useAnimatedStyle(() => { + const animatedStyles = useAnimatedStyle(() => { const xScaleOffset = 7 * screenWidth * (0.5 + index); const yScaleOffset = -screenHeight * 3.5; return { diff --git a/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx b/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx index f0c9fc6d8b..21559788de 100644 --- a/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx +++ b/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx @@ -8,7 +8,6 @@ import { Keyboard, KeyboardAvoidingViewProps, KeyboardEvent, - KeyboardEventListener, KeyboardMetrics, LayoutAnimation, LayoutChangeEvent, @@ -22,6 +21,7 @@ import { } from 'react-native'; import { KeyboardProvider } from '../../contexts/keyboardContext/KeyboardContext'; +import type { KeyboardEventListener, ViewRef } from '../../types/react-native-compat'; export type KeyboardCompatibleViewProps = KeyboardAvoidingViewProps; @@ -84,14 +84,14 @@ export class KeyboardCompatibleView extends React.Component< _keyboardEvent: KeyboardEvent | null = null; _subscriptions: EmitterSubscription[] = []; _appStateSubscription: NativeEventSubscription | null = null; - viewRef: React.RefObject; + viewRef: React.RefObject; _initialFrameHeight = 0; _bottom: number = 0; constructor(props: KeyboardAvoidingViewProps) { super(props); this.state = { - appState: AppState.currentState, + appState: (AppState.currentState as AppStateStatus | null | undefined) ?? 'unknown', bottom: 0, isKeyboardOpen: false, }; diff --git a/package/src/components/Message/Message.tsx b/package/src/components/Message/Message.tsx index 7eb6c97628..9bacbd964c 100644 --- a/package/src/components/Message/Message.tsx +++ b/package/src/components/Message/Message.tsx @@ -68,6 +68,7 @@ import { useIsOverlayActive, } from '../../state-store'; import { primitives } from '../../theme'; +import type { ViewRef } from '../../types/react-native-compat'; import { FileTypes } from '../../types/types'; import { checkMessageEquality, @@ -359,10 +360,10 @@ const MessageWithContext = (props: MessagePropsWithContext) => { const rectRef = useRef(undefined); const bubbleRect = useRef(undefined); - const contextMenuAnchorRef = useRef(null); - const messageOverlayTargetsRef = useRef>({}); + const contextMenuAnchorRef = useRef(null); + const messageOverlayTargetsRef = useRef>({}); const registerMessageOverlayTarget = useStableCallback( - ({ id, view }: { id: string; view: View | null }) => { + ({ id, view }: { id: string; view: ViewRef | null }) => { messageOverlayTargetsRef.current[id] = view; }, ); diff --git a/package/src/components/Message/MessageItemView/MessageContent.tsx b/package/src/components/Message/MessageItemView/MessageContent.tsx index 97267a3876..bd290cb861 100644 --- a/package/src/components/Message/MessageItemView/MessageContent.tsx +++ b/package/src/components/Message/MessageItemView/MessageContent.tsx @@ -197,14 +197,16 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => { } } + // Built in one literal rather than by mutation: React Native 0.87 types style objects as + // `Readonly`, so assigning onto an already-typed `ViewStyle` no longer compiles. const style: ViewStyle = { backgroundColor, borderBottomLeftRadius: borderBottomLeftRadius ?? computedBottomLeftRadius, borderBottomRightRadius: borderBottomRightRadius ?? computedBottomRightRadius, + ...(borderRadius !== undefined ? { borderRadius } : {}), + ...(borderTopLeftRadius !== undefined ? { borderTopLeftRadius } : {}), + ...(borderTopRightRadius !== undefined ? { borderTopRightRadius } : {}), }; - if (borderRadius !== undefined) style.borderRadius = borderRadius; - if (borderTopLeftRadius !== undefined) style.borderTopLeftRadius = borderTopLeftRadius; - if (borderTopRightRadius !== undefined) style.borderTopRightRadius = borderTopRightRadius; return style; }, [ diff --git a/package/src/components/Message/MessageItemView/__tests__/MessageAuthor.test.tsx b/package/src/components/Message/MessageItemView/__tests__/MessageAuthor.test.tsx index 49fbcbdb8e..32b623379a 100644 --- a/package/src/components/Message/MessageItemView/__tests__/MessageAuthor.test.tsx +++ b/package/src/components/Message/MessageItemView/__tests__/MessageAuthor.test.tsx @@ -3,8 +3,6 @@ import React from 'react'; import { cleanup, render, screen, waitFor } from '@testing-library/react-native'; import type { StreamChat } from 'stream-chat'; -import type { DeepPartial } from '../../../../contexts/themeContext/ThemeContext'; -import type { Theme } from '../../../../contexts/themeContext/utils/theme'; import { defaultTheme } from '../../../../contexts/themeContext/utils/theme'; import { generateMessage, @@ -30,7 +28,7 @@ describe('MessageAuthor', () => { user: { ...staticUser, image: undefined }, }); render( - }> + , ); @@ -40,7 +38,7 @@ describe('MessageAuthor', () => { }); screen.rerender( - }> + , ); @@ -55,7 +53,7 @@ describe('MessageAuthor', () => { }); screen.rerender( - }> + , ); diff --git a/package/src/components/Message/MessageItemView/__tests__/MessagePinnedHeader.test.tsx b/package/src/components/Message/MessageItemView/__tests__/MessagePinnedHeader.test.tsx index 9e3c7d487e..c8b28a20a7 100644 --- a/package/src/components/Message/MessageItemView/__tests__/MessagePinnedHeader.test.tsx +++ b/package/src/components/Message/MessageItemView/__tests__/MessagePinnedHeader.test.tsx @@ -2,9 +2,7 @@ import React from 'react'; import { cleanup, render, screen, waitFor } from '@testing-library/react-native'; -import type { DeepPartial } from '../../../../contexts/themeContext/ThemeContext'; import { ThemeProvider } from '../../../../contexts/themeContext/ThemeContext'; -import type { Theme } from '../../../../contexts/themeContext/utils/theme'; import { defaultTheme } from '../../../../contexts/themeContext/utils/theme'; import { generateMessage, @@ -23,7 +21,7 @@ describe('MessagePinnedHeader', () => { pinned: true, }); render( - }> + , ); @@ -33,7 +31,7 @@ describe('MessagePinnedHeader', () => { }); screen.rerender( - }> + , ); @@ -44,7 +42,7 @@ describe('MessagePinnedHeader', () => { }); screen.rerender( - }> + , ); diff --git a/package/src/components/Message/MessageItemView/__tests__/MessageReplies.test.tsx b/package/src/components/Message/MessageItemView/__tests__/MessageReplies.test.tsx index 7c97d36db1..a8ab4d2951 100644 --- a/package/src/components/Message/MessageItemView/__tests__/MessageReplies.test.tsx +++ b/package/src/components/Message/MessageItemView/__tests__/MessageReplies.test.tsx @@ -2,9 +2,7 @@ import React from 'react'; import { cleanup, render, screen, userEvent, waitFor } from '@testing-library/react-native'; -import type { DeepPartial } from '../../../../contexts/themeContext/ThemeContext'; import { ThemeProvider } from '../../../../contexts/themeContext/ThemeContext'; -import type { Theme } from '../../../../contexts/themeContext/utils/theme'; import { defaultTheme } from '../../../../contexts/themeContext/utils/theme'; import type { TranslationContextValue } from '../../../../contexts/translationContext/TranslationContext'; import { TranslationProvider } from '../../../../contexts/translationContext/TranslationContext'; @@ -26,7 +24,7 @@ describe('MessageReplies', () => { }); render( - }> + , @@ -51,7 +49,7 @@ describe('MessageReplies', () => { screen.rerender( - }> + , @@ -79,7 +77,7 @@ describe('MessageReplies', () => { }); render( - }> + null} /> , @@ -96,7 +94,7 @@ describe('MessageReplies', () => { screen.rerender( - }> + null} threadList /> , diff --git a/package/src/components/Message/MessageItemView/utils/renderText.tsx b/package/src/components/Message/MessageItemView/utils/renderText.tsx index 8a8edc2cdf..624ddb5e94 100644 --- a/package/src/components/Message/MessageItemView/utils/renderText.tsx +++ b/package/src/components/Message/MessageItemView/utils/renderText.tsx @@ -333,7 +333,11 @@ export const renderText = (params: RenderTextParams) => { const onLink = (url: string) => onLinkParams ? onLinkParams(url) - : Linking.canOpenURL(url).then((canOpenUrl) => canOpenUrl && Linking.openURL(url)); + : Linking.canOpenURL(url).then(async (canOpenUrl) => { + if (canOpenUrl) { + await Linking.openURL(url); + } + }); let previousLink: string | undefined; const linkReact: ReactNodeOutput = (node, output, { ...state }) => { diff --git a/package/src/components/Message/MessageOverlayWrapper.tsx b/package/src/components/Message/MessageOverlayWrapper.tsx index a27f2eb7af..870b7d54cb 100644 --- a/package/src/components/Message/MessageOverlayWrapper.tsx +++ b/package/src/components/Message/MessageOverlayWrapper.tsx @@ -8,6 +8,7 @@ import { useMessageContext, useMessageOverlayRuntimeContext, } from '../../contexts/messageContext/MessageContext'; +import type { ViewRef } from '../../types/react-native-compat'; export type MessageOverlayWrapperProps = PropsWithChildren<{ /** @@ -37,7 +38,7 @@ export const MessageOverlayWrapper = ({ const placeholderLayout = overlayTargetRectRef.current; const handleTargetRef = useCallback( - (view: View | null) => { + (view: ViewRef | null) => { registerMessageOverlayTarget({ id: targetId, view, diff --git a/package/src/components/Message/utils/__tests__/measureInWindow.test.ts b/package/src/components/Message/utils/__tests__/measureInWindow.test.ts index c096fcb855..833f102540 100644 --- a/package/src/components/Message/utils/__tests__/measureInWindow.test.ts +++ b/package/src/components/Message/utils/__tests__/measureInWindow.test.ts @@ -1,7 +1,9 @@ -import { Dimensions, Platform, View } from 'react-native'; +import { Dimensions, Platform } from 'react-native'; import type { EdgeInsets } from 'react-native-safe-area-context'; +import type { ViewRef } from '../../../../types/react-native-compat'; + import { isMeasuredRectBogus, measureInWindow } from '../measureInWindow'; // `measureInWindow` is globally mocked in jest-setup so other suites don't hit native @@ -42,14 +44,14 @@ const makeNode = ({ }: { measure?: MeasureTuple; measureInWindow: MeasureInWindowTuple; -}): { current: View | null } => { +}): { current: ViewRef | null } => { const handle: Record = { measureInWindow: (cb: (...args: MeasureInWindowTuple) => void) => cb(...miw), }; if (measure) { handle.measure = (cb: (...args: MeasureTuple) => void) => cb(...measure); } - return { current: handle as unknown as View }; + return { current: handle as unknown as ViewRef }; }; describe('isMeasuredRectBogus', () => { diff --git a/package/src/components/Message/utils/measureInWindow.ts b/package/src/components/Message/utils/measureInWindow.ts index 1833742941..e05d5a62b3 100644 --- a/package/src/components/Message/utils/measureInWindow.ts +++ b/package/src/components/Message/utils/measureInWindow.ts @@ -1,7 +1,10 @@ import React from 'react'; -import { Dimensions, Platform, View } from 'react-native'; +import { Dimensions, Platform } from 'react-native'; + import { EdgeInsets } from 'react-native-safe-area-context'; +import type { ViewRef } from '../../../types/react-native-compat'; + import { isRN86OrGreater } from '../../../utils/react-native-constants'; type MeasuredRect = { x: number; y: number; w: number; h: number }; @@ -49,7 +52,7 @@ export const isMeasuredRectBogus = (x: number, y: number, w: number, h: number): }; export const measureInWindow = ( - node: React.RefObject, + node: React.RefObject, insets: EdgeInsets, ): Promise => { return new Promise((resolve, reject) => { diff --git a/package/src/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.tsx b/package/src/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.tsx index b79109475f..68c42b9102 100644 --- a/package/src/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.tsx +++ b/package/src/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.tsx @@ -32,6 +32,7 @@ import { useTheme } from '../../../../contexts/themeContext/ThemeContext'; import { useLazyRef } from '../../../../hooks/useLazyRef'; import { isSoundPackageAvailable } from '../../../../native'; import { primitives } from '../../../../theme'; +import type { ScrollViewRef } from '../../../../types/react-native-compat'; const END_ANCHOR_THRESHOLD = 16; const ATTACHMENT_PREVIEW_ANIMATION_DURATION = 200; @@ -97,7 +98,7 @@ const UnMemoizedAttachmentUploadPreviewList = () => { const { attachmentManager } = useMessageComposer(); const { attachments } = useAttachmentManagerState(); const isRTL = I18nManager.isRTL; - const attachmentListRef = useRef(null); + const attachmentListRef = useRef(null); const soundPackageAvailable = useMemo(() => isSoundPackageAvailable(), []); const isAudioAttachmentPreview = useMemo( () => getIsAudioAttachmentPreview(soundPackageAvailable), diff --git a/package/src/components/MessageList/MessageFlashList.tsx b/package/src/components/MessageList/MessageFlashList.tsx index bcee871c05..f3658c10b2 100644 --- a/package/src/components/MessageList/MessageFlashList.tsx +++ b/package/src/components/MessageList/MessageFlashList.tsx @@ -1,13 +1,5 @@ import React, { PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { - LayoutChangeEvent, - ScrollViewProps, - StyleSheet, - View, - useColorScheme, - ViewabilityConfig, - ViewToken, -} from 'react-native'; +import { LayoutChangeEvent, ScrollViewProps, StyleSheet, View, useColorScheme } from 'react-native'; import Animated from 'react-native-reanimated'; @@ -60,6 +52,7 @@ import { isVideoPlayerAvailable } from '../../native'; import { bumpOverlayLayoutRevision, useHasActiveId } from '../../state-store'; import { MessageInputHeightState } from '../../state-store/message-input-height-store'; import { primitives } from '../../theme'; +import type { ScrollViewRef, ViewabilityConfig, ViewToken } from '../../types/react-native-compat'; import { FileTypes } from '../../types/types'; import { transitions } from '../../utils/animations/transitions'; import { MessageWrapper } from '../Message/MessageItemView/MessageWrapper'; @@ -680,97 +673,103 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) => threadList, ]); - const updateStickyHeaderDateIfNeeded = useStableCallback((viewableItems: ViewToken[]) => { - if (!viewableItems.length) { - return; - } + const updateStickyHeaderDateIfNeeded = useStableCallback( + (viewableItems: ViewToken[]) => { + if (!viewableItems.length) { + return; + } - const lastItem = viewableItems[0]; + const lastItem = viewableItems[0]; - if (!lastItem) return; + if (!lastItem) return; - if ( - !channel.state.messagePagination.hasPrev && - processedMessageList[0].id === lastItem.item.id - ) { - setStickyHeaderDate(undefined); - return; - } - const isMessageTypeDeleted = lastItem.item.type === 'deleted'; + if ( + !channel.state.messagePagination.hasPrev && + processedMessageList[0].id === lastItem.item.id + ) { + setStickyHeaderDate(undefined); + return; + } + const isMessageTypeDeleted = lastItem.item.type === 'deleted'; - if ( - lastItem?.item?.created_at && - !isMessageTypeDeleted && - typeof lastItem.item.created_at !== 'string' && - lastItem.item.created_at.toDateString() !== stickyHeaderDateRef.current?.toDateString() - ) { - stickyHeaderDateRef.current = lastItem.item.created_at; - setStickyHeaderDate(lastItem.item.created_at); - } - }); + if ( + lastItem?.item?.created_at && + !isMessageTypeDeleted && + typeof lastItem.item.created_at !== 'string' && + lastItem.item.created_at.toDateString() !== stickyHeaderDateRef.current?.toDateString() + ) { + stickyHeaderDateRef.current = lastItem.item.created_at; + setStickyHeaderDate(lastItem.item.created_at); + } + }, + ); /** * This function should show or hide the unread indicator depending on the */ - const updateStickyUnreadIndicator = useStableCallback((viewableItems: ViewToken[]) => { - const channelUnreadState = channelUnreadStateStore.channelUnreadState; - // we need this check to make sure that regular list change do not trigger - // the unread notification to appear (for example if the old last read messages - // go out of the viewport). - const lastReadMessageId = channelUnreadState?.last_read_message_id; - const lastReadMessageVisible = viewableItems.some((item) => item.item.id === lastReadMessageId); - - // Channels with disabled `read-events` (i.e livestreams) still surface the unread - // notification when the client opted into a local unread count, so the gate accepts - // either source. - const unreadNotificationSupported = readEvents || client.options.isLocalUnreadCountEnabled; + const updateStickyUnreadIndicator = useStableCallback( + (viewableItems: ViewToken[]) => { + const channelUnreadState = channelUnreadStateStore.channelUnreadState; + // we need this check to make sure that regular list change do not trigger + // the unread notification to appear (for example if the old last read messages + // go out of the viewport). + const lastReadMessageId = channelUnreadState?.last_read_message_id; + const lastReadMessageVisible = viewableItems.some( + (item) => item.item.id === lastReadMessageId, + ); - if ( - !viewableItems.length || - !unreadNotificationSupported || - lastReadMessageVisible || - attachmentPickerStore.state.getLatestValue().selectedPicker === 'images' - ) { - setIsUnreadNotificationOpen(false); - return; - } + // Channels with disabled `read-events` (i.e livestreams) still surface the unread + // notification when the client opted into a local unread count, so the gate accepts + // either source. + const unreadNotificationSupported = readEvents || client.options.isLocalUnreadCountEnabled; - const lastItem = viewableItems[0]; + if ( + !viewableItems.length || + !unreadNotificationSupported || + lastReadMessageVisible || + attachmentPickerStore.state.getLatestValue().selectedPicker === 'images' + ) { + setIsUnreadNotificationOpen(false); + return; + } - if (!lastItem) return; + const lastItem = viewableItems[0]; - const lastItemMessage = lastItem.item; - const lastItemCreatedAt = lastItemMessage.created_at; + if (!lastItem) return; - const unreadIndicatorDate = channelUnreadState?.last_read?.getTime(); - const lastItemDate = lastItemCreatedAt.getTime(); + const lastItemMessage = lastItem.item; + const lastItemCreatedAt = lastItemMessage.created_at; - if ( - !channel.state.messagePagination.hasPrev && - processedMessageList[0].id === lastItemMessage.id - ) { - setIsUnreadNotificationOpen(false); - return; - } - /** - * This is a special case where there is a single long message by the sender. - * When a message is sent, we mark it as read before it actually has a `created_at` timestamp. - * This is a workaround to prevent the unread indicator from showing when the message is sent. - */ - if ( - viewableItems.length === 1 && - channel.countUnread() === 0 && - lastItemMessage.user.id === client.userID - ) { - setIsUnreadNotificationOpen(false); - return; - } - if (unreadIndicatorDate && lastItemDate > unreadIndicatorDate) { - setIsUnreadNotificationOpen(true); - } else { - setIsUnreadNotificationOpen(false); - } - }); + const unreadIndicatorDate = channelUnreadState?.last_read?.getTime(); + const lastItemDate = lastItemCreatedAt.getTime(); + + if ( + !channel.state.messagePagination.hasPrev && + processedMessageList[0].id === lastItemMessage.id + ) { + setIsUnreadNotificationOpen(false); + return; + } + /** + * This is a special case where there is a single long message by the sender. + * When a message is sent, we mark it as read before it actually has a `created_at` timestamp. + * This is a workaround to prevent the unread indicator from showing when the message is sent. + */ + if ( + viewableItems.length === 1 && + channel.countUnread() === 0 && + lastItemMessage.user?.id === client.userID + ) { + setIsUnreadNotificationOpen(false); + return; + } + if (unreadIndicatorDate && lastItemDate > unreadIndicatorDate) { + setIsUnreadNotificationOpen(true); + } else { + setIsUnreadNotificationOpen(false); + } + }, + ); /** * FlatList doesn't accept changeable function for onViewableItemsChanged prop. @@ -779,7 +778,7 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) => const unstableOnViewableItemsChanged = ({ viewableItems, }: { - viewableItems: ViewToken[] | undefined; + viewableItems: ViewToken[] | undefined; }) => { if (!viewableItems) { return; @@ -795,7 +794,7 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) => onViewableItemsChanged.current = unstableOnViewableItemsChanged; const stableOnViewableItemsChanged = useCallback( - ({ viewableItems }: { viewableItems: ViewToken[] | undefined }) => { + ({ viewableItems }: { viewableItems: ViewToken[] | undefined }) => { onViewableItemsChanged.current({ viewableItems }); }, [], @@ -1085,8 +1084,18 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) => bumpOverlayLayoutRevision(closeCorrectionDeltaY); const changedBy = currentListHeightRef.current - height; - flashListRef.current?.getNativeScrollRef()?.setNativeProps({ - contentOffset: { x: 0, y: flashListRef.current?.getAbsoluteLastScrollOffset() + changedBy }, + // `getNativeScrollRef()` is typed as returning the `ScrollView` *component* by flash-list, + // which under React Native 0.87's Strict TypeScript API is a function type rather than the + // host instance it actually returns at runtime. Re-assert the instance type. + const nativeScrollRef = flashListRef.current?.getNativeScrollRef() as + | ScrollViewRef + | null + | undefined; + nativeScrollRef?.setNativeProps({ + contentOffset: { + x: 0, + y: (flashListRef.current?.getAbsoluteLastScrollOffset() ?? 0) + changedBy, + }, }); currentListHeightRef.current = height; }); diff --git a/package/src/components/MessageList/MessageList.tsx b/package/src/components/MessageList/MessageList.tsx index 9e5de27721..baf66fb98c 100644 --- a/package/src/components/MessageList/MessageList.tsx +++ b/package/src/components/MessageList/MessageList.tsx @@ -8,8 +8,6 @@ import { StyleSheet, View, useColorScheme, - ViewabilityConfig, - ViewToken, } from 'react-native'; import Animated from 'react-native-reanimated'; @@ -72,6 +70,7 @@ import { useStateStore } from '../../hooks/useStateStore'; import { bumpOverlayLayoutRevision, useHasActiveId } from '../../state-store'; import { MessageInputHeightState } from '../../state-store/message-input-height-store'; import { primitives } from '../../theme'; +import type { ViewabilityConfig, ViewToken } from '../../types/react-native-compat'; import { transitions } from '../../utils/animations/transitions'; import { useIncomingMessageAnnouncements } from '../Accessibility/hooks/useIncomingMessageAnnouncements'; import { MessageWrapper } from '../Message/MessageItemView/MessageWrapper'; @@ -515,99 +514,103 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => { const channelRef = useRef(channel); channelRef.current = channel; - const updateStickyHeaderDateIfNeeded = useStableCallback((viewableItems: ViewToken[]) => { - if (!viewableItems.length) { - return; - } - - const lastMessage = viewableItems[viewableItems.length - 1].item?.message; - - if (lastMessage) { - if ( - !channel.state.messagePagination.hasPrev && - processedMessageList[processedMessageList.length - 1].id === lastMessage.id - ) { - setStickyHeaderDate(undefined); + const updateStickyHeaderDateIfNeeded = useStableCallback( + (viewableItems: ViewToken[]) => { + if (!viewableItems.length) { return; } - const isMessageTypeDeleted = lastMessage.type === 'deleted'; - if ( - lastMessage?.created_at && - !isMessageTypeDeleted && - typeof lastMessage.created_at !== 'string' && - lastMessage.created_at.toDateString() !== stickyHeaderDateRef.current?.toDateString() - ) { - stickyHeaderDateRef.current = lastMessage.created_at; - setStickyHeaderDate(lastMessage.created_at); + const lastMessage = viewableItems[viewableItems.length - 1].item?.message; + + if (lastMessage) { + if ( + !channel.state.messagePagination.hasPrev && + processedMessageList[processedMessageList.length - 1].id === lastMessage.id + ) { + setStickyHeaderDate(undefined); + return; + } + const isMessageTypeDeleted = lastMessage.type === 'deleted'; + + if ( + lastMessage?.created_at && + !isMessageTypeDeleted && + typeof lastMessage.created_at !== 'string' && + lastMessage.created_at.toDateString() !== stickyHeaderDateRef.current?.toDateString() + ) { + stickyHeaderDateRef.current = lastMessage.created_at; + setStickyHeaderDate(lastMessage.created_at); + } } - } - }); + }, + ); /** * This function should show or hide the unread indicator depending on the */ - const updateStickyUnreadIndicator = useStableCallback((viewableItems: ViewToken[]) => { - const channelUnreadState = channelUnreadStateStore.channelUnreadState; - // we need this check to make sure that regular list change do not trigger - // the unread notification to appear (for example if the old last read messages - // go out of the viewport). - const lastReadMessageId = channelUnreadState?.last_read_message_id; - const lastReadMessageVisible = viewableItems.some( - (item) => item.item.message.id === lastReadMessageId, - ); - - // Channels with disabled `read-events` (i.e livestreams) still surface the unread - // notification when the client opted into a local unread count, so the gate accepts - // either source. - const unreadNotificationSupported = readEvents || client.options.isLocalUnreadCountEnabled; - - if ( - !viewableItems.length || - !unreadNotificationSupported || - lastReadMessageVisible || - attachmentPickerStore.state.getLatestValue().selectedPicker === 'images' - ) { - setIsUnreadNotificationOpen(false); - return; - } - - const lastItem = viewableItems[viewableItems.length - 1].item; - - if (lastItem) { - const lastItemMessage = lastItem.message; - const lastItemCreatedAt = lastItemMessage.created_at; + const updateStickyUnreadIndicator = useStableCallback( + (viewableItems: ViewToken[]) => { + const channelUnreadState = channelUnreadStateStore.channelUnreadState; + // we need this check to make sure that regular list change do not trigger + // the unread notification to appear (for example if the old last read messages + // go out of the viewport). + const lastReadMessageId = channelUnreadState?.last_read_message_id; + const lastReadMessageVisible = viewableItems.some( + (item) => item.item.message.id === lastReadMessageId, + ); - const unreadIndicatorDate = channelUnreadState?.last_read?.getTime(); - const lastItemDate = lastItemCreatedAt.getTime(); + // Channels with disabled `read-events` (i.e livestreams) still surface the unread + // notification when the client opted into a local unread count, so the gate accepts + // either source. + const unreadNotificationSupported = readEvents || client.options.isLocalUnreadCountEnabled; if ( - !channel.state.messagePagination.hasPrev && - processedMessageList[processedMessageList.length - 1].id === lastItemMessage.id - ) { - setIsUnreadNotificationOpen(false); - return; - } - /** - * This is a special case where there is a single long message by the sender. - * When a message is sent, we mark it as read before it actually has a `created_at` timestamp. - * This is a workaround to prevent the unread indicator from showing when the message is sent. - */ - if ( - viewableItems.length === 1 && - channel.countUnread() === 0 && - lastItemMessage.user.id === client.userID + !viewableItems.length || + !unreadNotificationSupported || + lastReadMessageVisible || + attachmentPickerStore.state.getLatestValue().selectedPicker === 'images' ) { setIsUnreadNotificationOpen(false); return; } - if (unreadIndicatorDate && lastItemDate > unreadIndicatorDate) { - setIsUnreadNotificationOpen(true); - } else { - setIsUnreadNotificationOpen(false); + + const lastItem = viewableItems[viewableItems.length - 1].item; + + if (lastItem) { + const lastItemMessage = lastItem.message; + const lastItemCreatedAt = lastItemMessage.created_at; + + const unreadIndicatorDate = channelUnreadState?.last_read?.getTime(); + const lastItemDate = lastItemCreatedAt.getTime(); + + if ( + !channel.state.messagePagination.hasPrev && + processedMessageList[processedMessageList.length - 1].id === lastItemMessage.id + ) { + setIsUnreadNotificationOpen(false); + return; + } + /** + * This is a special case where there is a single long message by the sender. + * When a message is sent, we mark it as read before it actually has a `created_at` timestamp. + * This is a workaround to prevent the unread indicator from showing when the message is sent. + */ + if ( + viewableItems.length === 1 && + channel.countUnread() === 0 && + lastItemMessage.user?.id === client.userID + ) { + setIsUnreadNotificationOpen(false); + return; + } + if (unreadIndicatorDate && lastItemDate > unreadIndicatorDate) { + setIsUnreadNotificationOpen(true); + } else { + setIsUnreadNotificationOpen(false); + } } - } - }); + }, + ); /** * FlatList doesn't accept changeable function for onViewableItemsChanged prop. @@ -616,7 +619,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => { const unstableOnViewableItemsChanged = ({ viewableItems, }: { - viewableItems: ViewToken[] | undefined; + viewableItems: ViewToken[] | undefined; }) => { viewabilityChangedCallback({ inverted, viewableItems }); @@ -633,7 +636,11 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => { onViewableItemsChanged.current = unstableOnViewableItemsChanged; const stableOnViewableItemsChanged = useCallback( - ({ viewableItems }: { viewableItems: ViewToken[] | undefined }) => { + ({ + viewableItems, + }: { + viewableItems: ViewToken[] | undefined; + }) => { onViewableItemsChanged.current({ viewableItems }); }, [], @@ -1199,6 +1206,17 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => { [additionalFlatListProps?.style, styles.listContainer], ); + /** + * `strictMode` only became a `FlatList` prop in React Native 0.87, and this SDK supports `>=0.76`. + * Passed via spread rather than as a literal attribute because JSX excess-property checking does + * not apply to spreads, so this compiles on every supported version. A `@ts-expect-error` cannot + * work here: it is required below 0.87 and reported as unused from 0.87 onwards. + */ + const liveStreamingListProps = useMemo( + () => ({ strictMode: isLiveStreaming }), + [isLiveStreaming], + ); + const flatListContentContainerStyle = useMemo( () => [ { paddingTop: messageInputFloating ? messageInputHeight : 0 }, @@ -1337,11 +1355,10 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => { renderItem={renderItem} scrollEventThrottle={isLiveStreaming ? 16 : undefined} showsVerticalScrollIndicator={false} - // @ts-expect-error Safe to do for now - strictMode={isLiveStreaming} style={flatListStyle} testID='message-flat-list' viewabilityConfig={flatListViewabilityConfig} + {...liveStreamingListProps} {...additionalFlatListPropsExcludingStyle} accessibilityActions={messageListAccessibilityActions} onAccessibilityAction={messageListOnAccessibilityAction} diff --git a/package/src/components/MessageList/hooks/useScrollToBottomAccessibilityAction.ts b/package/src/components/MessageList/hooks/useScrollToBottomAccessibilityAction.ts index d14dfdc578..91fb3bab62 100644 --- a/package/src/components/MessageList/hooks/useScrollToBottomAccessibilityAction.ts +++ b/package/src/components/MessageList/hooks/useScrollToBottomAccessibilityAction.ts @@ -1,6 +1,6 @@ import { useContext, useMemo } from 'react'; -import type { AccessibilityActionEvent, AccessibilityProps } from 'react-native'; +import type { AccessibilityActionEvent, ViewProps } from 'react-native'; import { mergeAccessibilityActions } from '../../../a11y/a11yUtils'; import { useAccessibilityContext } from '../../../contexts/accessibilityContext/AccessibilityContext'; @@ -13,8 +13,20 @@ import { export const SCROLL_TO_BOTTOM_ACCESSIBILITY_ACTION_NAME = 'streamScrollToBottom'; -type AccessibilityActions = AccessibilityProps['accessibilityActions']; -type OnAccessibilityAction = AccessibilityProps['onAccessibilityAction']; +type AccessibilityActions = ViewProps['accessibilityActions']; +type OnAccessibilityAction = ViewProps['onAccessibilityAction']; + +/** + * Declared explicitly rather than inferred. Left to inference, TypeScript expands + * `AccessibilityActionEvent` into its structural shape when emitting the declaration, and that + * shape names `HostInstance` - which React Native only exports from 0.78, while this SDK supports + * `>=0.76`. Naming the props here keeps the emitted `.d.ts` referring to `ViewProps[...]`, which + * resolves against whichever React Native the consumer actually has. + */ +export type UseScrollToBottomAccessibilityActionResult = { + accessibilityActions: AccessibilityActions; + onAccessibilityAction: OnAccessibilityAction; +}; type UseScrollToBottomAccessibilityActionParams = { accessibilityActions?: AccessibilityActions; @@ -30,7 +42,7 @@ export const useScrollToBottomAccessibilityAction = ({ onScrollToBottom, unreadCount, visible, -}: UseScrollToBottomAccessibilityActionParams) => { +}: UseScrollToBottomAccessibilityActionParams): UseScrollToBottomAccessibilityActionResult => { const { enabled } = useAccessibilityContext(); const { t } = useContext(TranslationContext); diff --git a/package/src/components/MessageMenu/MessageUserReactions.tsx b/package/src/components/MessageMenu/MessageUserReactions.tsx index a25f713ad0..d2a4ef83df 100644 --- a/package/src/components/MessageMenu/MessageUserReactions.tsx +++ b/package/src/components/MessageMenu/MessageUserReactions.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; + import { FlatList } from 'react-native-gesture-handler'; import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; @@ -25,6 +26,7 @@ import { useTranslationContext } from '../../contexts/translationContext/Transla import { useStableCallback } from '../../hooks'; import { IconProps } from '../../icons'; import { primitives } from '../../theme'; +import type { FlatListRef } from '../../types/react-native-compat'; import { Reaction } from '../../types/types'; import { ReactionData } from '../../utils/utils'; import { Button } from '../ui'; @@ -100,7 +102,7 @@ export const MessageUserReactions = (props: MessageUserReactionsProps) => { selectedReaction: propSelectedReaction, supportedReactions: propSupportedReactions, } = props; - const selectorListRef = useRef(null); + const selectorListRef = useRef>(null); const { close } = useBottomSheetContext(); const reactionTypes = useMemo( () => Object.keys(message?.reaction_groups ?? {}), diff --git a/package/src/components/MessageMenu/__tests__/MessageUserReactionsAvatar.test.tsx b/package/src/components/MessageMenu/__tests__/MessageUserReactionsAvatar.test.tsx index 3214bc4342..5db15384a7 100644 --- a/package/src/components/MessageMenu/__tests__/MessageUserReactionsAvatar.test.tsx +++ b/package/src/components/MessageMenu/__tests__/MessageUserReactionsAvatar.test.tsx @@ -4,8 +4,6 @@ import { render } from '@testing-library/react-native'; import type { StreamChat } from 'stream-chat'; -import type { DeepPartial } from '../../../contexts/themeContext/ThemeContext'; -import type { Theme } from '../../../contexts/themeContext/utils/theme'; import { defaultTheme } from '../../../contexts/themeContext/utils/theme'; import { getTestClientWithUser } from '../../../mock-builders/mock'; import { Chat } from '../../Chat/Chat'; @@ -21,7 +19,7 @@ describe('MessageUserReactionsAvatar', () => { it('should render Avatar with correct image, name, and default size', () => { const { queryByTestId } = render( - }> + , ); @@ -32,7 +30,7 @@ describe('MessageUserReactionsAvatar', () => { it('should render Avatar with correct image, name, and custom size', () => { const { queryByTestId } = render( - }> + , ); diff --git a/package/src/components/Poll/components/CreatePollOptions.tsx b/package/src/components/Poll/components/CreatePollOptions.tsx index cd36172bc2..f28acb528e 100644 --- a/package/src/components/Poll/components/CreatePollOptions.tsx +++ b/package/src/components/Poll/components/CreatePollOptions.tsx @@ -8,6 +8,7 @@ import { TextInput, View, } from 'react-native'; + import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import Animated, { interpolate, @@ -29,6 +30,7 @@ import { useComponentsContext } from '../../../contexts/componentsContext/Compon import { useMessageComposer } from '../../../contexts/messageInputContext/hooks/useMessageComposer'; import { useStateStore } from '../../../hooks/useStateStore'; import { primitives } from '../../../theme'; +import type { ViewRef } from '../../../types/react-native-compat'; export type CurrentOptionPositionsCache = { inverseIndexCache: { @@ -295,7 +297,7 @@ export const CreatePollOption = ({ normalizedCreatePollOptionGap, ); const top = useSharedValue(initialTop); - const optionContainerRef = useRef(null); + const optionContainerRef = useRef(null); const isMeasurementScheduledRef = useRef(false); const isDraggingDerived = useDerivedValue(() => isDragging.value); diff --git a/package/src/components/Poll/components/MultipleVotesSettings.tsx b/package/src/components/Poll/components/MultipleVotesSettings.tsx index dc7c178714..4a961ccc0f 100644 --- a/package/src/components/Poll/components/MultipleVotesSettings.tsx +++ b/package/src/components/Poll/components/MultipleVotesSettings.tsx @@ -11,6 +11,7 @@ import { useMessageComposer } from '../../../contexts/messageInputContext/hooks/ import { useStableCallback } from '../../../hooks'; import { useStateStore } from '../../../hooks/useStateStore'; import { primitives } from '../../../theme'; +import type { TextInputRef } from '../../../types/react-native-compat'; import { useRtlMirrorSwitchStyle } from '../../../utils/rtlMirrorSwitchStyle'; import { Button } from '../../ui'; @@ -35,7 +36,7 @@ const MaxVotesTextInput = () => { }, } = useTheme(); const hasSelectedInitialValueRef = useRef(false); - const inputRef = useRef(null); + const inputRef = useRef(null); const styles = useStyles(); diff --git a/package/src/components/ThreadList/ThreadList.tsx b/package/src/components/ThreadList/ThreadList.tsx index 5cc3d3c164..20a5dd67fc 100644 --- a/package/src/components/ThreadList/ThreadList.tsx +++ b/package/src/components/ThreadList/ThreadList.tsx @@ -70,7 +70,7 @@ export const DefaultThreadListComponent = () => { data={threads} keyExtractor={(props) => props.id} ListEmptyComponent={ThreadListEmptyPlaceholder} - ListFooterComponent={isLoadingNext ? ThreadListLoadingMoreIndicator : null} + ListFooterComponent={isLoadingNext ? ThreadListLoadingMoreIndicator : undefined} onEndReached={loadMore} renderItem={renderItem} testID='thread-flatlist' diff --git a/package/src/components/UIComponents/PortalWhileClosingView.tsx b/package/src/components/UIComponents/PortalWhileClosingView.tsx index 4d117615b6..50f97bee8d 100644 --- a/package/src/components/UIComponents/PortalWhileClosingView.tsx +++ b/package/src/components/UIComponents/PortalWhileClosingView.tsx @@ -15,6 +15,7 @@ import { useHasActiveId, useIsOverlayClosing, } from '../../state-store'; +import type { ViewRef } from '../../types/react-native-compat'; type PortalWhileClosingViewProps = { /** @@ -114,7 +115,7 @@ export const PortalWhileClosingView = ({ }; const useSyncingApi = (portalHostName: string, registrationId: string) => { - const containerRef = useRef(null); + const containerRef = useRef(null); const placeholderLayout = useSharedValue({ h: 0, w: 0 }); const insets = useSafeAreaInsets(); const hasActiveId = useHasActiveId(); diff --git a/package/src/components/UIComponents/SafeAreaViewWrapper.tsx b/package/src/components/UIComponents/SafeAreaViewWrapper.tsx index ea8ccbc4b2..8f5c48b637 100644 --- a/package/src/components/UIComponents/SafeAreaViewWrapper.tsx +++ b/package/src/components/UIComponents/SafeAreaViewWrapper.tsx @@ -6,7 +6,19 @@ import { SafeAreaViewProps, } from 'react-native-safe-area-context'; -export const SafeAreaView = SafeAreaViewOriginal ?? RNFSafeAreaView; +/** + * `react-native-safe-area-context`'s `SafeAreaView`, keeping React Native's own as a defensive + * runtime fallback. + * + * The type is pinned to the safe-area-context component deliberately. A `A ?? B` expression types as + * the *union* of both components, which makes the resulting `style` prop their intersection - and + * from React Native 0.87 that intersection is unsatisfiable, because RN's own `SafeAreaView` and + * safe-area-context's now describe styles differently. That in turn breaks + * `Animated.createAnimatedComponent()` wrapping (see `ImageGalleryFooter`). The fallback is only + * ever hit when safe-area-context is missing at runtime, so pinning the type costs nothing. + */ +export const SafeAreaView = (SafeAreaViewOriginal ?? + RNFSafeAreaView) as typeof SafeAreaViewOriginal; export const SafeAreaViewWrapper = ({ children, diff --git a/package/src/components/UIComponents/SearchInput.tsx b/package/src/components/UIComponents/SearchInput.tsx index dbe4fe9f01..bfcf5ef44a 100644 --- a/package/src/components/UIComponents/SearchInput.tsx +++ b/package/src/components/UIComponents/SearchInput.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useRef, useState } from 'react'; -import { StyleSheet, TextInput, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { Pressable } from 'react-native-gesture-handler'; @@ -7,6 +7,7 @@ import { useComponentsContext } from '../../contexts/componentsContext/Component import { useTheme } from '../../contexts/themeContext/ThemeContext'; import { useTranslationContext } from '../../contexts/translationContext/TranslationContext'; import { primitives } from '../../theme'; +import type { TextInputRef } from '../../types/react-native-compat'; import type { IconRenderer } from '../ui/Button/Button'; import { Input, InputProps } from '../ui/Input/Input'; @@ -19,7 +20,7 @@ export const SearchInput = ({ onChangeText, ...props }: SearchInputProps) => { theme: { semantics }, } = useTheme(); - const inputRef = useRef(null); + const inputRef = useRef(null); const [hasText, setHasText] = useState(() => Boolean(props.value || props.defaultValue)); const handleChangeText = useCallback( diff --git a/package/src/components/UIComponents/StreamBottomSheetModalFlatList.tsx b/package/src/components/UIComponents/StreamBottomSheetModalFlatList.tsx index a4f855fa08..dc59dc0418 100644 --- a/package/src/components/UIComponents/StreamBottomSheetModalFlatList.tsx +++ b/package/src/components/UIComponents/StreamBottomSheetModalFlatList.tsx @@ -1,10 +1,12 @@ import React, { useRef } from 'react'; import { FlatListProps } from 'react-native'; + import { FlatList } from 'react-native-gesture-handler'; import { runOnJS, useAnimatedReaction } from 'react-native-reanimated'; import { useBottomSheetContext } from '../../contexts/bottomSheetContext/BottomSheetContext'; import { useStableCallback } from '../../hooks'; +import type { FlatListRef } from '../../types/react-native-compat'; type StreamBottomSheetModalFlatListProps = FlatListProps; @@ -13,7 +15,7 @@ export const StreamBottomSheetModalFlatList = ({ ...props }: StreamBottomSheetModalFlatListProps) => { const { currentSnapIndex, topSnapIndex } = useBottomSheetContext(); - const listRef = useRef>(null); + const listRef = useRef>(null); const setNativeScrollEnabled = useStableCallback((value: boolean) => listRef.current?.setNativeProps({ scrollEnabled: value }), diff --git a/package/src/components/UIComponents/SvgAwareImage.tsx b/package/src/components/UIComponents/SvgAwareImage.tsx index 7900d75c03..105ed888f9 100644 --- a/package/src/components/UIComponents/SvgAwareImage.tsx +++ b/package/src/components/UIComponents/SvgAwareImage.tsx @@ -6,7 +6,7 @@ import { SvgUri } from 'react-native-svg'; import { useIsSvg } from '../../hooks/useIsSvg'; const getSourceUri = (source: ImageProps['source']): string | undefined => { - if (!source || typeof source !== 'object' || Array.isArray(source)) { + if (!source || typeof source !== 'object' || Array.isArray(source) || !('uri' in source)) { return undefined; } return source.uri; diff --git a/package/src/components/ui/Input/Input.tsx b/package/src/components/ui/Input/Input.tsx index 9ea14177c6..80a60d8a2b 100644 --- a/package/src/components/ui/Input/Input.tsx +++ b/package/src/components/ui/Input/Input.tsx @@ -16,6 +16,7 @@ import { import { useTheme } from '../../../contexts'; import { useComponentsContext } from '../../../contexts/componentsContext/ComponentsContext'; import { primitives } from '../../../theme'; +import type { TextInputRef } from '../../../types/react-native-compat'; import { IconRenderer } from '../Button'; const inputAccessibilityStates = { @@ -51,7 +52,11 @@ export type InputProps = TextInputProps & { containerStyle?: StyleProp; }; -export const Input = React.forwardRef(function Input( +// Explicitly annotated: the inferred type of a `forwardRef` component inlines React Native's +// internal style/instance helper types, which cannot be named in the emitted declarations (TS2883). +export const Input: React.ForwardRefExoticComponent< + InputProps & React.RefAttributes +> = React.forwardRef(function Input( { title, description, diff --git a/package/src/contexts/messageContext/MessageContext.tsx b/package/src/contexts/messageContext/MessageContext.tsx index 6dcd1585e4..c225526704 100644 --- a/package/src/contexts/messageContext/MessageContext.tsx +++ b/package/src/contexts/messageContext/MessageContext.tsx @@ -1,5 +1,4 @@ import React, { PropsWithChildren, useContext } from 'react'; -import type { View } from 'react-native'; import type { Attachment, LocalMessage } from 'stream-chat'; @@ -16,6 +15,7 @@ import type { MessageContentType } from '../../contexts/messagesContext/Messages import type { DeepPartial } from '../../contexts/themeContext/ThemeContext'; import type { Theme } from '../../contexts/themeContext/utils/theme'; import type { Rect } from '../../state-store/message-overlay-store'; +import type { ViewRef } from '../../types/react-native-compat'; import type { MessageComposerAPIContextValue } from '../messageComposerContext/MessageComposerAPIContext'; import { DEFAULT_BASE_CONTEXT_VALUE } from '../utils/defaultBaseContextValue'; @@ -73,7 +73,7 @@ export type MessageContextValue = { * Ref to the view that the message context menu should align with. * Custom message renderers can attach this to a different subview if needed. */ - contextMenuAnchorRef: React.RefObject; + contextMenuAnchorRef: React.RefObject; /** * Stable UI-instance identifier for the rendered message. * Used for overlay state so two rendered instances of the same message do not collide. @@ -114,7 +114,7 @@ export type MessageContextValue = { * Registers the subtree that should be measured and portaled into the message overlay. * Custom message renderers typically interact with this via `MessageOverlayWrapper`. */ - registerMessageOverlayTarget: (params: { id: string; view: View | null }) => void; + registerMessageOverlayTarget: (params: { id: string; view: ViewRef | null }) => void; unregisterMessageOverlayTarget: (id: string) => void; reactions: ReactionSummary[]; /** Read count of the message */ diff --git a/package/src/contexts/messageInputContext/MessageInputContext.tsx b/package/src/contexts/messageInputContext/MessageInputContext.tsx index bca66a675a..7bf77ec6dd 100644 --- a/package/src/contexts/messageInputContext/MessageInputContext.tsx +++ b/package/src/contexts/messageInputContext/MessageInputContext.tsx @@ -7,7 +7,7 @@ import React, { useRef, useState, } from 'react'; -import { Alert, Linking, TextInput, TextInputProps } from 'react-native'; +import { Alert, Linking, TextInputProps } from 'react-native'; import { lookup as lookupMimeType } from 'mime-types'; import { @@ -37,6 +37,7 @@ import { import { isDocumentPickerAvailable, MediaTypes, NativeHandlers } from '../../native'; import { AudioRecorderManager } from '../../state-store/audio-recorder-manager'; import { MessageInputHeightStore } from '../../state-store/message-input-height-store'; +import { TextInputRef } from '../../types/react-native-compat'; import { File } from '../../types/types'; import { compressedImageURI } from '../../utils/compressImage'; import { useAttachmentPickerContext } from '../attachmentPickerContext/AttachmentPickerContext'; @@ -78,7 +79,7 @@ export type LocalMessageInputContext = { stopVoiceRecording: () => Promise; }; -export type InputBoxRef = TextInput & { +export type InputBoxRef = TextInputRef & { clearState: () => void; restoreState: (text: string) => void; }; @@ -202,7 +203,7 @@ export type InputMessageInputContextValue = { * * @overrideType Function */ - setInputRef?: (ref: TextInput | null) => void; + setInputRef?: (ref: TextInputRef | null) => void; showPollCreationDialog?: boolean; messageInputHeightStore: MessageInputHeightStore; }; diff --git a/package/src/contexts/themeContext/ThemeContext.tsx b/package/src/contexts/themeContext/ThemeContext.tsx index 3751aa6e2d..9115e01f80 100644 --- a/package/src/contexts/themeContext/ThemeContext.tsx +++ b/package/src/contexts/themeContext/ThemeContext.tsx @@ -16,15 +16,27 @@ export type DeepPartial = { [P in keyof T]?: DeepPartial; }; +/** + * A custom theme passed to `` / ``: either a partial + * override tree or a complete `Theme`. + * + * `Theme` is listed first deliberately. A full `Theme` *is* structurally a valid `DeepPartial`, + * but from React Native 0.87 the style types underpinning `Theme` are deep enough that asking + * TypeScript to prove it exceeds its instantiation depth limit (TS2589). Accepting `Theme` directly + * means neither the SDK nor integrators need the `as DeepPartial` cast that used to be + * required, and the cheap branch is matched first. + */ +export type ThemeStyle = Theme | DeepPartial; + export type ThemeProviderInputValue = { mergedStyle?: Theme; - style?: DeepPartial; + style?: ThemeStyle; }; export type MergedThemesParams = { - style?: DeepPartial; + style?: ThemeStyle; theme?: Theme; - scheme?: ColorSchemeName; + scheme?: ColorSchemeName | null; }; export type ThemeContextValue = { diff --git a/package/src/hooks/useAppStateListener.ts b/package/src/hooks/useAppStateListener.ts index 364a9a275f..47b74d83f1 100644 --- a/package/src/hooks/useAppStateListener.ts +++ b/package/src/hooks/useAppStateListener.ts @@ -2,7 +2,12 @@ import { useEffect, useRef } from 'react'; import { AppState, AppStateStatus } from 'react-native'; export const useAppStateListener = (onForeground?: () => void, onBackground?: () => void) => { - const appStateRef = useRef(AppState.currentState); + // React Native 0.87 widened `AppState.currentState` to `string | null | undefined` (it was + // `AppStateStatus` up to 0.86). Normalise once here so the comparisons below stay total on + // every supported version. + const appStateRef = useRef( + (AppState.currentState as AppStateStatus | null | undefined) ?? 'unknown', + ); const onForegroundRef = useRef(onForeground); const onBackgroundRef = useRef(onBackground); diff --git a/package/src/hooks/usePrunableMessageList.ts b/package/src/hooks/usePrunableMessageList.ts index 4973ec74f1..827c1068d5 100644 --- a/package/src/hooks/usePrunableMessageList.ts +++ b/package/src/hooks/usePrunableMessageList.ts @@ -1,12 +1,11 @@ import { useRef } from 'react'; -import { ViewToken } from 'react-native'; - import { Channel } from 'stream-chat'; import { useStableCallback } from './useStableCallback'; import { ChannelPropsWithContext } from '../components'; +import type { ViewToken } from '../types/react-native-compat'; export type VisibleRangeConfig = { first: number; last: number; inverted: boolean }; export type ViewabilityChangedCallbackInput = { diff --git a/package/src/index.ts b/package/src/index.ts index cb8584481d..0e23a7d668 100644 --- a/package/src/index.ts +++ b/package/src/index.ts @@ -17,6 +17,7 @@ export * from './icons'; export * from './middlewares'; +export * from './types/react-native-compat'; export * from './types/types'; export * from './utils/patchMessageTextCommand'; diff --git a/package/src/nativeMultipartUpload.ts b/package/src/nativeMultipartUpload.ts index 81164eaf20..8888bafcfb 100644 --- a/package/src/nativeMultipartUpload.ts +++ b/package/src/nativeMultipartUpload.ts @@ -250,7 +250,12 @@ export const createNativeMultipartUploader = ( return undefined; } - const multipartUploadEventEmitter = options.eventEmitter ?? new NativeEventEmitter(nativeModule); + // Annotated with the SDK's own structural emitter type: React Native 0.87 made + // `NativeEventEmitter` generic over an event -> args map, so the inferred union of + // `options.eventEmitter` and a raw `NativeEventEmitter` no longer accepts a typed listener. + const multipartUploadEventEmitter: NativeMultipartUploadEventEmitter = + options.eventEmitter ?? + (new NativeEventEmitter(nativeModule) as unknown as NativeMultipartUploadEventEmitter); return async ({ headers, diff --git a/package/src/store/SqliteClient.ts b/package/src/store/SqliteClient.ts index 22b532e971..b8b0c28f6f 100644 --- a/package/src/store/SqliteClient.ts +++ b/package/src/store/SqliteClient.ts @@ -93,7 +93,7 @@ export class SqliteClient { * LogBox turns red. */ private static recordError = (e: SqliteClientError) => { - this.logger?.('error', e.message, { tag: e.code }); + SqliteClient.logger?.('error', e.message, { tag: e.code }); throw e; }; @@ -109,10 +109,10 @@ export class SqliteClient { */ static preflightEncryption = async () => { try { - this.preflightedKey = await this.resolveEncryptionKey(); + SqliteClient.preflightedKey = await SqliteClient.resolveEncryptionKey(); } catch (e) { if (e instanceof SqliteClientError) { - this.recordError(e); + SqliteClient.recordError(e); } throw e; } @@ -125,7 +125,7 @@ export class SqliteClient { * cache of its users' messages. */ private static resolveEncryptionKey = async () => { - const { getEncryptionKey } = this; + const { getEncryptionKey } = SqliteClient; if (!getEncryptionKey) { return undefined; @@ -185,10 +185,11 @@ export class SqliteClient { 'Please install "@op-engineering/op-sqlite" package to enable offline support', ); } - const encryptionKey = this.preflightedKey ?? (await this.resolveEncryptionKey()); - this.preflightedKey = undefined; + const encryptionKey = + SqliteClient.preflightedKey ?? (await SqliteClient.resolveEncryptionKey()); + SqliteClient.preflightedKey = undefined; - this.db = sqlite.open({ + SqliteClient.db = sqlite.open({ location: SqliteClient.dbLocation, name: SqliteClient.dbName, ...(encryptionKey ? { encryptionKey } : {}), @@ -198,12 +199,12 @@ export class SqliteClient { // any pages, but rather look at a connection level flag. The first failure // is going to be whatever actually reads something, which is going to be // the user_version read in initializeDatabase. - await this.db?.execute('PRAGMA foreign_keys = ON', []); + await SqliteClient.db?.execute('PRAGMA foreign_keys = ON', []); } catch (e) { if (e instanceof SqliteClientError) { throw e; } - this.logger?.('error', `Error opening database ${SqliteClient.dbName}`, { + SqliteClient.logger?.('error', `Error opening database ${SqliteClient.dbName}`, { error: e, }); console.error(`Error opening database ${SqliteClient.dbName}: ${e}`); @@ -212,13 +213,13 @@ export class SqliteClient { static closeDB = () => { try { - if (!this.db) { + if (!SqliteClient.db) { throw new Error('DB is not open or initialized.'); } - this.db.close(); - this.db = undefined; + SqliteClient.db.close(); + SqliteClient.db = undefined; } catch (e) { - this.logger?.('error', `Error closing database ${SqliteClient.dbName}`, { + SqliteClient.logger?.('error', `Error closing database ${SqliteClient.dbName}`, { error: e, }); console.error(`Error closing database ${SqliteClient.dbName}: ${e}`); @@ -231,7 +232,7 @@ export class SqliteClient { } try { - if (!this.db) { + if (!SqliteClient.db) { throw new Error('DB is not open or initialized.'); } // This is a workaround to make the executeBatch method work. @@ -244,9 +245,9 @@ export class SqliteClient { } return query; }); - await this.db.executeBatch(finalQueries); + await SqliteClient.db.executeBatch(finalQueries); } catch (e) { - this.logger?.('error', 'SqlBatch queries failed', { + SqliteClient.logger?.('error', 'SqlBatch queries failed', { error: e, queries, }); @@ -256,14 +257,14 @@ export class SqliteClient { static executeSql = async (query: string, params?: Scalar[]) => { try { - if (!this.db) { + if (!SqliteClient.db) { throw new Error('DB is not open or initialized.'); } - const { rows } = await this.db.execute(query, params); + const { rows } = await SqliteClient.db.execute(query, params); return rows ? (rows as Record[]) : []; } catch (e) { - this.logger?.('error', 'Sql single query failed', { + SqliteClient.logger?.('error', 'Sql single query failed', { error: e, query, }); @@ -276,24 +277,24 @@ export class SqliteClient { `DROP TABLE IF EXISTS ${table}`, [], ]); - this.logger?.('info', 'Dropping tables', { + SqliteClient.logger?.('info', 'Dropping tables', { tables: Object.keys(tables), }); await SqliteClient.executeSqlBatch(queries); }; static deleteDatabase = () => { - this.logger?.('info', 'deleteDatabase', { + SqliteClient.logger?.('info', 'deleteDatabase', { dbLocation: SqliteClient.dbLocation, dbname: SqliteClient.dbName, }); try { - if (!this.db) { + if (!SqliteClient.db) { throw new Error('DB is not open or initialized.'); } - this.db.delete(); + SqliteClient.db.delete(); } catch (e) { - this.logger?.('error', 'Error deleting DB', { + SqliteClient.logger?.('error', 'Error deleting DB', { dbLocation: SqliteClient.dbLocation, dbname: SqliteClient.dbName, error: e, @@ -313,11 +314,11 @@ export class SqliteClient { static isUnreadableDbError = (e: unknown) => { const message = String((e as Error)?.message ?? e); - if (this.TRANSIENT_ERROR.test(message)) { + if (SqliteClient.TRANSIENT_ERROR.test(message)) { return false; } - return this.UNREADABLE_ERROR.test(message); + return SqliteClient.UNREADABLE_ERROR.test(message); }; static initializeDatabase = async (): Promise => { @@ -347,11 +348,11 @@ export class SqliteClient { return true; } catch (e) { if (e instanceof SqliteClientError) { - this.recordError(e); + SqliteClient.recordError(e); } - if (this.isUnreadableDbError(e)) { - this.recordError( + if (SqliteClient.isUnreadableDbError(e)) { + SqliteClient.recordError( new SqliteClientError( 'OFFLINE_DB_UNREADABLE', 'The offline database exists but could not be read. Usually the encryption ' + @@ -365,7 +366,7 @@ export class SqliteClient { } console.log('Error initializing DB', e); - this.logger?.('error', 'Error initializing DB', { + SqliteClient.logger?.('error', 'Error initializing DB', { dbLocation: SqliteClient.dbLocation, dbname: SqliteClient.dbName, error: e, @@ -377,20 +378,20 @@ export class SqliteClient { static updateUserPragmaVersion = async (version: number) => { SqliteClient.logger?.('info', `updateUserPragmaVersion to ${version}`); - if (!this.db) { + if (!SqliteClient.db) { throw new Error('DB is not open or initialized.'); } - await this.db.execute(`PRAGMA user_version = ${version}`, []); + await SqliteClient.db.execute(`PRAGMA user_version = ${version}`, []); }; static getUserPragmaVersion = async () => { try { - if (!this.db) { + if (!SqliteClient.db) { throw new Error('DB is not open or initialized.'); } - const { rows } = await this.db.execute('PRAGMA user_version', []); + const { rows } = await SqliteClient.db.execute('PRAGMA user_version', []); const result = rows ? rows : []; - this.logger?.('info', 'getUserPragmaVersion', { + SqliteClient.logger?.('info', 'getUserPragmaVersion', { result, }); return result[0].user_version as number; @@ -401,8 +402,8 @@ export class SqliteClient { }; static resetDB = async () => { - this.logger?.('info', 'resetDB'); - if (this.db) { + SqliteClient.logger?.('info', 'resetDB'); + if (SqliteClient.db) { await SqliteClient.dropTables(); SqliteClient.closeDB(); } diff --git a/package/src/types/react-native-compat.ts b/package/src/types/react-native-compat.ts new file mode 100644 index 0000000000..c4d58c20bf --- /dev/null +++ b/package/src/types/react-native-compat.ts @@ -0,0 +1,83 @@ +import type * as React from 'react'; +import type { + FlatList, + KeyboardEvent, + ScrollView, + SectionList, + Text, + TextInput, + View, +} from 'react-native'; + +/** + * Portable React Native ref (host instance) types. + * + * React Native 0.87 made the Strict TypeScript API the default. Under it the core components are + * function components rather than classes, so a component type is no longer usable as its own ref + * type - `useRef(null)` no longer type checks against ``. React Native now + * publishes dedicated instance types (`ViewInstance`, `TextInputInstance`, ...) instead. + * + * Those `*Instance` types do not exist on React Native < 0.87 and this SDK supports `>=0.76`, so we + * cannot import them directly. `React.ComponentRef` resolves to the correct instance type + * on *both* type generations - it is exactly how React Native itself defines `ViewInstance` and + * friends internally - so it is the portable spelling and the one to use throughout the SDK. + * + * When the SDK's minimum supported React Native reaches 0.87, these aliases can be swapped for the + * upstream `*Instance` types in a single edit. + */ + +export type ViewRef = React.ComponentRef; +export type TextRef = React.ComponentRef; +export type TextInputRef = React.ComponentRef; +export type ScrollViewRef = React.ComponentRef; +export type FlatListRef = React.ComponentRef>; +export type SectionListRef = React.ComponentRef>; + +/** + * `KeyboardEventListener` was dropped from React Native's root type exports in 0.87. It was only ever + * an alias for a handler taking a `KeyboardEvent`, so we re-declare it here rather than deep importing. + */ +export type KeyboardEventListener = (event: KeyboardEvent) => void; + +/** + * `ViewToken` and `ViewabilityConfig` are no longer exported from the `react-native` root under the + * Strict TypeScript API - `ViewToken` moved to `@react-native/virtualized-lists` (re-exported from + * the root as `ListViewToken`) and `ViewabilityConfig` is no longer re-exported at all. Both appear + * in this SDK's public message list surface, and neither spelling exists across the whole `>=0.76` + * range, so we declare them structurally. The shapes are stable and shared by `FlatList` and + * `@shopify/flash-list`. + * + * `index` is deliberately widened to `number | null | undefined`: React Native typed it `number | null` + * up to 0.86 and `number | undefined` from 0.87, and as consumers of the callback we must accept + * whichever the running version hands us. + */ +export type ViewToken = { + index: number | null | undefined; + isViewable: boolean; + item: ItemT; + key: string; + section?: unknown; +}; + +export type ViewabilityConfig = { + /** + * Minimum amount of time (in milliseconds) that an item must be physically viewable before the + * viewability callback will be fired. + */ + minimumViewTime?: number | undefined; + /** + * Percent of viewport that must be covered for a partially occluded item to count as "viewable", + * 0-100. Fully visible items are always considered viewable. + */ + viewAreaCoveragePercentThreshold?: number | undefined; + /** + * Similar to `viewAreaCoveragePercentThreshold`, but considers the percent of the item that is + * visible rather than the fraction of the viewable area it covers. + */ + itemVisiblePercentThreshold?: number | undefined; + /** + * Nothing is considered viewable until the user scrolls or `recordInteraction` is called after + * render. + */ + waitForInteraction?: boolean | undefined; +}; diff --git a/yarn.lock b/yarn.lock index 22e9fc5f9f..42801841f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3950,6 +3950,13 @@ __metadata: languageName: node linkType: hard +"@nodable/entities@npm:^3.0.0": + version: 3.0.0 + resolution: "@nodable/entities@npm:3.0.0" + checksum: 10c0/5e57422ce08d9f83a7ad09d8f90953e2e63b3274c3f941bf7ddf64f290473e70d47acbd6c8b9e60169c2a8c5c2424c4131bdd99b937792413f55a4a146f76658 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -4319,6 +4326,20 @@ __metadata: languageName: node linkType: hard +"@op-engineering/op-sqlite@npm:^18.1.0": + version: 18.1.1 + resolution: "@op-engineering/op-sqlite@npm:18.1.1" + peerDependencies: + "@sqlite.org/sqlite-wasm": "*" + react: "*" + react-native: "*" + peerDependenciesMeta: + "@sqlite.org/sqlite-wasm": + optional: true + checksum: 10c0/b40e3886f4a44cf78e7e3d05b9dc2389bb847da9d783bf5921495c0f129ac8f11c671495b20344cf327ee01a985532938e94faa3458a104f778a9e0171a4f607 + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -4878,65 +4899,65 @@ __metadata: languageName: node linkType: hard -"@react-native-community/cli-clean@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-clean@npm:20.1.0" +"@react-native-community/cli-clean@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-clean@npm:20.2.0" dependencies: - "@react-native-community/cli-tools": "npm:20.1.0" + "@react-native-community/cli-tools": "npm:20.2.0" execa: "npm:^5.0.0" fast-glob: "npm:^3.3.2" picocolors: "npm:^1.1.1" - checksum: 10c0/5d7fce73545154035e55801e9596b634c85bbe7bb04861f3d0787f2a9588b7b6f927709bfce4e45a8d62da03e5a92789281e07dafd29e2db86ca52ca3e5ad49c + checksum: 10c0/217c0a17e69892d6042840a66bf179dfbb3b6b4dcf6e1f0401574e7eedbcb31c4ed92f6b052cf0ffe6ce6963b5af3d68bfba6a699c9837fe7a744a45a05dd622 languageName: node linkType: hard -"@react-native-community/cli-config-android@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-config-android@npm:20.1.0" +"@react-native-community/cli-config-android@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-config-android@npm:20.2.0" dependencies: - "@react-native-community/cli-tools": "npm:20.1.0" + "@react-native-community/cli-tools": "npm:20.2.0" fast-glob: "npm:^3.3.2" - fast-xml-parser: "npm:^4.4.1" + fast-xml-parser: "npm:^5.3.6" picocolors: "npm:^1.1.1" - checksum: 10c0/66e95adf2fb9750a12592a146b034ab4b8ea15aa41efb5d497d6be2f08029a59a0be2450e97de0bf59aa6b66778b3af0bbd693a42b7e7f9f60c9d9b5f966aadc + checksum: 10c0/86dbdaf9a038a8e9f0a2a1f149e2f68eaa15379ad4119b3b4278d590f301a2e9314d2c449921abbfcd7fa174e5fb62d0f6971e4f40c285b49abc787d62cbf0ba languageName: node linkType: hard -"@react-native-community/cli-config-apple@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-config-apple@npm:20.1.0" +"@react-native-community/cli-config-apple@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-config-apple@npm:20.2.0" dependencies: - "@react-native-community/cli-tools": "npm:20.1.0" + "@react-native-community/cli-tools": "npm:20.2.0" execa: "npm:^5.0.0" fast-glob: "npm:^3.3.2" picocolors: "npm:^1.1.1" - checksum: 10c0/2c7b7116594c7b0035ef7e41e0219fcff664c3883471460ae48f19dd88ec9a26dd5b4bd60e33c054603b3a10efd78570490bc84599ad987e4ffb274b05512351 + checksum: 10c0/193b3c55cb824e4ea86bdac596dfc1d13f8c9be894ba4b2770242e012ea7eb1f9dc56b232341ec988f7c5a3acbe24f3ec138c61769bd01e07e6cd14b8ef0b095 languageName: node linkType: hard -"@react-native-community/cli-config@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-config@npm:20.1.0" +"@react-native-community/cli-config@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-config@npm:20.2.0" dependencies: - "@react-native-community/cli-tools": "npm:20.1.0" + "@react-native-community/cli-tools": "npm:20.2.0" cosmiconfig: "npm:^9.0.0" deepmerge: "npm:^4.3.0" fast-glob: "npm:^3.3.2" joi: "npm:^17.2.1" picocolors: "npm:^1.1.1" - checksum: 10c0/e3d863ffc5d18aa7b7406908c1360de7ea7a1bab32897e34d0afe184f4682bb5d110d4db0c6ba2c35c4f1330ca4a281b8fc3548e53a2978b8b07ca3beca5765c + checksum: 10c0/bba382952b4ece338746ee8a5e415e67f438d5fb89ef89bd9b7146f564d9e438643a97b8ba5d36461af3a2b036a33a9d168605ecb2f42db4503fb4c7eb650dd8 languageName: node linkType: hard -"@react-native-community/cli-doctor@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-doctor@npm:20.1.0" +"@react-native-community/cli-doctor@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-doctor@npm:20.2.0" dependencies: - "@react-native-community/cli-config": "npm:20.1.0" - "@react-native-community/cli-platform-android": "npm:20.1.0" - "@react-native-community/cli-platform-apple": "npm:20.1.0" - "@react-native-community/cli-platform-ios": "npm:20.1.0" - "@react-native-community/cli-tools": "npm:20.1.0" + "@react-native-community/cli-config": "npm:20.2.0" + "@react-native-community/cli-platform-android": "npm:20.2.0" + "@react-native-community/cli-platform-apple": "npm:20.2.0" + "@react-native-community/cli-platform-ios": "npm:20.2.0" + "@react-native-community/cli-tools": "npm:20.2.0" command-exists: "npm:^1.2.8" deepmerge: "npm:^4.3.0" envinfo: "npm:^7.13.0" @@ -4947,51 +4968,51 @@ __metadata: semver: "npm:^7.5.2" wcwidth: "npm:^1.0.1" yaml: "npm:^2.2.1" - checksum: 10c0/79762f3e789fe98f21c6f500f6c65df399508979f05174272652a9422295f4dace099e87575776bf209b533a6667070fc355c5b510e14a8a61b31a9a8af7b3be + checksum: 10c0/159e20b869ae7bc568f07f6d8c2e9a0559883a3f12b2f1ad5e5cc3c92e6138593d09a84dd894eeff3b2590cd8e8e9f1628c76e4ba0616afa61b4eb8827cfde40 languageName: node linkType: hard -"@react-native-community/cli-platform-android@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-platform-android@npm:20.1.0" +"@react-native-community/cli-platform-android@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-platform-android@npm:20.2.0" dependencies: - "@react-native-community/cli-config-android": "npm:20.1.0" - "@react-native-community/cli-tools": "npm:20.1.0" + "@react-native-community/cli-config-android": "npm:20.2.0" + "@react-native-community/cli-tools": "npm:20.2.0" execa: "npm:^5.0.0" logkitty: "npm:^0.7.1" picocolors: "npm:^1.1.1" - checksum: 10c0/8b44178a78245085824c3fda9d6753d03cd817772fdde82b1d4f1a89f177f10339822401c0b8ef17e8353f3168c3971ae4cfb0a158cef03c22e55f5dff66179b + checksum: 10c0/dd586892129b54ab6cd89fbfe9759ae00f93b89b802f50f178679186e50a99d2ad7642a6ea30c16df72a36452cf95b4abcaac473f97dc65a93d6b0cb1b3af247 languageName: node linkType: hard -"@react-native-community/cli-platform-apple@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-platform-apple@npm:20.1.0" +"@react-native-community/cli-platform-apple@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-platform-apple@npm:20.2.0" dependencies: - "@react-native-community/cli-config-apple": "npm:20.1.0" - "@react-native-community/cli-tools": "npm:20.1.0" + "@react-native-community/cli-config-apple": "npm:20.2.0" + "@react-native-community/cli-tools": "npm:20.2.0" execa: "npm:^5.0.0" - fast-xml-parser: "npm:^4.4.1" + fast-xml-parser: "npm:^5.3.6" picocolors: "npm:^1.1.1" - checksum: 10c0/2218e172bf13c0bb027f7f2a036a0515f8fe023d98fc65d22d0dab601036c5b461b1af7a5d826df2dd3e86a3894f9531d43e8861746a071ee22c0b33b8e47c29 + checksum: 10c0/17c6ec29865a4bdbd31f9547c5857ed5cbed4fb5e2ef6a083e6915c684810006f0297ac36a35228736bdda108ece3a2269833dcbc487e39ef9054c28d3b9d1a8 languageName: node linkType: hard -"@react-native-community/cli-platform-ios@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-platform-ios@npm:20.1.0" +"@react-native-community/cli-platform-ios@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-platform-ios@npm:20.2.0" dependencies: - "@react-native-community/cli-platform-apple": "npm:20.1.0" - checksum: 10c0/f5eef9dfbe3dad7dc69f4b3a37ebee9d124f3a649071e0275624f88468a7f506b622646dcbd61ba0435cc35e705badfc287690d6b7c3867c0a86c9a45d4f463e + "@react-native-community/cli-platform-apple": "npm:20.2.0" + checksum: 10c0/d78149cfee7c32c9c9048a8c06c5e8a06fd6259046d5ee95f1993c8d343afa463afac92edba3c9f109ff8a6deb6e8a4bc5fef5eb6c22ba9aed3fef4b1ee20eb6 languageName: node linkType: hard -"@react-native-community/cli-server-api@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-server-api@npm:20.1.0" +"@react-native-community/cli-server-api@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-server-api@npm:20.2.0" dependencies: - "@react-native-community/cli-tools": "npm:20.1.0" - body-parser: "npm:^1.20.3" + "@react-native-community/cli-tools": "npm:20.2.0" + body-parser: "npm:^2.2.2" compression: "npm:^1.7.1" connect: "npm:^3.6.5" errorhandler: "npm:^1.5.1" @@ -5000,13 +5021,13 @@ __metadata: pretty-format: "npm:^29.7.0" serve-static: "npm:^1.13.1" ws: "npm:^6.2.3" - checksum: 10c0/d83c9bbff36fb84201478ed8efc17c80f1474cb0346a2577eee7299407e9e34892c213e2316062a2c59d47ebfe9f0c8215b82cd1abb05c85fe7702a94a52cdf5 + checksum: 10c0/c17d8580faf7aa5758e51fbd7bb57325dfb242cc5a26e98658fedb675f26801c4233c43e1e7aefc04da0afbf5188a9ccb6a69b48cc3cb96588b73573b1e355e0 languageName: node linkType: hard -"@react-native-community/cli-tools@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-tools@npm:20.1.0" +"@react-native-community/cli-tools@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-tools@npm:20.2.0" dependencies: "@vscode/sudo-prompt": "npm:^9.0.0" appdirsjs: "npm:^1.2.4" @@ -5018,29 +5039,29 @@ __metadata: picocolors: "npm:^1.1.1" prompts: "npm:^2.4.2" semver: "npm:^7.5.2" - checksum: 10c0/993f5dfbc1ae6301e616269896b2e5ecc29888cc8a12325881148b546b07d45572bd065e392ceaad32f86067e31b0eabedbe71fac3b1b820cd489ab86e56094d + checksum: 10c0/3c5056b6d6a7a967e44b55715c13b2a8de57c58cd1034eaa0cffacf99dc68cb2a903417ed2fe00d36bc8c9c25daa87d5aa874104cc0f7766a41c155af3662296 languageName: node linkType: hard -"@react-native-community/cli-types@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli-types@npm:20.1.0" +"@react-native-community/cli-types@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli-types@npm:20.2.0" dependencies: joi: "npm:^17.2.1" - checksum: 10c0/247deebf26b435c0cbabfb7dd7de09a4e79c544ee72a2ef2817268108f7c085dab4d54c7fbc8111ad89360e13227f2041c5eac208971c251da332e4d33311a7b + checksum: 10c0/8070361fa078b1d26b0a38238fe13616d5b21e298f544f0929f77c3539dcce93dfb23553662be906c6cfe2a750d3707b6b52bd3724c03eb454fbbd419aec80da languageName: node linkType: hard -"@react-native-community/cli@npm:20.1.0": - version: 20.1.0 - resolution: "@react-native-community/cli@npm:20.1.0" +"@react-native-community/cli@npm:20.2.0": + version: 20.2.0 + resolution: "@react-native-community/cli@npm:20.2.0" dependencies: - "@react-native-community/cli-clean": "npm:20.1.0" - "@react-native-community/cli-config": "npm:20.1.0" - "@react-native-community/cli-doctor": "npm:20.1.0" - "@react-native-community/cli-server-api": "npm:20.1.0" - "@react-native-community/cli-tools": "npm:20.1.0" - "@react-native-community/cli-types": "npm:20.1.0" + "@react-native-community/cli-clean": "npm:20.2.0" + "@react-native-community/cli-config": "npm:20.2.0" + "@react-native-community/cli-doctor": "npm:20.2.0" + "@react-native-community/cli-server-api": "npm:20.2.0" + "@react-native-community/cli-tools": "npm:20.2.0" + "@react-native-community/cli-types": "npm:20.2.0" commander: "npm:^9.4.1" deepmerge: "npm:^4.3.0" execa: "npm:^5.0.0" @@ -5052,7 +5073,7 @@ __metadata: semver: "npm:^7.5.2" bin: rnc-cli: build/bin.js - checksum: 10c0/f3fa43fc8c170ba291ae81e796f4ac35956e143bf1094f15f91061b93835ed0665749ca11da91df7fbfcdd96ee32fc16da6cab60adef1a21022195aa1947bd5d + checksum: 10c0/652e0bf9c746cef9b3126ebcf46a4a537fa98b9c67e4b8a1be2ec428197fe8aecb97a3c106e61faa7abdd04ad1860396ae3b48da7fc5373ed153516c6668bc74 languageName: node linkType: hard @@ -5132,6 +5153,13 @@ __metadata: languageName: node linkType: hard +"@react-native/asset-utils@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/asset-utils@npm:0.87.0" + checksum: 10c0/37b439b5fd72bd05702a48907e05486593d6b86197191fb29c8eb9586c0738e5f3db1c74ddc228bc2fa9c2dcb9c905bbc09647461b2de59c2e375017d91e720b + languageName: node + linkType: hard + "@react-native/assets-registry@npm:0.86.0": version: 0.86.0 resolution: "@react-native/assets-registry@npm:0.86.0" @@ -5139,13 +5167,6 @@ __metadata: languageName: node linkType: hard -"@react-native/assets-registry@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/assets-registry@npm:0.86.2" - checksum: 10c0/e063219dd8548240a278138a595f758a6a140cafe1493631a3d12c3da673aaf6b7d6c2c7115000b13d93c88145ec54b9be509aa683846554273c01bc4a8759c7 - languageName: node - linkType: hard - "@react-native/babel-plugin-codegen@npm:0.86.0": version: 0.86.0 resolution: "@react-native/babel-plugin-codegen@npm:0.86.0" @@ -5156,19 +5177,19 @@ __metadata: languageName: node linkType: hard -"@react-native/babel-plugin-codegen@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/babel-plugin-codegen@npm:0.86.2" +"@react-native/babel-plugin-codegen@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/babel-plugin-codegen@npm:0.87.0" dependencies: "@babel/traverse": "npm:^7.29.0" - "@react-native/codegen": "npm:0.86.2" - checksum: 10c0/ea2faab711f07578f0d0b1a62ccc731a60fdb21b0bf3924209fa055d62f597d6104f6646b5cbf416da4dc1a012f5c708e7d58694ddfafab9325dd60b0bf495f2 + "@react-native/codegen": "npm:0.87.0" + checksum: 10c0/76d094cc72c9f7db533a82213fd6b9ea5eead5d9afe9c03071934c40240bca4ebaab12b2fef0fcd1025e77eb8c5a1656adf39954a6dbca0ce92ebabd408b73da languageName: node linkType: hard -"@react-native/babel-preset@npm:0.86.0": - version: 0.86.0 - resolution: "@react-native/babel-preset@npm:0.86.0" +"@react-native/babel-preset@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/babel-preset@npm:0.87.0" dependencies: "@babel/core": "npm:^7.25.2" "@babel/plugin-proposal-export-default-from": "npm:^7.24.7" @@ -5199,56 +5220,13 @@ __metadata: "@babel/plugin-transform-runtime": "npm:^7.24.7" "@babel/plugin-transform-typescript": "npm:^7.25.2" "@babel/plugin-transform-unicode-regex": "npm:^7.24.7" - "@react-native/babel-plugin-codegen": "npm:0.86.0" - babel-plugin-syntax-hermes-parser: "npm:0.36.0" + "@react-native/babel-plugin-codegen": "npm:0.87.0" + babel-plugin-syntax-hermes-parser: "npm:0.36.1" babel-plugin-transform-flow-enums: "npm:^0.0.2" react-refresh: "npm:^0.14.0" peerDependencies: "@babel/core": "*" - checksum: 10c0/cf86875fa72d9bdd0b6ac3819b7a683764f154ff8df5144cce9cc60dd734d9d100bde563acbb48104e0033650dcc28d968eb985ff59e47349dc6861169df02f6 - languageName: node - linkType: hard - -"@react-native/babel-preset@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/babel-preset@npm:0.86.2" - dependencies: - "@babel/core": "npm:^7.25.2" - "@babel/plugin-proposal-export-default-from": "npm:^7.24.7" - "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" - "@babel/plugin-syntax-export-default-from": "npm:^7.24.7" - "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" - "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" - "@babel/plugin-transform-async-generator-functions": "npm:^7.25.4" - "@babel/plugin-transform-async-to-generator": "npm:^7.24.7" - "@babel/plugin-transform-block-scoping": "npm:^7.25.0" - "@babel/plugin-transform-class-properties": "npm:^7.25.4" - "@babel/plugin-transform-classes": "npm:^7.25.4" - "@babel/plugin-transform-destructuring": "npm:^7.24.8" - "@babel/plugin-transform-flow-strip-types": "npm:^7.25.2" - "@babel/plugin-transform-for-of": "npm:^7.24.7" - "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" - "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7" - "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7" - "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7" - "@babel/plugin-transform-optional-chaining": "npm:^7.24.8" - "@babel/plugin-transform-private-methods": "npm:^7.24.7" - "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7" - "@babel/plugin-transform-react-display-name": "npm:^7.24.7" - "@babel/plugin-transform-react-jsx": "npm:^7.25.2" - "@babel/plugin-transform-react-jsx-self": "npm:^7.24.7" - "@babel/plugin-transform-react-jsx-source": "npm:^7.24.7" - "@babel/plugin-transform-regenerator": "npm:^7.24.7" - "@babel/plugin-transform-runtime": "npm:^7.24.7" - "@babel/plugin-transform-typescript": "npm:^7.25.2" - "@babel/plugin-transform-unicode-regex": "npm:^7.24.7" - "@react-native/babel-plugin-codegen": "npm:0.86.2" - babel-plugin-syntax-hermes-parser: "npm:0.36.0" - babel-plugin-transform-flow-enums: "npm:^0.0.2" - react-refresh: "npm:^0.14.0" - peerDependencies: - "@babel/core": "*" - checksum: 10c0/12358b3898771cf7340c3fa0199e3608b9aca03fd33d6f10af59d0acef65181d9893acab51c068adc816231df9e5baff360b042963e633fc5f24a73faf6387b2 + checksum: 10c0/9599f7e34143af8026031f7769183b90d2f749bb37294fc62a8631edd5849eaa030bf71a26c6cd83961ad69c55caa4175a74019ace324babe95f44bc94638247 languageName: node linkType: hard @@ -5269,20 +5247,20 @@ __metadata: languageName: node linkType: hard -"@react-native/codegen@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/codegen@npm:0.86.2" +"@react-native/codegen@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/codegen@npm:0.87.0" dependencies: "@babel/core": "npm:^7.25.2" "@babel/parser": "npm:^7.29.0" - hermes-parser: "npm:0.36.0" + hermes-parser: "npm:0.36.1" invariant: "npm:^2.2.4" nullthrows: "npm:^1.1.1" tinyglobby: "npm:^0.2.15" yargs: "npm:^17.6.2" peerDependencies: "@babel/core": "*" - checksum: 10c0/3b36b5d99a525195e61c1197d88118c774654de6e644a677b29461bd2b28718a751f1c45ee25efa6b274f27d36efb096a3ec816c5fba1cda1a5f5843e4239a0a + checksum: 10c0/b6eca6502b9b77eea367318ee2ae38c9643b76c20d4d1e6be97d88bde5ffb4a7caec61489042e280f65f3d15c3f7a382c0810d96063f1e6e0eefb7fdd1d09217 languageName: node linkType: hard @@ -5309,26 +5287,25 @@ __metadata: languageName: node linkType: hard -"@react-native/community-cli-plugin@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/community-cli-plugin@npm:0.86.2" +"@react-native/community-cli-plugin@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/community-cli-plugin@npm:0.87.0" dependencies: - "@react-native/dev-middleware": "npm:0.86.2" + "@react-native/asset-utils": "npm:0.87.0" + "@react-native/dev-middleware": "npm:0.87.0" debug: "npm:^4.4.0" invariant: "npm:^2.2.4" - metro: "npm:^0.84.3" - metro-config: "npm:^0.84.3" - metro-core: "npm:^0.84.3" + metro: "npm:^0.87.0" semver: "npm:^7.1.3" peerDependencies: "@react-native-community/cli": "*" - "@react-native/metro-config": 0.86.2 + "@react-native/metro-config": 0.87.0 peerDependenciesMeta: "@react-native-community/cli": optional: true "@react-native/metro-config": optional: true - checksum: 10c0/f86e5fbe759b3538538c5534e78bc262d549fcb4a6e5066b71e512c9d326e787ebc8050d977f768c39671a1e995aa9eebab642bc51cbfc96dfcfac1127e329e6 + checksum: 10c0/548eaacde40a590710682983feb929408b6266a3650083e372666bd8e5da18d10717f2c5be0769ceac780db073d2a8eba3f351ed5e8335f403e6ee79a77eed2b languageName: node linkType: hard @@ -5339,10 +5316,10 @@ __metadata: languageName: node linkType: hard -"@react-native/debugger-frontend@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/debugger-frontend@npm:0.86.2" - checksum: 10c0/fa3e9c514f7748cc154455fa699351824bde2098bed6c0772892dbbdb91ed3f5d3bbdfbc7fa8ecb17914fbeac05870b3ca462ab33bc5064ecf0539da7a2ed003 +"@react-native/debugger-frontend@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/debugger-frontend@npm:0.87.0" + checksum: 10c0/a631ec47c18db292bfd8dde5ead36e9b1a6de7ee885185dea66c043ae750cb82936cce5dae70c30a7eef527ab98f0ce335abe232435829c7a678cf444cf2ae48 languageName: node linkType: hard @@ -5357,14 +5334,14 @@ __metadata: languageName: node linkType: hard -"@react-native/debugger-shell@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/debugger-shell@npm:0.86.2" +"@react-native/debugger-shell@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/debugger-shell@npm:0.87.0" dependencies: cross-spawn: "npm:^7.0.6" debug: "npm:^4.4.0" fb-dotslash: "npm:0.5.8" - checksum: 10c0/8aec11d0ccf8c30d968173d5ff024be1b854f600acc48c3f96f1743bfaad708e3da1a49ed672c63fed21c58017dcc4c442b5c456e7e212149295be33bfcf6e9b + checksum: 10c0/3eb08b5dc26d95dd25499ad90a6c79b24442a3216a92609bbde876a165e6fc4e89b1b76b1359504613808b8ce143b1e44f93318becd8c3df0a6737af35228c98 languageName: node linkType: hard @@ -5388,13 +5365,13 @@ __metadata: languageName: node linkType: hard -"@react-native/dev-middleware@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/dev-middleware@npm:0.86.2" +"@react-native/dev-middleware@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/dev-middleware@npm:0.87.0" dependencies: "@isaacs/ttlcache": "npm:^1.4.1" - "@react-native/debugger-frontend": "npm:0.86.2" - "@react-native/debugger-shell": "npm:0.86.2" + "@react-native/debugger-frontend": "npm:0.87.0" + "@react-native/debugger-shell": "npm:0.87.0" chrome-launcher: "npm:^0.15.2" chromium-edge-launcher: "npm:^0.3.0" connect: "npm:^3.6.5" @@ -5404,7 +5381,7 @@ __metadata: open: "npm:^7.0.3" serve-static: "npm:^1.16.2" ws: "npm:^7.5.10" - checksum: 10c0/4e61e17f1cf581b15d570a82401b0c324e6e93ae26661e5dba40579353af122d22dd6d9b4bffefeb5ac4858866bf6a1974b4322f6a560129a6bffb55377bf6c1 + checksum: 10c0/10ada5940b3b138f7e18c9615a5d8f829f9f2211ad30daba2417fac109b312f2a8eb8c5ebc8c2f582f8fe800870ab11168e511babf88d1f29984c4b5149b0f71 languageName: node linkType: hard @@ -5415,25 +5392,25 @@ __metadata: languageName: node linkType: hard -"@react-native/gradle-plugin@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/gradle-plugin@npm:0.86.2" - checksum: 10c0/1c6409d79fddb48d7ff4bc2c59b645e65cfa1a9aa1ecc12861d96af9b4cc5e77b24c6daceb2269461059960617832261761538ab4e3af8b480d29750b2b0fe10 +"@react-native/gradle-plugin@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/gradle-plugin@npm:0.87.0" + checksum: 10c0/1a9274ac000587336854f20145d1e2b91d092936a77d7f162da149cdf09329364998714b74324a04c22d975780849aeef092f7ccada198eac5d69a70e90552d3 languageName: node linkType: hard -"@react-native/jest-preset@npm:0.86.0": - version: 0.86.0 - resolution: "@react-native/jest-preset@npm:0.86.0" +"@react-native/jest-preset@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/jest-preset@npm:0.87.0" dependencies: "@jest/create-cache-key-function": "npm:^29.7.0" - "@react-native/js-polyfills": "npm:0.86.0" + "@react-native/js-polyfills": "npm:0.87.0" babel-jest: "npm:^29.7.0" jest-environment-node: "npm:^29.7.0" regenerator-runtime: "npm:^0.13.2" peerDependencies: react: ^19.2.3 - checksum: 10c0/34f6ffe9c6cedbb23eaeffa801a3dfea75034740dca2c7685aa1bbef42a5f12aa735c17e041142a4551176410b641c35656861c94827e73341a39d40b20b5fbb + checksum: 10c0/22e86f6c415247d982302295a350e40e9b1c2c53cf72519afa98c94bd989632eff2cf6b98d0e87024fec4ccefcc7194c937510bfe271fafc15ca58d84aed0d7b languageName: node linkType: hard @@ -5444,36 +5421,36 @@ __metadata: languageName: node linkType: hard -"@react-native/js-polyfills@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/js-polyfills@npm:0.86.2" - checksum: 10c0/184c7d2815f79b55bd960f2f76919841a2e4959d9957b159f4d364319e225bf41fdf5eb14c533cafce0cba2e2e47a8ad734e99d82eba7effeca3ccd1282f1f49 +"@react-native/js-polyfills@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/js-polyfills@npm:0.87.0" + checksum: 10c0/aed86f4975323f9622154a79e113d7f722322124d366739b62db0dd6589b45d8c8601384cf0ad29bd775524a67b1c179305aa015184d1fc2f7e8bf5e36b4014d languageName: node linkType: hard -"@react-native/metro-babel-transformer@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/metro-babel-transformer@npm:0.86.2" +"@react-native/metro-babel-transformer@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/metro-babel-transformer@npm:0.87.0" dependencies: "@babel/core": "npm:^7.25.2" - "@react-native/babel-preset": "npm:0.86.2" - hermes-parser: "npm:0.36.0" + "@react-native/babel-preset": "npm:0.87.0" + hermes-parser: "npm:0.36.1" nullthrows: "npm:^1.1.1" peerDependencies: "@babel/core": "*" - checksum: 10c0/c4841accee16c5cf0054462bc6d11d94cb327192e11934b153f28feb980ebe2079167058805621593b0dd61f0fe8f17cac3340db4b8644797a15893210c0bf7d + checksum: 10c0/ee8661b5b321f9df62e7396ab250572e5e378c7a709fd9b5c352609e586b71582a739f0ea29973b73e49e22b3eb340d2fbd3f0595a18d271cdc8cb0db26a7f22 languageName: node linkType: hard -"@react-native/metro-config@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/metro-config@npm:0.86.2" +"@react-native/metro-config@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/metro-config@npm:0.87.0" dependencies: - "@react-native/js-polyfills": "npm:0.86.2" - "@react-native/metro-babel-transformer": "npm:0.86.2" - metro-config: "npm:^0.84.3" - metro-runtime: "npm:^0.84.3" - checksum: 10c0/5e7408301b9a06f9da34598d0fd355ef5bf610721113c3e3d7ee8ff2a9a92ce4218bcda8302b7d3d722e1ae6f4e1db906703fb8ce08466b4b89bdd2fdd4fc111 + "@react-native/js-polyfills": "npm:0.87.0" + "@react-native/metro-babel-transformer": "npm:0.87.0" + metro-config: "npm:^0.87.0" + metro-runtime: "npm:^0.87.0" + checksum: 10c0/66cd5766f1abd7895ee7b18ae88eeed32c6353f13787490880e3d2d88212cb78160c372e8292a204307e6af1785d85b098a98f69dc8a91cf92aa03642b0217db languageName: node linkType: hard @@ -5484,10 +5461,10 @@ __metadata: languageName: node linkType: hard -"@react-native/normalize-colors@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/normalize-colors@npm:0.86.2" - checksum: 10c0/40abd68911f0692cfb6548560bacb48c417556df119e98f06e89ccf1ef29e98d2087af5c958cc052dc9a20bf0da69366779b4b626e56f16a945a3bb41a9ccfbe +"@react-native/normalize-colors@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/normalize-colors@npm:0.87.0" + checksum: 10c0/85c4ac262f2c1429b105c6f598614665732e24c9b18268acac7557a225c617ea2f3eff35512530af8e403074400b0a9126a09de447a3e9e680e758392099dbe7 languageName: node linkType: hard @@ -5498,10 +5475,10 @@ __metadata: languageName: node linkType: hard -"@react-native/typescript-config@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/typescript-config@npm:0.86.2" - checksum: 10c0/473908af62140c9fecb13ea41a6d3f9ae4296cf6d4f481964aebac56f484b56500e07462e2940585001d4dd0fadb102d4c4de871d57e8b981ffffc2d4a83fddb +"@react-native/typescript-config@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/typescript-config@npm:0.87.0" + checksum: 10c0/0d5836325cc5af58206d093feeb8c28e302ba782ac275b9a8cd1c7f872e09555e660a036539e12e61428c501ada4747122a7a19ff927e93398d0683c3aeb928f languageName: node linkType: hard @@ -5522,20 +5499,20 @@ __metadata: languageName: node linkType: hard -"@react-native/virtualized-lists@npm:0.86.2": - version: 0.86.2 - resolution: "@react-native/virtualized-lists@npm:0.86.2" +"@react-native/virtualized-lists@npm:0.87.0": + version: 0.87.0 + resolution: "@react-native/virtualized-lists@npm:0.87.0" dependencies: invariant: "npm:^2.2.4" nullthrows: "npm:^1.1.1" peerDependencies: "@types/react": ^19.2.0 react: "*" - react-native: 0.86.2 + react-native: 0.87.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/ed5625dae2929c726a8311fb0193d301c78fe4af74697bcbed8b5b6f77a915c36dc56615c482601299c15ff7985c3f892939ee34dd04a361467c6d50c7a4a42f + checksum: 10c0/1bfe0fbd1211a83c83392ad7f901a4eb6772106e94495a3d38cc063a864838a8cecd01b185676320b9e304cd07a84dad245a8cb48fee163cf7cc13663d202aa8 languageName: node linkType: hard @@ -7265,6 +7242,13 @@ __metadata: languageName: node linkType: hard +"anynum@npm:^1.0.1": + version: 1.0.1 + resolution: "anynum@npm:1.0.1" + checksum: 10c0/cfda92c9215722fc4b15faa33d0eb3d5dfd28fba6cb67c1f50d214feaa7ba6497814a3e110777853585de6d91c8c962a1ae425b637d3598d9f9d8f6f6802e5bb + languageName: node + linkType: hard + "appdirsjs@npm:^1.2.4": version: 1.2.7 resolution: "appdirsjs@npm:1.2.7" @@ -7749,6 +7733,15 @@ __metadata: languageName: node linkType: hard +"babel-plugin-syntax-hermes-parser@npm:0.36.1, babel-plugin-syntax-hermes-parser@npm:^0.36.0": + version: 0.36.1 + resolution: "babel-plugin-syntax-hermes-parser@npm:0.36.1" + dependencies: + hermes-parser: "npm:0.36.1" + checksum: 10c0/d6343ef16032253408cc8f4585c7acc275101f3194c312606e0f0d65ccf768db36b74e907296a93f3844c108ed19776052d5e844cbd21ec18c43a23ce7c2055e + languageName: node + linkType: hard + "babel-plugin-syntax-hermes-parser@npm:^0.28.0": version: 0.28.1 resolution: "babel-plugin-syntax-hermes-parser@npm:0.28.1" @@ -7758,15 +7751,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-syntax-hermes-parser@npm:^0.36.0": - version: 0.36.1 - resolution: "babel-plugin-syntax-hermes-parser@npm:0.36.1" - dependencies: - hermes-parser: "npm:0.36.1" - checksum: 10c0/d6343ef16032253408cc8f4585c7acc275101f3194c312606e0f0d65ccf768db36b74e907296a93f3844c108ed19776052d5e844cbd21ec18c43a23ce7c2055e - languageName: node - linkType: hard - "babel-plugin-transform-flow-enums@npm:^0.0.2": version: 0.0.2 resolution: "babel-plugin-transform-flow-enums@npm:0.0.2" @@ -8001,23 +7985,20 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:^1.20.3": - version: 1.20.5 - resolution: "body-parser@npm:1.20.5" +"body-parser@npm:^2.2.2": + version: 2.3.0 + resolution: "body-parser@npm:2.3.0" dependencies: - bytes: "npm:~3.1.2" - content-type: "npm:~1.0.5" - debug: "npm:2.6.9" - depd: "npm:2.0.0" - destroy: "npm:~1.2.0" - http-errors: "npm:~2.0.1" - iconv-lite: "npm:~0.4.24" - on-finished: "npm:~2.4.1" - qs: "npm:~6.15.1" - raw-body: "npm:~2.5.3" - type-is: "npm:~1.6.18" - unpipe: "npm:~1.0.0" - checksum: 10c0/ad777ca5e4711eae253c93f50fdc4608c60b76a9710d79e5e5b84581c76691e6ad21ecc9158986d9ea2b365df73e403ca33c27a8bccc1a7cfc2ccc248548118d + bytes: "npm:^3.1.2" + content-type: "npm:^2.0.0" + debug: "npm:^4.4.3" + http-errors: "npm:^2.0.1" + iconv-lite: "npm:^0.7.2" + on-finished: "npm:^2.4.1" + qs: "npm:^6.15.2" + raw-body: "npm:^3.0.2" + type-is: "npm:^2.1.0" + checksum: 10c0/2a8fbbdc471b588338555a3e1a597d1eb0ad0c21cf20fdc3bac5d3f8d9c3a4b19b4163575ab852a43a5dfc0df7770ced5284f979e561f1a24c2f851ce89e695a languageName: node linkType: hard @@ -8147,7 +8128,7 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.1.2, bytes@npm:~3.1.2": +"bytes@npm:3.1.2, bytes@npm:^3.1.2, bytes@npm:~3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e @@ -8770,10 +8751,10 @@ __metadata: languageName: node linkType: hard -"content-type@npm:~1.0.5": - version: 1.0.5 - resolution: "content-type@npm:1.0.5" - checksum: 10c0/b76ebed15c000aee4678c3707e0860cb6abd4e680a598c0a26e17f0bfae723ec9cc2802f0ff1bc6e4d80603719010431d2231018373d4dde10f9ccff9dadf5af +"content-type@npm:^2.0.0": + version: 2.1.0 + resolution: "content-type@npm:2.1.0" + checksum: 10c0/f5d420f55fd0c7a7f7cbae9637777ce67a074aa79b489d8d9fee8599089592b5f8bb194e1d6885741fee20271034baca0d68d5419535b52d070c4f7e39f4b448 languageName: node linkType: hard @@ -9209,7 +9190,7 @@ __metadata: languageName: node linkType: hard -"destroy@npm:1.2.0, destroy@npm:~1.2.0": +"destroy@npm:1.2.0": version: 1.2.0 resolution: "destroy@npm:1.2.0" checksum: 10c0/bd7633942f57418f5a3b80d5cb53898127bcf53e24cdf5d5f4396be471417671f0fee48a4ebe9a1e9defbde2a31280011af58a57e090ff822f589b443ed4e643 @@ -10760,14 +10741,29 @@ __metadata: languageName: node linkType: hard -"fast-xml-parser@npm:^4.4.1": - version: 4.5.6 - resolution: "fast-xml-parser@npm:4.5.6" +"fast-xml-builder@npm:^1.2.0": + version: 1.3.1 + resolution: "fast-xml-builder@npm:1.3.1" dependencies: - strnum: "npm:^1.0.5" + path-expression-matcher: "npm:^1.6.2" + xml-naming: "npm:^0.3.0" + checksum: 10c0/5aaf30465e6ba4ae9780eb3916878d5bd40763a3fd6b8b079a76aabe8f3e9e852327280e7c1be2b63ec141424b6ec61b7ab8acca8b3948779b164b004a294b4c + languageName: node + linkType: hard + +"fast-xml-parser@npm:^5.3.6": + version: 5.11.0 + resolution: "fast-xml-parser@npm:5.11.0" + dependencies: + "@nodable/entities": "npm:^3.0.0" + fast-xml-builder: "npm:^1.2.0" + is-unsafe: "npm:^2.0.0" + path-expression-matcher: "npm:^1.6.2" + strnum: "npm:^2.4.2" + xml-naming: "npm:^0.3.0" bin: fxparser: src/cli/cli.js - checksum: 10c0/1c19e183b5ee93bea9b24e1ddb0aed8564b273c6106af622b1c11ff8eb1fc8d2033cd7a0cb68976a5f3e05d1cbf0a0026e6f300f904be0bc854ff896dbdf38d2 + checksum: 10c0/bf3821c123f057283dd413f79c043af830e8b3bff3c0a6d02f97a8c5677c205379c156b735620c981ebe504d4992e9cf7f435512d2e77d845ac25f8045c53c5f languageName: node linkType: hard @@ -11801,7 +11797,7 @@ __metadata: languageName: node linkType: hard -"http-errors@npm:~2.0.1": +"http-errors@npm:^2.0.1, http-errors@npm:~2.0.1": version: 2.0.1 resolution: "http-errors@npm:2.0.1" dependencies: @@ -11967,12 +11963,12 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:~0.4.24": - version: 0.4.24 - resolution: "iconv-lite@npm:0.4.24" +"iconv-lite@npm:~0.7.0": + version: 0.7.3 + resolution: "iconv-lite@npm:0.7.3" dependencies: - safer-buffer: "npm:>= 2.1.2 < 3" - checksum: 10c0/c6886a24cc00f2a059767440ec1bc00d334a89f250db8e0f7feb4961c8727118457e27c495ba94d082e51d3baca378726cd110aaf7ded8b9bbfd6a44760cf1d4 + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10c0/be2fd2414f7e94be3a63063fa0ad5919c16bc7b6e00c77eea0765ced6db405739f38dd51016583058fba25cc1d0106ba30b83fbb7a7e32f824d4db76f23d8d33 languageName: node linkType: hard @@ -12671,6 +12667,13 @@ __metadata: languageName: node linkType: hard +"is-unsafe@npm:^2.0.0": + version: 2.0.0 + resolution: "is-unsafe@npm:2.0.0" + checksum: 10c0/d7f721ae13e1abcec419e31f375b9b0840afc790250af3650709160ac9301d9f73e357b2ff832282258529b4f2d3cd622fcddd648f99704e686b2673f4e76a16 + languageName: node + linkType: hard + "is-weakmap@npm:^2.0.2": version: 2.0.2 resolution: "is-weakmap@npm:2.0.2" @@ -14474,10 +14477,10 @@ __metadata: languageName: node linkType: hard -"media-typer@npm:0.3.0": - version: 0.3.0 - resolution: "media-typer@npm:0.3.0" - checksum: 10c0/d160f31246907e79fed398470285f21bafb45a62869dc469b1c8877f3f064f5eabc4bcc122f9479b8b605bc5c76187d7871cf84c4ee3ecd3e487da1993279928 +"media-typer@npm:^1.1.0": + version: 1.1.1 + resolution: "media-typer@npm:1.1.1" + checksum: 10c0/924644de220c3107fc53ec0299b7c1488503470265d32f1492535bd798cc369ba7ceaf9c2a4533361590dcf7208d1cc7b7032dbc1a5918773f5f5d912cab5211 languageName: node linkType: hard @@ -14550,6 +14553,19 @@ __metadata: languageName: node linkType: hard +"metro-babel-transformer@npm:0.87.0": + version: 0.87.0 + resolution: "metro-babel-transformer@npm:0.87.0" + dependencies: + "@babel/core": "npm:^7.25.2" + flow-enums-runtime: "npm:^0.0.6" + hermes-parser: "npm:0.36.1" + metro-cache-key: "npm:0.87.0" + nullthrows: "npm:^1.1.1" + checksum: 10c0/0a84ff061f0692f69738fd33d8e0571594c249cb03cd461c381a7dcea7c820ceefe84ee36a8aa57764aa64ddc25846bc1227769d4222bb2c18bac42982811b98 + languageName: node + linkType: hard + "metro-cache-key@npm:0.80.12": version: 0.80.12 resolution: "metro-cache-key@npm:0.80.12" @@ -14568,6 +14584,15 @@ __metadata: languageName: node linkType: hard +"metro-cache-key@npm:0.87.0": + version: 0.87.0 + resolution: "metro-cache-key@npm:0.87.0" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10c0/f75b20b43cd99f684d215c57b195611e04d2d2a9442ad4df17fa2a8929ec7f31a428331881ab950c25e585c65946dcda3c7377d58905c2c7b600a5b66286ec2d + languageName: node + linkType: hard + "metro-cache@npm:0.80.12": version: 0.80.12 resolution: "metro-cache@npm:0.80.12" @@ -14591,6 +14616,18 @@ __metadata: languageName: node linkType: hard +"metro-cache@npm:0.87.0": + version: 0.87.0 + resolution: "metro-cache@npm:0.87.0" + dependencies: + exponential-backoff: "npm:^3.1.1" + flow-enums-runtime: "npm:^0.0.6" + https-proxy-agent: "npm:^7.0.5" + metro-core: "npm:0.87.0" + checksum: 10c0/150404598aa6ab7d2a98243214191009d698f841e8126dffe6de83d3861869bd7e4ab527ede6fdf6c738db9fff94f691998e9213fffe38cbc72dca8cd80a8f43 + languageName: node + linkType: hard + "metro-config@npm:0.80.12, metro-config@npm:^0.80.9": version: 0.80.12 resolution: "metro-config@npm:0.80.12" @@ -14623,6 +14660,21 @@ __metadata: languageName: node linkType: hard +"metro-config@npm:0.87.0, metro-config@npm:^0.87.0": + version: 0.87.0 + resolution: "metro-config@npm:0.87.0" + dependencies: + connect: "npm:^3.6.5" + flow-enums-runtime: "npm:^0.0.6" + jest-validate: "npm:^29.7.0" + metro: "npm:0.87.0" + metro-cache: "npm:0.87.0" + metro-core: "npm:0.87.0" + metro-runtime: "npm:0.87.0" + checksum: 10c0/a3c5a131f875bf8a52cf9ccb528e0343abb501159a18c7985de218ce188768c91f66ce4348e5707eb885bcf09948219de76ece03498179ba4db5aeadf3ddf7ea + languageName: node + linkType: hard + "metro-core@npm:0.80.12": version: 0.80.12 resolution: "metro-core@npm:0.80.12" @@ -14645,6 +14697,17 @@ __metadata: languageName: node linkType: hard +"metro-core@npm:0.87.0": + version: 0.87.0 + resolution: "metro-core@npm:0.87.0" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + lodash.throttle: "npm:^4.1.1" + metro-resolver: "npm:0.87.0" + checksum: 10c0/3ddefabf34f98e757a8b95768982dc0eb11f15eed001a9063d0144529fd85bcf22b7c33b770636aec3e1ba5b50219143486b87dd2e54b04e4d5b027ae02642b0 + languageName: node + linkType: hard + "metro-file-map@npm:0.80.12": version: 0.80.12 resolution: "metro-file-map@npm:0.80.12" @@ -14685,6 +14748,23 @@ __metadata: languageName: node linkType: hard +"metro-file-map@npm:0.87.0": + version: 0.87.0 + resolution: "metro-file-map@npm:0.87.0" + dependencies: + debug: "npm:^4.4.0" + fb-watchman: "npm:^2.0.0" + flow-enums-runtime: "npm:^0.0.6" + graceful-fs: "npm:^4.2.4" + invariant: "npm:^2.2.4" + jest-worker: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + nullthrows: "npm:^1.1.1" + walker: "npm:^1.0.7" + checksum: 10c0/49b9d859c3a7d6da277699a04c55db8a77f404cebec0cacfa7916664ebec607d8e57f8120e2ba28a4aec615d4af33fd5a3cdb164dc80d78b1d9b722f2351aadc + languageName: node + linkType: hard + "metro-minify-terser@npm:0.80.12": version: 0.80.12 resolution: "metro-minify-terser@npm:0.80.12" @@ -14705,6 +14785,16 @@ __metadata: languageName: node linkType: hard +"metro-minify-terser@npm:0.87.0": + version: 0.87.0 + resolution: "metro-minify-terser@npm:0.87.0" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + terser: "npm:^5.15.0" + checksum: 10c0/db0cff372672b9de914be0d0c6440873df88ccbc38ed5e299cf534bc806c2cc0caa005098f0c498cc2d3e51e4f3f6e05f7671b9c48c873cf62709557cc07689d + languageName: node + linkType: hard + "metro-resolver@npm:0.80.12": version: 0.80.12 resolution: "metro-resolver@npm:0.80.12" @@ -14723,6 +14813,15 @@ __metadata: languageName: node linkType: hard +"metro-resolver@npm:0.87.0": + version: 0.87.0 + resolution: "metro-resolver@npm:0.87.0" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10c0/e2283f83d14794948eb0153a3f9d8b39519be25d21f5d482f27972c47702d58ed340f49c6ab87a175e3a340c53878eff49b4764cffdd508f86bce30079914c58 + languageName: node + linkType: hard + "metro-runtime@npm:0.80.12": version: 0.80.12 resolution: "metro-runtime@npm:0.80.12" @@ -14743,6 +14842,16 @@ __metadata: languageName: node linkType: hard +"metro-runtime@npm:0.87.0, metro-runtime@npm:^0.87.0": + version: 0.87.0 + resolution: "metro-runtime@npm:0.87.0" + dependencies: + "@babel/runtime": "npm:^7.25.0" + flow-enums-runtime: "npm:^0.0.6" + checksum: 10c0/6811ee32a759dbccaa3241d98ec26f3e26fd5e4a1cdd5929c98df94fd46329c20f026759da6ca055cb068f8a57dfc7054b6cfc7bb9fb7b3280144055d2f8d67f + languageName: node + linkType: hard + "metro-source-map@npm:0.80.12": version: 0.80.12 resolution: "metro-source-map@npm:0.80.12" @@ -14777,6 +14886,23 @@ __metadata: languageName: node linkType: hard +"metro-source-map@npm:0.87.0, metro-source-map@npm:^0.87.0": + version: 0.87.0 + resolution: "metro-source-map@npm:0.87.0" + dependencies: + "@babel/traverse": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + flow-enums-runtime: "npm:^0.0.6" + invariant: "npm:^2.2.4" + metro-symbolicate: "npm:0.87.0" + nullthrows: "npm:^1.1.1" + ob1: "npm:0.87.0" + source-map: "npm:^0.5.6" + vlq: "npm:^1.0.0" + checksum: 10c0/8074cf0bb4ea7f1027924155b57a0e1205ed9381611abebddd1d7e5a92d0c231cb5c6dd65964711a3a2a2757486b478869d971246158b28dd95232daaef18eff + languageName: node + linkType: hard + "metro-symbolicate@npm:0.80.12": version: 0.80.12 resolution: "metro-symbolicate@npm:0.80.12" @@ -14810,6 +14936,22 @@ __metadata: languageName: node linkType: hard +"metro-symbolicate@npm:0.87.0": + version: 0.87.0 + resolution: "metro-symbolicate@npm:0.87.0" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + invariant: "npm:^2.2.4" + metro-source-map: "npm:0.87.0" + nullthrows: "npm:^1.1.1" + source-map: "npm:^0.5.6" + vlq: "npm:^1.0.0" + bin: + metro-symbolicate: src/index.js + checksum: 10c0/aacc80d9b46cefedd4078fd2fea0212978a570aa7f2eba6042b33d288858be1e5150d71320dd420bcc0d357c46237edb1b7312639693d288bf6d67fa502c4203 + languageName: node + linkType: hard + "metro-transform-plugins@npm:0.80.12": version: 0.80.12 resolution: "metro-transform-plugins@npm:0.80.12" @@ -14838,6 +14980,20 @@ __metadata: languageName: node linkType: hard +"metro-transform-plugins@npm:0.87.0": + version: 0.87.0 + resolution: "metro-transform-plugins@npm:0.87.0" + dependencies: + "@babel/core": "npm:^7.25.2" + "@babel/generator": "npm:^7.29.1" + "@babel/template": "npm:^7.28.6" + "@babel/traverse": "npm:^7.29.0" + flow-enums-runtime: "npm:^0.0.6" + nullthrows: "npm:^1.1.1" + checksum: 10c0/66e622b701e3fcb3e7034b7ac008c34351381d27e76c712d332814fde05eb6f1d98268bce16b62933a591b81b123e26cdd536c9744c8fbf122c087e7275c4e83 + languageName: node + linkType: hard + "metro-transform-worker@npm:0.80.12": version: 0.80.12 resolution: "metro-transform-worker@npm:0.80.12" @@ -14880,6 +15036,27 @@ __metadata: languageName: node linkType: hard +"metro-transform-worker@npm:0.87.0": + version: 0.87.0 + resolution: "metro-transform-worker@npm:0.87.0" + dependencies: + "@babel/core": "npm:^7.25.2" + "@babel/generator": "npm:^7.29.1" + "@babel/parser": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + flow-enums-runtime: "npm:^0.0.6" + metro: "npm:0.87.0" + metro-babel-transformer: "npm:0.87.0" + metro-cache: "npm:0.87.0" + metro-cache-key: "npm:0.87.0" + metro-minify-terser: "npm:0.87.0" + metro-source-map: "npm:0.87.0" + metro-transform-plugins: "npm:0.87.0" + nullthrows: "npm:^1.1.1" + checksum: 10c0/2fc3d49874bc9f76a055e14e0a4c5e6c5659efdae148a275ef13a261a013975114c2aacdb5f42815da0be01196ecfcb3a722b6691752edda69110b655b8522ba + languageName: node + linkType: hard + "metro@npm:0.80.12": version: 0.80.12 resolution: "metro@npm:0.80.12" @@ -14981,6 +15158,55 @@ __metadata: languageName: node linkType: hard +"metro@npm:0.87.0, metro@npm:^0.87.0": + version: 0.87.0 + resolution: "metro@npm:0.87.0" + dependencies: + "@babel/code-frame": "npm:^7.29.0" + "@babel/core": "npm:^7.25.2" + "@babel/generator": "npm:^7.29.1" + "@babel/parser": "npm:^7.29.0" + "@babel/template": "npm:^7.28.6" + "@babel/traverse": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + accepts: "npm:^2.0.0" + ci-info: "npm:^2.0.0" + connect: "npm:^3.6.5" + debug: "npm:^4.4.0" + error-stack-parser: "npm:^2.0.6" + flow-enums-runtime: "npm:^0.0.6" + graceful-fs: "npm:^4.2.4" + hermes-parser: "npm:0.36.1" + image-size: "npm:^1.0.2" + invariant: "npm:^2.2.4" + jest-worker: "npm:^29.7.0" + jsc-safe-url: "npm:^0.2.2" + lodash.throttle: "npm:^4.1.1" + metro-babel-transformer: "npm:0.87.0" + metro-cache: "npm:0.87.0" + metro-cache-key: "npm:0.87.0" + metro-config: "npm:0.87.0" + metro-core: "npm:0.87.0" + metro-file-map: "npm:0.87.0" + metro-resolver: "npm:0.87.0" + metro-runtime: "npm:0.87.0" + metro-source-map: "npm:0.87.0" + metro-symbolicate: "npm:0.87.0" + metro-transform-plugins: "npm:0.87.0" + metro-transform-worker: "npm:0.87.0" + mime-types: "npm:^3.0.1" + nullthrows: "npm:^1.1.1" + serialize-error: "npm:^2.1.0" + source-map: "npm:^0.5.6" + throat: "npm:^5.0.0" + ws: "npm:^7.5.10" + yargs: "npm:^17.6.2" + bin: + metro: src/cli.js + checksum: 10c0/e7aa8b6968c45b4a68dfdf7f6d68e40be0ec888facd8d1bd304a2655ecec245533e9fbf525bd41bf01dd32a327c55a4fd81c229cd589d5796431a663777b0017 + languageName: node + linkType: hard + "micromark@npm:~2.11.0": version: 2.11.4 resolution: "micromark@npm:2.11.4" @@ -15015,7 +15241,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.27, mime-types@npm:^2.1.35, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.27, mime-types@npm:^2.1.35, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -15788,6 +16014,15 @@ __metadata: languageName: node linkType: hard +"ob1@npm:0.87.0": + version: 0.87.0 + resolution: "ob1@npm:0.87.0" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10c0/917478964d9d0876fb0f1d07232ba02115589505972c98468780a174fc61b15ef8ffe322667836df385271db7b4beaf7916cd837b559776b601e3e6a46ca56c3 + languageName: node + linkType: hard + "object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -15870,21 +16105,21 @@ __metadata: languageName: node linkType: hard -"on-finished@npm:~2.3.0": - version: 2.3.0 - resolution: "on-finished@npm:2.3.0" +"on-finished@npm:^2.4.1, on-finished@npm:~2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" dependencies: ee-first: "npm:1.1.1" - checksum: 10c0/c904f9e518b11941eb60279a3cbfaf1289bd0001f600a950255b1dede9fe3df8cd74f38483550b3bb9485165166acb5db500c3b4c4337aec2815c88c96fcc2ea + checksum: 10c0/46fb11b9063782f2d9968863d9cbba33d77aa13c17f895f56129c274318b86500b22af3a160fe9995aa41317efcd22941b6eba747f718ced08d9a73afdb087b4 languageName: node linkType: hard -"on-finished@npm:~2.4.1": - version: 2.4.1 - resolution: "on-finished@npm:2.4.1" +"on-finished@npm:~2.3.0": + version: 2.3.0 + resolution: "on-finished@npm:2.3.0" dependencies: ee-first: "npm:1.1.1" - checksum: 10c0/46fb11b9063782f2d9968863d9cbba33d77aa13c17f895f56129c274318b86500b22af3a160fe9995aa41317efcd22941b6eba747f718ced08d9a73afdb087b4 + checksum: 10c0/c904f9e518b11941eb60279a3cbfaf1289bd0001f600a950255b1dede9fe3df8cd74f38483550b3bb9485165166acb5db500c3b4c4337aec2815c88c96fcc2ea languageName: node linkType: hard @@ -16338,6 +16573,13 @@ __metadata: languageName: node linkType: hard +"path-expression-matcher@npm:^1.6.2": + version: 1.6.2 + resolution: "path-expression-matcher@npm:1.6.2" + checksum: 10c0/8241302f563de367a81acacd417055a22dd3792a84700569dc2a7888da31aa18eb01131c55f05200c92790b6c45b01fee1984b02540cc5f15726ba1b73a8a075 + languageName: node + linkType: hard + "path-is-absolute@npm:^1.0.0": version: 1.0.1 resolution: "path-is-absolute@npm:1.0.1" @@ -16786,12 +17028,13 @@ __metadata: languageName: node linkType: hard -"qs@npm:~6.15.1": - version: 6.15.1 - resolution: "qs@npm:6.15.1" +"qs@npm:^6.15.2": + version: 6.15.3 + resolution: "qs@npm:6.15.3" dependencies: - side-channel: "npm:^1.1.0" - checksum: 10c0/19ee504f0ebff72598503e38cd6d9bd7b52a8ab62ae18b1e6bee3d4db58469bd65871ef1893a881bafb0f80ef2f9ab586e1f255cf25cc8d816c0f5a704721d97 + es-define-property: "npm:^1.0.1" + side-channel: "npm:^1.1.1" + checksum: 10c0/8f3f6e45ece255347d57696628401cde29e9ec649fff698b53bd3150dea7cefdf33036e1bc1826b9f110bfa7cb0ec4ab9f5297eca628ce216c55af82c304e08e languageName: node linkType: hard @@ -16830,15 +17073,15 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:~2.5.3": - version: 2.5.3 - resolution: "raw-body@npm:2.5.3" +"raw-body@npm:^3.0.2": + version: 3.0.2 + resolution: "raw-body@npm:3.0.2" dependencies: bytes: "npm:~3.1.2" http-errors: "npm:~2.0.1" - iconv-lite: "npm:~0.4.24" + iconv-lite: "npm:~0.7.0" unpipe: "npm:~1.0.0" - checksum: 10c0/449844344fc90547fb994383a494b83300e4f22199f146a79f68d78a199a8f2a923ea9fd29c3be979bfd50291a3884733619ffc15ba02a32e703b612f8d3f74a + checksum: 10c0/d266678d08e1e7abea62c0ce5864344e980fa81c64f6b481e9842c5beaed2cdcf975f658a3ccd67ad35fc919c1f6664ccc106067801850286a6cbe101de89f29 languageName: node linkType: hard @@ -17171,7 +17414,7 @@ __metadata: languageName: node linkType: hard -"react-native-reanimated@npm:4.5.2, react-native-reanimated@npm:^4.5.2": +"react-native-reanimated@npm:4.5.2": version: 4.5.2 resolution: "react-native-reanimated@npm:4.5.2" dependencies: @@ -17185,6 +17428,20 @@ __metadata: languageName: node linkType: hard +"react-native-reanimated@npm:4.6.0, react-native-reanimated@npm:^4.6.0": + version: 4.6.0 + resolution: "react-native-reanimated@npm:4.6.0" + dependencies: + react-native-is-edge-to-edge: "npm:^1.3.1" + semver: "npm:^7.7.3" + peerDependencies: + react: "*" + react-native: 0.83 - 0.87 + react-native-worklets: 0.12.x + checksum: 10c0/947c9038b9c876a5e77364afd4b256327e05317537022b283aa76dc35af20b5cb4786989117fd1c3926c583595165703903e91ab3c204f90fdb6296022eff614 + languageName: node + linkType: hard + "react-native-safe-area-context@npm:^5.8.0": version: 5.8.0 resolution: "react-native-safe-area-context@npm:5.8.0" @@ -17195,6 +17452,16 @@ __metadata: languageName: node linkType: hard +"react-native-safe-area-context@npm:^5.8.1": + version: 5.9.0 + resolution: "react-native-safe-area-context@npm:5.9.0" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/f8d7634b7ab114e21c88c87de044f2a5d94d3f9641c9c203e924733c014f0c8f5a4fd95fd70275b400e6696245a5d78b263635c0be175c882b02b723a580678e + languageName: node + linkType: hard + "react-native-safe-area-context@npm:~5.7.0": version: 5.7.0 resolution: "react-native-safe-area-context@npm:5.7.0" @@ -17205,7 +17472,7 @@ __metadata: languageName: node linkType: hard -"react-native-screens@npm:4.26.2, react-native-screens@npm:^4.26.2": +"react-native-screens@npm:4.26.2": version: 4.26.2 resolution: "react-native-screens@npm:4.26.2" dependencies: @@ -17231,6 +17498,19 @@ __metadata: languageName: node linkType: hard +"react-native-screens@npm:^4.27.0": + version: 4.27.0 + resolution: "react-native-screens@npm:4.27.0" + dependencies: + react-freeze: "npm:^1.0.0" + warn-once: "npm:^0.1.0" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/357674b881189ff8933f457acadbfc3030a376d0ccdfb4b6005462aabbf38287d4d0f39a301cb94c45df6c2e09be28bcfc4d71298c8d34b8ac87ebe639e46ff9 + languageName: node + linkType: hard + "react-native-share@npm:^12.3.1": version: 12.3.1 resolution: "react-native-share@npm:12.3.1" @@ -17316,7 +17596,7 @@ __metadata: languageName: node linkType: hard -"react-native-worklets@npm:0.11.1, react-native-worklets@npm:^0.11.1": +"react-native-worklets@npm:0.11.1": version: 0.11.1 resolution: "react-native-worklets@npm:0.11.1" dependencies: @@ -17343,6 +17623,33 @@ __metadata: languageName: node linkType: hard +"react-native-worklets@npm:^0.12.1": + version: 0.12.1 + resolution: "react-native-worklets@npm:0.12.1" + dependencies: + "@babel/generator": "npm:^7.27.1" + "@babel/plugin-transform-arrow-functions": "npm:^7.27.1" + "@babel/plugin-transform-class-properties": "npm:^7.28.6" + "@babel/plugin-transform-classes": "npm:^7.28.6" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.28.6" + "@babel/plugin-transform-optional-chaining": "npm:^7.28.6" + "@babel/plugin-transform-shorthand-properties": "npm:^7.27.1" + "@babel/plugin-transform-template-literals": "npm:^7.27.1" + "@babel/plugin-transform-unicode-regex": "npm:^7.27.1" + "@babel/preset-typescript": "npm:^7.28.5" + "@babel/traverse": "npm:^7.27.1" + "@babel/types": "npm:^7.27.1" + convert-source-map: "npm:^2.0.0" + semver: "npm:^7.7.4" + peerDependencies: + "@babel/core": "*" + "@react-native/metro-config": "*" + react: "*" + react-native: 0.83 - 0.87 + checksum: 10c0/86b9043ed2ceacd3dada3f94d62393a5bb54c7a26877e5bb54171cd4455b34f4148009a6b41bf53be70f265444bd7fff287f4d0fd8599e27d476c10e6cd7923f + languageName: node + linkType: hard + "react-native@npm:0.86.0": version: 0.86.0 resolution: "react-native@npm:0.86.0" @@ -17394,29 +17701,27 @@ __metadata: languageName: node linkType: hard -"react-native@npm:0.86.2": - version: 0.86.2 - resolution: "react-native@npm:0.86.2" +"react-native@npm:0.87.0": + version: 0.87.0 + resolution: "react-native@npm:0.87.0" dependencies: - "@react-native/assets-registry": "npm:0.86.2" - "@react-native/codegen": "npm:0.86.2" - "@react-native/community-cli-plugin": "npm:0.86.2" - "@react-native/gradle-plugin": "npm:0.86.2" - "@react-native/js-polyfills": "npm:0.86.2" - "@react-native/normalize-colors": "npm:0.86.2" - "@react-native/virtualized-lists": "npm:0.86.2" - abort-controller: "npm:^3.0.0" + "@react-native/asset-utils": "npm:0.87.0" + "@react-native/codegen": "npm:0.87.0" + "@react-native/community-cli-plugin": "npm:0.87.0" + "@react-native/gradle-plugin": "npm:0.87.0" + "@react-native/normalize-colors": "npm:0.87.0" + "@react-native/virtualized-lists": "npm:0.87.0" anser: "npm:^1.4.9" ansi-regex: "npm:^5.0.0" - babel-plugin-syntax-hermes-parser: "npm:0.36.0" + babel-plugin-syntax-hermes-parser: "npm:0.36.1" base64-js: "npm:^1.5.1" commander: "npm:^12.0.0" flow-enums-runtime: "npm:^0.0.6" hermes-compiler: "npm:250829098.0.16" invariant: "npm:^2.2.4" memoize-one: "npm:^5.0.0" - metro-runtime: "npm:^0.84.3" - metro-source-map: "npm:^0.84.3" + metro-runtime: "npm:^0.87.0" + metro-source-map: "npm:^0.87.0" nullthrows: "npm:^1.1.1" pretty-format: "npm:^29.7.0" promise: "npm:^8.3.0" @@ -17431,17 +17736,14 @@ __metadata: ws: "npm:^7.5.10" yargs: "npm:^17.6.2" peerDependencies: - "@react-native/jest-preset": 0.86.2 "@types/react": ^19.1.1 react: ^19.2.3 peerDependenciesMeta: - "@react-native/jest-preset": - optional: true "@types/react": optional: true bin: react-native: cli.js - checksum: 10c0/cb2d91c52f0d1ab18b46c239f801ba86b173126eb53e71aff1e6f22068b58cbfe909f222ff53bdcbfc0d286ddb7448f9c5607b7d6ba0e269a877d014331df337 + checksum: 10c0/0f3e8760b9417d79d1fa840d4be5891e8baf9087ddc4a523107166e14975f0f311bb3ee7aa122a99e93ae2fc1c1e1d3f95182ab6ae7fe6abb0206386392f982e languageName: node linkType: hard @@ -18046,7 +18348,7 @@ __metadata: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": +"safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 @@ -18063,22 +18365,22 @@ __metadata: "@emoji-mart/data": "npm:^1.2.1" "@gorhom/bottom-sheet": "npm:^5.2.14" "@notifee/react-native": "npm:^9.1.8" - "@op-engineering/op-sqlite": "npm:^17.1.2" + "@op-engineering/op-sqlite": "npm:^18.1.0" "@react-native-async-storage/async-storage": "npm:^3.1.1" "@react-native-camera-roll/camera-roll": "npm:^7.10.2" "@react-native-clipboard/clipboard": "npm:^1.16.3" "@react-native-community/blur": "npm:^4.4.1" - "@react-native-community/cli": "npm:20.1.0" - "@react-native-community/cli-platform-android": "npm:20.1.0" - "@react-native-community/cli-platform-ios": "npm:20.1.0" + "@react-native-community/cli": "npm:20.2.0" + "@react-native-community/cli-platform-android": "npm:20.2.0" + "@react-native-community/cli-platform-ios": "npm:20.2.0" "@react-native-community/geolocation": "npm:^3.4.0" "@react-native-community/netinfo": "npm:^12.0.1" "@react-native-documents/picker": "npm:^12.0.1" "@react-native-firebase/app": "npm:^24.0.0" "@react-native-firebase/messaging": "npm:^24.0.0" - "@react-native/babel-preset": "npm:0.86.2" - "@react-native/metro-config": "npm:0.86.2" - "@react-native/typescript-config": "npm:0.86.2" + "@react-native/babel-preset": "npm:0.87.0" + "@react-native/metro-config": "npm:0.87.0" + "@react-native/typescript-config": "npm:0.87.0" "@react-navigation/bottom-tabs": "npm:^7.18.11" "@react-navigation/core": "npm:^7.21.8" "@react-navigation/drawer": "npm:^7.13.2" @@ -18094,7 +18396,7 @@ __metadata: emoji-mart: "npm:^5.6.0" lodash.mergewith: "npm:^4.6.2" react: "npm:19.2.3" - react-native: "npm:0.86.2" + react-native: "npm:0.87.0" react-native-blob-util: "npm:^0.24.10" react-native-gesture-handler: "npm:^3.1.0" react-native-haptic-feedback: "npm:^3.0.0" @@ -18102,14 +18404,14 @@ __metadata: react-native-maps: "npm:^1.29.0" react-native-nitro-modules: "npm:0.36.5" react-native-nitro-sound: "npm:0.2.17" - react-native-reanimated: "npm:4.5.2" + react-native-reanimated: "npm:4.6.0" react-native-safe-area-context: "npm:^5.8.0" - react-native-screens: "npm:^4.26.2" + react-native-screens: "npm:^4.27.0" react-native-share: "npm:^12.3.1" react-native-svg: "npm:^15.15.5" react-native-teleport: "npm:^1.1.12" react-native-video: "npm:^6.19.2" - react-native-worklets: "npm:^0.11.1" + react-native-worklets: "npm:^0.12.1" stream-chat: "npm:^9.51.0" stream-chat-react-native: "workspace:^" stream-chat-react-native-core: "workspace:^" @@ -18336,7 +18638,7 @@ __metadata: languageName: node linkType: hard -"side-channel-list@npm:^1.0.0": +"side-channel-list@npm:^1.0.0, side-channel-list@npm:^1.0.1": version: 1.0.1 resolution: "side-channel-list@npm:1.0.1" dependencies: @@ -18384,6 +18686,19 @@ __metadata: languageName: node linkType: hard +"side-channel@npm:^1.1.1": + version: 1.1.1 + resolution: "side-channel@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.4" + side-channel-list: "npm:^1.0.1" + side-channel-map: "npm:^1.0.1" + side-channel-weakmap: "npm:^1.0.2" + checksum: 10c0/dc0ab81d67f61bda9247d053ce93f41c3fd8ad2bdcb9cf9d8d2f8540d488f26d87a5e99ebfc07eea49ec025867b2452b705442d974b1478f0395e69f6bfb3270 + languageName: node + linkType: hard + "signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" @@ -18800,10 +19115,10 @@ __metadata: "@babel/core": "npm:^7.29.0" "@babel/runtime": "npm:^7.29.2" "@gorhom/bottom-sheet": "npm:5.2.9" - "@op-engineering/op-sqlite": "npm:^17.1.2" + "@op-engineering/op-sqlite": "npm:^18.1.0" "@react-native-community/netinfo": "npm:^12.0.1" - "@react-native/babel-preset": "npm:0.86.0" - "@react-native/jest-preset": "npm:0.86.0" + "@react-native/babel-preset": "npm:0.87.0" + "@react-native/jest-preset": "npm:0.87.0" "@shopify/flash-list": "npm:^2.3.2" "@stream-io/typescript-config": "workspace:^" "@testing-library/jest-native": "npm:^5.4.3" @@ -18837,16 +19152,16 @@ __metadata: moment-timezone: "npm:^0.6.0" path: "npm:0.12.7" react: "npm:19.2.3" - react-native: "npm:0.86.0" + react-native: "npm:0.87.0" react-native-builder-bob: "npm:0.40.11" react-native-gesture-handler: "npm:^3.1.0" react-native-markdown-package: "npm:1.8.2" - react-native-reanimated: "npm:^4.5.2" - react-native-safe-area-context: "npm:^5.8.0" + react-native-reanimated: "npm:^4.6.0" + react-native-safe-area-context: "npm:^5.8.1" react-native-svg: "npm:15.15.5" react-native-teleport: "npm:^1.1.12" react-native-url-polyfill: "npm:^2.0.0" - react-native-worklets: "npm:^0.11.1" + react-native-worklets: "npm:^0.12.1" react-test-renderer: "npm:19.2.3" rimraf: "npm:^6.0.1" stream-chat: "npm:^9.51.0" @@ -18884,7 +19199,7 @@ __metadata: "@stream-io/typescript-config": "workspace:^" es6-symbol: "npm:^3.1.3" mime: "npm:^4.0.7" - react-native: "npm:0.86.0" + react-native: "npm:0.87.0" stream-chat-react-native-core: "workspace:^" typescript: "npm:6.0.3" peerDependencies: @@ -19187,10 +19502,12 @@ __metadata: languageName: node linkType: hard -"strnum@npm:^1.0.5": - version: 1.1.2 - resolution: "strnum@npm:1.1.2" - checksum: 10c0/a0fce2498fa3c64ce64a40dada41beb91cabe3caefa910e467dc0518ef2ebd7e4d10f8c2202a6104f1410254cae245066c0e94e2521fb4061a5cb41831952392 +"strnum@npm:^2.4.2": + version: 2.4.2 + resolution: "strnum@npm:2.4.2" + dependencies: + anynum: "npm:^1.0.1" + checksum: 10c0/71a94dcc12cf3187e10089ed2ddcf7c289fed168ce4a33ee393bbd009a9199c237e3ef71597f3c91fac4203dd1e675c090eb4131226fd70af6fa2a31b0175de2 languageName: node linkType: hard @@ -19636,13 +19953,14 @@ __metadata: languageName: node linkType: hard -"type-is@npm:~1.6.18": - version: 1.6.18 - resolution: "type-is@npm:1.6.18" +"type-is@npm:^2.1.0": + version: 2.1.0 + resolution: "type-is@npm:2.1.0" dependencies: - media-typer: "npm:0.3.0" - mime-types: "npm:~2.1.24" - checksum: 10c0/a23daeb538591b7efbd61ecf06b6feb2501b683ffdc9a19c74ef5baba362b4347e42f1b4ed81f5882a8c96a3bfff7f93ce3ffaf0cbbc879b532b04c97a55db9d + content-type: "npm:^2.0.0" + media-typer: "npm:^1.1.0" + mime-types: "npm:^3.0.0" + checksum: 10c0/a6018f8f509de48f2c7429305e3a920e73b374fa93127dd0877ae1c2df65a5d33907caac8afb0c37a9b9fc7c49f29e3f55d668963dc845d966930b667c07f50e languageName: node linkType: hard @@ -20526,6 +20844,13 @@ __metadata: languageName: node linkType: hard +"xml-naming@npm:^0.3.0": + version: 0.3.0 + resolution: "xml-naming@npm:0.3.0" + checksum: 10c0/48bfc4fe888cdf1c3eceb7853632ebce59e4aa71f0ae33c3f9ce1fbd3213caad293457de0a311114491f0d4efcfba70977dcba3a4f66dfed8c92edbb938056bf + languageName: node + linkType: hard + "xml2js@npm:0.6.0": version: 0.6.0 resolution: "xml2js@npm:0.6.0" From 824818bf4daf71ddb152f707d4ff4d7aede4ead1 Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj <31964049+isekovanic@users.noreply.github.com> Date: Wed, 26 Aug 2026 11:03:12 +0200 Subject: [PATCH 2/2] fix: timeout on ios uploads on http 1.1 connection (#3787) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 🎯 Goal This PR fixes iOS attachment uploads hanging for 60s and then failing with `-1001` (timed out) after the server has already accepted the file. Affects apps using `useNativeMultipartUpload` specifically. The `CFNetwork` trace of a failing upload looks something like this: ``` resuming, timeouts(60.0, …) received response, status 201 ← server took the file …60 s of nothing… finished with error [-1001] summary for task failure { response_status=201, request_bytes=2810041, response_duration_ms=0, protocol="http/1.1" } ``` So the upload succeeds and the client throws it away a minute later. The user sees a failed attachment (and the message with the attachment is not sent consequently). ## πŸ›  Implementation details The root cause is unfortunately not as simple. The multipart body was pretty much a hand rolled `NSInputStream` subclass. `CFNetwork` drives an `HTTP/1.1` request body through the `CFReadStream` interface, which a plain `InputStream` subclass cannot participate in, so it can never report end-of-stream. `CFNetwork` stops calling `read` the moment `Content-Length` is satisfied, so the subclass never returned 0, never reached `.atEnd` and so `CFNetwork` never learned the body had ended. The transaction stayed open until the timeout (which is 60 seconds later). This isn't something that would fire all the time, however. It happens whenever the connection ends up on `HTTP/1.1`. That's decided on a much lower level (during the TLS handshake), so it's server and network determined rather than something the app controls, so if an endpoint that doesn't advertise `h2`, a TLS intercepting proxy or a local debugging proxy such as `Charles` or `Proxyman` will all put us there. Over `HTTP/2` and `HTTP/3` the request body is framed and terminated by `Content-Length`, so the missing end-of-stream signal never mattered and the bug stayed dormant. Hence, why this has gone unnoticed for so long. Naturally, this is an edge case altogether but I've decided to rewrite chunks of the body stream class. There have always existed some certain parts of it that bothered me and we attempt to address them here as well. `makeStream()` now hands `URLSession` the read end of a `CFStreamCreateBoundPair`, so a real `CFReadStream` that reports every event and a new `StreamMultipartBodyProducer` feeds the write end from the same element list. Closing the write end is what tells `CFNetwork` the body is complete. The producer is driven by GCD (`CFWriteStreamSetClient` + `CFWriteStreamSetDispatchQueue`) rather than a run loop, so it owns no thread and can't outlive its work. Because a bound pair has no error channel, body production failures are recorded in a `StreamMultipartBodyErrorBox` and preferred over the transport error in `didCompleteWithError`. The box is **per attempt** so then `URLSession` can request a fresh body stream on retry and an abandoned attempt's failure must not fail a later one that succeeds. Aside from a bit of code complexity, this also costs about `128 KiB` of extra memory per request (which is nothing). In turn we handle a lot of odd edge cases, like: - no longer relying on kind of undocumented behaviour - failures actually get reported every time now - backpressure is now being handled instead of just falsely quitting the upload - retry scoping is now strictly And naturally, `HTTP/1.1` no longer fails uploads. ## 🎨 UI Changes ## πŸ§ͺ Testing ## β˜‘οΈ Checklist - [x] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [x] PR targets the `develop` branch - [ ] Documentation is updated - [x] New code is tested in main example apps, including all possible scenarios - [x] SampleApp iOS and Android - [x] Expo iOS and Android --- .../ios/StreamMultipartUploadBodyStream.swift | 384 +++++++++++++----- .../ios/StreamMultipartUploadManager.swift | 15 + 2 files changed, 296 insertions(+), 103 deletions(-) diff --git a/package/shared-native/ios/StreamMultipartUploadBodyStream.swift b/package/shared-native/ios/StreamMultipartUploadBodyStream.swift index f3b1376cd0..261775acc2 100644 --- a/package/shared-native/ios/StreamMultipartUploadBodyStream.swift +++ b/package/shared-native/ios/StreamMultipartUploadBodyStream.swift @@ -5,10 +5,48 @@ private enum StreamMultipartBodyElement { case file(URL) } +/// Carries a body-production failure out of band. +/// +/// A bound stream pair has no error channel: the only way the producer can signal failure is to +/// close the write end, which the server sees as a truncated body. Recording the real error here +/// lets the upload manager surface it instead of the generic transport error. +final class StreamMultipartBodyErrorBox: @unchecked Sendable { + private let lock = NSLock() + private var storedError: Error? + + var error: Error? { + lock.lock() + defer { lock.unlock() } + return storedError + } + + func record(_ error: Error) { + lock.lock() + defer { lock.unlock() } + if storedError == nil { + storedError = error + } + } +} + final class StreamMultipartUploadBodyStreamFactory { let boundary: String let contentLength: Int64? + /// Set when the body of the **most recent** attempt could not be produced in full. + /// + /// `URLSession` can ask for a fresh body stream (redirect, auth retry) via + /// `needNewBodyStream`. Each attempt therefore gets its own box and `makeStream()` installs it + /// as the current one, so a failure recorded by an abandoned attempt β€” e.g. its reader going + /// away because URLSession decided to retry β€” can never fail a later attempt that succeeds. + var bodyError: Error? { + boxLock.lock() + defer { boxLock.unlock() } + return currentErrorBox.error + } + + private let boxLock = NSLock() + private var currentErrorBox = StreamMultipartBodyErrorBox() private let elements: [StreamMultipartBodyElement] private init( @@ -63,7 +101,37 @@ final class StreamMultipartUploadBodyStreamFactory { } func makeStream() -> InputStream { - StreamMultipartSequentialInputStream(elements: elements) + var readStream: Unmanaged? + var writeStream: Unmanaged? + + CFStreamCreateBoundPair( + kCFAllocatorDefault, + &readStream, + &writeStream, + CFIndex(StreamMultipartBodyProducer.transferBufferSize) + ) + + // A fresh box per attempt; see `bodyError`. + let errorBox = StreamMultipartBodyErrorBox() + boxLock.lock() + currentErrorBox = errorBox + boxLock.unlock() + + guard + let input = readStream?.takeRetainedValue() as InputStream?, + let output = writeStream?.takeRetainedValue() + else { + errorBox.record(StreamMultipartUploadError.invalidRequest("Could not create a request body stream")) + return InputStream(data: Data()) + } + + StreamMultipartBodyProducer( + elements: elements, + output: output, + errorBox: errorBox + ).start() + + return input } private static func multipartTextData(boundary: String, part: StreamMultipartTextPart) -> Data { @@ -102,153 +170,263 @@ final class StreamMultipartUploadBodyStreamFactory { } } -private final class StreamMultipartSequentialInputStream: InputStream { +/// Feeds the write end of a `CFStreamCreateBoundPair` from the multipart element list. +/// +/// The body is handed to `URLSession` as the *read* end of a Core Foundation bound stream pair +/// rather than as a hand-rolled `InputStream` subclass. That matters: CFNetwork drives an HTTP/1.1 +/// request body through the `CFReadStream` client-callback machinery, and a plain `InputStream` +/// subclass cannot participate in it β€” it can never report end-of-stream. CFNetwork stops reading +/// as soon as `Content-Length` is satisfied, so with a subclass it never observed the end of the +/// body, never considered the request finished, and the task sat idle until it timed out (the +/// server had already answered 201). A real bound pair reports every event, and closing the write +/// end is what tells CFNetwork the body is complete. +/// +/// The write end is driven by **GCD** (`CFWriteStreamSetDispatchQueue`) rather than a run loop, so +/// this owns no thread and cannot outlive its work. +private final class StreamMultipartBodyProducer { + static let transferBufferSize = 64 * 1024 + + private enum Refill { + case filled + case drained + case failed(Error) + } + private let elements: [StreamMultipartBodyElement] + private let output: CFWriteStream + private let errorBox: StreamMultipartBodyErrorBox + private let queue: DispatchQueue + private let buffer = UnsafeMutablePointer.allocate( + capacity: StreamMultipartBodyProducer.transferBufferSize + ) + private var currentIndex = 0 private var currentStream: InputStream? - private weak var internalDelegate: StreamDelegate? - private var internalStatus: Stream.Status = .notOpen - private var internalError: Error? - private var scheduledRunLoops: [(runLoop: RunLoop, mode: RunLoop.Mode)] = [] - - init(elements: [StreamMultipartBodyElement]) { + private var bufferOffset = 0 + private var bufferLength = 0 + private var isFinished = false + /// Keeps the producer alive while the stream client holds an unretained pointer to it. + private var selfRetain: StreamMultipartBodyProducer? + + init( + elements: [StreamMultipartBodyElement], + output: CFWriteStream, + errorBox: StreamMultipartBodyErrorBox + ) { self.elements = elements - super.init(data: Data()) + self.output = output + self.errorBox = errorBox + queue = DispatchQueue( + label: "io.getstream.chat.multipart-upload-body", + qos: .userInitiated + ) } - override var delegate: StreamDelegate? { - get { - internalDelegate - } - set { - internalDelegate = newValue - currentStream?.delegate = newValue - } + deinit { + buffer.deallocate() } - override var hasBytesAvailable: Bool { - guard internalStatus != .closed, internalStatus != .error else { - return false - } + func start() { + selfRetain = self - if let currentStream, currentStream.hasBytesAvailable { - return true - } - - return currentIndex < elements.count - } + var context = CFStreamClientContext( + version: 0, + info: Unmanaged.passUnretained(self).toOpaque(), + retain: nil, + release: nil, + copyDescription: nil + ) - override var streamError: Error? { - internalError - } + let events: CFOptionFlags = CFStreamEventType.canAcceptBytes.rawValue + | CFStreamEventType.errorOccurred.rawValue + | CFStreamEventType.endEncountered.rawValue - override var streamStatus: Stream.Status { - internalStatus - } + let didSetClient = CFWriteStreamSetClient( + output, + events, + { _, event, info in + guard let info else { + return + } + Unmanaged.fromOpaque(info) + .takeUnretainedValue() + .handle(event) + }, + &context + ) - override func open() { - guard internalStatus == .notOpen else { + guard didSetClient else { + // No callbacks will ever arrive; finishing here is safe because the dispatch queue has not + // been attached yet, so nothing else can be running. + finish(error: writeStreamError() ?? StreamMultipartUploadError.invalidRequest( + "Could not observe the request body stream" + )) return } - internalStatus = .opening - advanceStreamIfNeeded() - if internalStatus == .error { + // Deliver client callbacks on our serial queue instead of scheduling on a run loop, so the + // producer needs no thread of its own and GCD owns its lifetime. + CFWriteStreamSetDispatchQueue(output, queue) + + guard CFWriteStreamOpen(output) else { + // The queue is attached now, so tear down on it to stay single-threaded. + queue.async { [self] in + finish(error: writeStreamError() ?? StreamMultipartUploadError.invalidRequest( + "Could not open the request body stream" + )) + } return } - internalStatus = currentStream == nil ? .atEnd : .open } - override func close() { - currentStream?.close() - currentStream = nil - internalStatus = .closed + /// The write end's own error, when Core Foundation has one to give. + private func writeStreamError() -> Error? { + CFWriteStreamCopyError(output) as Error? } - override func schedule(in aRunLoop: RunLoop, forMode mode: RunLoop.Mode) { - scheduledRunLoops.append((runLoop: aRunLoop, mode: mode)) - currentStream?.schedule(in: aRunLoop, forMode: mode) + // MARK: - Callbacks (always on `queue`) + + private func handle(_ event: CFStreamEventType) { + switch event { + case .canAcceptBytes: + pump() + case .errorOccurred: + // Usually the reader going away (a cancelled upload), but it can be a genuine write-side + // failure β€” record whatever CF gives us. Cancellation still wins in the manager, which + // checks `NSURLErrorCancelled` first. + finish(error: writeStreamError()) + case .endEncountered: + finish(error: nil) + default: + break + } } - override func remove(from aRunLoop: RunLoop, forMode mode: RunLoop.Mode) { - scheduledRunLoops.removeAll { $0.runLoop == aRunLoop && $0.mode == mode } - currentStream?.remove(from: aRunLoop, forMode: mode) - } + private func pump() { + while !isFinished, CFWriteStreamCanAcceptBytes(output) { + if bufferOffset >= bufferLength { + switch refill() { + case .filled: + break + case .drained: + finish(error: nil) + return + case .failed(let error): + finish(error: error) + return + } + } - override func read(_ buffer: UnsafeMutablePointer, maxLength len: Int) -> Int { - guard internalStatus != .closed else { - return 0 - } + let written = CFWriteStreamWrite( + output, + buffer + bufferOffset, + bufferLength - bufferOffset + ) + + if written > 0 { + bufferOffset += written + continue + } + + if written == 0 { + // Backpressure, NOT end of body: `CFWriteStreamCanAcceptBytes` may answer true without + // knowing, and the pair reports 0 when it is full. The unwritten remainder stays in + // `buffer` at `bufferOffset`, so the next `.canAcceptBytes` resumes exactly here. + // Closing the stream here would silently truncate the body. + return + } - if internalStatus == .notOpen { - open() + // A negative write is itself the failure signal β€” do not depend on CF having an error + // object, or the failure degrades into the clean stream close this refactor exists to + // disambiguate. + finish(error: writeStreamError() ?? StreamMultipartUploadError.invalidRequest( + "Could not write the request body stream" + )) + return } + } + /// Fills `buffer` from the next available element. + private func refill() -> Refill { while true { - guard let currentStream else { - if internalStatus == .error { - return -1 + if currentStream == nil { + guard currentIndex < elements.count else { + return .drained } - internalStatus = .atEnd - return 0 - } + let element = elements[currentIndex] + currentIndex += 1 + + switch element { + case .data(let data): + currentStream = InputStream(data: data) + case .file(let url): + guard let stream = InputStream(url: url) else { + return .failed(StreamMultipartUploadError.unreadableFile(url.path)) + } + currentStream = stream + } + + guard let stream = currentStream else { + return .drained + } - let bytesRead = currentStream.read(buffer, maxLength: len) + stream.open() - if bytesRead > 0 { - internalStatus = .open - return bytesRead + if stream.streamStatus == .error { + return .failed(stream.streamError ?? StreamMultipartUploadError.unreadableFile(elementPath())) + } } - if bytesRead < 0 { - internalError = currentStream.streamError - internalStatus = .error - return -1 + guard let stream = currentStream else { + return .drained } - currentStream.close() - self.currentStream = nil - advanceStreamIfNeeded() + let read = stream.read(buffer, maxLength: StreamMultipartBodyProducer.transferBufferSize) - if self.currentStream == nil { - internalStatus = .atEnd - return 0 + if read > 0 { + bufferOffset = 0 + bufferLength = read + return .filled + } + + let readError = read < 0 ? (stream.streamError ?? StreamMultipartUploadError.unreadableFile(elementPath())) : nil + stream.close() + currentStream = nil + + if let readError { + return .failed(readError) } } } - private func advanceStreamIfNeeded() { - guard currentStream == nil else { + private func elementPath() -> String { + guard currentIndex > 0, case .file(let url) = elements[currentIndex - 1] else { + return "" + } + return url.path + } + + private func finish(error: Error?) { + guard !isFinished else { return } - while currentIndex < elements.count { - let nextElement = elements[currentIndex] - currentIndex += 1 - - let nextStream: InputStream? - switch nextElement { - case .data(let data): - nextStream = InputStream(data: data) - case .file(let url): - nextStream = InputStream(url: url) - if nextStream == nil { - internalError = StreamMultipartUploadError.unreadableFile(url.path) - internalStatus = .error - return - } - } + isFinished = true - if let nextStream { - nextStream.delegate = internalDelegate - for scheduled in scheduledRunLoops { - nextStream.schedule(in: scheduled.runLoop, forMode: scheduled.mode) - } - nextStream.open() - currentStream = nextStream - return - } + if let error { + errorBox.record(error) } + + currentStream?.close() + currentStream = nil + + // Unregister before dropping the retain so no callback can arrive against a dead pointer. + CFWriteStreamSetClient(output, 0, nil, nil) + CFWriteStreamSetDispatchQueue(output, nil) + // Closing the write end is what surfaces end-of-stream on the read end. + CFWriteStreamClose(output) + + selfRetain = nil } } diff --git a/package/shared-native/ios/StreamMultipartUploadManager.swift b/package/shared-native/ios/StreamMultipartUploadManager.swift index 951c988ebb..7f2b9b062d 100644 --- a/package/shared-native/ios/StreamMultipartUploadManager.swift +++ b/package/shared-native/ios/StreamMultipartUploadManager.swift @@ -390,6 +390,11 @@ extension StreamMultipartUploadManager: URLSessionDataDelegate, URLSessionTaskDe if nsError.domain == NSURLErrorDomain, nsError.code == NSURLErrorCancelled { state.completion?(.failure(StreamMultipartUploadError.cancelled)) + } else if let bodyError = state.bodyFactory.bodyError { + // The request body could not be produced in full. A bound stream pair has no error + // channel β€” the reader only sees a truncated body β€” so prefer the recorded cause over + // the transport error it surfaces as. + state.completion?(.failure(bodyError)) } else { state.completion?(.failure(nsError)) } @@ -397,6 +402,16 @@ extension StreamMultipartUploadManager: URLSessionDataDelegate, URLSessionTaskDe return } + // The task completed without a transport error, but the body may still not have been produced + // in full. A bound stream pair has no error channel, so a producer failure closes the write end + // and the reader sees a clean EOF β€” with no `Content-Length` (chunked) that is a well-formed + // short body the server can happily accept. Never report a truncated upload as a success. + if let bodyError = state.bodyFactory.bodyError { + state.completion?(.failure(bodyError)) + state.completion = nil + return + } + guard let response = state.response else { state.completion?(.failure(StreamMultipartUploadError.missingHTTPResponse)) state.completion = nil