diff --git a/.changeset/config.json b/.changeset/config.json index d66664ad..432d2a8e 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -5,7 +5,8 @@ "fixed": [ [ "react-native-bottom-tabs", - "@bottom-tabs/react-navigation" + "@bottom-tabs/react-navigation", + "@bottom-tabs/navigation" ] ], "linked": [], diff --git a/.changeset/drop-color-dependency.md b/.changeset/drop-color-dependency.md new file mode 100644 index 00000000..718ebae2 --- /dev/null +++ b/.changeset/drop-color-dependency.md @@ -0,0 +1,5 @@ +--- +'@bottom-tabs/react-navigation': patch +--- + +Drop the `color` dependency in favour of React Native's `processColor`. `color` is ESM-only, so consumers had to extend `transformIgnorePatterns` before Jest would run at all diff --git a/.changeset/standard-navigation-shared-adapter.md b/.changeset/standard-navigation-shared-adapter.md new file mode 100644 index 00000000..cd893ddf --- /dev/null +++ b/.changeset/standard-navigation-shared-adapter.md @@ -0,0 +1,12 @@ +--- +'@bottom-tabs/navigation': minor +'@bottom-tabs/react-navigation': minor +--- + +Move the native tabs implementation into a new framework-agnostic `@bottom-tabs/navigation` package built on the `standard-navigation` contract. This lets Expo Router SDK 56+ apps drive the same navigator through `unstable_createStandardRouterNavigator` without loading a second copy of React Navigation. + +`NativeBottomTabsContent` carries its event map and navigator props on phantom properties, so an integrator can type `tabBarActiveTintColor`, `tabBar` and the rest. Neither is inferable from the contract on its own - the event map appears only as an argument to `emitter.emit`, and the navigator props only inside an `Omit<…>`. + +`createNativeBottomTabNavigator` renders through the shared navigator, and keeps building its own state with `useNavigationBuilder`, so `state.key` and the per-route `navigation` and `route` objects a custom `tabBar` reads stay intact. + +`@bottom-tabs/react-navigation` now requires `@react-navigation/native` 7.3.0 or newer, which is the version that introduced `createStandardNavigationFactories` used by the documented shared-navigator recipe diff --git a/README.md b/README.md index 2452dfd3..639f155d 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ https://github.com/user-attachments/assets/09e96ac3-827d-4ac0-add0-e7b88ee9197c | [react-native-bottom-tabs](/packages/react-native-bottom-tabs) | [![badge](https://img.shields.io/npm/v/react-native-bottom-tabs?style=for-the-badge)](https://www.npmjs.com/package/react-native-bottom-tabs) | | [@bottom-tabs/expo-template](/packages/expo-template) | [![badge](https://img.shields.io/npm/v/@bottom-tabs/expo-template.svg?style=for-the-badge)](https://www.npmjs.com/package/@bottom-tabs/expo-template) | | [@bottom-tabs/react-navigation](/packages/react-navigation) | [![badge](https://img.shields.io/npm/v/@bottom-tabs/react-navigation.svg?style=for-the-badge)](https://www.npmjs.com/package/@bottom-tabs/react-navigation) | +| [@bottom-tabs/navigation](/packages/navigation) | [![badge](https://img.shields.io/npm/v/@bottom-tabs/navigation.svg?style=for-the-badge)](https://www.npmjs.com/package/@bottom-tabs/navigation) | ## Documentation diff --git a/apps/expo-router-test/jest.config.js b/apps/expo-router-test/jest.config.js new file mode 100644 index 00000000..5381bc82 --- /dev/null +++ b/apps/expo-router-test/jest.config.js @@ -0,0 +1,32 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +const PROJECT_COPIES = ['react', 'react-dom']; + +const reactNavigationScope = path.dirname( + path.dirname(require.resolve('@react-navigation/native/package.json')) +); + +const reactNavigationCopies = fs + .readdirSync(reactNavigationScope) + .map((name) => [ + `^@react-navigation/${name}($|/.*)`, + `${reactNavigationScope}/${name}$1`, + ]); + +module.exports = { + preset: 'jest-expo', + + setupFilesAfterEnv: ['/jest.setup.ts'], + + moduleNameMapper: { + ...Object.fromEntries( + PROJECT_COPIES.map((name) => [ + `^${name}($|/.*)`, + path.dirname(require.resolve(`${name}/package.json`)) + '$1', + ]) + ), + ...Object.fromEntries(reactNavigationCopies), + }, + +}; diff --git a/apps/expo-router-test/jest.setup.ts b/apps/expo-router-test/jest.setup.ts new file mode 100644 index 00000000..fb9746b2 --- /dev/null +++ b/apps/expo-router-test/jest.setup.ts @@ -0,0 +1,23 @@ +const consoleMethods = ['warn', 'error'] as const; + +let output: string[] = []; + +beforeEach(() => { + output = []; + + for (const method of consoleMethods) { + jest.spyOn(console, method).mockImplementation((...args: unknown[]) => { + output.push(`console.${method}: ${args.join(' ')}`); + }); + } +}); + +afterEach(() => { + const captured = output; + + jest.restoreAllMocks(); + + if (captured.length > 0) { + throw new Error(`Expected no console output, got:\n${captured.join('\n')}`); + } +}); diff --git a/apps/expo-router-test/package.json b/apps/expo-router-test/package.json new file mode 100644 index 00000000..9173be06 --- /dev/null +++ b/apps/expo-router-test/package.json @@ -0,0 +1,38 @@ +{ + "name": "expo-router-test", + "version": "0.0.1", + "private": true, + "scripts": { + "test": "jest", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@bottom-tabs/navigation": "*", + "@bottom-tabs/react-navigation": "*", + "@react-navigation/native": "^7.3.0", + "expo": "~57.0.21", + "expo-constants": "~57.0.17", + "expo-linking": "~57.0.9", + "expo-router": "~57.0.20", + "react": "19.2.3", + "react-dom": "19.2.3", + "react-native": "0.86.3", + "react-native-bottom-tabs": "*", + "react-native-gesture-handler": "~2.32.0", + "react-native-reanimated": "4.5.1", + "react-native-safe-area-context": "~5.7.0", + "react-native-screens": "~4.26.0", + "react-native-web": "~0.21.0", + "react-native-worklets": "0.10.1" + }, + "devDependencies": { + "@react-native/jest-preset": "^0.86.3", + "@testing-library/react-native": "^14.0.1", + "@types/jest": "^29.5.5", + "@types/react": "~19.2.2", + "jest": "^29.7.0", + "jest-expo": "~57.0.5", + "test-renderer": "^1.2.0", + "typescript": "^5.9.2" + } +} diff --git a/apps/expo-router-test/src/__tests__/adapters.test.tsx b/apps/expo-router-test/src/__tests__/adapters.test.tsx new file mode 100644 index 00000000..bcc4a494 --- /dev/null +++ b/apps/expo-router-test/src/__tests__/adapters.test.tsx @@ -0,0 +1,116 @@ +import { NavigationContainer } from '@react-navigation/native'; +import { act, render } from '@testing-library/react-native'; +import { renderRouter } from 'expo-router/testing-library'; +import { Text } from 'react-native'; + +import { ExpoRouterTabs, ReactNavigationTabs } from '../tabs'; + +let tabViewProps: Record; + +jest.mock('react-native-bottom-tabs', () => ({ + __esModule: true, + default: (props: Record) => { + tabViewProps = props; + return null; + }, +})); + +const OPTIONS = { + index: { title: 'Home', tabBarBadge: '3' }, + explore: { title: 'Explore' }, +}; + +const adapters = [ + { + name: 'expo-router', + render: async () => { + await renderRouter( + { + _layout: () => ( + + + + + ), + index: () => index scene, + explore: () => explore scene, + }, + { initialUrl: '/' } + ); + }, + }, + { + name: 'react-navigation', + render: async () => { + await render( + + + + {() => index scene} + + + {() => explore scene} + + + + ); + }, + }, +]; + +beforeEach(() => { + tabViewProps = {}; +}); + +describe.each(adapters)('$name', ({ render: renderAdapter }) => { + const routeNamed = (name: string) => + tabViewProps.navigationState.routes.find( + (it: { name: string }) => it.name === name + ); + + it('drives the shared navigator', async () => { + await renderAdapter(); + + expect( + tabViewProps.navigationState.routes.map((it: { name: string }) => it.name) + ).toEqual(['index', 'explore']); + expect(tabViewProps.navigationState.index).toBe(0); + }); + + it('maps screen options onto the native view', async () => { + await renderAdapter(); + + expect(tabViewProps.getLabelText({ route: routeNamed('index') })).toBe( + 'Home' + ); + expect(tabViewProps.getBadge({ route: routeNamed('index') })).toBe('3'); + expect( + tabViewProps.getBadge({ route: routeNamed('explore') }) + ).toBeUndefined(); + }); + + it('navigates when a tab is pressed', async () => { + await renderAdapter(); + + await act(async () => { + tabViewProps.onIndexChange(1); + }); + + expect(tabViewProps.navigationState.index).toBe(1); + }); + + it('exposes only the declared screens as tabs', async () => { + await renderAdapter(); + + expect(tabViewProps.navigationState.routes).toHaveLength(2); + }); + + it('does not forward router options to the native view', async () => { + await renderAdapter(); + + expect(tabViewProps).not.toHaveProperty('backBehavior'); + }); +}); diff --git a/apps/expo-router-test/src/__tests__/types.test.tsx b/apps/expo-router-test/src/__tests__/types.test.tsx new file mode 100644 index 00000000..b58abd7f --- /dev/null +++ b/apps/expo-router-test/src/__tests__/types.test.tsx @@ -0,0 +1,47 @@ +import { ExpoRouterTabs } from '../tabs'; + +export const tintColors = () => ( + +); + +export const defaultTintColors = () => ( + +); + +export const customTabBar = () => ( + state.routes.map((it) => it.name).join()} + /> +); + +export const nativeProps = () => ( + +); + +export const screenOptions = () => ( + + + +); + +export const rejectsAWrongNavigatorProp = () => ( + // @ts-expect-error - a number is not a valid tint color + +); + +export const rejectsAnUnknownScreenOption = () => ( + + {/* @ts-expect-error - not a screen option this navigator accepts */} + + +); + +it('type checks the navigator surface', () => { + expect(typeof ExpoRouterTabs).toBe('object'); +}); diff --git a/apps/expo-router-test/src/tabs.tsx b/apps/expo-router-test/src/tabs.tsx new file mode 100644 index 00000000..bed6c30b --- /dev/null +++ b/apps/expo-router-test/src/tabs.tsx @@ -0,0 +1,10 @@ +import { NativeBottomTabsContent } from '@bottom-tabs/navigation'; +import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation'; +import { TabRouter, unstable_createStandardRouterNavigator } from 'expo-router'; + +export const ExpoRouterTabs = unstable_createStandardRouterNavigator( + NativeBottomTabsContent, + TabRouter +); + +export const ReactNavigationTabs = createNativeBottomTabNavigator(); diff --git a/apps/expo-router-test/tsconfig.json b/apps/expo-router-test/tsconfig.json new file mode 100644 index 00000000..808fd9fa --- /dev/null +++ b/apps/expo-router-test/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "expo/tsconfig.base", + "compilerOptions": { + "strict": true, + "types": ["jest"] + }, + "include": ["**/*.ts", "**/*.tsx"] +} diff --git a/docs/docs/docs/getting-started/quick-start.mdx b/docs/docs/docs/getting-started/quick-start.mdx index e8de61c8..977a63f0 100644 --- a/docs/docs/docs/getting-started/quick-start.mdx +++ b/docs/docs/docs/getting-started/quick-start.mdx @@ -15,10 +15,14 @@ npx create-expo-app@latest NativeTabs --template @bottom-tabs/expo-template -If you are going to use [React Navigation / Expo Router Integration](/docs/guides/usage-with-react-navigation) make sure to install `@bottom-tabs/react-navigation`. +If you are going to use the [React Navigation integration](/docs/guides/usage-with-react-navigation), also install `@bottom-tabs/react-navigation`. +For [Expo Router](/docs/guides/usage-with-expo-router) on SDK 56 or newer, install `@bottom-tabs/navigation` instead. + + + ### Expo diff --git a/docs/docs/docs/guides/usage-with-expo-router.mdx b/docs/docs/docs/guides/usage-with-expo-router.mdx index 111025c3..f5fa43bd 100644 --- a/docs/docs/docs/guides/usage-with-expo-router.mdx +++ b/docs/docs/docs/guides/usage-with-expo-router.mdx @@ -2,39 +2,41 @@ import { PackageManagerTabs } from '@theme'; # Usage with Expo Router -First install `@bottom-tabs/react-navigation` which provides a native bottom tab navigator for React Navigation. +Which package you need depends on your Expo SDK version, because Expo Router stopped depending on React Navigation starting from SDK 56. - +| Expo SDK | Package | Integration | +| ------------ | ------------------------------- | ---------------------------------------- | +| 56 and newer | `@bottom-tabs/navigation` | `unstable_createStandardRouterNavigator` | +| 55 and older | `@bottom-tabs/react-navigation` | `withLayoutContext` | -Next, create a custom layout adapter for the native bottom tabs using `withLayoutContext` from Expo Router. Put it in `components/bottom-tabs.tsx` and export the navigator, so your layouts can import it: +## Expo SDK 56 and newer -```tsx -import { withLayoutContext } from 'expo-router'; -import { - createNativeBottomTabNavigator, - NativeBottomTabNavigationOptions, - NativeBottomTabNavigationEventMap, -} from '@bottom-tabs/react-navigation'; -import { ParamListBase, TabNavigationState } from '@react-navigation/native'; +Install `@bottom-tabs/navigation`, which provides a native bottom tab navigator built on the [`standard-navigation`](https://github.com/react-navigation/standard-navigation) contract that Expo Router speaks. -const BottomTabNavigator = createNativeBottomTabNavigator().Navigator; + -export const Tabs = withLayoutContext< - NativeBottomTabNavigationOptions, - typeof BottomTabNavigator, - TabNavigationState, - NativeBottomTabNavigationEventMap ->(BottomTabNavigator); -``` +:::warning +Do not install `@bottom-tabs/react-navigation` in an Expo Router 56+ app. Expo Router bundles its own copy of React Navigation. +::: -> [!NOTE] Make sure to provide the correct types for the `withLayoutContext` function. Without it screen options won't have proper types. +Create the navigator by handing the tab view to Expo Router's `unstable_createStandardRouterNavigator`. Put it in `components/bottom-tabs.tsx` and export it, so your layouts can import it: + +```tsx +import { NativeBottomTabsContent } from '@bottom-tabs/navigation'; +import { TabRouter, unstable_createStandardRouterNavigator } from 'expo-router'; + +export const Tabs = unstable_createStandardRouterNavigator( + NativeBottomTabsContent, + TabRouter +); +``` -Then, use the `Tabs` navigator in your app: +Then use it in your layout: ```tsx -import { Platform } from "react-native"; +import { Platform } from 'react-native'; -import { Tabs } from "@/components/bottom-tabs"; +import { Tabs } from '@/components/bottom-tabs'; export default function TabLayout() { return ( @@ -42,21 +44,21 @@ export default function TabLayout() { - Platform.OS === "ios" - ? { sfSymbol: "house" } - : require("@/assets/icons/house.png"), + Platform.OS === 'ios' + ? { sfSymbol: 'house' } + : require('@/assets/icons/house.png'), }} /> - Platform.OS === "ios" - ? { sfSymbol: "person" } - : require("@/assets/icons/person.png"), + Platform.OS === 'ios' + ? { sfSymbol: 'person' } + : require('@/assets/icons/person.png'), }} /> @@ -66,7 +68,59 @@ export default function TabLayout() { > [!NOTE] SF Symbols are only available on Apple platforms. On Android and web, pass an image with `require()` or a `{ uri }` object instead. -For props and more information, see the [React Navigation integration guide](/docs/guides/usage-with-react-navigation), which documents every accepted `tabBarIcon` source. +> [!NOTE] `unstable_createStandardRouterNavigator` is marked unstable by Expo and may change between minor releases. + +### Tint colors + +There is no theme in the Expo Router integration, so the tab bar falls back to platform defaults. Pass `tabBarActiveTintColor` and `tabBarInactiveTintColor` on the navigator to set them yourself. + +### Custom tab bar + +The `tabBar` function receives `state`, `descriptors`, `actions` and `emitter` - the vocabulary of the navigation contract rather than a React Navigation `navigation` object. If your tab bar needs the router, call `useNavigation()` from `expo-router` inside it. + +`BottomTabBar` from `@react-navigation/bottom-tabs` cannot be used here, since it expects a React Navigation `navigation` object. + +## Expo SDK 55 and older + +Install `@bottom-tabs/react-navigation`, which provides a native bottom tab navigator for React Navigation. + + + +Create a custom layout adapter using `withLayoutContext` from Expo Router. Put it in `components/bottom-tabs.tsx` and export the navigator, so your layouts can import it: + +```tsx +import { withLayoutContext } from 'expo-router'; +import { + createNativeBottomTabNavigator, + NativeBottomTabNavigationOptions, + NativeBottomTabNavigationEventMap, +} from '@bottom-tabs/react-navigation'; +import { ParamListBase, TabNavigationState } from '@react-navigation/native'; + +const BottomTabNavigator = createNativeBottomTabNavigator().Navigator; + +export const Tabs = withLayoutContext< + NativeBottomTabNavigationOptions, + typeof BottomTabNavigator, + TabNavigationState, + NativeBottomTabNavigationEventMap +>(BottomTabNavigator); +``` + +> [!NOTE] Make sure to provide the correct types for the `withLayoutContext` function. Without it screen options won't have proper types. + +The layout itself is the same as above. + +### Upgrading to SDK 56 or newer + +On SDK 56+ this setup loads a second copy of React Navigation, whose contexts do not line up with the router's. + +To upgrade: + +1. Replace `@bottom-tabs/react-navigation` with `@bottom-tabs/navigation`. +2. Swap `withLayoutContext` for `unstable_createStandardRouterNavigator` as shown above. The generic type arguments are no longer needed. +3. Uninstall the `@react-navigation/*` packages, and import from `expo-router/react-navigation` instead. That entry point re-exports Expo Router's own copy of React Navigation. -Example: [okwasniewski/ExpoNativeTabs](https://github.com/okwasniewski/ExpoNativeTabs) +## Options and props +For props and more information, see the [React Navigation integration guide](/docs/guides/usage-with-react-navigation), which documents every accepted `tabBarIcon` source. Screen options are identical across both integrations. diff --git a/docs/docs/docs/guides/usage-with-react-navigation.mdx b/docs/docs/docs/guides/usage-with-react-navigation.mdx index 78e49d55..92d488a8 100644 --- a/docs/docs/docs/guides/usage-with-react-navigation.mdx +++ b/docs/docs/docs/guides/usage-with-react-navigation.mdx @@ -491,3 +491,61 @@ import { BottomTabBarHeightContext } from 'react-native-bottom-tabs'; )} ``` + +## Using the shared navigator directly + +`createNativeBottomTabNavigator` is a thin wrapper over the framework-agnostic navigator in `@bottom-tabs/navigation`. If you need a different router, or want to supply your own props, wire it up yourself: + +```tsx +import { + nativeBottomTabsNavigator, + type NativeBottomTabNavigationOptions, + type NativeBottomTabsEventMap, + type NativeBottomTabsNavigatorProps, +} from '@bottom-tabs/navigation'; +import { + createStandardNavigationFactories, + TabRouter, + type ParamListBase, + type StandardNavigationTypeBagBase, + type TabActionHelpers, + type TabNavigationState, + type TabRouterOptions, +} from '@react-navigation/native'; +import type { ComponentType } from 'react'; + +interface NativeBottomTabsTypeBag extends StandardNavigationTypeBagBase { + State: TabNavigationState; + ScreenOptions: NativeBottomTabNavigationOptions; + EventMap: NativeBottomTabsEventMap; + RouterOptions: TabRouterOptions; + ActionHelpers: TabActionHelpers; + Navigator: ComponentType; +} + +const { createNavigator } = createStandardNavigationFactories< + NativeBottomTabsTypeBag, + NativeBottomTabsNavigatorProps +>(nativeBottomTabsNavigator, TabRouter); + +const Tab = createNavigator(); +``` + +This requires `@react-navigation/native` 7.3.0 or newer. + +> [!NOTE] `createStandardNavigationFactories` cannot infer it, so calling it without the type arguments leaves screen options as `{}` and makes every navigator prop a type error. `Navigator` is what carries the navigator props through to `Tab.Navigator`; swap `State`, `RouterOptions` and `ActionHelpers` to match whichever router you pass. + +Wiring it yourself skips the theme, so the tab bar falls back to platform defaults. Pass `defaultTintColors` to supply your own fallbacks, either as a prop on `Tab.Navigator` or from a mapper: + +```tsx +import { useTheme } from '@react-navigation/native'; + +const { createNavigator } = createStandardNavigationFactories< + NativeBottomTabsTypeBag, + NativeBottomTabsNavigatorProps +>(nativeBottomTabsNavigator, TabRouter, () => { + const { colors } = useTheme(); + + return { defaultTintColors: { active: colors.primary } }; +}); +``` diff --git a/packages/navigation/README.md b/packages/navigation/README.md new file mode 100644 index 00000000..1bf65098 --- /dev/null +++ b/packages/navigation/README.md @@ -0,0 +1,5 @@ +# `@bottom-tabs/navigation` + +Framework-agnostic native bottom tabs navigator for `react-native-bottom-tabs`. + +Installation instructions and documentation can be found on the [React Native Bottom Tabs website](https://oss.callstack.com/react-native-bottom-tabs/). diff --git a/packages/navigation/babel.config.js b/packages/navigation/babel.config.js new file mode 100644 index 00000000..29f3a606 --- /dev/null +++ b/packages/navigation/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + ['module:react-native-builder-bob/babel-preset', { modules: 'commonjs' }], + ], +}; diff --git a/packages/navigation/package.json b/packages/navigation/package.json new file mode 100644 index 00000000..27001c66 --- /dev/null +++ b/packages/navigation/package.json @@ -0,0 +1,108 @@ +{ + "name": "@bottom-tabs/navigation", + "version": "1.4.0", + "description": "Framework-agnostic native bottom tabs navigator built on the standard-navigation contract", + "source": "./src/index.tsx", + "main": "./lib/commonjs/index.js", + "module": "./lib/module/index.js", + "types": "./lib/typescript/commonjs/src/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "import": { + "types": "./lib/typescript/module/src/index.d.ts", + "default": "./lib/module/index.js" + }, + "require": { + "types": "./lib/typescript/commonjs/src/index.d.ts", + "default": "./lib/commonjs/index.js" + } + }, + "./package.json": "./package.json" + }, + "files": [ + "src", + "lib", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__", + "!**/.*" + ], + "scripts": { + "test": "jest", + "typecheck": "tsc -b", + "lint": "eslint \"**/*.{js,ts,tsx}\"", + "build": "bob build" + }, + "keywords": [ + "react-native", + "ios", + "android", + "standard-navigation", + "react-navigation", + "expo-router" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/callstack/react-native-bottom-tabs.git" + }, + "author": "Oskar Kwasniewski (https://github.com/okwasniewski)", + "license": "MIT", + "bugs": { + "url": "https://github.com/callstack/react-native-bottom-tabs/issues" + }, + "homepage": "https://github.com/callstack/react-native-bottom-tabs#readme", + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "devDependencies": { + "@testing-library/react-native": "^14.0.1", + "@types/jest": "^29.5.5", + "jest": "^29.7.0", + "react": "^19.1.0", + "react-native": "0.81.1", + "react-native-bottom-tabs": "*", + "react-native-builder-bob": "^0.40.13", + "standard-navigation": "^0.0.9", + "test-renderer": "^1.2.0", + "typescript": "^5.9.2" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-bottom-tabs": "*" + }, + "jest": { + "preset": "react-native", + "modulePathIgnorePatterns": [ + "/lib/" + ] + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + [ + "commonjs", + { + "esm": true, + "configFile": true + } + ], + [ + "module", + { + "esm": true, + "configFile": true + } + ], + [ + "typescript", + { + "project": "tsconfig.build.json" + } + ] + ] + } +} diff --git a/packages/navigation/src/NativeBottomTabsContent.tsx b/packages/navigation/src/NativeBottomTabsContent.tsx new file mode 100644 index 00000000..e86a84e7 --- /dev/null +++ b/packages/navigation/src/NativeBottomTabsContent.tsx @@ -0,0 +1,112 @@ +import TabView from 'react-native-bottom-tabs'; + +import type { NativeBottomTabsContentProps, NavigatorRoute } from './types'; + +export default function NativeBottomTabsContent({ + state, + descriptors, + actions, + emitter, + tabBar, + tabBarActiveTintColor, + tabBarInactiveTintColor, + defaultTintColors, + extraTabBarProps, + backBehavior: _backBehavior, + ...rest +}: NativeBottomTabsContentProps & { backBehavior?: unknown }) { + if (state.routes.length === 0) { + return null; + } + + const optionsFor = (route: NavigatorRoute) => descriptors[route.key]?.options; + + return ( + descriptors[route.key]?.render()} + getActiveTintColor={({ route }) => + optionsFor(route)?.tabBarActiveTintColor + } + getLabelText={({ route }) => { + const options = optionsFor(route); + + return options?.tabBarLabel !== undefined + ? options.tabBarLabel + : options?.title !== undefined + ? options.title + : route.name; + }} + getBadge={({ route }) => optionsFor(route)?.tabBarBadge} + getBadgeBackgroundColor={({ route }) => + optionsFor(route)?.tabBarBadgeBackgroundColor + } + getBadgeTextColor={({ route }) => optionsFor(route)?.tabBarBadgeTextColor} + getHidden={({ route }) => optionsFor(route)?.tabBarItemHidden === true} + getTestID={({ route }) => optionsFor(route)?.tabBarButtonTestID} + getRole={({ route }) => optionsFor(route)?.role} + getIconRenderingMode={({ route }) => + optionsFor(route)?.tabBarIconRenderingMode + } + tabBar={ + tabBar + ? () => + tabBar({ + state, + descriptors, + actions, + emitter, + ...extraTabBarProps, + }) + : undefined + } + getIcon={({ route, focused }) => { + const tabBarIcon = optionsFor(route)?.tabBarIcon; + + return tabBarIcon ? tabBarIcon({ focused }) : null; + }} + getLazy={({ route }) => optionsFor(route)?.lazy ?? true} + getFreezeOnBlur={({ route }) => optionsFor(route)?.freezeOnBlur} + getSceneStyle={({ route }) => optionsFor(route)?.sceneStyle} + getPreventsDefault={({ route }) => optionsFor(route)?.preventsDefault} + onTabLongPress={(index) => { + const route = state.routes[index]; + + if (!route) { + return; + } + + emitter.emit({ type: 'tabLongPress', target: route.key }); + }} + onIndexChange={(index) => { + const focused = index === state.index; + const route = state.routes[index]; + + if (!route) { + return; + } + + const event = emitter.emit({ + type: 'tabPress', + target: route.key, + canPreventDefault: true, + }); + + if ( + focused || + event.defaultPrevented || + optionsFor(route)?.preventsDefault + ) { + return; + } + + actions.navigate(route.name, route.params); + }} + /> + ); +} diff --git a/packages/navigation/src/__tests__/NativeBottomTabsContent.test.tsx b/packages/navigation/src/__tests__/NativeBottomTabsContent.test.tsx new file mode 100644 index 00000000..4deff56f --- /dev/null +++ b/packages/navigation/src/__tests__/NativeBottomTabsContent.test.tsx @@ -0,0 +1,302 @@ +import { render } from '@testing-library/react-native'; + +import NativeBottomTabsContent from '../NativeBottomTabsContent'; +import type { + NativeBottomTabNavigationOptions, + NativeBottomTabsNavigatorProps, + NavigatorRoute, +} from '../types'; + +let tabViewProps: Record; + +jest.mock('react-native-bottom-tabs', () => ({ + __esModule: true, + default: (props: Record) => { + tabViewProps = props; + return null; + }, +})); + +const ROUTES = ['Home', 'Explore'] as const; + +type RouteName = (typeof ROUTES)[number]; + +const route = (name: RouteName): NavigatorRoute => ({ + key: `${name}-key`, + name, + params: undefined, + href: undefined, +}); + +const renderContent = async ({ + index = 0, + routes = ROUTES.map(route), + options = {}, + defaultPrevented = false, + ...props +}: NativeBottomTabsNavigatorProps & { + index?: number; + routes?: NavigatorRoute[]; + options?: Partial>; + defaultPrevented?: boolean; + backBehavior?: unknown; +} = {}) => { + const actions = { navigate: jest.fn(), back: jest.fn() }; + const emitter = { + emit: jest.fn((event: { type: string; target?: string }) => ({ + ...event, + defaultPrevented, + })), + }; + + const descriptors = Object.fromEntries( + routes.map((it) => [ + it.key, + { + options: options[it.name as RouteName] ?? {}, + render: () => `${it.name} scene`, + }, + ]) + ); + + await render( + + ); + + return { actions, emitter }; +}; + +beforeEach(() => { + tabViewProps = {}; +}); + +describe('navigation state', () => { + it('passes the contract state straight through to the tab view', async () => { + await renderContent(); + + expect(tabViewProps.navigationState.index).toBe(0); + expect( + tabViewProps.navigationState.routes.map((it: NavigatorRoute) => it.name) + ).toEqual(['Home', 'Explore']); + }); + + it('renders a scene through the descriptor', async () => { + await renderContent(); + + expect(tabViewProps.renderScene({ route: route('Home') })).toBe( + 'Home scene' + ); + }); + + it('renders nothing when there are no routes', async () => { + await renderContent({ routes: [] }); + + expect(tabViewProps.navigationState).toBeUndefined(); + }); +}); + +describe('tint colors', () => { + it('falls back to the defaults supplied by the integrator', async () => { + await renderContent({ + defaultTintColors: { active: '#ff0000', inactive: '#808080' }, + }); + + expect(tabViewProps.tabBarActiveTintColor).toBe('#ff0000'); + expect(tabViewProps.tabBarInactiveTintColor).toBe('#808080'); + }); + + it('lets explicit props win over the defaults', async () => { + await renderContent({ + defaultTintColors: { active: '#ff0000', inactive: '#808080' }, + tabBarActiveTintColor: '#00ff00', + tabBarInactiveTintColor: '#0000ff', + }); + + expect(tabViewProps.tabBarActiveTintColor).toBe('#00ff00'); + expect(tabViewProps.tabBarInactiveTintColor).toBe('#0000ff'); + }); +}); + +describe('router options', () => { + it('does not forward backBehavior to the native view', async () => { + await renderContent({ backBehavior: 'history' }); + + expect(tabViewProps).not.toHaveProperty('backBehavior'); + }); +}); + +describe('screen options', () => { + it.each([ + ['getBadge', 'tabBarBadge', '3'], + ['getBadgeBackgroundColor', 'tabBarBadgeBackgroundColor', '#ff0000'], + ['getBadgeTextColor', 'tabBarBadgeTextColor', '#00ff00'], + ['getTestID', 'tabBarButtonTestID', 'home-tab'], + ['getRole', 'role', 'search'], + ['getIconRenderingMode', 'tabBarIconRenderingMode', 'original'], + ['getActiveTintColor', 'tabBarActiveTintColor', '#123456'], + ['getFreezeOnBlur', 'freezeOnBlur', true], + ['getSceneStyle', 'sceneStyle', { backgroundColor: 'red' }], + ['getPreventsDefault', 'preventsDefault', true], + ] as const)('maps %s from the %s option', async (getter, option, value) => { + await renderContent({ options: { Home: { [option]: value } } }); + + expect(tabViewProps[getter]({ route: route('Home') })).toEqual(value); + expect(tabViewProps[getter]({ route: route('Explore') })).toBeUndefined(); + }); + + it('reports a tab as hidden only when tabBarItemHidden is true', async () => { + await renderContent({ options: { Home: { tabBarItemHidden: true } } }); + + expect(tabViewProps.getHidden({ route: route('Home') })).toBe(true); + expect(tabViewProps.getHidden({ route: route('Explore') })).toBe(false); + }); + + it('defaults lazy to true', async () => { + await renderContent({ options: { Home: { lazy: false } } }); + + expect(tabViewProps.getLazy({ route: route('Home') })).toBe(false); + expect(tabViewProps.getLazy({ route: route('Explore') })).toBe(true); + }); + + it('calls tabBarIcon with the focused state', async () => { + const tabBarIcon = jest.fn(() => ({ sfSymbol: 'house' }) as const); + + await renderContent({ options: { Home: { tabBarIcon } } }); + + expect( + tabViewProps.getIcon({ route: route('Home'), focused: true }) + ).toEqual({ sfSymbol: 'house' }); + expect(tabBarIcon).toHaveBeenCalledWith({ focused: true }); + }); + + it('returns no icon when the option is unset', async () => { + await renderContent(); + + expect( + tabViewProps.getIcon({ route: route('Home'), focused: false }) + ).toBeNull(); + }); +}); + +describe('label', () => { + it('prefers tabBarLabel over title', async () => { + await renderContent({ + options: { Home: { tabBarLabel: 'Label', title: 'Title' } }, + }); + + expect(tabViewProps.getLabelText({ route: route('Home') })).toBe('Label'); + }); + + it('falls back to title when tabBarLabel is unset', async () => { + await renderContent({ options: { Home: { title: 'Title' } } }); + + expect(tabViewProps.getLabelText({ route: route('Home') })).toBe('Title'); + }); + + it('falls back to the route name when neither is set', async () => { + await renderContent(); + + expect(tabViewProps.getLabelText({ route: route('Home') })).toBe('Home'); + }); +}); + +describe('tab press', () => { + it('emits tabPress and navigates to the pressed tab', async () => { + const { actions, emitter } = await renderContent(); + + tabViewProps.onIndexChange(1); + + expect(emitter.emit).toHaveBeenCalledWith({ + type: 'tabPress', + target: 'Explore-key', + canPreventDefault: true, + }); + expect(actions.navigate).toHaveBeenCalledWith('Explore', undefined); + }); + + it('does not navigate when the event default was prevented', async () => { + const { actions } = await renderContent({ defaultPrevented: true }); + + tabViewProps.onIndexChange(1); + + expect(actions.navigate).not.toHaveBeenCalled(); + }); + + it('does not navigate when the screen sets preventsDefault', async () => { + const { actions } = await renderContent({ + options: { Explore: { preventsDefault: true } }, + }); + + tabViewProps.onIndexChange(1); + + expect(actions.navigate).not.toHaveBeenCalled(); + }); + + it('emits tabPress on the focused tab but does not re-navigate', async () => { + const { actions, emitter } = await renderContent(); + + tabViewProps.onIndexChange(0); + + expect(emitter.emit).toHaveBeenCalledTimes(1); + expect(actions.navigate).not.toHaveBeenCalled(); + }); + + it('ignores an out-of-range index', async () => { + const { actions, emitter } = await renderContent(); + + tabViewProps.onIndexChange(99); + + expect(emitter.emit).not.toHaveBeenCalled(); + expect(actions.navigate).not.toHaveBeenCalled(); + }); +}); + +describe('tab long press', () => { + it('emits tabLongPress for the pressed tab', async () => { + const { emitter } = await renderContent(); + + tabViewProps.onTabLongPress(1); + + expect(emitter.emit).toHaveBeenCalledWith({ + type: 'tabLongPress', + target: 'Explore-key', + }); + }); + + it('ignores an out-of-range index', async () => { + const { emitter } = await renderContent(); + + tabViewProps.onTabLongPress(99); + + expect(emitter.emit).not.toHaveBeenCalled(); + }); +}); + +describe('custom tab bar', () => { + it('is called with the contract vocabulary', async () => { + const tabBar = jest.fn(() => null); + + const { actions, emitter } = await renderContent({ tabBar }); + + tabViewProps.tabBar(); + + expect(tabBar).toHaveBeenCalledWith({ + state: { index: 0, routes: ROUTES.map(route) }, + descriptors: expect.any(Object), + actions, + emitter, + }); + }); + + it('is left undefined when no custom tab bar is given', async () => { + await renderContent(); + + expect(tabViewProps.tabBar).toBeUndefined(); + }); +}); diff --git a/packages/navigation/src/__tests__/conformance.test.ts b/packages/navigation/src/__tests__/conformance.test.ts new file mode 100644 index 00000000..3981e82e --- /dev/null +++ b/packages/navigation/src/__tests__/conformance.test.ts @@ -0,0 +1,25 @@ +import type { StandardNavigator } from 'standard-navigation'; + +import { nativeBottomTabsNavigator } from '../index'; +import type { + NativeBottomTabNavigationOptions, + NativeBottomTabsEventMap, + NativeBottomTabsNavigatorProps, +} from '../types'; + +jest.mock('react-native-bottom-tabs', () => ({ + __esModule: true, + default: () => null, +})); + +const conformsToTheContract: StandardNavigator< + NativeBottomTabNavigationOptions, + NativeBottomTabsEventMap, + NativeBottomTabsNavigatorProps +> = nativeBottomTabsNavigator; + +it('declares a navigator matching the published standard-navigation contract', () => { + expect(conformsToTheContract.type).toBe('standard'); + expect(conformsToTheContract.version).toBe(1); + expect(typeof conformsToTheContract.NavigatorContent).toBe('function'); +}); diff --git a/packages/navigation/src/index.tsx b/packages/navigation/src/index.tsx new file mode 100644 index 00000000..fa9a3bf4 --- /dev/null +++ b/packages/navigation/src/index.tsx @@ -0,0 +1,25 @@ +import NativeBottomTabsContent from './NativeBottomTabsContent'; + +export const nativeBottomTabsNavigator = { + type: 'standard', + version: 1, + NavigatorContent: NativeBottomTabsContent, +} as const; + +export { default as NativeBottomTabsContent } from './NativeBottomTabsContent'; + +export type { + NativeBottomTabNavigationOptions, + NativeBottomTabsContentProps, + NativeBottomTabsDescriptorMap, + NativeBottomTabsEventMap, + NativeBottomTabsInferenceCarrier, + NativeBottomTabsNavigatorProps, + NativeBottomTabsTabBarProps, + NavigatorActions, + NavigatorArgs, + NavigatorDescriptor, + NavigatorEmitter, + NavigatorRoute, + NavigatorState, +} from './types'; diff --git a/packages/navigation/src/types.ts b/packages/navigation/src/types.ts new file mode 100644 index 00000000..424a4eeb --- /dev/null +++ b/packages/navigation/src/types.ts @@ -0,0 +1,190 @@ +import type { ImageSourcePropType, StyleProp, ViewStyle } from 'react-native'; +import type TabView from 'react-native-bottom-tabs'; +import type { + AppleIcon, + IconRenderingMode, + TabRole, +} from 'react-native-bottom-tabs'; + +export type NavigatorRoute = { + key: string; + name: string; + params?: object | undefined; + href?: string | undefined; +}; + +export type NavigatorState = { + index: number; + routes: NavigatorRoute[]; +}; + +export type NavigatorDescriptor = { + options: Options; + render: () => React.ReactNode; +}; + +export type NavigatorActions = { + navigate(name: string, params?: object | undefined): void; + back(): void; +}; + +export type NavigatorEmitter = { + emit(options: { + type: string; + target?: string; + canPreventDefault?: boolean; + data?: object | undefined; + }): { + readonly type: string; + readonly target?: string | undefined; + readonly defaultPrevented?: boolean; + }; +}; + +export type NavigatorArgs = { + state: NavigatorState; + descriptors: Record>; + actions: NavigatorActions; + emitter: NavigatorEmitter; +}; + +export type NativeBottomTabsEventMap = { + /** + * Event which fires on tapping on the tab in the tab bar. + */ + tabPress: { data: undefined; canPreventDefault: true }; + /** + * Event which fires on long press on tab bar. + */ + tabLongPress: { data: undefined; canPreventDefault: false }; +}; + +export type NativeBottomTabNavigationOptions = { + /** + * Title text for the screen. + */ + title?: string; + + /** + * Label text of the tab displayed in the navigation bar. When undefined, scene title is used. + */ + tabBarLabel?: string; + + /** + * Function that given { focused: boolean } returns ImageSource or AppleIcon to display in the navigation bar. + */ + tabBarIcon?: (props: { focused: boolean }) => ImageSourcePropType | AppleIcon; + + /** + * Rendering mode for the tab icon. Use `original` to preserve multicolor image icons. + */ + tabBarIconRenderingMode?: IconRenderingMode; + + /** + * Whether the tab bar item is visible. Defaults to true. + */ + tabBarItemHidden?: boolean; + + /** + * Badge to show on the tab icon. + */ + tabBarBadge?: string; + + /** + * Badge background color. (Android only) + */ + tabBarBadgeBackgroundColor?: string; + + /** + * Badge text color. (Android only) + */ + tabBarBadgeTextColor?: string; + + /** + * Whether this screens should render the first time it's accessed. Defaults to true. Set it to false if you want to render the screen on initial render. + */ + lazy?: boolean; + + /** + * Active tab color. + */ + tabBarActiveTintColor?: string; + + /** + * TestID for the tab. + */ + tabBarButtonTestID?: string; + + /** + * Role for the tab. (iOS only) + */ + role?: TabRole; + + /** + * Whether inactive screens should be suspended from re-rendering. Defaults to `false`. + */ + freezeOnBlur?: boolean; + + /** + * Style object for the component wrapping the screen content. + */ + sceneStyle?: StyleProp; + + /** + * Whether to prevent default action of the tab. Defaults to `false`. + */ + preventsDefault?: boolean; +}; + +export type NativeBottomTabsDescriptorMap = Record< + string, + NavigatorDescriptor +>; + +export type NativeBottomTabsTabBarProps = { + state: NavigatorState; + descriptors: NativeBottomTabsDescriptorMap; + actions: NavigatorActions; + emitter: NavigatorEmitter; +}; + +export type NativeBottomTabsNavigatorProps = Partial< + Omit< + React.ComponentProps, + | 'navigationState' + | 'onIndexChange' + | 'renderScene' + | 'getLazy' + | 'getIcon' + | 'getIconRenderingMode' + | 'getLabelText' + | 'getBadge' + | 'getBadgeBackgroundColor' + | 'getBadgeTextColor' + | 'onTabLongPress' + | 'getActiveTintColor' + | 'getTestID' + | 'getRole' + | 'getHidden' + | 'tabBar' + | 'getFreezeOnBlur' + | 'getSceneStyle' + | 'getPreventsDefault' + > +> & { + tabBar?: (props: NativeBottomTabsTabBarProps) => React.ReactNode; + defaultTintColors?: { active?: string; inactive?: string }; + extraTabBarProps?: object; +}; + +export type NativeBottomTabsInferenceCarrier = { + /** @internal */ + readonly __eventMap__?: NativeBottomTabsEventMap; + /** @internal */ + readonly __navigatorProps__?: NativeBottomTabsNavigatorProps; +}; + +export type NativeBottomTabsContentProps = + NavigatorArgs & + NativeBottomTabsNavigatorProps & + NativeBottomTabsInferenceCarrier; diff --git a/packages/navigation/tsconfig.build.json b/packages/navigation/tsconfig.build.json new file mode 100644 index 00000000..543a7876 --- /dev/null +++ b/packages/navigation/tsconfig.build.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "paths": {} + } +} diff --git a/packages/navigation/tsconfig.json b/packages/navigation/tsconfig.json new file mode 100644 index 00000000..f622c26f --- /dev/null +++ b/packages/navigation/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig", + "references": [{ "path": "../react-native-bottom-tabs" }], + "compilerOptions": { + "types": ["jest"], + "rootDir": ".", + "outDir": "./lib/typescript" + } +} diff --git a/packages/react-navigation/package.json b/packages/react-navigation/package.json index ebf38249..db7058e7 100644 --- a/packages/react-navigation/package.json +++ b/packages/react-navigation/package.json @@ -29,7 +29,7 @@ "!**/.*" ], "scripts": { - "test": "jest --passWithNoTests", + "test": "jest", "typecheck": "tsc -b", "lint": "eslint \"**/*.{js,ts,tsx}\"", "build": "bob build" @@ -57,28 +57,35 @@ }, "devDependencies": { "@react-navigation/native": "^7.3.0", - "@types/color": "^4.2.0", + "@testing-library/react-native": "^14.0.1", + "@types/jest": "^29.5.5", "jest": "^29.7.0", "react": "^19.1.0", "react-native": "0.81.1", "react-native-bottom-tabs": "*", "react-native-builder-bob": "^0.40.13", + "test-renderer": "^1.2.0", "typescript": "^5.9.2" }, "dependencies": { - "color": "^5.0.0" + "@bottom-tabs/navigation": "1.4.0" }, "peerDependencies": { - "@react-navigation/native": ">=7", + "@react-navigation/native": ">=7.3.0", "react": "*", "react-native": "*", "react-native-bottom-tabs": "*" }, "jest": { "preset": "react-native", + "moduleNameMapper": { + "^@bottom-tabs/navigation$": "/../navigation/src/index.tsx" + }, "modulePathIgnorePatterns": [ - "/example/node_modules", "/lib/" + ], + "transformIgnorePatterns": [ + "node_modules/(?!((jest-)?react-native|@react-native(-community)?|@react-navigation|standard-navigation)/)" ] }, "react-native-builder-bob": { diff --git a/packages/react-navigation/src/__tests__/NativeBottomTabView.test.tsx b/packages/react-navigation/src/__tests__/NativeBottomTabView.test.tsx new file mode 100644 index 00000000..bdb84be7 --- /dev/null +++ b/packages/react-navigation/src/__tests__/NativeBottomTabView.test.tsx @@ -0,0 +1,97 @@ +import { render } from '@testing-library/react-native'; + +import NativeBottomTabView from '../views/NativeBottomTabView'; + +let tabViewProps: Record; + +jest.mock('react-native-bottom-tabs', () => ({ + __esModule: true, + default: (props: Record) => { + tabViewProps = props; + return null; + }, +})); + +const state = { + key: 'tab-state', + index: 0, + routeNames: ['Home', 'Explore'], + routes: [ + { key: 'Home-key', name: 'Home' }, + { key: 'Explore-key', name: 'Explore' }, + ], + type: 'tab' as const, + stale: false as const, + history: [], + preloadedRouteKeys: [], +}; + +const renderView = async (props: Record = {}) => { + const navigation = { + dispatch: jest.fn(), + goBack: jest.fn(), + emit: jest.fn(() => ({ defaultPrevented: false })), + }; + + const descriptors = Object.fromEntries( + state.routes.map((route) => [ + route.key, + { options: {}, render: () => `${route.name} scene` }, + ]) + ); + + await render( + + ); + + return { navigation }; +}; + +beforeEach(() => { + tabViewProps = {}; +}); + +it('renders the tab view from React Navigation props', async () => { + await renderView(); + + expect(tabViewProps.navigationState.index).toBe(0); + expect(tabViewProps.getLabelText({ route: state.routes[0] })).toBe('Home'); +}); + +it('dispatches a navigate action targeting the navigator state', async () => { + const { navigation } = await renderView(); + + tabViewProps.onIndexChange(1); + + expect(navigation.dispatch).toHaveBeenCalledWith( + expect.objectContaining({ target: 'tab-state' }) + ); +}); + +it('emits tab events through the navigation helpers', async () => { + const { navigation } = await renderView(); + + tabViewProps.onTabLongPress(1); + + expect(navigation.emit).toHaveBeenCalledWith({ + type: 'tabLongPress', + target: 'Explore-key', + }); +}); + +it('passes navigation to a custom tab bar', async () => { + const tabBar = jest.fn(() => null); + + const { navigation } = await renderView({ tabBar }); + + tabViewProps.tabBar(); + + expect(tabBar).toHaveBeenCalledWith( + expect.objectContaining({ navigation, descriptors: expect.any(Object) }) + ); +}); diff --git a/packages/react-navigation/src/__tests__/createNativeBottomTabNavigator.test.tsx b/packages/react-navigation/src/__tests__/createNativeBottomTabNavigator.test.tsx new file mode 100644 index 00000000..4846c1b2 --- /dev/null +++ b/packages/react-navigation/src/__tests__/createNativeBottomTabNavigator.test.tsx @@ -0,0 +1,357 @@ +import { + type Theme, + DefaultTheme, + NavigationContainer, +} from '@react-navigation/native'; +import { act, render } from '@testing-library/react-native'; +import * as React from 'react'; +import { Text } from 'react-native'; + +import createNativeBottomTabNavigator from '../navigators/createNativeBottomTabNavigator'; +import type { + BottomTabBarProps, + NativeBottomTabNavigationOptions, +} from '../types'; + +/** + * These tests assert on the props the navigator hands to the native `TabView`, + * which is the contract every consumer ultimately depends on. + */ +let tabViewProps: Record; + +jest.mock('react-native-bottom-tabs', () => ({ + __esModule: true, + default: (props: Record) => { + tabViewProps = props; + return null; + }, +})); + +const Tab = createNativeBottomTabNavigator(); + +const ROUTES = ['Home', 'Explore'] as const; + +type NavigatorProps = Partial> & { + theme?: Theme; +}; + +const renderNavigator = async ({ + screenOptions, + options = {}, + theme, + ...navigatorProps +}: NavigatorProps & { + options?: Partial< + Record<(typeof ROUTES)[number], NativeBottomTabNavigationOptions> + >; +} = {}) => { + await render( + + + {ROUTES.map((name) => ( + + {() => {name} screen} + + ))} + + + ); +}; + +/** Every `getX` prop is called by `TabView` with the route it is rendering. */ +const forRoute = (name: (typeof ROUTES)[number]) => { + const route = tabViewProps.navigationState.routes.find( + (it: { name: string }) => it.name === name + ); + + if (!route) { + throw new Error(`No route named '${name}' in the navigation state.`); + } + + return route; +}; + +beforeEach(() => { + tabViewProps = {}; +}); + +describe('navigator props', () => { + it('rejects a prop of the wrong type', () => { + type TintColor = React.ComponentProps< + typeof Tab.Navigator + >['tabBarActiveTintColor']; + + // @ts-expect-error - a number is not a valid tint color + const invalidTintColor: TintColor = 42; + + expect(invalidTintColor).toBe(42); + }); +}); + +describe('navigation state', () => { + it('passes the router state straight through to the tab view', async () => { + await renderNavigator(); + + expect(tabViewProps.navigationState.index).toBe(0); + expect( + tabViewProps.navigationState.routes.map((it: { name: string }) => it.name) + ).toEqual(['Home', 'Explore']); + }); + + it('renders a scene through the descriptor', async () => { + await renderNavigator(); + + expect(tabViewProps.renderScene({ route: forRoute('Home') })).toBeTruthy(); + }); +}); + +describe('screen options', () => { + it.each([ + ['getBadge', 'tabBarBadge', '3'], + ['getBadgeBackgroundColor', 'tabBarBadgeBackgroundColor', '#ff0000'], + ['getBadgeTextColor', 'tabBarBadgeTextColor', '#00ff00'], + ['getTestID', 'tabBarButtonTestID', 'home-tab'], + ['getRole', 'role', 'search'], + ['getIconRenderingMode', 'tabBarIconRenderingMode', 'original'], + ['getActiveTintColor', 'tabBarActiveTintColor', '#123456'], + ['getFreezeOnBlur', 'freezeOnBlur', true], + ['getSceneStyle', 'sceneStyle', { backgroundColor: 'red' }], + ['getPreventsDefault', 'preventsDefault', true], + ] as const)('maps %s from the %s option', async (getter, option, value) => { + await renderNavigator({ options: { Home: { [option]: value } } }); + + expect(tabViewProps[getter]({ route: forRoute('Home') })).toEqual(value); + expect( + tabViewProps[getter]({ route: forRoute('Explore') }) + ).toBeUndefined(); + }); + + it('reports a tab as hidden only when tabBarItemHidden is true', async () => { + await renderNavigator({ options: { Home: { tabBarItemHidden: true } } }); + + expect(tabViewProps.getHidden({ route: forRoute('Home') })).toBe(true); + expect(tabViewProps.getHidden({ route: forRoute('Explore') })).toBe(false); + }); + + it('defaults lazy to true', async () => { + await renderNavigator({ options: { Home: { lazy: false } } }); + + expect(tabViewProps.getLazy({ route: forRoute('Home') })).toBe(false); + expect(tabViewProps.getLazy({ route: forRoute('Explore') })).toBe(true); + }); + + it('calls tabBarIcon with the focused state', async () => { + const tabBarIcon = jest.fn(() => ({ sfSymbol: 'house' }) as const); + + await renderNavigator({ options: { Home: { tabBarIcon } } }); + + expect( + tabViewProps.getIcon({ route: forRoute('Home'), focused: true }) + ).toEqual({ sfSymbol: 'house' }); + expect(tabBarIcon).toHaveBeenCalledWith({ focused: true }); + }); + + it('returns no icon when the option is unset', async () => { + await renderNavigator(); + + expect( + tabViewProps.getIcon({ route: forRoute('Home'), focused: false }) + ).toBeNull(); + }); +}); + +describe('label', () => { + it('prefers tabBarLabel over title', async () => { + await renderNavigator({ + options: { Home: { tabBarLabel: 'Label', title: 'Title' } }, + }); + + expect(tabViewProps.getLabelText({ route: forRoute('Home') })).toBe( + 'Label' + ); + }); + + it('falls back to title when tabBarLabel is unset', async () => { + await renderNavigator({ options: { Home: { title: 'Title' } } }); + + expect(tabViewProps.getLabelText({ route: forRoute('Home') })).toBe( + 'Title' + ); + }); + + it('falls back to the route name when neither is set', async () => { + await renderNavigator(); + + expect(tabViewProps.getLabelText({ route: forRoute('Home') })).toBe('Home'); + }); +}); + +describe('tint colors', () => { + const theme: Theme = { + ...DefaultTheme, + colors: { + ...DefaultTheme.colors, + primary: '#ff0000', + text: '#000000', + card: '#ffffff', + }, + }; + + it('derives defaults from the navigation theme', async () => { + await renderNavigator({ theme }); + + expect(tabViewProps.tabBarActiveTintColor).toBe('#ff0000'); + // A 50% mix of the theme's text and card colors. + expect(tabViewProps.tabBarInactiveTintColor).toBe('#808080'); + }); + + it('lets the navigator props win over the theme', async () => { + await renderNavigator({ + theme, + tabBarActiveTintColor: '#00ff00', + tabBarInactiveTintColor: '#0000ff', + }); + + expect(tabViewProps.tabBarActiveTintColor).toBe('#00ff00'); + expect(tabViewProps.tabBarInactiveTintColor).toBe('#0000ff'); + }); +}); + +describe('tab press', () => { + const pressTab = async (index: number) => { + await act(async () => { + tabViewProps.onIndexChange(index); + }); + }; + + it('navigates to the pressed tab', async () => { + await renderNavigator(); + + await pressTab(1); + + expect(tabViewProps.navigationState.index).toBe(1); + }); + + it('emits tabPress before navigating', async () => { + const tabPress = jest.fn(); + + await renderNavigator({ screenListeners: { tabPress } }); + await pressTab(1); + + expect(tabPress).toHaveBeenCalledTimes(1); + expect(tabViewProps.navigationState.index).toBe(1); + }); + + it('does not navigate when a listener prevents the default', async () => { + await renderNavigator({ + screenListeners: { + tabPress: (event: { preventDefault: () => void }) => + event.preventDefault(), + }, + }); + + await pressTab(1); + + expect(tabViewProps.navigationState.index).toBe(0); + }); + + it('does not navigate when the screen sets preventsDefault', async () => { + await renderNavigator({ options: { Explore: { preventsDefault: true } } }); + + await pressTab(1); + + expect(tabViewProps.navigationState.index).toBe(0); + }); + + it('emits tabPress on the focused tab but does not re-navigate', async () => { + const tabPress = jest.fn(); + + await renderNavigator({ screenListeners: { tabPress } }); + await pressTab(0); + + expect(tabPress).toHaveBeenCalledTimes(1); + expect(tabViewProps.navigationState.index).toBe(0); + }); + + it('ignores an out-of-range index', async () => { + await renderNavigator(); + + await pressTab(99); + + expect(tabViewProps.navigationState.index).toBe(0); + }); +}); + +describe('tab long press', () => { + it('emits tabLongPress for the pressed tab', async () => { + const tabLongPress = jest.fn(); + + await renderNavigator({ screenListeners: { tabLongPress } }); + + await act(async () => { + tabViewProps.onTabLongPress(1); + }); + + expect(tabLongPress).toHaveBeenCalledTimes(1); + }); + + it('ignores an out-of-range index', async () => { + const tabLongPress = jest.fn(); + + await renderNavigator({ screenListeners: { tabLongPress } }); + + await act(async () => { + tabViewProps.onTabLongPress(99); + }); + + expect(tabLongPress).not.toHaveBeenCalled(); + }); +}); + +describe('custom tab bar', () => { + it('is called with the navigation state, descriptors and navigation', async () => { + const tabBar = jest.fn((_: BottomTabBarProps) => null); + + await renderNavigator({ tabBar }); + + tabViewProps.tabBar(); + + const props = tabBar.mock.calls[0]![0]; + + expect(props.state.routes.map((it) => it.name)).toEqual([ + 'Home', + 'Explore', + ]); + expect(Object.keys(props.descriptors)).toHaveLength(2); + expect(typeof props.navigation.emit).toBe('function'); + }); + + /** + * `BottomTabBar` from `@react-navigation/bottom-tabs` reads `state.key` and + * the per-route `navigation` and `route` off the descriptors. The standard + * navigation contract projects those away, so the navigator has to keep + * building the React Navigation state itself. + */ + it('is called with descriptors carrying the full React Navigation shape', async () => { + const tabBar = jest.fn((_: BottomTabBarProps) => null); + + await renderNavigator({ tabBar }); + + tabViewProps.tabBar(); + + const props = tabBar.mock.calls[0]![0]; + const descriptor = props.descriptors[props.state.routes[0]!.key]!; + + expect(typeof props.state.key).toBe('string'); + expect(typeof descriptor.navigation.dispatch).toBe('function'); + expect(typeof descriptor.navigation.navigate).toBe('function'); + expect(descriptor.route.name).toBe('Home'); + expect(typeof descriptor.render).toBe('function'); + }); + + it('is left undefined when no custom tab bar is given', async () => { + await renderNavigator(); + + expect(tabViewProps.tabBar).toBeUndefined(); + }); +}); diff --git a/packages/react-navigation/src/__tests__/mixColors.test.ts b/packages/react-navigation/src/__tests__/mixColors.test.ts new file mode 100644 index 00000000..ef589449 --- /dev/null +++ b/packages/react-navigation/src/__tests__/mixColors.test.ts @@ -0,0 +1,29 @@ +import mixColors from '../mixColors'; + +it.each([ + ['#000000', '#ffffff', 0.5, '#808080'], + ['#000000', '#ffffff', 0, '#000000'], + ['#000000', '#ffffff', 1, '#ffffff'], + ['#ff0000', '#0000ff', 0.5, '#800080'], +])('mixes %s with %s at %s', (from, to, ratio, expected) => { + expect(mixColors(from, to, ratio)).toBe(expected); +}); + +it.each([ + ['rgb(0, 0, 0)', 'rgb(255, 255, 255)'], + ['#000', '#fff'], + ['black', 'white'], + ['rgba(0, 0, 0, 1)', 'rgba(255, 255, 255, 1)'], +])('accepts %s and %s', (from, to) => { + expect(mixColors(from, to, 0.5)).toBe('#808080'); +}); + +it('ignores alpha and mixes the color channels', () => { + expect(mixColors('rgba(0, 0, 0, 0.2)', 'rgba(255, 255, 255, 0.9)', 0.5)).toBe( + '#808080' + ); +}); + +it('returns undefined when a color cannot be parsed', () => { + expect(mixColors('not a color', '#ffffff', 0.5)).toBeUndefined(); +}); diff --git a/packages/react-navigation/src/__tests__/sharedNavigator.test.tsx b/packages/react-navigation/src/__tests__/sharedNavigator.test.tsx new file mode 100644 index 00000000..730b72cf --- /dev/null +++ b/packages/react-navigation/src/__tests__/sharedNavigator.test.tsx @@ -0,0 +1,117 @@ +import { + nativeBottomTabsNavigator, + type NativeBottomTabNavigationOptions, + type NativeBottomTabsEventMap, + type NativeBottomTabsNavigatorProps, +} from '@bottom-tabs/navigation'; +import { + createStandardNavigationFactories, + NavigationContainer, + TabRouter, + useTheme, + type ParamListBase, + type StandardNavigationTypeBagBase, + type TabActionHelpers, + type TabNavigationState, + type TabRouterOptions, +} from '@react-navigation/native'; +import { render } from '@testing-library/react-native'; +import type * as React from 'react'; +import { Text } from 'react-native'; + +let tabViewProps: Record; + +jest.mock('react-native-bottom-tabs', () => ({ + __esModule: true, + default: (props: Record) => { + tabViewProps = props; + return null; + }, +})); + +interface NativeBottomTabsTypeBag extends StandardNavigationTypeBagBase { + State: TabNavigationState; + ScreenOptions: NativeBottomTabNavigationOptions; + EventMap: NativeBottomTabsEventMap; + RouterOptions: TabRouterOptions; + ActionHelpers: TabActionHelpers; + Navigator: React.ComponentType; +} + +const { createNavigator } = createStandardNavigationFactories< + NativeBottomTabsTypeBag, + NativeBottomTabsNavigatorProps +>(nativeBottomTabsNavigator, TabRouter); + +const Tab = createNavigator(); + +const { createNavigator: createThemedNavigator } = + createStandardNavigationFactories< + NativeBottomTabsTypeBag, + NativeBottomTabsNavigatorProps + >(nativeBottomTabsNavigator, TabRouter, () => { + const { colors } = useTheme(); + + return { defaultTintColors: { active: colors.primary } }; + }); + +const ThemedTab = createThemedNavigator(); + +const screens = (Navigator: typeof Tab) => ( + + + {() => Home screen} + + +); + +beforeEach(() => { + tabViewProps = {}; +}); + +it('drives the native view through the shared navigator', async () => { + await render({screens(Tab)}); + + expect( + tabViewProps.navigationState.routes.map((it: { name: string }) => it.name) + ).toEqual(['Home']); + expect(tabViewProps.tabBarActiveTintColor).toBe('#ff0000'); + expect( + tabViewProps.getLabelText({ route: tabViewProps.navigationState.routes[0] }) + ).toBe('Home'); +}); + +it('takes tint fallbacks from a mapper', async () => { + await render( + + + + {() => Home screen} + + + + ); + + expect(tabViewProps.tabBarActiveTintColor).toBe('rgb(0, 122, 255)'); +}); + +it('types the navigator props and screen options', () => { + const rejectsAWrongNavigatorProp = () => ( + // @ts-expect-error - a number is not a valid tint color + + ); + + const rejectsAnUnknownScreenOption = () => ( + + {/* @ts-expect-error - not a screen option this navigator accepts */} + + {() => Home screen} + + + ); + + expect([ + rejectsAWrongNavigatorProp, + rejectsAnUnknownScreenOption, + ]).toHaveLength(2); +}); diff --git a/packages/react-navigation/src/mixColors.ts b/packages/react-navigation/src/mixColors.ts new file mode 100644 index 00000000..185ca3dc --- /dev/null +++ b/packages/react-navigation/src/mixColors.ts @@ -0,0 +1,25 @@ +import { processColor } from 'react-native'; + +// eslint-disable-next-line no-bitwise +const channel = (color: number, shift: number) => (color >> shift) & 0xff; + +const hex = (value: number) => Math.round(value).toString(16).padStart(2, '0'); + +export default function mixColors( + from: string, + to: string, + ratio: number +): string | undefined { + const start = processColor(from); + const end = processColor(to); + + if (typeof start !== 'number' || typeof end !== 'number') { + return undefined; + } + + const mixed = [16, 8, 0].map( + (shift) => channel(start, shift) * (1 - ratio) + channel(end, shift) * ratio + ); + + return `#${mixed.map(hex).join('')}`; +} diff --git a/packages/react-navigation/src/navigators/createNativeBottomTabNavigator.tsx b/packages/react-navigation/src/navigators/createNativeBottomTabNavigator.tsx index 58f16478..66a07b55 100644 --- a/packages/react-navigation/src/navigators/createNativeBottomTabNavigator.tsx +++ b/packages/react-navigation/src/navigators/createNativeBottomTabNavigator.tsx @@ -12,8 +12,8 @@ import { useNavigationBuilder, useTheme, } from '@react-navigation/native'; -import Color from 'color'; +import mixColors from '../mixColors'; import type { NativeBottomTabNavigationConfig, NativeBottomTabNavigationEventMap, @@ -44,21 +44,9 @@ function NativeBottomTabNavigator({ screenOptions, screenLayout, UNSTABLE_router, - tabBarActiveTintColor: customActiveTintColor, - tabBarInactiveTintColor: customInactiveTintColor, - layoutDirection = 'locale', ...rest }: NativeBottomTabNavigatorProps) { const { colors } = useTheme(); - const activeTintColor = - customActiveTintColor === undefined - ? colors.primary - : customActiveTintColor; - - const inactiveTintColor = - customInactiveTintColor === undefined - ? Color(colors.text).mix(Color(colors.card), 0.5).hex() - : customInactiveTintColor; const { state, descriptors, navigation, NavigationContent } = useNavigationBuilder< @@ -84,9 +72,10 @@ function NativeBottomTabNavigator({ ; navigation: NativeBottomTabNavigationHelpers; descriptors: NativeBottomTabDescriptorMap; + defaultTintColors?: { active?: string; inactive?: string }; }; export default function NativeBottomTabView({ state, - layoutDirection, navigation, descriptors, tabBar, ...rest }: Props) { return ( - descriptors[route.key]?.render()} - getActiveTintColor={({ route }) => { - return descriptors[route.key]?.options.tabBarActiveTintColor; - }} - getLabelText={({ route }) => { - const options = descriptors[route.key]?.options; - - return options?.tabBarLabel !== undefined - ? options.tabBarLabel - : options?.title !== undefined - ? options.title - : (route as Route).name; - }} - getBadge={({ route }) => descriptors[route.key]?.options.tabBarBadge} - getBadgeBackgroundColor={({ route }) => - descriptors[route.key]?.options.tabBarBadgeBackgroundColor - } - getBadgeTextColor={({ route }) => - descriptors[route.key]?.options.tabBarBadgeTextColor - } - getHidden={({ route }) => { - const options = descriptors[route.key]?.options; - return options?.tabBarItemHidden === true; + state={state} + descriptors={descriptors} + actions={{ + navigate: (name, params) => + navigation.dispatch({ + ...CommonActions.navigate(name, params), + target: state.key, + }), + back: () => navigation.goBack(), }} - getTestID={({ route }) => - descriptors[route.key]?.options.tabBarButtonTestID - } - getRole={({ route }) => descriptors[route.key]?.options.role} - getIconRenderingMode={({ route }) => - descriptors[route.key]?.options.tabBarIconRenderingMode - } + emitter={{ emit: navigation.emit }} tabBar={ tabBar ? () => tabBar({ state, descriptors, navigation }) : undefined } - getIcon={({ route, focused }) => { - const options = descriptors[route.key]?.options; - - if (options?.tabBarIcon) { - const { tabBarIcon } = options; - return tabBarIcon({ focused }); - } - - return null; - }} - getLazy={({ route }) => descriptors[route.key]?.options.lazy ?? true} - getFreezeOnBlur={({ route }) => - descriptors[route.key]?.options.freezeOnBlur - } - getSceneStyle={({ route }) => descriptors[route.key]?.options.sceneStyle} - onTabLongPress={(index) => { - const route = state.routes[index]; - if (!route) { - return; - } - - navigation.emit({ - type: 'tabLongPress', - target: route.key, - }); - }} - getPreventsDefault={({ route }) => - descriptors[route.key]?.options.preventsDefault - } - onIndexChange={(index) => { - const focused = index === state.index; - const route = state.routes[index]; - if (!route) { - return; - } - - const event = navigation.emit({ - type: 'tabPress', - target: route.key, - canPreventDefault: true, - }); - - if ( - focused || - event.defaultPrevented || - descriptors[route.key]?.options.preventsDefault - ) { - return; - } else { - navigation.dispatch({ - ...CommonActions.navigate(route.name, route.params), - target: state.key, - }); - } - }} - layoutDirection={layoutDirection} /> ); } diff --git a/packages/react-navigation/tsconfig.json b/packages/react-navigation/tsconfig.json index 00b6db71..67fc2e29 100644 --- a/packages/react-navigation/tsconfig.json +++ b/packages/react-navigation/tsconfig.json @@ -2,9 +2,11 @@ "extends": "../../tsconfig", "references": [ { "path": "../react-native-bottom-tabs" }, + { "path": "../navigation" } ], "compilerOptions": { + "types": ["jest"], "rootDir": ".", - "outDir": "./lib/typescript", + "outDir": "./lib/typescript" } } diff --git a/tsconfig.json b/tsconfig.json index b1f456ec..76b8a422 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,13 @@ { "compilerOptions": { "paths": { - "react-native-bottom-tabs": ["./packages/react-native-bottom-tabs/src/index"], - "@bottom-tabs/react-navigation": ["./packages/react-navigation/src/index"], + "react-native-bottom-tabs": [ + "./packages/react-native-bottom-tabs/src/index" + ], + "@bottom-tabs/react-navigation": [ + "./packages/react-navigation/src/index" + ], + "@bottom-tabs/navigation": ["./packages/navigation/src/index"] }, "composite": true, "allowUnreachableCode": false, diff --git a/turbo.json b/turbo.json index 8236bfd4..5b188a2a 100644 --- a/turbo.json +++ b/turbo.json @@ -14,7 +14,9 @@ "$TURBO_ROOT$/tsconfig.json" ] }, - "test": {}, + "test": { + "dependsOn": ["^build"] + }, "build": { "dependsOn": ["^build"], "outputs": ["lib/**"] diff --git a/yarn.lock b/yarn.lock index 9342c4a9..c184e7de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2869,6 +2869,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-jsx-development@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-jsx-development@npm:7.29.7" + dependencies: + "@babel/plugin-transform-react-jsx": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/c535bb5ee09e07839a422f7a8e55849cd30525af57021888eceb84d33391290f6250207319bb4fbb4d4cbdcdb894b2a2b963f0769a3e95536370159b4b505855 + languageName: node + linkType: hard + "@babel/plugin-transform-react-jsx-self@npm:^7.24.7": version: 7.25.9 resolution: "@babel/plugin-transform-react-jsx-self@npm:7.25.9" @@ -2906,6 +2917,21 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-jsx@npm:^7.28.6, @babel/plugin-transform-react-jsx@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-jsx@npm:7.29.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.29.7" + "@babel/helper-module-imports": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + "@babel/plugin-syntax-jsx": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/ad735e6666e296404fd3c55378ad9ac712816a9ba3292bf491fd44e2d7bf32217a02c70fd8977c5ae07a246cfdbd75ea3bf5906e69af5dc1d0f404bca09aa7bf + languageName: node + linkType: hard + "@babel/plugin-transform-react-pure-annotations@npm:^7.25.9": version: 7.25.9 resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.25.9" @@ -2918,6 +2944,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-pure-annotations@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.29.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/7b6bc9e9db06f2c40685b4f0a043af17a98a8bee833831aa28f70c89876dc649fdd682ae572445143b53fe091258964107bae9d3583480eb1c4f1d9c22780b38 + languageName: node + linkType: hard + "@babel/plugin-transform-regenerator@npm:^7.24.7, @babel/plugin-transform-regenerator@npm:^7.25.9": version: 7.25.9 resolution: "@babel/plugin-transform-regenerator@npm:7.25.9" @@ -3932,21 +3970,44 @@ __metadata: languageName: unknown linkType: soft +"@bottom-tabs/navigation@npm:*, @bottom-tabs/navigation@npm:1.4.0, @bottom-tabs/navigation@workspace:packages/navigation": + version: 0.0.0-use.local + resolution: "@bottom-tabs/navigation@workspace:packages/navigation" + dependencies: + "@testing-library/react-native": "npm:^14.0.1" + "@types/jest": "npm:^29.5.5" + jest: "npm:^29.7.0" + react: "npm:^19.1.0" + react-native: "npm:0.81.1" + react-native-bottom-tabs: "npm:*" + react-native-builder-bob: "npm:^0.40.13" + standard-navigation: "npm:^0.0.9" + test-renderer: "npm:^1.2.0" + typescript: "npm:^5.9.2" + peerDependencies: + react: "*" + react-native: "*" + react-native-bottom-tabs: "*" + languageName: unknown + linkType: soft + "@bottom-tabs/react-navigation@npm:*, @bottom-tabs/react-navigation@npm:1.4.0, @bottom-tabs/react-navigation@workspace:packages/react-navigation": version: 0.0.0-use.local resolution: "@bottom-tabs/react-navigation@workspace:packages/react-navigation" dependencies: + "@bottom-tabs/navigation": "npm:1.4.0" "@react-navigation/native": "npm:^7.3.0" - "@types/color": "npm:^4.2.0" - color: "npm:^5.0.0" + "@testing-library/react-native": "npm:^14.0.1" + "@types/jest": "npm:^29.5.5" jest: "npm:^29.7.0" react: "npm:^19.1.0" react-native: "npm:0.81.1" react-native-bottom-tabs: "npm:*" react-native-builder-bob: "npm:^0.40.13" + test-renderer: "npm:^1.2.0" typescript: "npm:^5.9.2" peerDependencies: - "@react-navigation/native": ">=7" + "@react-navigation/native": ">=7.3.0" react: "*" react-native: "*" react-native-bottom-tabs: "*" @@ -4646,6 +4707,13 @@ __metadata: languageName: node linkType: hard +"@expo-google-fonts/material-symbols@npm:^0.4.1": + version: 0.4.47 + resolution: "@expo-google-fonts/material-symbols@npm:0.4.47" + checksum: 10/0965b63c90eecc0a3e56322b45be6a7ed3c94930082702f1403ae1428ee194dc7d079765e56f610e74ccaf77de8ccdd80365496935006f78abf8aee9f82f4316 + languageName: node + linkType: hard + "@expo/cli@npm:54.0.25": version: 54.0.25 resolution: "@expo/cli@npm:54.0.25" @@ -4728,6 +4796,84 @@ __metadata: languageName: node linkType: hard +"@expo/cli@npm:^57.0.23": + version: 57.0.23 + resolution: "@expo/cli@npm:57.0.23" + dependencies: + "@expo/code-signing-certificates": "npm:^0.0.6" + "@expo/config": "npm:~57.0.9" + "@expo/config-plugins": "npm:~57.0.9" + "@expo/devcert": "npm:^1.2.1" + "@expo/env": "npm:~2.4.3" + "@expo/image-utils": "npm:^0.11.5" + "@expo/inline-modules": "npm:^0.1.7" + "@expo/json-file": "npm:^11.0.1" + "@expo/log-box": "npm:^57.0.4" + "@expo/metro": "npm:~56.0.2" + "@expo/metro-config": "npm:~57.0.12" + "@expo/metro-file-map": "npm:^57.0.3" + "@expo/osascript": "npm:^2.7.1" + "@expo/package-manager": "npm:^1.13.1" + "@expo/plist": "npm:^0.8.1" + "@expo/prebuild-config": "npm:^57.0.15" + "@expo/require-utils": "npm:^57.0.5" + "@expo/router-server": "npm:^57.0.9" + "@expo/schema-utils": "npm:^57.0.2" + "@expo/spawn-async": "npm:^1.8.0" + "@expo/ws-tunnel": "npm:^2.0.0" + "@expo/xcpretty": "npm:^4.4.4" + "@react-native/dev-middleware": "npm:0.86.3" + accepts: "npm:^1.3.8" + agent-cli-detector: "npm:0.1.7" + arg: "npm:^5.0.2" + bplist-creator: "npm:0.1.0" + bplist-parser: "npm:^0.3.1" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.3.0" + compression: "npm:^1.7.4" + connect: "npm:^3.7.0" + debug: "npm:^4.3.4" + dnssd-advertise: "npm:^1.1.4" + expo-server: "npm:^57.0.3" + fetch-nodeshim: "npm:^0.4.10" + getenv: "npm:^2.0.0" + glob: "npm:^13.0.0" + lan-network: "npm:^0.2.1" + multitars: "npm:^1.0.2" + node-forge: "npm:^1.3.3" + npm-package-arg: "npm:^11.0.0" + ora: "npm:^3.4.0" + picomatch: "npm:^4.0.4" + pretty-format: "npm:^29.7.0" + progress: "npm:^2.0.3" + prompts: "npm:^2.3.2" + resolve-from: "npm:^5.0.0" + sandbox-cli-detector: "npm:^0.2.0" + semver: "npm:^7.6.0" + send: "npm:^0.19.0" + slugify: "npm:^1.3.4" + stacktrace-parser: "npm:^0.1.10" + structured-headers: "npm:^0.4.1" + terminal-link: "npm:^2.1.1" + toqr: "npm:^0.1.1" + wrap-ansi: "npm:^7.0.0" + ws: "npm:^8.12.1" + zod: "npm:^3.25.76" + peerDependencies: + expo: "*" + expo-router: "*" + react-native: "*" + peerDependenciesMeta: + expo-router: + optional: true + react-native: + optional: true + bin: + expo-internal: main.js + checksum: 10/3c7aea704c350a14dff5d2ea9a384413dcdbef4126133081c640a276a4dd10b673daa7fc5d228cc6e427c26904ff51169af4c2dd9547eac9b62da2609eb28694 + languageName: node + linkType: hard + "@expo/code-signing-certificates@npm:^0.0.6": version: 0.0.6 resolution: "@expo/code-signing-certificates@npm:0.0.6" @@ -4782,6 +4928,27 @@ __metadata: languageName: node linkType: hard +"@expo/config-plugins@npm:~57.0.9": + version: 57.0.9 + resolution: "@expo/config-plugins@npm:57.0.9" + dependencies: + "@expo/config-types": "npm:^57.0.2" + "@expo/json-file": "npm:~11.0.1" + "@expo/plist": "npm:^0.8.1" + "@expo/require-utils": "npm:^57.0.5" + "@expo/sdk-runtime-versions": "npm:^1.0.0" + chalk: "npm:^4.1.2" + debug: "npm:^4.3.5" + getenv: "npm:^2.0.0" + glob: "npm:^13.0.0" + semver: "npm:^7.5.4" + slugify: "npm:^1.6.6" + xcode: "npm:^3.0.1" + xml2js: "npm:0.6.0" + checksum: 10/20cd957f568fcb92a9e708db8ee5b45cc0095f20801c1ae510261387de6cef64046e8a3a4334fcf44526d64b53302944939a6d2eb78382b320c23d77afc3688f + languageName: node + linkType: hard + "@expo/config-types@npm:^51.0.3": version: 51.0.3 resolution: "@expo/config-types@npm:51.0.3" @@ -4796,6 +4963,13 @@ __metadata: languageName: node linkType: hard +"@expo/config-types@npm:^57.0.2": + version: 57.0.2 + resolution: "@expo/config-types@npm:57.0.2" + checksum: 10/ab4e30b2c8473e4148c4dc21cf66d7d6f3c0d301595b671c0e2dfe75168a4858e5a01549aa919ca0a10731d7d26df754be7676e9799f3490b484d0eb465955ee + languageName: node + linkType: hard + "@expo/config@npm:~12.0.13": version: 12.0.13 resolution: "@expo/config@npm:12.0.13" @@ -4817,6 +4991,24 @@ __metadata: languageName: node linkType: hard +"@expo/config@npm:~57.0.9": + version: 57.0.9 + resolution: "@expo/config@npm:57.0.9" + dependencies: + "@expo/config-plugins": "npm:~57.0.9" + "@expo/config-types": "npm:^57.0.2" + "@expo/json-file": "npm:^11.0.1" + "@expo/require-utils": "npm:^57.0.5" + deepmerge: "npm:^4.3.1" + getenv: "npm:^2.0.0" + glob: "npm:^13.0.0" + resolve-workspace-root: "npm:^2.0.0" + semver: "npm:^7.6.0" + slugify: "npm:^1.3.4" + checksum: 10/4330d85fbe734706cda85bd624fa11d4a63df8bc80c6f4e9aa71b036cf944c5a5d7837ea326d84ef6e4bf68ba62d1e9e3077e67e5cd243061f896823b53ec23e + languageName: node + linkType: hard + "@expo/devcert@npm:^1.2.1": version: 1.2.1 resolution: "@expo/devcert@npm:1.2.1" @@ -4844,6 +5036,45 @@ __metadata: languageName: node linkType: hard +"@expo/devtools@npm:~57.0.1": + version: 57.0.1 + resolution: "@expo/devtools@npm:57.0.1" + dependencies: + chalk: "npm:^4.1.2" + peerDependencies: + react: "*" + react-native: "*" + peerDependenciesMeta: + react: + optional: true + react-native: + optional: true + checksum: 10/06ad3b522b2eee060332c39027134c91bb4180a09c27364255bc9e6024dce02d597e645428ae723fc28fda35f81ada09fb5efbe37a87c372fbbb5d7a55cff859 + languageName: node + linkType: hard + +"@expo/dom-webview@npm:^57.0.1, @expo/dom-webview@npm:~57.0.1": + version: 57.0.1 + resolution: "@expo/dom-webview@npm:57.0.1" + peerDependencies: + expo: "*" + react: "*" + react-native: "*" + checksum: 10/692a9efd1b45cc93a781dc7c9531fa01cdd491a19f43e1fd7df110c4f7423cd865a3058ac4a8c7f456d58b545aa5c7d2c1796ee72f1309e2a9051b59a5e4512c + languageName: node + linkType: hard + +"@expo/env@npm:^2.4.3, @expo/env@npm:~2.4.3": + version: 2.4.3 + resolution: "@expo/env@npm:2.4.3" + dependencies: + chalk: "npm:^4.0.0" + debug: "npm:^4.3.4" + getenv: "npm:^2.0.0" + checksum: 10/bf7b5201ff56f29be0e0584b79cfd49c927ad6f65ac948b1179345368c864b672dca6323e7ad2531d9e77fb984a93861ac89800521d6f0dcdfca93cad6ed61d7 + languageName: node + linkType: hard + "@expo/env@npm:~2.0.8": version: 2.0.11 resolution: "@expo/env@npm:2.0.11" @@ -4857,6 +5088,13 @@ __metadata: languageName: node linkType: hard +"@expo/expo-modules-macros-plugin@npm:0.6.1": + version: 0.6.1 + resolution: "@expo/expo-modules-macros-plugin@npm:0.6.1" + checksum: 10/fd8aa144a385eeca41e96d3d95d6b67d42afb04c48d1f2aa3cdc84cbc829fb2066f98923085e047814160768885c6e4df126e5f9f4a28b22b0ed81ad53933024 + languageName: node + linkType: hard + "@expo/fingerprint@npm:0.15.5": version: 0.15.5 resolution: "@expo/fingerprint@npm:0.15.5" @@ -4878,6 +5116,42 @@ __metadata: languageName: node linkType: hard +"@expo/fingerprint@npm:^0.20.12": + version: 0.20.12 + resolution: "@expo/fingerprint@npm:0.20.12" + dependencies: + "@expo/env": "npm:^2.4.3" + "@expo/spawn-async": "npm:^1.8.0" + arg: "npm:^5.0.2" + chalk: "npm:^4.1.2" + debug: "npm:^4.3.4" + getenv: "npm:^2.0.0" + glob: "npm:^13.0.0" + ignore: "npm:^5.3.1" + minimatch: "npm:^10.2.2" + resolve-from: "npm:^5.0.0" + semver: "npm:^7.6.0" + bin: + fingerprint: bin/cli.js + checksum: 10/a60fa9f97a0e6d11b1a780fe5af11d15a3fa0da066c85723669798864390aa6f38fe2db908e6cd4c4fb815d6b8f00610ec3a9ca8d27f47cf86f126ad18a67ca1 + languageName: node + linkType: hard + +"@expo/image-utils@npm:^0.11.5": + version: 0.11.5 + resolution: "@expo/image-utils@npm:0.11.5" + dependencies: + "@expo/require-utils": "npm:^57.0.5" + "@expo/spawn-async": "npm:^1.8.0" + chalk: "npm:^4.0.0" + getenv: "npm:^2.0.0" + jimp-compact: "npm:0.16.1" + parse-png: "npm:^2.1.0" + semver: "npm:^7.6.0" + checksum: 10/d3e06ab3237df3d50414006f394ebca80757d21d9c5c14812e1927e1d07dffc01b8fd33b02ccae7cec33cfd33ae2835c0c2a25a73f450913ab9071d1fb583005 + languageName: node + linkType: hard + "@expo/image-utils@npm:^0.8.8": version: 0.8.14 resolution: "@expo/image-utils@npm:0.8.14" @@ -4893,6 +5167,15 @@ __metadata: languageName: node linkType: hard +"@expo/inline-modules@npm:^0.1.7": + version: 0.1.7 + resolution: "@expo/inline-modules@npm:0.1.7" + dependencies: + "@expo/config-plugins": "npm:~57.0.9" + checksum: 10/f5449a977ef09beeed2ddd03e9b267c220267645dd0c1e44f97506a4dddfd9ed7c21a0ca2d64c9672c9f168231d158e9e88261d5818e1222a546b7d78c73ceda + languageName: node + linkType: hard + "@expo/json-file@npm:^10.0.16, @expo/json-file@npm:^10.0.8, @expo/json-file@npm:^10.2.0": version: 10.2.0 resolution: "@expo/json-file@npm:10.2.0" @@ -4903,6 +5186,16 @@ __metadata: languageName: node linkType: hard +"@expo/json-file@npm:^11.0.1, @expo/json-file@npm:~11.0.1": + version: 11.0.1 + resolution: "@expo/json-file@npm:11.0.1" + dependencies: + "@babel/code-frame": "npm:^7.20.0" + json5: "npm:^2.2.3" + checksum: 10/17ea664fe0e9ceaf56b067441e8633102aef68ff8a6fc26a02768600102b761e412996fd54cff244eeec7618ad4d2b492305abb689d694073934835aa07e8005 + languageName: node + linkType: hard + "@expo/json-file@npm:~10.0.16, @expo/json-file@npm:~10.0.8": version: 10.0.16 resolution: "@expo/json-file@npm:10.0.16" @@ -4924,6 +5217,32 @@ __metadata: languageName: node linkType: hard +"@expo/local-build-cache-provider@npm:^57.0.8": + version: 57.0.8 + resolution: "@expo/local-build-cache-provider@npm:57.0.8" + dependencies: + "@expo/config": "npm:~57.0.9" + chalk: "npm:^4.1.2" + checksum: 10/a69d290074c696c629e193771f45572360ac8d57a743cd832979e4835dc10d29ca13d7d926b4855edf793881bf9e1871067e672913271d2a7a5064d0072ab8a4 + languageName: node + linkType: hard + +"@expo/log-box@npm:^57.0.4": + version: 57.0.4 + resolution: "@expo/log-box@npm:57.0.4" + dependencies: + "@expo/dom-webview": "npm:^57.0.1" + anser: "npm:^1.4.9" + stacktrace-parser: "npm:^0.1.10" + peerDependencies: + "@expo/dom-webview": ^57.0.1 + expo: "*" + react: "*" + react-native: "*" + checksum: 10/020f1bbf3a47fd39bdaca7f51e479a1b063864a5c72a5e7810290dfdf099d7563240bbefe19b0d25dba8f101c479117edffa42ff0a3d89ed5ca3073a3d3882f0 + languageName: node + linkType: hard + "@expo/metro-config@npm:54.0.16, @expo/metro-config@npm:~54.0.16": version: 54.0.16 resolution: "@expo/metro-config@npm:54.0.16" @@ -4958,6 +5277,78 @@ __metadata: languageName: node linkType: hard +"@expo/metro-config@npm:~57.0.12": + version: 57.0.12 + resolution: "@expo/metro-config@npm:57.0.12" + dependencies: + "@babel/code-frame": "npm:^7.20.0" + "@babel/core": "npm:^7.20.0" + "@babel/generator": "npm:^7.20.5" + "@expo/config": "npm:~57.0.9" + "@expo/env": "npm:~2.4.3" + "@expo/json-file": "npm:~11.0.1" + "@expo/metro": "npm:~56.0.2" + "@expo/require-utils": "npm:^57.0.5" + "@expo/spawn-async": "npm:^1.8.0" + "@jridgewell/gen-mapping": "npm:^0.3.13" + "@jridgewell/remapping": "npm:^2.3.5" + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + browserslist: "npm:^4.25.0" + chalk: "npm:^4.1.0" + debug: "npm:^4.3.2" + getenv: "npm:^2.0.0" + glob: "npm:^13.0.0" + hermes-parser: "npm:^0.36.0" + jsc-safe-url: "npm:^0.2.4" + lightningcss: "npm:^1.30.1" + picomatch: "npm:^4.0.4" + postcss: "npm:^8.5.14" + resolve-from: "npm:^5.0.0" + peerDependencies: + expo: "*" + peerDependenciesMeta: + expo: + optional: true + checksum: 10/d8cc707e31e06ca28951884a7bca7dc27ca2f8269d4cba90ce86dc3c82e451e2bdab10292d7741a01a2f3dbaa931701872e222f0f470b6ad1e3133d68bd167eb + languageName: node + linkType: hard + +"@expo/metro-file-map@npm:^57.0.3": + version: 57.0.3 + resolution: "@expo/metro-file-map@npm:57.0.3" + dependencies: + debug: "npm:^4.3.4" + fb-watchman: "npm:^2.0.2" + invariant: "npm:^2.2.4" + jest-worker: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + walker: "npm:^1.0.8" + checksum: 10/a96b1f99a136496b6f3c97b1d328f8e2ac09194692f76dc179514a827818347323799cf10b778eea04dee3fe21d8dae4b6792e81e3b3218596af394713684b45 + languageName: node + linkType: hard + +"@expo/metro-runtime@npm:^57.0.15": + version: 57.0.15 + resolution: "@expo/metro-runtime@npm:57.0.15" + dependencies: + "@expo/log-box": "npm:^57.0.4" + anser: "npm:^1.4.9" + pretty-format: "npm:^29.7.0" + stacktrace-parser: "npm:^0.1.10" + whatwg-fetch: "npm:^3.0.0" + peerDependencies: + "@expo/log-box": ^57.0.4 + expo: "*" + react: "*" + react-dom: "*" + react-native: "*" + peerDependenciesMeta: + react-dom: + optional: true + checksum: 10/6923e626434b575d1fadb6e3cb664db045ce34ea84a2098b0317248fc0609fe18e481b7a5adf8a66292fad04cc4faaef84a9bb2cfcac41c7c54ae1efa449cfbd + languageName: node + linkType: hard + "@expo/metro-runtime@npm:^6.1.2": version: 6.1.2 resolution: "@expo/metro-runtime@npm:6.1.2" @@ -5000,6 +5391,28 @@ __metadata: languageName: node linkType: hard +"@expo/metro@npm:~56.0.2": + version: 56.0.2 + resolution: "@expo/metro@npm:56.0.2" + dependencies: + metro: "npm:0.84.5" + metro-babel-transformer: "npm:0.84.5" + metro-cache: "npm:0.84.5" + metro-cache-key: "npm:0.84.5" + metro-config: "npm:0.84.5" + metro-core: "npm:0.84.5" + metro-file-map: "npm:0.84.5" + metro-minify-terser: "npm:0.84.5" + metro-resolver: "npm:0.84.5" + metro-runtime: "npm:0.84.5" + metro-source-map: "npm:0.84.5" + metro-symbolicate: "npm:0.84.5" + metro-transform-plugins: "npm:0.84.5" + metro-transform-worker: "npm:0.84.5" + checksum: 10/f576796e9f7ae8ffe7da930d98e10995b05dc38c305d250416aa7baa84560e447bb1db84c2a83f641a14f6aeb341b1e1c0b9d35c8c1df33a29cab6cfcd7e73b0 + languageName: node + linkType: hard + "@expo/osascript@npm:^2.3.8": version: 2.6.0 resolution: "@expo/osascript@npm:2.6.0" @@ -5009,6 +5422,29 @@ __metadata: languageName: node linkType: hard +"@expo/osascript@npm:^2.7.1": + version: 2.7.1 + resolution: "@expo/osascript@npm:2.7.1" + dependencies: + "@expo/spawn-async": "npm:^1.8.0" + checksum: 10/6d79bfdc56d85b349f0437472cafc5076ccafb2b4dbfe33d6d715d606b2c3e9cec4df532aa1d5585240f53c2e995b575a537b2072549fd54a09fad7c411a3a05 + languageName: node + linkType: hard + +"@expo/package-manager@npm:^1.13.1": + version: 1.13.1 + resolution: "@expo/package-manager@npm:1.13.1" + dependencies: + "@expo/json-file": "npm:^11.0.1" + "@expo/spawn-async": "npm:^1.8.0" + chalk: "npm:^4.0.0" + npm-package-arg: "npm:^11.0.0" + ora: "npm:^3.4.0" + resolve-workspace-root: "npm:^2.0.0" + checksum: 10/31b1282e27f54c81551c12e3db42f82cdd68456222d3941b10ef2c06916344cfe391181bca91abf4fafe3da062227ebdb0b2194bbc62264f4117e065ef83f999 + languageName: node + linkType: hard + "@expo/package-manager@npm:^1.9.10": version: 1.12.1 resolution: "@expo/package-manager@npm:1.12.1" @@ -5045,6 +5481,17 @@ __metadata: languageName: node linkType: hard +"@expo/plist@npm:^0.8.1": + version: 0.8.1 + resolution: "@expo/plist@npm:0.8.1" + dependencies: + "@xmldom/xmldom": "npm:^0.8.8" + base64-js: "npm:^1.5.1" + xmlbuilder: "npm:^15.1.1" + checksum: 10/0ce22d2f132d85b89b655b2b54596ba1bf9feac9ae3c0f84af171478002a9b9ba5609e11f958f31fda66547444720cfb6da1ef15197e5dcc22a2a84c4b4ecd48 + languageName: node + linkType: hard + "@expo/prebuild-config@npm:^54.0.8": version: 54.0.8 resolution: "@expo/prebuild-config@npm:54.0.8" @@ -5065,6 +5512,24 @@ __metadata: languageName: node linkType: hard +"@expo/prebuild-config@npm:^57.0.15": + version: 57.0.15 + resolution: "@expo/prebuild-config@npm:57.0.15" + dependencies: + "@expo/config": "npm:~57.0.9" + "@expo/config-plugins": "npm:~57.0.9" + "@expo/config-types": "npm:^57.0.2" + "@expo/image-utils": "npm:^0.11.5" + "@expo/json-file": "npm:^11.0.1" + "@react-native/normalize-colors": "npm:0.86.3" + debug: "npm:^4.3.1" + expo-modules-autolinking: "npm:~57.0.12" + resolve-from: "npm:^5.0.0" + semver: "npm:^7.6.0" + checksum: 10/67516879cd48ec871afcc558708ef40df682204021376456205e87bbbb9cfa35ad6235bd72e9ae21e8711596534cdb00e463e7695d8918201a2cb8e5f1786a61 + languageName: node + linkType: hard + "@expo/require-utils@npm:^55.0.5": version: 55.0.5 resolution: "@expo/require-utils@npm:55.0.5" @@ -5081,6 +5546,50 @@ __metadata: languageName: node linkType: hard +"@expo/require-utils@npm:^57.0.5": + version: 57.0.5 + resolution: "@expo/require-utils@npm:57.0.5" + dependencies: + "@babel/code-frame": "npm:^7.20.0" + "@babel/core": "npm:^7.25.2" + "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" + peerDependencies: + typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/e7c6e1c33d4f6645fd9a4fbaf60079a3dff5f0376f8c73df247463f1604320128731c5e42f9627aa771f612110e43a0257d69fe0d569812697c799048e14ddf0 + languageName: node + linkType: hard + +"@expo/router-server@npm:^57.0.9": + version: 57.0.9 + resolution: "@expo/router-server@npm:57.0.9" + dependencies: + debug: "npm:^4.3.4" + peerDependencies: + "@expo/metro-runtime": ^57.0.15 + expo: "*" + expo-constants: ^57.0.17 + expo-font: ^57.0.3 + expo-router: "*" + expo-server: ^57.0.3 + react: "*" + react-dom: "*" + react-server-dom-webpack: ~19.0.1 || ~19.1.2 || ~19.2.1 + peerDependenciesMeta: + "@expo/metro-runtime": + optional: true + expo-router: + optional: true + react-dom: + optional: true + react-server-dom-webpack: + optional: true + checksum: 10/6640bea509f5d6e0bf6f22478b338c4af1e3f36569f787ade8853b77b2fa7096ff1ec3ec9a7e4b0fb01ba4023c6a3d57dbe453128d3270db9f7819d49b5c19e4 + languageName: node + linkType: hard + "@expo/schema-utils@npm:^0.1.8": version: 0.1.8 resolution: "@expo/schema-utils@npm:0.1.8" @@ -5088,6 +5597,13 @@ __metadata: languageName: node linkType: hard +"@expo/schema-utils@npm:^57.0.2": + version: 57.0.2 + resolution: "@expo/schema-utils@npm:57.0.2" + checksum: 10/724e462dbbc81328c115900254d62e1852ffd7c45d91c1c3175bc29e1eb240b5ffd7f64d8186a9edb8336bdac8ad7d61600c66718eaa0d634ce6268acef5332a + languageName: node + linkType: hard + "@expo/sdk-runtime-versions@npm:^1.0.0": version: 1.0.0 resolution: "@expo/sdk-runtime-versions@npm:1.0.0" @@ -5120,6 +5636,30 @@ __metadata: languageName: node linkType: hard +"@expo/ui@npm:^57.0.17": + version: 57.0.17 + resolution: "@expo/ui@npm:57.0.17" + dependencies: + sf-symbols-typescript: "npm:^2.1.0" + vaul: "npm:^1.1.2" + peerDependencies: + "@babel/core": "*" + expo: "*" + react: "*" + react-dom: "*" + react-native: "*" + react-native-worklets: "*" + peerDependenciesMeta: + "@babel/core": + optional: true + react-dom: + optional: true + react-native-worklets: + optional: true + checksum: 10/7dd360b85afac7a70aa723c5cefe39e6252aee3f4ce78bdecd5350c9676d17ea0da48eb9b9d8ca4c2cc233bafc816f464dc2ea20ee287269d54170db5b110521 + languageName: node + linkType: hard + "@expo/vector-icons@npm:^15.0.3": version: 15.1.1 resolution: "@expo/vector-icons@npm:15.1.1" @@ -5138,6 +5678,15 @@ __metadata: languageName: node linkType: hard +"@expo/ws-tunnel@npm:^2.0.0": + version: 2.0.0 + resolution: "@expo/ws-tunnel@npm:2.0.0" + peerDependencies: + ws: ^8.0.0 + checksum: 10/3ac95f4aac217a096f6c4b521a7bbf07e377af6d018f1bc6b6a7daff17b0e25b16512bdcb620ea2cd6953f4324da3c22748db906e32dc0c0b281de0829fee6d2 + languageName: node + linkType: hard + "@expo/xcpretty@npm:^4.3.0": version: 4.3.2 resolution: "@expo/xcpretty@npm:4.3.2" @@ -5152,6 +5701,19 @@ __metadata: languageName: node linkType: hard +"@expo/xcpretty@npm:^4.4.4": + version: 4.4.5 + resolution: "@expo/xcpretty@npm:4.4.5" + dependencies: + "@babel/code-frame": "npm:^7.20.0" + chalk: "npm:^4.1.0" + js-yaml: "npm:^4.1.0" + bin: + excpretty: build/cli.js + checksum: 10/fd3e94f8ce6760c069527098c03f241c569ccc28c036fcb9cedeb693ee369c0b32db078aee9892d147100a6c07839dac96441e4c3407c24896a4d5c6cf14c74d + languageName: node + linkType: hard + "@hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.3.0": version: 9.3.0 resolution: "@hapi/hoek@npm:9.3.0" @@ -5568,7 +6130,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.12": +"@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.13": version: 0.3.13 resolution: "@jridgewell/gen-mapping@npm:0.3.13" dependencies: @@ -5637,6 +6199,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.5.5": + version: 1.6.0 + resolution: "@jridgewell/sourcemap-codec@npm:1.6.0" + checksum: 10/eb53fbf7eb4051302a599d6b91425f381168b2856f321336fb00852cea1e0c1904af2610c157b1535e94a61caa98f0bd068d1d0394dd46adf9e9c45c4e11af91 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:0.3.9": version: 0.3.9 resolution: "@jridgewell/trace-mapping@npm:0.3.9" @@ -5956,6 +6525,19 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-compose-refs@npm:1.1.5": + version: 1.1.5 + resolution: "@radix-ui/react-compose-refs@npm:1.1.5" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/ffc1a289d944ba9db818bc645e44efb6ce122a1585b61fcd97737ba4d18b60ca585b3d46ff1836b6ad6c4a21c795c5082d540af58cb32daa1b692dffed3d2a15 + languageName: node + linkType: hard + "@radix-ui/react-context@npm:1.1.2": version: 1.1.2 resolution: "@radix-ui/react-context@npm:1.1.2" @@ -6202,6 +6784,21 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-slot@npm:^1.2.0": + version: 1.3.3 + resolution: "@radix-ui/react-slot@npm:1.3.3" + dependencies: + "@radix-ui/react-compose-refs": "npm:1.1.5" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/97f305fb29c6284e1292cfee441cd807744f32322c0a1b541190a294f878edacce3bc37c174b4024724210243dad7b4feb762222e4c43668db2b65b9576d222a + languageName: node + linkType: hard + "@radix-ui/react-tabs@npm:^1.1.12": version: 1.1.13 resolution: "@radix-ui/react-tabs@npm:1.1.13" @@ -6478,6 +7075,16 @@ __metadata: languageName: node linkType: hard +"@react-native-masked-view/masked-view@npm:^0.3.2": + version: 0.3.2 + resolution: "@react-native-masked-view/masked-view@npm:0.3.2" + peerDependencies: + react: ">=16" + react-native: ">=0.57" + checksum: 10/04ffbc01083aa563ca1e2d7ef6759e7b326b8129f5bb1aa5f3142348adab06d5e321a400cf70a5434324dfa906add383f8214640697c48c9e5311b30bfea03d9 + languageName: node + linkType: hard + "@react-native/asset-utils@npm:0.87.1": version: 0.87.1 resolution: "@react-native/asset-utils@npm:0.87.1" @@ -6499,6 +7106,13 @@ __metadata: languageName: node linkType: hard +"@react-native/assets-registry@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/assets-registry@npm:0.86.3" + checksum: 10/a40aba66f5656f3a0fa64968e6c6479c5ac0294d02867cb2ad53ac74759ddf1fb8e0836ab8105255ee931d8be25cf1f1fb2922ad22e86cadb607a43cec3fef17 + languageName: node + linkType: hard + "@react-native/babel-plugin-codegen@npm:0.81.1": version: 0.81.1 resolution: "@react-native/babel-plugin-codegen@npm:0.81.1" @@ -6519,6 +7133,16 @@ __metadata: languageName: node linkType: hard +"@react-native/babel-plugin-codegen@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/babel-plugin-codegen@npm:0.86.3" + dependencies: + "@babel/traverse": "npm:^7.29.0" + "@react-native/codegen": "npm:0.86.3" + checksum: 10/a890cf1d03960581b38e0299fc87b28e6191c8980d6d97cb9aaad4d14005accff0177a934e7286437dda0f2c316e713a2b6ae51bf09aa3928020fdcd6452e022 + languageName: node + linkType: hard + "@react-native/babel-plugin-codegen@npm:0.87.1": version: 0.87.1 resolution: "@react-native/babel-plugin-codegen@npm:0.87.1" @@ -6661,6 +7285,23 @@ __metadata: languageName: node linkType: hard +"@react-native/codegen@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/codegen@npm:0.86.3" + dependencies: + "@babel/core": "npm:^7.25.2" + "@babel/parser": "npm:^7.29.0" + hermes-parser: "npm:0.36.0" + invariant: "npm:^2.2.4" + nullthrows: "npm:^1.1.1" + tinyglobby: "npm:^0.2.15" + yargs: "npm:^17.6.2" + peerDependencies: + "@babel/core": "*" + checksum: 10/4da407a19bd1afa99e9f0409c385b87486f09be1591523667def4b0b2ab5515cb1471f65fd46632f672b550628c2c11640640c4a3ff739a9dbaae676fed0de1c + languageName: node + linkType: hard + "@react-native/codegen@npm:0.87.1": version: 0.87.1 resolution: "@react-native/codegen@npm:0.87.1" @@ -6724,6 +7365,29 @@ __metadata: languageName: node linkType: hard +"@react-native/community-cli-plugin@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/community-cli-plugin@npm:0.86.3" + dependencies: + "@react-native/dev-middleware": "npm:0.86.3" + 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" + semver: "npm:^7.1.3" + peerDependencies: + "@react-native-community/cli": "*" + "@react-native/metro-config": 0.86.3 + peerDependenciesMeta: + "@react-native-community/cli": + optional: true + "@react-native/metro-config": + optional: true + checksum: 10/c01aa3f061b365522b7690deb18228510c151ca6ae2711f29717bf295f41987d0b417ee815057b5d8fc487507ab9fb37b5616e0239dfef072471eff6a4c3e09b + languageName: node + linkType: hard + "@react-native/community-cli-plugin@npm:0.87.1": version: 0.87.1 resolution: "@react-native/community-cli-plugin@npm:0.87.1" @@ -6760,6 +7424,13 @@ __metadata: languageName: node linkType: hard +"@react-native/debugger-frontend@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/debugger-frontend@npm:0.86.3" + checksum: 10/10313744826ee57414f333e570ef8a877d9d67fb71d855539dee63c6647ad9176bcde7bd4bb6839921a20127ef7cf87a6d0852064622ba30767fce842a9303a4 + languageName: node + linkType: hard + "@react-native/debugger-frontend@npm:0.87.1": version: 0.87.1 resolution: "@react-native/debugger-frontend@npm:0.87.1" @@ -6767,6 +7438,17 @@ __metadata: languageName: node linkType: hard +"@react-native/debugger-shell@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/debugger-shell@npm:0.86.3" + dependencies: + cross-spawn: "npm:^7.0.6" + debug: "npm:^4.4.0" + fb-dotslash: "npm:0.5.8" + checksum: 10/626cfd35da8c8e39e0300ce6339fd20ea5eb1c188edb7002889bf10c6d89818b411e39f865e961dd70d64a3f38210d309d25c785403dccbf85ff74249aa54764 + languageName: node + linkType: hard + "@react-native/debugger-shell@npm:0.87.1": version: 0.87.1 resolution: "@react-native/debugger-shell@npm:0.87.1" @@ -6816,6 +7498,26 @@ __metadata: languageName: node linkType: hard +"@react-native/dev-middleware@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/dev-middleware@npm:0.86.3" + dependencies: + "@isaacs/ttlcache": "npm:^1.4.1" + "@react-native/debugger-frontend": "npm:0.86.3" + "@react-native/debugger-shell": "npm:0.86.3" + chrome-launcher: "npm:^0.15.2" + chromium-edge-launcher: "npm:^0.3.0" + connect: "npm:^3.6.5" + debug: "npm:^4.4.0" + invariant: "npm:^2.2.4" + nullthrows: "npm:^1.1.1" + open: "npm:^7.0.3" + serve-static: "npm:^1.16.2" + ws: "npm:^7.5.10" + checksum: 10/53ae45d9d6ba8c445a4cbc7beaada78fa6c5be38add30156bad8215ec9fee2212ab703080649944e62258b5aa5017348d3687ff7b0285d2df647b49ce5090fe1 + languageName: node + linkType: hard + "@react-native/dev-middleware@npm:0.87.1": version: 0.87.1 resolution: "@react-native/dev-middleware@npm:0.87.1" @@ -6910,6 +7612,13 @@ __metadata: languageName: node linkType: hard +"@react-native/gradle-plugin@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/gradle-plugin@npm:0.86.3" + checksum: 10/6f725b1e7b3332d9a5411c82d83db9080ccec5d3a6b56b25486a595012c7bbbe0d1ba8be493efb47f30f18a928b58f378b24880f1d04bfe2a11fc9e4c1f9969a + languageName: node + linkType: hard + "@react-native/gradle-plugin@npm:0.87.1": version: 0.87.1 resolution: "@react-native/gradle-plugin@npm:0.87.1" @@ -6932,6 +7641,21 @@ __metadata: languageName: node linkType: hard +"@react-native/jest-preset@npm:^0.86.3": + version: 0.86.3 + resolution: "@react-native/jest-preset@npm:0.86.3" + dependencies: + "@jest/create-cache-key-function": "npm:^29.7.0" + "@react-native/js-polyfills": "npm:0.86.3" + 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: 10/d39acb4188894891210b2e15e1e7092776d6b1a2f40a47414247277f6cfd051151077c0c82cdc69f1843fa652a8ba44517b105df8e13993c363c54fb3bb6ab50 + languageName: node + linkType: hard + "@react-native/js-polyfills@npm:0.81.1": version: 0.81.1 resolution: "@react-native/js-polyfills@npm:0.81.1" @@ -6946,6 +7670,13 @@ __metadata: languageName: node linkType: hard +"@react-native/js-polyfills@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/js-polyfills@npm:0.86.3" + checksum: 10/5e17bc2f5b638dd54800f749343ed5155d9a5e1076dc4a2d20a5e40079993d049d9e70681dcf62de215d7ad366ea7624a0999712b5a309763ccec86f4f25a749 + languageName: node + linkType: hard + "@react-native/js-polyfills@npm:0.87.1": version: 0.87.1 resolution: "@react-native/js-polyfills@npm:0.87.1" @@ -6993,6 +7724,13 @@ __metadata: languageName: node linkType: hard +"@react-native/normalize-colors@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/normalize-colors@npm:0.86.3" + checksum: 10/b172d08e8cd100f263462da610dd2ef207757028f14ac9b25bc0357c967536ca1fcc9849ab4720c6c9dfda9744892dbe2a8414c8c4ca22280aa245615cdde70d + languageName: node + linkType: hard + "@react-native/normalize-colors@npm:0.87.1": version: 0.87.1 resolution: "@react-native/normalize-colors@npm:0.87.1" @@ -7048,6 +7786,23 @@ __metadata: languageName: node linkType: hard +"@react-native/virtualized-lists@npm:0.86.3": + version: 0.86.3 + resolution: "@react-native/virtualized-lists@npm:0.86.3" + dependencies: + invariant: "npm:^2.2.4" + nullthrows: "npm:^1.1.1" + peerDependencies: + "@types/react": ^19.2.0 + react: "*" + react-native: 0.86.3 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/f3590cd32c0ca481a3c723da39be060731ac72c917eaf9eeab4e8e1fc85cb0d4e4c8182cb1a2af65f69f9b325ef1a460b5d1ff3dc90a96f551f2cc07aa04f61e + languageName: node + linkType: hard + "@react-native/virtualized-lists@npm:0.87.1": version: 0.87.1 resolution: "@react-native/virtualized-lists@npm:0.87.1" @@ -8106,33 +8861,8 @@ __metadata: version: 7.20.6 resolution: "@types/babel__traverse@npm:7.20.6" dependencies: - "@babel/types": "npm:^7.20.7" - checksum: 10/63d13a3789aa1e783b87a8b03d9fb2c2c90078de7782422feff1631b8c2a25db626e63a63ac5a1465d47359201c73069dacb4b52149d17c568187625da3064ae - languageName: node - linkType: hard - -"@types/color-convert@npm:*": - version: 2.0.4 - resolution: "@types/color-convert@npm:2.0.4" - dependencies: - "@types/color-name": "npm:^1.1.0" - checksum: 10/08385948a3baf0c1dcb021791c3980d948423e92d1f1f400d7d50d089f4d26cf3a6ef1fdc402bd5bbccc325c44d2884834e66cde242c794483950e99967b9c8d - languageName: node - linkType: hard - -"@types/color-name@npm:^1.1.0": - version: 1.1.5 - resolution: "@types/color-name@npm:1.1.5" - checksum: 10/47600afcd1dbd7c668103ccaed912acd97760336dbb9fb08bdb34be004daa1f06a336183b51d44852e4460fe8b336347c7f5af330160c755f6a8fb4fa19e6bc0 - languageName: node - linkType: hard - -"@types/color@npm:^4.2.0": - version: 4.2.0 - resolution: "@types/color@npm:4.2.0" - dependencies: - "@types/color-convert": "npm:*" - checksum: 10/4f9ddddac07f9c2d79823a5dfaf8689330f3e102c183200c5f772545ac421942ed9e42a2591afe96bbccddf5d5a87ad1bb46671da6056583ebf9c9fdc238b54e + "@babel/types": "npm:^7.20.7" + checksum: 10/63d13a3789aa1e783b87a8b03d9fb2c2c90078de7782422feff1631b8c2a25db626e63a63ac5a1465d47359201c73069dacb4b52149d17c568187625da3064ae languageName: node linkType: hard @@ -8357,6 +9087,15 @@ __metadata: languageName: node linkType: hard +"@types/react-reconciler@npm:~0.33.0": + version: 0.33.0 + resolution: "@types/react-reconciler@npm:0.33.0" + peerDependencies: + "@types/react": "*" + checksum: 10/b65408e08f9e9924a6040fb7a4820ed12efdad598dccb9e08efc70a7101199cce4a5aa1f8695a82d8efe7b503bd9b8dc5122db8a23bac39f9bc473d595873ba4 + languageName: node + linkType: hard + "@types/react-test-renderer@npm:^19.1.0": version: 19.1.0 resolution: "@types/react-test-renderer@npm:19.1.0" @@ -9109,6 +9848,15 @@ __metadata: languageName: node linkType: hard +"agent-cli-detector@npm:0.1.7": + version: 0.1.7 + resolution: "agent-cli-detector@npm:0.1.7" + bin: + agent-cli-detector: dist/cli.js + checksum: 10/12222d06c41fdf4c81f483529a66555fc33de950ddf710fa715359e7a66af52f746ecafa028718418e40f5934ca89c1be6afc61a2ff7689c6bd216f0d7612a25 + languageName: node + linkType: hard + "aggregate-error@npm:^3.0.0": version: 3.1.0 resolution: "aggregate-error@npm:3.1.0" @@ -9731,7 +10479,16 @@ __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.0 + resolution: "babel-plugin-syntax-hermes-parser@npm:0.36.0" + dependencies: + hermes-parser: "npm:0.36.0" + checksum: 10/22489c19d416f1e071c046ab42d5dd475ed5b76514621f755f55b4e8019ecd665dce8da40be2a5436bbd5d4f2d258ebceda30601e078104ce11fe4371b7a36a0 + 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: @@ -9822,6 +10579,68 @@ __metadata: languageName: node linkType: hard +"babel-preset-expo@npm:~57.0.11": + version: 57.0.11 + resolution: "babel-preset-expo@npm:57.0.11" + dependencies: + "@babel/generator": "npm:^7.20.5" + "@babel/helper-module-imports": "npm:^7.25.9" + "@babel/plugin-proposal-decorators": "npm:^7.12.9" + "@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-class-static-block": "npm:^7.27.1" + "@babel/plugin-transform-classes": "npm:^7.25.4" + "@babel/plugin-transform-destructuring": "npm:^7.24.8" + "@babel/plugin-transform-export-namespace-from": "npm:^7.25.9" + "@babel/plugin-transform-flow-strip-types": "npm:^7.25.2" + "@babel/plugin-transform-for-of": "npm:^7.24.7" + "@babel/plugin-transform-logical-assignment-operators": "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-object-rest-spread": "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-parameters": "npm:^7.24.7" + "@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.28.6" + "@babel/plugin-transform-react-jsx-development": "npm:^7.27.1" + "@babel/plugin-transform-react-pure-annotations": "npm:^7.27.1" + "@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" + "@babel/preset-typescript": "npm:^7.23.0" + "@react-native/babel-plugin-codegen": "npm:0.86.3" + babel-plugin-react-compiler: "npm:^1.0.0" + babel-plugin-react-native-web: "npm:~0.21.0" + babel-plugin-syntax-hermes-parser: "npm:^0.36.0" + babel-plugin-transform-flow-enums: "npm:^0.0.2" + debug: "npm:^4.3.4" + peerDependencies: + "@babel/runtime": ^7.20.0 + expo: "*" + expo-widgets: ^57.0.18 + react-refresh: ">=0.14.0 <1.0.0" + peerDependenciesMeta: + "@babel/runtime": + optional: true + expo: + optional: true + expo-widgets: + optional: true + checksum: 10/26993f61c70ebbf873b3d6fb246c5412c0520ea4a4710a161ceb56747b9a01021dcb4ee905320a21cf9aee62847c6faee13ba3c7043bed5773443300d352fb95 + languageName: node + linkType: hard + "babel-preset-jest@npm:^29.6.3": version: 29.6.3 resolution: "babel-preset-jest@npm:29.6.3" @@ -11330,6 +12149,13 @@ __metadata: languageName: node linkType: hard +"dnssd-advertise@npm:^1.1.4": + version: 1.1.6 + resolution: "dnssd-advertise@npm:1.1.6" + checksum: 10/acc3020be92771c676094cc20782ae0534db3d28271b2cb9eaba2ba8768506d020b085dcea76fd3fea4b5a84b12b265b07d2ffac4ab19197f8edeba9adff7768 + languageName: node + linkType: hard + "doctrine@npm:^2.1.0": version: 2.1.0 resolution: "doctrine@npm:2.1.0" @@ -12661,6 +13487,20 @@ __metadata: languageName: node linkType: hard +"expo-asset@npm:~57.0.16": + version: 57.0.16 + resolution: "expo-asset@npm:57.0.16" + dependencies: + "@expo/image-utils": "npm:^0.11.5" + expo-constants: "npm:~57.0.17" + peerDependencies: + expo: "*" + react: "*" + react-native: "*" + checksum: 10/fec87d4f05aafbb40fce7f3da4ef9e64003bbf6435b521ad4f3900dbb9998d28fc610a4223fb6aebc4d74be70a8434b421bae69c9353516a95e71a5a0a86f1ff + languageName: node + linkType: hard + "expo-constants@npm:~18.0.13": version: 18.0.13 resolution: "expo-constants@npm:18.0.13" @@ -12674,6 +13514,18 @@ __metadata: languageName: node linkType: hard +"expo-constants@npm:~57.0.17": + version: 57.0.17 + resolution: "expo-constants@npm:57.0.17" + dependencies: + "@expo/env": "npm:~2.4.3" + peerDependencies: + expo: "*" + react-native: "*" + checksum: 10/d958fa27f3eab27060dc05e51bdef41cee87ad94d109098c28f04a852adb700d7ad8c2f74396f17eb99d1ff6c157099a452715914d551e9e350f17e126437c67 + languageName: node + linkType: hard + "expo-file-system@npm:~19.0.23": version: 19.0.23 resolution: "expo-file-system@npm:19.0.23" @@ -12684,6 +13536,16 @@ __metadata: languageName: node linkType: hard +"expo-file-system@npm:~57.0.6": + version: 57.0.6 + resolution: "expo-file-system@npm:57.0.6" + peerDependencies: + expo: "*" + react-native: "*" + checksum: 10/0e91f63f13e299184e29ae2ebe2db22b3beccecc1d675d83d8a0db7e264f7f5bac8404291701d10c5c5014c98c572295248efca46a24c96aef73693533bcfcfb + languageName: node + linkType: hard + "expo-font@npm:~14.0.12": version: 14.0.12 resolution: "expo-font@npm:14.0.12" @@ -12697,6 +13559,30 @@ __metadata: languageName: node linkType: hard +"expo-font@npm:~57.0.3": + version: 57.0.3 + resolution: "expo-font@npm:57.0.3" + dependencies: + fontfaceobserver: "npm:^2.1.0" + peerDependencies: + expo: "*" + react: "*" + react-native: "*" + checksum: 10/a9c53d865ee7268a477700a69d5bd0ce4a498da6c27b4af59bc3fcb4c02d348a99a92f385162887e3e26a9ffde2904f0ebdd748b1960dad18f2c74aa7b2c4578 + languageName: node + linkType: hard + +"expo-glass-effect@npm:^57.0.2": + version: 57.0.2 + resolution: "expo-glass-effect@npm:57.0.2" + peerDependencies: + expo: "*" + react: "*" + react-native: "*" + checksum: 10/6bb34edae587cfff00f6b67690113fb21a21b090d1165abc6b64c3e43b308edc0d90656b1030a72edb81ebd672fdd7dfa0cf3bcb698f799ac87beb659af895f7 + languageName: node + linkType: hard + "expo-keep-awake@npm:~15.0.8": version: 15.0.8 resolution: "expo-keep-awake@npm:15.0.8" @@ -12707,6 +13593,29 @@ __metadata: languageName: node linkType: hard +"expo-keep-awake@npm:~57.0.1": + version: 57.0.1 + resolution: "expo-keep-awake@npm:57.0.1" + peerDependencies: + expo: "*" + react: "*" + checksum: 10/34260d33c674c984c2dfa992d4957b07266842b7d397f508c1a77eb9c4ebdb7dcdccd2a42caab2cb59dc51d702aef66ef919739fa53240bc2f6ca03a0e4fff6f + languageName: node + linkType: hard + +"expo-linking@npm:~57.0.9": + version: 57.0.9 + resolution: "expo-linking@npm:57.0.9" + dependencies: + expo-constants: "npm:~57.0.17" + invariant: "npm:^2.2.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10/39c1a154a485df1bf96ff365bac1e79360490cbcf17f9a92669cc958fec5026a09a62446eb037c17efbc8297a1150b30c01412332ac5377177beb1e4c8e98f61 + languageName: node + linkType: hard + "expo-linking@npm:~8.0.12": version: 8.0.12 resolution: "expo-linking@npm:8.0.12" @@ -12735,6 +13644,20 @@ __metadata: languageName: node linkType: hard +"expo-modules-autolinking@npm:~57.0.12": + version: 57.0.12 + resolution: "expo-modules-autolinking@npm:57.0.12" + dependencies: + "@expo/require-utils": "npm:^57.0.5" + "@expo/spawn-async": "npm:^1.8.0" + chalk: "npm:^4.1.0" + commander: "npm:^7.2.0" + bin: + expo-modules-autolinking: bin/expo-modules-autolinking.js + checksum: 10/b1e4ec4a3b17808f1fc8897e732874bf16d32bd2b73a945ba885823e932d803d2701015dd684109de57d38d7e81f6c7029a363f06aa8a0ea4a70c80b33db1636 + languageName: node + linkType: hard + "expo-modules-core@npm:3.0.30": version: 3.0.30 resolution: "expo-modules-core@npm:3.0.30" @@ -12747,6 +13670,129 @@ __metadata: languageName: node linkType: hard +"expo-modules-core@npm:~57.0.17": + version: 57.0.17 + resolution: "expo-modules-core@npm:57.0.17" + dependencies: + "@expo/expo-modules-macros-plugin": "npm:0.6.1" + expo-modules-jsi: "npm:~57.1.0" + invariant: "npm:^2.2.4" + peerDependencies: + react: "*" + react-native: "*" + react-native-worklets: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + peerDependenciesMeta: + react-native-worklets: + optional: true + checksum: 10/da6905eadb05bf330477b6b70ae2e11e6511a8335c976bdb2145174cb453b0343d29e06abdf8a08f32445e508a72a32a171c5ac4e1864f3f6362bc9bc7388ddc + languageName: node + linkType: hard + +"expo-modules-jsi@npm:~57.1.0": + version: 57.1.0 + resolution: "expo-modules-jsi@npm:57.1.0" + peerDependencies: + react-native: "*" + checksum: 10/555391fdc94aa3ff225ba05564e3fea727c695ddf06239480ca0e4038c7614e153dcfc5caf76512c6a53ed5417183735ae3e0d4bf9c09d6c5f9772e8825d92e5 + languageName: node + linkType: hard + +"expo-router-test@workspace:apps/expo-router-test": + version: 0.0.0-use.local + resolution: "expo-router-test@workspace:apps/expo-router-test" + dependencies: + "@bottom-tabs/navigation": "npm:*" + "@bottom-tabs/react-navigation": "npm:*" + "@react-native/jest-preset": "npm:^0.86.3" + "@react-navigation/native": "npm:^7.3.0" + "@testing-library/react-native": "npm:^14.0.1" + "@types/jest": "npm:^29.5.5" + "@types/react": "npm:~19.2.2" + expo: "npm:~57.0.21" + expo-constants: "npm:~57.0.17" + expo-linking: "npm:~57.0.9" + expo-router: "npm:~57.0.20" + jest: "npm:^29.7.0" + jest-expo: "npm:~57.0.5" + react: "npm:19.2.3" + react-dom: "npm:19.2.3" + react-native: "npm:0.86.3" + react-native-bottom-tabs: "npm:*" + react-native-gesture-handler: "npm:~2.32.0" + react-native-reanimated: "npm:4.5.1" + react-native-safe-area-context: "npm:~5.7.0" + react-native-screens: "npm:~4.26.0" + react-native-web: "npm:~0.21.0" + react-native-worklets: "npm:0.10.1" + test-renderer: "npm:^1.2.0" + typescript: "npm:^5.9.2" + languageName: unknown + linkType: soft + +"expo-router@npm:~57.0.20": + version: 57.0.20 + resolution: "expo-router@npm:57.0.20" + dependencies: + "@expo/log-box": "npm:^57.0.4" + "@expo/metro-runtime": "npm:^57.0.15" + "@expo/schema-utils": "npm:^57.0.2" + "@expo/ui": "npm:^57.0.17" + "@radix-ui/react-slot": "npm:^1.2.0" + "@radix-ui/react-tabs": "npm:^1.1.12" + "@react-native-masked-view/masked-view": "npm:^0.3.2" + client-only: "npm:^0.0.1" + color: "npm:^4.2.3" + debug: "npm:^4.3.4" + escape-string-regexp: "npm:^4.0.0" + expo-glass-effect: "npm:^57.0.2" + expo-server: "npm:^57.0.3" + expo-symbols: "npm:^57.0.2" + fast-deep-equal: "npm:^3.1.3" + invariant: "npm:^2.2.4" + nanoid: "npm:^3.3.8" + query-string: "npm:^7.1.3" + react-fast-compare: "npm:^3.2.2" + react-is: "npm:^19.1.0" + react-native-drawer-layout: "npm:^4.2.2" + react-native-screens: "npm:^4.26.0" + server-only: "npm:^0.0.1" + sf-symbols-typescript: "npm:^2.1.0" + shallowequal: "npm:^1.1.0" + standard-navigation: "npm:^0.0.5" + vaul: "npm:^1.1.2" + peerDependencies: + "@expo/log-box": ^57.0.4 + "@expo/metro-runtime": ^57.0.15 + "@testing-library/react-native": ">= 13.2.0" + expo: "*" + expo-constants: ^57.0.17 + expo-linking: ^57.0.9 + react: "*" + react-dom: "*" + react-native: "*" + react-native-gesture-handler: "*" + react-native-reanimated: "*" + react-native-safe-area-context: ">= 5.4.0" + react-native-screens: ^4.26.0 + react-native-web: "*" + react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 + peerDependenciesMeta: + "@testing-library/react-native": + optional: true + react-dom: + optional: true + react-native-gesture-handler: + optional: true + react-native-reanimated: + optional: true + react-native-web: + optional: true + react-server-dom-webpack: + optional: true + checksum: 10/da8141515cb620d26842ec36159072ea106a9fac29d657aa5e73f493dd6824dd95e0ad8c5444174ec7bdc549627786a4eb5e271d09248d0f7b9adf1ab8ee70e8 + languageName: node + linkType: hard + "expo-router@npm:~6.0.24": version: 6.0.24 resolution: "expo-router@npm:6.0.24" @@ -12816,6 +13862,13 @@ __metadata: languageName: node linkType: hard +"expo-server@npm:^57.0.3": + version: 57.0.3 + resolution: "expo-server@npm:57.0.3" + checksum: 10/ae41672973efb24966f70f36714e6e14a719b4cdb17c3eeb6c36f3a094fb363693fdcdfa15d01aa4e1f78a11abda7b28d5ebc26c05b19b6c09d36d6b974f32f7 + languageName: node + linkType: hard + "expo-splash-screen@npm:~31.0.13": version: 31.0.13 resolution: "expo-splash-screen@npm:31.0.13" @@ -12839,6 +13892,21 @@ __metadata: languageName: node linkType: hard +"expo-symbols@npm:^57.0.2": + version: 57.0.2 + resolution: "expo-symbols@npm:57.0.2" + dependencies: + "@expo-google-fonts/material-symbols": "npm:^0.4.1" + sf-symbols-typescript: "npm:^2.0.0" + peerDependencies: + expo: "*" + expo-font: "*" + react: "*" + react-native: "*" + checksum: 10/94539dc139a8c3fefe61360422678f142202752d897cec02da1a717836d4ee7029e3ec2174524b12192dadee8e88308c1966930777f65d1c5b0c2aa7bc8c2f61 + languageName: node + linkType: hard + "expo-symbols@npm:~1.0.8": version: 1.0.8 resolution: "expo-symbols@npm:1.0.8" @@ -12924,6 +13992,60 @@ __metadata: languageName: node linkType: hard +"expo@npm:~57.0.21": + version: 57.0.21 + resolution: "expo@npm:57.0.21" + dependencies: + "@babel/runtime": "npm:^7.20.0" + "@expo/cli": "npm:^57.0.23" + "@expo/config": "npm:~57.0.9" + "@expo/config-plugins": "npm:~57.0.9" + "@expo/devtools": "npm:~57.0.1" + "@expo/dom-webview": "npm:~57.0.1" + "@expo/fingerprint": "npm:^0.20.12" + "@expo/local-build-cache-provider": "npm:^57.0.8" + "@expo/log-box": "npm:^57.0.4" + "@expo/metro": "npm:~56.0.2" + "@expo/metro-config": "npm:~57.0.12" + "@ungap/structured-clone": "npm:^1.3.0" + babel-preset-expo: "npm:~57.0.11" + expo-asset: "npm:~57.0.16" + expo-constants: "npm:~57.0.17" + expo-file-system: "npm:~57.0.6" + expo-font: "npm:~57.0.3" + expo-keep-awake: "npm:~57.0.1" + expo-modules-autolinking: "npm:~57.0.12" + expo-modules-core: "npm:~57.0.17" + pretty-format: "npm:^29.7.0" + react-refresh: "npm:^0.14.2" + whatwg-url-minimum: "npm:^0.1.2" + peerDependencies: + "@expo/dom-webview": "*" + "@expo/metro-runtime": "*" + react: "*" + react-dom: "*" + react-native: "*" + react-native-web: "*" + react-native-webview: "*" + peerDependenciesMeta: + "@expo/dom-webview": + optional: true + "@expo/metro-runtime": + optional: true + react-dom: + optional: true + react-native-web: + optional: true + react-native-webview: + optional: true + bin: + expo: bin/cli + expo-modules-autolinking: bin/autolinking + fingerprint: bin/fingerprint + checksum: 10/3ca64779672a23643e67ab4d81832311a68b19b8ad41b124cb1c51e76d00cc9bb25b11d0960469078b7d45b737f65298616873f6b07d02d501a28605a5d0c004 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.2 resolution: "exponential-backoff@npm:3.1.2" @@ -13046,7 +14168,7 @@ __metadata: languageName: node linkType: hard -"fb-watchman@npm:^2.0.0": +"fb-watchman@npm:^2.0.0, fb-watchman@npm:^2.0.2": version: 2.0.2 resolution: "fb-watchman@npm:2.0.2" dependencies: @@ -13089,6 +14211,13 @@ __metadata: languageName: node linkType: hard +"fetch-nodeshim@npm:^0.4.10": + version: 0.4.10 + resolution: "fetch-nodeshim@npm:0.4.10" + checksum: 10/4abc48fe6bb2c44493f4d781a8d746e99133b18e456f44626fde36852e9f63fe7a3f71c1b0316e49725398c19b46d503389b4622e6e62b81f70add6da4b43cd7 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -14011,10 +15140,24 @@ __metadata: languageName: node linkType: hard -"hermes-estree@npm:0.32.0": - version: 0.32.0 - resolution: "hermes-estree@npm:0.32.0" - checksum: 10/65a30a86a5a560152a2de1842c7bc7ecdadebd62e9cdd7d1809a824de7bc19e8d6a42907d3caff91d9f823862405d4b200447aa0bc25ba16072937e93d0acbd5 +"hermes-estree@npm:0.32.0": + version: 0.32.0 + resolution: "hermes-estree@npm:0.32.0" + checksum: 10/65a30a86a5a560152a2de1842c7bc7ecdadebd62e9cdd7d1809a824de7bc19e8d6a42907d3caff91d9f823862405d4b200447aa0bc25ba16072937e93d0acbd5 + languageName: node + linkType: hard + +"hermes-estree@npm:0.35.0": + version: 0.35.0 + resolution: "hermes-estree@npm:0.35.0" + checksum: 10/d10283d0189ab2270ecae08632ed4f15eb79f206add4960d198aa6efd5686e1c92ed37c17a020c730281e46ff2f56661f3d787bdfb1692218c1495b329049747 + languageName: node + linkType: hard + +"hermes-estree@npm:0.36.0": + version: 0.36.0 + resolution: "hermes-estree@npm:0.36.0" + checksum: 10/76f3fec9700a8ccb299cf53be22cd9038caf3bfba711370dd24f276444c1ed3c38c27fedd3097f6d9cccaa4dfaa4d02de0f8f02e208ab3fb00c97f8845b8aac5 languageName: node linkType: hard @@ -14052,7 +15195,25 @@ __metadata: languageName: node linkType: hard -"hermes-parser@npm:0.36.1": +"hermes-parser@npm:0.35.0": + version: 0.35.0 + resolution: "hermes-parser@npm:0.35.0" + dependencies: + hermes-estree: "npm:0.35.0" + checksum: 10/62be25fa41b708db21c4db9153b0d60cfbf9bd4645f1712eb559b3be8c191266b5b381df60fbbc45416799f73c2361eb69a81eead21dc5159fe2ea72f946d5f7 + languageName: node + linkType: hard + +"hermes-parser@npm:0.36.0": + version: 0.36.0 + resolution: "hermes-parser@npm:0.36.0" + dependencies: + hermes-estree: "npm:0.36.0" + checksum: 10/81150c00bd8da17187f421a75e6e427187465386681427024f3dc51de996c44cee18fb062d95254028aa1f4ffcdbe7cbdeda50e6edece5c378c9f909067f5bae + languageName: node + linkType: hard + +"hermes-parser@npm:0.36.1, hermes-parser@npm:^0.36.0": version: 0.36.1 resolution: "hermes-parser@npm:0.36.1" dependencies: @@ -15287,6 +16448,38 @@ __metadata: languageName: node linkType: hard +"jest-expo@npm:~57.0.5": + version: 57.0.5 + resolution: "jest-expo@npm:57.0.5" + dependencies: + "@jest/create-cache-key-function": "npm:^29.2.1" + "@jest/globals": "npm:^29.2.1" + babel-jest: "npm:^29.2.1" + jest-environment-jsdom: "npm:^29.2.1" + jest-snapshot: "npm:^29.2.1" + jest-watch-select-projects: "npm:^2.0.0" + jest-watch-typeahead: "npm:2.2.1" + json5: "npm:^2.2.3" + lodash: "npm:^4.17.19" + react-test-renderer: "npm:19.2.3" + server-only: "npm:^0.0.1" + stacktrace-js: "npm:^2.0.2" + peerDependencies: + "@react-native/jest-preset": ^0.86.3 + expo: "*" + react-native: "*" + react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 + peerDependenciesMeta: + expo: + optional: true + react-server-dom-webpack: + optional: true + bin: + jest: bin/jest.js + checksum: 10/3297ce32335877e2312774f695eb34e6707accc536f2069a7078a81c96a8d9e1544e1707ab8c65decd216e3bd01ac1907675e5e506f603873dade8ca9cd85f93 + languageName: node + linkType: hard + "jest-get-type@npm:^29.6.3": version: 29.6.3 resolution: "jest-get-type@npm:29.6.3" @@ -16675,6 +17868,32 @@ __metadata: languageName: node linkType: hard +"metro-babel-transformer@npm:0.84.5": + version: 0.84.5 + resolution: "metro-babel-transformer@npm:0.84.5" + dependencies: + "@babel/core": "npm:^7.25.2" + flow-enums-runtime: "npm:^0.0.6" + hermes-parser: "npm:0.35.0" + metro-cache-key: "npm:0.84.5" + nullthrows: "npm:^1.1.1" + checksum: 10/73694b1c2707fa69abad039327031e5e73e69e98fa2a438065246ca6fcf189bce68844f69b93f30ac174e31c19df26fed2c5f46381c5bd690e2a091dacdaaba3 + languageName: node + linkType: hard + +"metro-babel-transformer@npm:0.84.6": + version: 0.84.6 + resolution: "metro-babel-transformer@npm:0.84.6" + dependencies: + "@babel/core": "npm:^7.25.2" + flow-enums-runtime: "npm:^0.0.6" + hermes-parser: "npm:0.35.0" + metro-cache-key: "npm:0.84.6" + nullthrows: "npm:^1.1.1" + checksum: 10/dbad75739b227e3cacf800f9bdd01805b011c7da347859ca193d5753b53877f42aef4e7ee52ffe411ffeb6812836a6bc675fc3954e9aefc54df7156175727a99 + languageName: node + linkType: hard + "metro-babel-transformer@npm:0.87.0": version: 0.87.0 resolution: "metro-babel-transformer@npm:0.87.0" @@ -16706,6 +17925,24 @@ __metadata: languageName: node linkType: hard +"metro-cache-key@npm:0.84.5": + version: 0.84.5 + resolution: "metro-cache-key@npm:0.84.5" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10/ad6d7980687391281b5ee498b2485fe7f65a0d74b43bb6d336bae892983d54722024a119c2ae71de8670ed7f2e6db840075e18b326e1fca6397043ad1a7d0114 + languageName: node + linkType: hard + +"metro-cache-key@npm:0.84.6": + version: 0.84.6 + resolution: "metro-cache-key@npm:0.84.6" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10/b906f5004f770110dc69948f34cd76b0d2b83816c636a05f27e622d361f9a9f20f2767f8824a76f3655e1d0c204a7feda867ceb21bfe3bbda1b05cc3a4c0343a + languageName: node + linkType: hard + "metro-cache-key@npm:0.87.0": version: 0.87.0 resolution: "metro-cache-key@npm:0.87.0" @@ -16739,6 +17976,30 @@ __metadata: languageName: node linkType: hard +"metro-cache@npm:0.84.5": + version: 0.84.5 + resolution: "metro-cache@npm:0.84.5" + 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.84.5" + checksum: 10/5bfaf78412d044a861cc42455d008edb8f8ea4e11db39e4328fe9137ed0f68a74a22d1db46cf3232e59892f7a1c4d58d83d3ea88730bca0d51ed19f2939f9507 + languageName: node + linkType: hard + +"metro-cache@npm:0.84.6": + version: 0.84.6 + resolution: "metro-cache@npm:0.84.6" + 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.84.6" + checksum: 10/9a2aeb047b84ab6abc715382fd4a914c9ded1bb57a3b06b340d3b3ac3dab1a393f43694588953fa4260a511128f2f140e00a4472467cab476d8fda7effb2e41f + languageName: node + linkType: hard + "metro-cache@npm:0.87.0": version: 0.87.0 resolution: "metro-cache@npm:0.87.0" @@ -16783,6 +18044,38 @@ __metadata: languageName: node linkType: hard +"metro-config@npm:0.84.5": + version: 0.84.5 + resolution: "metro-config@npm:0.84.5" + dependencies: + connect: "npm:^3.6.5" + flow-enums-runtime: "npm:^0.0.6" + jest-validate: "npm:^29.7.0" + metro: "npm:0.84.5" + metro-cache: "npm:0.84.5" + metro-core: "npm:0.84.5" + metro-runtime: "npm:0.84.5" + yaml: "npm:^2.6.1" + checksum: 10/e4c4a98c34abf8b6fe7a3f31c02e7987bf89f1a0ac7e7297280b3e94a6aac1d38c8aeaccbb3abfe2f742a8ba3e9e6a90cc236ab5c2dabdc898e06f47ab465195 + languageName: node + linkType: hard + +"metro-config@npm:0.84.6, metro-config@npm:^0.84.3": + version: 0.84.6 + resolution: "metro-config@npm:0.84.6" + dependencies: + connect: "npm:^3.6.5" + flow-enums-runtime: "npm:^0.0.6" + jest-validate: "npm:^29.7.0" + metro: "npm:0.84.6" + metro-cache: "npm:0.84.6" + metro-core: "npm:0.84.6" + metro-runtime: "npm:0.84.6" + yaml: "npm:^2.6.1" + checksum: 10/d4ed4a6f836d1bc5f7adcd6dd18b60000046686b8dbb80cf4c41aa6ee1ad77612b2dd5749e5913c2d8ef1c1b44ed8c91d6ffc26af806619f35e7e2229d8eeb5b + 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" @@ -16820,6 +18113,28 @@ __metadata: languageName: node linkType: hard +"metro-core@npm:0.84.5": + version: 0.84.5 + resolution: "metro-core@npm:0.84.5" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + lodash.throttle: "npm:^4.1.1" + metro-resolver: "npm:0.84.5" + checksum: 10/e3b90d3f11421a73e070992c22e73d1f6f6d58694fee36383a233d75bfde284bdf01f8db66bb9acd51050875615b31cc72c4879176f66861d5d8adec674aa204 + languageName: node + linkType: hard + +"metro-core@npm:0.84.6, metro-core@npm:^0.84.3": + version: 0.84.6 + resolution: "metro-core@npm:0.84.6" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + lodash.throttle: "npm:^4.1.1" + metro-resolver: "npm:0.84.6" + checksum: 10/19b470ea0ca11e5b2d5d02657d73ae6eabc0e0a8cb1a53058f7ee73971815f1f16eac6ef3ea4f6d2ad02766cae8724423421a3003fe252b4c200421b7a0b3047 + languageName: node + linkType: hard + "metro-core@npm:0.87.0": version: 0.87.0 resolution: "metro-core@npm:0.87.0" @@ -16865,6 +18180,40 @@ __metadata: languageName: node linkType: hard +"metro-file-map@npm:0.84.5": + version: 0.84.5 + resolution: "metro-file-map@npm:0.84.5" + 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: 10/8ded0ed6d5d1ead1df88957ef875b1d6108bff61bd869e56a55ae46e682e1467b81bd1fcb5f7783f4beb7cf078334ebbc592827e903dbd76ed58ed9923038bc4 + languageName: node + linkType: hard + +"metro-file-map@npm:0.84.6": + version: 0.84.6 + resolution: "metro-file-map@npm:0.84.6" + 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: 10/e9f0cbb07aa1566ab534b5b8eec73b87cbe1e536541542de0240aa0abcb8274e3fd41cc4a1d935b415a39128fcad3adc50b2ee8f484d638b7a9488a8a6b9b327 + languageName: node + linkType: hard + "metro-file-map@npm:0.87.0": version: 0.87.0 resolution: "metro-file-map@npm:0.87.0" @@ -16902,6 +18251,26 @@ __metadata: languageName: node linkType: hard +"metro-minify-terser@npm:0.84.5": + version: 0.84.5 + resolution: "metro-minify-terser@npm:0.84.5" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + terser: "npm:^5.15.0" + checksum: 10/85e884edadbbc4862ddd4bdb8a562de1b6d05338bbd40871ced8bc267bd4d40417776d32a786cfc6cafe2fce13ba9115c8d72b042fdb2ab7a7b5ab500643b5fb + languageName: node + linkType: hard + +"metro-minify-terser@npm:0.84.6": + version: 0.84.6 + resolution: "metro-minify-terser@npm:0.84.6" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + terser: "npm:^5.15.0" + checksum: 10/fd1b5f1c60272c01c8e2563741e0d8ca1e268df5940ef31d4ebc635f4f7aad09d0414b51473f830d4696cddc21d3e50abca9efdcf33ee8787a73a1fa59fce8ff + languageName: node + linkType: hard + "metro-minify-terser@npm:0.87.0": version: 0.87.0 resolution: "metro-minify-terser@npm:0.87.0" @@ -16930,6 +18299,24 @@ __metadata: languageName: node linkType: hard +"metro-resolver@npm:0.84.5": + version: 0.84.5 + resolution: "metro-resolver@npm:0.84.5" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10/601a26c5b143f857a7be6a03aceb9783f60e9526172017808479fae3c06e18bc3c16ffd0e0cbdc33ba0343a878a99545aee5923da8b4a5bd5672ef80584d9424 + languageName: node + linkType: hard + +"metro-resolver@npm:0.84.6": + version: 0.84.6 + resolution: "metro-resolver@npm:0.84.6" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10/94575d9c48d59a02735e72b5cf12152f2320936e12f9ac05469683bedc3c5aa148d134eae1a2c78dbe072ae184a005312b905f0583495d5d4d0a5f4dd4a8ab00 + languageName: node + linkType: hard + "metro-resolver@npm:0.87.0": version: 0.87.0 resolution: "metro-resolver@npm:0.87.0" @@ -16959,6 +18346,26 @@ __metadata: languageName: node linkType: hard +"metro-runtime@npm:0.84.5": + version: 0.84.5 + resolution: "metro-runtime@npm:0.84.5" + dependencies: + "@babel/runtime": "npm:^7.25.0" + flow-enums-runtime: "npm:^0.0.6" + checksum: 10/ea5977797c1c7908ce4612e8ed18b582158e7874368470f46e0cee1e19abbeff6dc233e52154d7821260087134882c96274dfeddd7586fdc023931d287cee471 + languageName: node + linkType: hard + +"metro-runtime@npm:0.84.6, metro-runtime@npm:^0.84.3": + version: 0.84.6 + resolution: "metro-runtime@npm:0.84.6" + dependencies: + "@babel/runtime": "npm:^7.25.0" + flow-enums-runtime: "npm:^0.0.6" + checksum: 10/239e9fda15350916f47028f62fcb50e1f91f0bc32592542bd86cf0efb2741bc7d4ef80e9511390d50457af92194413820f774b06f028e5355122105a55ada268 + 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" @@ -17005,6 +18412,40 @@ __metadata: languageName: node linkType: hard +"metro-source-map@npm:0.84.5": + version: 0.84.5 + resolution: "metro-source-map@npm:0.84.5" + 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.84.5" + nullthrows: "npm:^1.1.1" + ob1: "npm:0.84.5" + source-map: "npm:^0.5.6" + vlq: "npm:^1.0.0" + checksum: 10/df78c1c6e31a800976a848eaa6aa1dc9736f18d7bc67a18196a73f923178ab3136c18b580263adca471004d9421b381ec6814bf5bf49ba515919f628e9a919af + languageName: node + linkType: hard + +"metro-source-map@npm:0.84.6, metro-source-map@npm:^0.84.3": + version: 0.84.6 + resolution: "metro-source-map@npm:0.84.6" + 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.84.6" + nullthrows: "npm:^1.1.1" + ob1: "npm:0.84.6" + source-map: "npm:^0.5.6" + vlq: "npm:^1.0.0" + checksum: 10/d85af35dce002a29c32cd23d94c46e86b6210509089d9ffc7728e7ae2ff3db50cf5944011c41e0006dcef5ac8b4536f3ff990809c519b0acb5fbb81ef3e4b323 + 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" @@ -17054,6 +18495,38 @@ __metadata: languageName: node linkType: hard +"metro-symbolicate@npm:0.84.5": + version: 0.84.5 + resolution: "metro-symbolicate@npm:0.84.5" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + invariant: "npm:^2.2.4" + metro-source-map: "npm:0.84.5" + nullthrows: "npm:^1.1.1" + source-map: "npm:^0.5.6" + vlq: "npm:^1.0.0" + bin: + metro-symbolicate: src/index.js + checksum: 10/9725953c3df5e9b59bca7bf310b42fef48cbdf7134891307271e7ecf594d3d500d2a9ec2e98f3c1d71eade4bf036c275ec1f4d2b1943b22755d948d6b5023fc0 + languageName: node + linkType: hard + +"metro-symbolicate@npm:0.84.6": + version: 0.84.6 + resolution: "metro-symbolicate@npm:0.84.6" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + invariant: "npm:^2.2.4" + metro-source-map: "npm:0.84.6" + nullthrows: "npm:^1.1.1" + source-map: "npm:^0.5.6" + vlq: "npm:^1.0.0" + bin: + metro-symbolicate: src/index.js + checksum: 10/66cc54a45d9ddc42f3c2afb328e48fbb01d21aeafa8b47000a8d1bedfcf97cae39c4550d9bd547b761325dd2c545f0187bb5f0d323ea8f25b2853a44f796e8e3 + languageName: node + linkType: hard + "metro-symbolicate@npm:0.87.0": version: 0.87.0 resolution: "metro-symbolicate@npm:0.87.0" @@ -17098,6 +18571,34 @@ __metadata: languageName: node linkType: hard +"metro-transform-plugins@npm:0.84.5": + version: 0.84.5 + resolution: "metro-transform-plugins@npm:0.84.5" + 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: 10/929d5ea4a796d335c730ee9721a75eaf30e6068930f968a78b2e82f3bbb7edfa820910e8cd178127868872af1386044cfebf251c60a7f560f48b8fe105477f58 + languageName: node + linkType: hard + +"metro-transform-plugins@npm:0.84.6": + version: 0.84.6 + resolution: "metro-transform-plugins@npm:0.84.6" + 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: 10/de001d127bd220c9495db0514490aefcd657f0fd67efc416e17fe434f1da2552e4417d2d75be6203789bfe62bb9baa84fa6332ec92a0065cfbd06713e63f60f7 + languageName: node + linkType: hard + "metro-transform-plugins@npm:0.87.0": version: 0.87.0 resolution: "metro-transform-plugins@npm:0.87.0" @@ -17138,19 +18639,61 @@ __metadata: resolution: "metro-transform-worker@npm:0.83.3" dependencies: "@babel/core": "npm:^7.25.2" - "@babel/generator": "npm:^7.25.0" - "@babel/parser": "npm:^7.25.3" - "@babel/types": "npm:^7.25.2" + "@babel/generator": "npm:^7.25.0" + "@babel/parser": "npm:^7.25.3" + "@babel/types": "npm:^7.25.2" + flow-enums-runtime: "npm:^0.0.6" + metro: "npm:0.83.3" + metro-babel-transformer: "npm:0.83.3" + metro-cache: "npm:0.83.3" + metro-cache-key: "npm:0.83.3" + metro-minify-terser: "npm:0.83.3" + metro-source-map: "npm:0.83.3" + metro-transform-plugins: "npm:0.83.3" + nullthrows: "npm:^1.1.1" + checksum: 10/e6db9b54a9b21f4b06fc665321a7aebc6206dbac3976bda74bdf4d101dbd50f91b2e49163581ca1c27b684a4eecc2db988f0fc7aaeb200d2d947cb05d3e89f18 + languageName: node + linkType: hard + +"metro-transform-worker@npm:0.84.5": + version: 0.84.5 + resolution: "metro-transform-worker@npm:0.84.5" + 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.84.5" + metro-babel-transformer: "npm:0.84.5" + metro-cache: "npm:0.84.5" + metro-cache-key: "npm:0.84.5" + metro-minify-terser: "npm:0.84.5" + metro-source-map: "npm:0.84.5" + metro-transform-plugins: "npm:0.84.5" + nullthrows: "npm:^1.1.1" + checksum: 10/5facb1071dd73cf1e5e44eb4247c29478a967631a8b54683f0953aa68d361a2f8a667cb1f15f926718e6c1af9dd0a1e6e78e34a88401ec93e2c0966b3502c47e + languageName: node + linkType: hard + +"metro-transform-worker@npm:0.84.6": + version: 0.84.6 + resolution: "metro-transform-worker@npm:0.84.6" + 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.83.3" - metro-babel-transformer: "npm:0.83.3" - metro-cache: "npm:0.83.3" - metro-cache-key: "npm:0.83.3" - metro-minify-terser: "npm:0.83.3" - metro-source-map: "npm:0.83.3" - metro-transform-plugins: "npm:0.83.3" + metro: "npm:0.84.6" + metro-babel-transformer: "npm:0.84.6" + metro-cache: "npm:0.84.6" + metro-cache-key: "npm:0.84.6" + metro-minify-terser: "npm:0.84.6" + metro-source-map: "npm:0.84.6" + metro-transform-plugins: "npm:0.84.6" nullthrows: "npm:^1.1.1" - checksum: 10/e6db9b54a9b21f4b06fc665321a7aebc6206dbac3976bda74bdf4d101dbd50f91b2e49163581ca1c27b684a4eecc2db988f0fc7aaeb200d2d947cb05d3e89f18 + checksum: 10/14239c4e9c90ec1b708072aab67035cad13e5074c8c05fdbd8c653a3c50c8ff21e3c0bb1abbcef54713ed347a85dbb737057141ea2526eddbf29531febb3f2d7 languageName: node linkType: hard @@ -17275,6 +18818,102 @@ __metadata: languageName: node linkType: hard +"metro@npm:0.84.5": + version: 0.84.5 + resolution: "metro@npm:0.84.5" + 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.35.0" + 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.84.5" + metro-cache: "npm:0.84.5" + metro-cache-key: "npm:0.84.5" + metro-config: "npm:0.84.5" + metro-core: "npm:0.84.5" + metro-file-map: "npm:0.84.5" + metro-resolver: "npm:0.84.5" + metro-runtime: "npm:0.84.5" + metro-source-map: "npm:0.84.5" + metro-symbolicate: "npm:0.84.5" + metro-transform-plugins: "npm:0.84.5" + metro-transform-worker: "npm:0.84.5" + 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: 10/d6c97cdbce2933db3ad2ee56b83a36c5b2673537899d116f301cca58bb70c3eb72c45fca63836565733834c1fec2c898822cb73001f3b193bff31c242a106545 + languageName: node + linkType: hard + +"metro@npm:0.84.6, metro@npm:^0.84.3": + version: 0.84.6 + resolution: "metro@npm:0.84.6" + 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.35.0" + 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.84.6" + metro-cache: "npm:0.84.6" + metro-cache-key: "npm:0.84.6" + metro-config: "npm:0.84.6" + metro-core: "npm:0.84.6" + metro-file-map: "npm:0.84.6" + metro-resolver: "npm:0.84.6" + metro-runtime: "npm:0.84.6" + metro-source-map: "npm:0.84.6" + metro-symbolicate: "npm:0.84.6" + metro-transform-plugins: "npm:0.84.6" + metro-transform-worker: "npm:0.84.6" + 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: 10/e367a0d31e0cb60787c8aaa4652ced1989e4ef6af368d83d7b8884e00f0a91339daeaa6c64a1b99b12c5c5d7d370a1339823f54ce53eda55836ba8d62a3554c2 + languageName: node + linkType: hard + "metro@npm:0.87.0, metro@npm:^0.87.0": version: 0.87.0 resolution: "metro@npm:0.87.0" @@ -18060,6 +19699,13 @@ __metadata: languageName: node linkType: hard +"multitars@npm:^1.0.2": + version: 1.0.2 + resolution: "multitars@npm:1.0.2" + checksum: 10/1afa9284a9dd64b6da226cefd2b91e0fb4a6a88e02e13f7710b15e795efd2209aa95c77789f1dd9c97d14d99848f38a26e95be942990058fee9cbfbb578f223f + languageName: node + linkType: hard + "mz@npm:^2.7.0": version: 2.7.0 resolution: "mz@npm:2.7.0" @@ -18080,6 +19726,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.18": + version: 3.3.18 + resolution: "nanoid@npm:3.3.18" + bin: + nanoid: bin/nanoid.cjs + checksum: 10/1b3b4fdac831b92b56d1dbe8b1e63a372432faf86ea383134e3b53141ef8108a60b7f84343b5cefecac9550bb74edf8164da93ea07d1c4f757039bc75d8a5d9e + languageName: node + linkType: hard + "nanoid@npm:^3.3.7": version: 3.3.8 resolution: "nanoid@npm:3.3.8" @@ -18323,6 +19978,24 @@ __metadata: languageName: node linkType: hard +"ob1@npm:0.84.5": + version: 0.84.5 + resolution: "ob1@npm:0.84.5" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10/7a0389c584b1068486d945e0300deef5f5732207aaec3666c1381aaa3ae1845210bb7eeb6aef61aae8c1eaa4231a7e393563d114a2cb6185d54bbaf619b65b2c + languageName: node + linkType: hard + +"ob1@npm:0.84.6": + version: 0.84.6 + resolution: "ob1@npm:0.84.6" + dependencies: + flow-enums-runtime: "npm:^0.0.6" + checksum: 10/2ecb61faad2fa2520936a93da50897da917d49d722474bc1b00c0d01ea011254f24b169530861649cd90dc916030408e5221c45081a87381db782c1869c106f7 + languageName: node + linkType: hard + "ob1@npm:0.87.0": version: 0.87.0 resolution: "ob1@npm:0.87.0" @@ -18937,6 +20610,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.14": + version: 8.5.28 + resolution: "postcss@npm:8.5.28" + dependencies: + nanoid: "npm:^3.3.18" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10/c34814c1da499f370cd3b02a085a2d866989a966629b7bd4025f4a2a10d538a2ec83dc3181e1008550edb4cf44e49a0c40ec82332b08d9600877e6841240ad05 + languageName: node + linkType: hard + "postcss@npm:~8.4.32": version: 8.4.49 resolution: "postcss@npm:8.4.49" @@ -19260,6 +20944,17 @@ __metadata: languageName: node linkType: hard +"react-dom@npm:19.2.3": + version: 19.2.3 + resolution: "react-dom@npm:19.2.3" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.3 + checksum: 10/5780f6d4c8e8ece09f82c5500ba2d55e01c30b5273f9281734d7d3b65013cd1fa52ec4e4436e5248c0a9e5bc340836044051168bbad8d7eac4d33ee6c2a867a1 + languageName: node + linkType: hard + "react-dom@npm:^19.1.1": version: 19.1.1 resolution: "react-dom@npm:19.1.1" @@ -19410,6 +21105,21 @@ __metadata: languageName: node linkType: hard +"react-native-drawer-layout@npm:^4.2.2": + version: 4.2.10 + resolution: "react-native-drawer-layout@npm:4.2.10" + dependencies: + color: "npm:^4.2.3" + use-latest-callback: "npm:^0.2.4" + peerDependencies: + react: ">= 18.2.0" + react-native: "*" + react-native-gesture-handler: ">= 2.0.0" + react-native-reanimated: ">= 2.0.0" + checksum: 10/ed74459371e4f450bfccc2ae12e11399508917442a96a0039a8aacaea3903615e64c866a9da4365edf70bf34534fffc6c7bc4423250f9d264cddb4a56cc136f5 + languageName: node + linkType: hard + "react-native-edge-to-edge@npm:^1.7.0": version: 1.7.0 resolution: "react-native-edge-to-edge@npm:1.7.0" @@ -19447,6 +21157,21 @@ __metadata: languageName: node linkType: hard +"react-native-gesture-handler@npm:~2.32.0": + version: 2.32.0 + resolution: "react-native-gesture-handler@npm:2.32.0" + dependencies: + "@egjs/hammerjs": "npm:^2.0.17" + "@types/react-test-renderer": "npm:^19.1.0" + hoist-non-react-statics: "npm:^3.3.0" + invariant: "npm:^2.2.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10/b43d350fd281ed9f75a469873a1eb74ae7cfa3d3e15dc7e57b11c0da5702a107b2824e1a9115386d9812e1179e86ccb952d6617cecbdbb26097d7c186088210f + languageName: node + linkType: hard + "react-native-gesture-handler@patch:react-native-gesture-handler@npm%3A3.2.1#~/.yarn/patches/react-native-gesture-handler-npm-3.2.1-73a74f5c79.patch": version: 3.2.1 resolution: "react-native-gesture-handler@patch:react-native-gesture-handler@npm%3A3.2.1#~/.yarn/patches/react-native-gesture-handler-npm-3.2.1-73a74f5c79.patch::version=3.2.1&hash=290fa5" @@ -19480,6 +21205,16 @@ __metadata: languageName: node linkType: hard +"react-native-is-edge-to-edge@npm:^1.3.1": + version: 1.3.1 + resolution: "react-native-is-edge-to-edge@npm:1.3.1" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10/dc82d54e0bf8f89208a538bb0d14e4891af6efae27ed5b7b21be683a72c38c5219ab9be1ea9bd40aa1c905d481174e649d0b71aeceaa9946e6c707f251568282 + languageName: node + linkType: hard + "react-native-monorepo-config@npm:^0.1.8": version: 0.1.9 resolution: "react-native-monorepo-config@npm:0.1.9" @@ -19505,6 +21240,20 @@ __metadata: languageName: node linkType: hard +"react-native-reanimated@npm:4.5.1": + version: 4.5.1 + resolution: "react-native-reanimated@npm:4.5.1" + dependencies: + react-native-is-edge-to-edge: "npm:^1.3.1" + semver: "npm:^7.7.3" + peerDependencies: + react: "*" + react-native: 0.83 - 0.86 + react-native-worklets: 0.10.x + checksum: 10/da53fe28fd0eb492eecd5f5f988153b0e214c6010636762cb9e2e508795e6aff0b94ac9288a0a16559e3b60bdf5e005218baaee69b23a13d0afa7b85c09c51bf + languageName: node + linkType: hard + "react-native-reanimated@npm:~4.1.1": version: 4.1.7 resolution: "react-native-reanimated@npm:4.1.7" @@ -19539,6 +21288,16 @@ __metadata: 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" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10/9f5d3469c18c90c527c4068560ce4dcc7689e9fc8ddc68b8a5e76c94a6eafdba975ced89a48e08fecfd1303331e8c2498722bc249fcd87b1c5ea64ef3abd1d00 + languageName: node + linkType: hard + "react-native-safe-area-context@patch:react-native-safe-area-context@npm%3A5.9.1#~/.yarn/patches/react-native-safe-area-context-npm-5.9.1-b51fb9c5f7.patch": version: 5.9.1 resolution: "react-native-safe-area-context@patch:react-native-safe-area-context@npm%3A5.9.1#~/.yarn/patches/react-native-safe-area-context-npm-5.9.1-b51fb9c5f7.patch::version=5.9.1&hash=1fdb4c" @@ -19549,7 +21308,7 @@ __metadata: languageName: node linkType: hard -"react-native-screens@npm:4.27.0, react-native-screens@npm:^4.27.0": +"react-native-screens@npm:4.27.0, react-native-screens@npm:^4.26.0, react-native-screens@npm:^4.27.0": version: 4.27.0 resolution: "react-native-screens@npm:4.27.0" dependencies: @@ -19576,6 +21335,19 @@ __metadata: languageName: node linkType: hard +"react-native-screens@npm:~4.26.0": + version: 4.26.2 + resolution: "react-native-screens@npm:4.26.2" + dependencies: + react-freeze: "npm:^1.0.0" + warn-once: "npm:^0.1.0" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10/9d171ca538ba511ea30a7e7f47088d408f9bb565b7bfeedbb5443ffe430ac0c48f805fb2b89b7b16e61c9b81480a01049864ecd2651f77e3d0d0b1f5d6ce54f1 + languageName: node + linkType: hard + "react-native-screens@patch:react-native-screens@npm%3A4.27.0#~/.yarn/patches/react-native-screens-npm-4.27.0-78335f53c6.patch": version: 4.27.0 resolution: "react-native-screens@patch:react-native-screens@npm%3A4.27.0#~/.yarn/patches/react-native-screens-npm-4.27.0-78335f53c6.patch::version=4.27.0&hash=8b4662" @@ -19674,6 +21446,31 @@ __metadata: languageName: node linkType: hard +"react-native-worklets@npm:0.10.1": + version: 0.10.1 + resolution: "react-native-worklets@npm:0.10.1" + dependencies: + "@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/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.86 + checksum: 10/5c728806660e8798fc39dd7733129d69254413800ae3ecef64c999720a7545125ffa52e93f3d5715aec96fc79d97f60b4be3e5dabd39a7a051f6dd30b10778a2 + languageName: node + linkType: hard + "react-native-worklets@npm:0.12.1, react-native-worklets@npm:^0.12.1": version: 0.12.1 resolution: "react-native-worklets@npm:0.12.1" @@ -19851,6 +21648,57 @@ __metadata: languageName: node linkType: hard +"react-native@npm:0.86.3": + version: 0.86.3 + resolution: "react-native@npm:0.86.3" + dependencies: + "@react-native/assets-registry": "npm:0.86.3" + "@react-native/codegen": "npm:0.86.3" + "@react-native/community-cli-plugin": "npm:0.86.3" + "@react-native/gradle-plugin": "npm:0.86.3" + "@react-native/js-polyfills": "npm:0.86.3" + "@react-native/normalize-colors": "npm:0.86.3" + "@react-native/virtualized-lists": "npm:0.86.3" + abort-controller: "npm:^3.0.0" + anser: "npm:^1.4.9" + ansi-regex: "npm:^5.0.0" + babel-plugin-syntax-hermes-parser: "npm:0.36.0" + base64-js: "npm:^1.5.1" + commander: "npm:^12.0.0" + flow-enums-runtime: "npm:^0.0.6" + hermes-compiler: "npm:250829098.0.17" + invariant: "npm:^2.2.4" + memoize-one: "npm:^5.0.0" + metro-runtime: "npm:^0.84.3" + metro-source-map: "npm:^0.84.3" + nullthrows: "npm:^1.1.1" + pretty-format: "npm:^29.7.0" + promise: "npm:^8.3.0" + react-devtools-core: "npm:^6.1.5" + react-refresh: "npm:^0.14.0" + regenerator-runtime: "npm:^0.13.2" + scheduler: "npm:0.27.0" + semver: "npm:^7.1.3" + stacktrace-parser: "npm:^0.1.10" + tinyglobby: "npm:^0.2.15" + whatwg-fetch: "npm:^3.0.0" + ws: "npm:^7.5.10" + yargs: "npm:^17.6.2" + peerDependencies: + "@react-native/jest-preset": 0.86.3 + "@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: 10/494aa0ca3b8f915813c3f39cca50479fb61643e8ca48bf1854bda1abfac195231d2ab9dca2f9e8e445a354dfb6c85db97e32c9e85ee1caa03ba9a4a8cc5fd0ed + languageName: node + linkType: hard + "react-native@npm:^0.87.1": version: 0.87.1 resolution: "react-native@npm:0.87.1" @@ -19908,6 +21756,17 @@ __metadata: languageName: node linkType: hard +"react-reconciler@npm:~0.33.0": + version: 0.33.0 + resolution: "react-reconciler@npm:0.33.0" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.0 + checksum: 10/eb8ddb8b5cfe850e022379df5330fad1871ab75dd7bc3a4e4c4fef7aa1b951b4cd784263186a0e5b01313de91b389499f95fc616f96b7d9e5dd33c13bdacb014 + languageName: node + linkType: hard + "react-refresh@npm:^0.14.0, react-refresh@npm:^0.14.2": version: 0.14.2 resolution: "react-refresh@npm:0.14.2" @@ -20028,6 +21887,13 @@ __metadata: languageName: node linkType: hard +"react@npm:19.2.3": + version: 19.2.3 + resolution: "react@npm:19.2.3" + checksum: 10/d16b7f35c0d35a56f63d9d1693819762e4abc479c57dd6310298920bed3820fcec7e17a99d44983416d8f5049143ea45b8005d3ab8324bae8973224400502b08 + languageName: node + linkType: hard + "react@npm:^19.1.1": version: 19.1.1 resolution: "react@npm:19.1.1" @@ -20792,6 +22658,15 @@ __metadata: languageName: node linkType: hard +"sandbox-cli-detector@npm:^0.2.0": + version: 0.2.0 + resolution: "sandbox-cli-detector@npm:0.2.0" + bin: + sandbox-cli-detector: dist/cli.js + checksum: 10/900b3f5ddccadaf0d9c49d8aaa1074b2ff43b8c2b9648b82453a55eb1322a46d011077404af71e490ef807a9b7686b2b33d3cc1341a0b69f45c667e39cd8de7e + languageName: node + linkType: hard + "sax@npm:>=0.6.0": version: 1.4.1 resolution: "sax@npm:1.4.1" @@ -21471,6 +23346,13 @@ __metadata: languageName: node linkType: hard +"standard-navigation@npm:^0.0.5": + version: 0.0.5 + resolution: "standard-navigation@npm:0.0.5" + checksum: 10/89fdd54c597133e658829073abdce086aff111fd2c5593c771a9793757662ca582680193db6f5fc8c35374cd0fc0f28826417cb2c18a68ce5decb8718da93d08 + languageName: node + linkType: hard + "standard-navigation@npm:^0.0.7": version: 0.0.7 resolution: "standard-navigation@npm:0.0.7" @@ -21478,6 +23360,15 @@ __metadata: languageName: node linkType: hard +"standard-navigation@npm:^0.0.9": + version: 0.0.9 + resolution: "standard-navigation@npm:0.0.9" + peerDependencies: + react: "*" + checksum: 10/67381be75f33ee302a8267028d179a4a52a7f80a542a8059e4e2aaedc64ea9a40211a52a32c5ca723ad4ed10567aeb2e471df7e54b7bb79d0d975d032b3a012c + languageName: node + linkType: hard + "statuses@npm:2.0.1": version: 2.0.1 resolution: "statuses@npm:2.0.1" @@ -21934,6 +23825,18 @@ __metadata: languageName: node linkType: hard +"test-renderer@npm:^1.2.0": + version: 1.2.0 + resolution: "test-renderer@npm:1.2.0" + dependencies: + "@types/react-reconciler": "npm:~0.33.0" + react-reconciler: "npm:~0.33.0" + peerDependencies: + react: ^19.0.0 + checksum: 10/65be16c35ce1e0e2cef6b6f28043de96954077c303016fad98feee5d8fafd81ca62c215d566780e99b0f80064c40a92364122fbc96ec672e647f3f40fc365830 + languageName: node + linkType: hard + "test-renderer@npm:~1.1.0": version: 1.1.0 resolution: "test-renderer@npm:1.1.0" @@ -22058,6 +23961,13 @@ __metadata: languageName: node linkType: hard +"toqr@npm:^0.1.1": + version: 0.1.1 + resolution: "toqr@npm:0.1.1" + checksum: 10/b75da11ce8bf645f805c43fc8a2ea6dfe5e7d2da9a751404deb72d48def027abccdf4ea3af5dce771852717f5c2c5d2eb7fdee246566eccbdab9b86a98ba9100 + languageName: node + linkType: hard + "tough-cookie@npm:^4.1.2": version: 4.1.4 resolution: "tough-cookie@npm:4.1.4" @@ -23050,6 +24960,13 @@ __metadata: languageName: node linkType: hard +"whatwg-url-minimum@npm:^0.1.2": + version: 0.1.2 + resolution: "whatwg-url-minimum@npm:0.1.2" + checksum: 10/190334ec6bc9e3807ff7bd89ca6e7893b8fe8633505f267e094fa9204344d601099644ac42fbf143bf92ea7d943595c4dacdd3981e24c4a4c7e1f0cb8cdb2d19 + languageName: node + linkType: hard + "whatwg-url-without-unicode@npm:8.0.0-3": version: 8.0.0-3 resolution: "whatwg-url-without-unicode@npm:8.0.0-3" @@ -23510,7 +25427,7 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.23.8": +"zod@npm:^3.23.8, zod@npm:^3.25.76": version: 3.25.76 resolution: "zod@npm:3.25.76" checksum: 10/f0c963ec40cd96858451d1690404d603d36507c1fc9682f2dae59ab38b578687d542708a7fdbf645f77926f78c9ed558f57c3d3aa226c285f798df0c4da16995