From 5d0aeeff5853a22e56668fc75af7b4add4a511cd Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 25 Jun 2024 19:07:47 +0300 Subject: [PATCH 01/21] feat(clerk-expo): Introduce LocalAuth utility --- packages/expo/src/ClerkProvider.tsx | 15 +- packages/expo/src/experimental/LocalAuth.tsx | 142 +++++++++++++++++++ packages/expo/src/experimental/index.ts | 1 + packages/expo/src/index.ts | 2 + 4 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 packages/expo/src/experimental/LocalAuth.tsx create mode 100644 packages/expo/src/experimental/index.ts diff --git a/packages/expo/src/ClerkProvider.tsx b/packages/expo/src/ClerkProvider.tsx index c25b7280663..0cca2fb8ac3 100644 --- a/packages/expo/src/ClerkProvider.tsx +++ b/packages/expo/src/ClerkProvider.tsx @@ -5,15 +5,28 @@ import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react'; import React from 'react'; import type { TokenCache } from './cache'; +// import { LocalAuthProvider } from './experimental/LocalAuth'; import { isReactNative } from './runtime'; import { getClerkInstance } from './singleton'; export type ClerkProviderProps = ClerkReactProviderProps & { tokenCache?: TokenCache; + // localAuth?: { + // lockTimeout?: number; + // inactiveScreen?: React.ReactNode; + // onLockTimeOutReached?: () => void; + // lockTimeOutScreen?: React.FunctionComponent<{ authenticateWithBiometrics: () => Promise }>; + // }; }; export function ClerkProvider(props: ClerkProviderProps): JSX.Element { - const { children, tokenCache, publishableKey, ...rest } = props; + const { + children, + tokenCache, + publishableKey, + // localAuth, + ...rest + } = props; const pk = publishableKey || process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY || process.env.CLERK_PUBLISHABLE_KEY || ''; return ( diff --git a/packages/expo/src/experimental/LocalAuth.tsx b/packages/expo/src/experimental/LocalAuth.tsx new file mode 100644 index 00000000000..26b24346f5c --- /dev/null +++ b/packages/expo/src/experimental/LocalAuth.tsx @@ -0,0 +1,142 @@ +import type { UserResource } from '@clerk/types'; +import * as LocalAuthentication from 'expo-local-authentication'; +import type { PropsWithChildren } from 'react'; +import React, { createContext, useCallback, useEffect, useRef, useState } from 'react'; +// eslint-disable-next-line import/namespace +import { AppState } from 'react-native'; +import { MMKV } from 'react-native-mmkv'; + +import { getClerkInstance } from '../singleton'; + +export const UserInactivityStore = new MMKV({ + id: 'UserInactivity', +}); + +// Utility type to check if a type is a function +type IsFunction = T extends (...args: any[]) => any ? true : false; + +// Main utility type to exclude function properties +type ExcludeFunctions = { + [K in keyof T]: IsFunction extends true ? never : T[K]; +}; + +export const UserInactivityContext = createContext<{ + localAuthUser: ExcludeFunctions | null; +}>({ + localAuthUser: null, +}); + +type LocalAuthProviderProps = { + lockTimeout?: number; + // onLockTimeOutReached?: () => void; + // inactiveScreen?: React.ReactNode; + lockTimeOutScreen?: React.FunctionComponent<{ + authenticateWithBiometrics: () => Promise; + localAuthUser: ExcludeFunctions; + }>; +}; +// export const useUserInactivity = () => useContext(UserInactivityContext); + +export function LocalAuthProvider(props: PropsWithChildren): JSX.Element { + const { + children, + lockTimeout = 1000 * 10, + // onLockTimeOutReached, + lockTimeOutScreen: LockTimeOutScreen, + } = props; + const lockTimeoutRef = useRef(lockTimeout); + // const onLockTimeOutReachedRef = useRef(onLockTimeOutReached); + const appState = useRef(AppState.currentState); + const [localUser, setLocalUser] = useState | null>(null); + const getElapsed = useCallback(() => { + const hasUser = !!JSON.parse(UserInactivityStore.getString('user') || '')?.id; + const elapsed = Date.now() - (UserInactivityStore.getNumber('startTime') || 0); + + if (!hasUser) { + return false; + } + + return elapsed >= lockTimeoutRef.current; + }, []); + + const [isLocked, setLocked] = useState(getElapsed()); + // const [isInactive, setInactive] = useState(false); + + const authenticateWithBiometrics = useCallback(async () => { + const { success } = await LocalAuthentication.authenticateAsync(); + setLocked(!success); + return success; + }, []); + + const handleActiveState = useCallback( + async (skipElapsed = false) => { + if (!skipElapsed && !getElapsed()) { + return; + } + + console.log('is ellapsed'); + setLocked(true); + // router.replace('/(modals)/lock'); + // if (typeof onLockTimeOutReachedRef.current === 'function') { + // onLockTimeOutReachedRef.current(); + // } else { + await authenticateWithBiometrics(); + }, + [authenticateWithBiometrics, getElapsed], + ); + + useEffect(() => { + setLocalUser(JSON.parse(UserInactivityStore.getString('user') || '') || {}); + void handleActiveState(true); + }, [handleActiveState]); + + useEffect(() => { + const sub = AppState.addEventListener('change', nextAppState => { + if (nextAppState === 'inactive') { + console.log('--- to inactive'); + // setInactive(true); + UserInactivityStore.set('user', JSON.stringify(getClerkInstance().user || '')); + } else { + // setInactive(false); + } + + if (nextAppState === 'background') { + console.log('--- to background'); + UserInactivityStore.set('user', JSON.stringify(getClerkInstance().user || '')); + recordStartTime(); + } else if (nextAppState === 'active' && appState.current.match(/background/i)) { + console.log('--- active'); + + setLocalUser(JSON.parse(UserInactivityStore.getString('user') || '') || {}); + void handleActiveState(); + } + + appState.current = nextAppState; + }); + + return () => { + sub.remove(); + }; + }, [handleActiveState]); + + const recordStartTime = () => { + UserInactivityStore.set('startTime', Date.now()); + }; + + return ( + + {LockTimeOutScreen && isLocked ? ( + + ) : ( + children + )} + + ); +} diff --git a/packages/expo/src/experimental/index.ts b/packages/expo/src/experimental/index.ts new file mode 100644 index 00000000000..5b6bba91709 --- /dev/null +++ b/packages/expo/src/experimental/index.ts @@ -0,0 +1 @@ +export { LocalAuthProvider } from './LocalAuth'; diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts index 4fa4c6b6a0e..df8f652f53a 100644 --- a/packages/expo/src/index.ts +++ b/packages/expo/src/index.ts @@ -17,6 +17,8 @@ export { export { isClerkAPIResponseError, isEmailLinkError, isKnownError, isMetamaskError } from '@clerk/clerk-react/errors'; +export { LocalAuthProvider as __experimental_LocalAuthProvider } from './experimental'; + /** * @deprecated Use `getClerkInstance()` instead. */ From d93018f4b1a6935d081b406b9368a57d093cf53d Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 25 Jun 2024 22:29:33 +0300 Subject: [PATCH 02/21] chore(clerk-expo): Update package.json --- packages/expo/package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/expo/package.json b/packages/expo/package.json index 342c79610dd..36519589f88 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -55,12 +55,18 @@ "@types/react-dom": "*", "expo-auth-session": "^5.4.0", "expo-web-browser": "^12.8.2", + "expo-local-authentication": "^14.0.1", + "react-native": "^0.74.2", + "react-native-mmkv": "^2.12.2", "typescript": "*" }, "peerDependencies": { + "expo-local-authentication": ">=14", "expo-auth-session": ">=4", "expo-web-browser": ">=12.5.0", "react": ">=18", + "react-native-mmkv": ">=2.12", + "react-native": ">=0.74", "react-dom": ">=18" }, "engines": { From f194c8b5122789693328f56e978e79eb4e1695fc Mon Sep 17 00:00:00 2001 From: panteliselef Date: Fri, 28 Jun 2024 09:33:45 +0300 Subject: [PATCH 03/21] chore(clerk-js): Add LocalAuthCredentials --- .../src/experimental/LocalAuthCredentials.tsx | 89 +++++++++++++++++++ packages/expo/src/experimental/index.ts | 3 +- packages/expo/src/index.ts | 6 +- 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 packages/expo/src/experimental/LocalAuthCredentials.tsx diff --git a/packages/expo/src/experimental/LocalAuthCredentials.tsx b/packages/expo/src/experimental/LocalAuthCredentials.tsx new file mode 100644 index 00000000000..ac9098abcb1 --- /dev/null +++ b/packages/expo/src/experimental/LocalAuthCredentials.tsx @@ -0,0 +1,89 @@ +import { useClerk, useSignIn } from '@clerk/clerk-react'; +import type { SignInResource } from '@clerk/types'; +import * as SecureStore from 'expo-secure-store'; +import type { PropsWithChildren } from 'react'; +import React, { createContext, useContext, useState } from 'react'; + +type LocalAuthCredentials = { + identifier: string; + password: string; +}; + +export const LocalAuthContext = createContext<{ + setLocalAuthCredentials: (creds: LocalAuthCredentials) => Promise; + hasLocalAuthCredentials: boolean; + clearLocalAuthAccount: () => void; + authenticateWithLocalAccount: () => Promise; +}>({ + // @ts-expect-error Initial value cannot return what the type expects + setLocalAuthCredentials: () => {}, + hasLocalAuthCredentials: false, + clearLocalAuthAccount: () => {}, + // @ts-expect-error Initial value cannot return what the type expects + authenticateWithLocalAccount: () => {}, +}); + +export const useLocalAuthCredentials = () => { + return useContext(LocalAuthContext); +}; + +export function LocalAuthCredentialsProvider(props: PropsWithChildren): JSX.Element { + const { isLoaded, signIn } = useSignIn(); + const { publishableKey } = useClerk(); + const key = `__clerk_local_auth_${publishableKey}_identifier`; + const pkey = `__clerk_local_auth_${publishableKey}_password`; + const [hasLocalAuthCredentials, setHasLocalAuthCredentials] = useState(!!SecureStore.getItem(key)); + + const setLocalAuthCredentials = async (creds: LocalAuthCredentials) => { + if (!SecureStore.canUseBiometricAuthentication()) { + return; + } + await SecureStore.setItemAsync(key, creds.identifier); + await SecureStore.setItemAsync(pkey, creds.password, { + keychainAccessible: SecureStore.WHEN_PASSCODE_SET_THIS_DEVICE_ONLY, + requireAuthentication: true, + }); + setHasLocalAuthCredentials(true); + }; + + const clearLocalAuthAccount = async () => { + await Promise.all([SecureStore.deleteItemAsync(key), SecureStore.deleteItemAsync(pkey)]); + setHasLocalAuthCredentials(false); + }; + + const authenticateWithLocalAccount = async () => { + if (!isLoaded) { + throw 'not loaded'; + } + const identifier = await SecureStore.getItemAsync(key); + if (!identifier) { + // TODO: improve error + throw 'Identifier not found'; + } + const password = await SecureStore.getItemAsync(pkey); + + if (!password) { + // TODO: improve error + throw 'password not found'; + } + + return signIn.create({ + strategy: 'password', + identifier, + password, + }); + }; + + return ( + + {props.children} + + ); +} diff --git a/packages/expo/src/experimental/index.ts b/packages/expo/src/experimental/index.ts index 5b6bba91709..6a025b2e517 100644 --- a/packages/expo/src/experimental/index.ts +++ b/packages/expo/src/experimental/index.ts @@ -1 +1,2 @@ -export { LocalAuthProvider } from './LocalAuth'; +// export { LocalAuthProvider } from './LocalAuth'; +export { LocalAuthCredentialsProvider, useLocalAuthCredentials } from './LocalAuthCredentials'; diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts index df8f652f53a..b73e0edc7eb 100644 --- a/packages/expo/src/index.ts +++ b/packages/expo/src/index.ts @@ -17,7 +17,11 @@ export { export { isClerkAPIResponseError, isEmailLinkError, isKnownError, isMetamaskError } from '@clerk/clerk-react/errors'; -export { LocalAuthProvider as __experimental_LocalAuthProvider } from './experimental'; +export { + // LocalAuthProvider as __experimental_LocalAuthProvider, + LocalAuthCredentialsProvider as __experimental_LocalAuthCredentialsProvider, + useLocalAuthCredentials as __experimental_useLocalAuthCredentials, +} from './experimental'; /** * @deprecated Use `getClerkInstance()` instead. From c2a14252710cc2875b638319a85c17f55b4240c1 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 3 Jul 2024 16:27:59 +0300 Subject: [PATCH 04/21] chore(clerk-expo): Rename to LocalCredentials --- packages/expo/package.json | 4 +- packages/expo/src/ClerkProvider.tsx | 15 +- packages/expo/src/experimental/LocalAuth.tsx | 142 ------------------ ...thCredentials.tsx => LocalCredentials.tsx} | 36 ++--- packages/expo/src/experimental/index.ts | 3 +- packages/expo/src/index.ts | 5 +- 6 files changed, 25 insertions(+), 180 deletions(-) delete mode 100644 packages/expo/src/experimental/LocalAuth.tsx rename packages/expo/src/experimental/{LocalAuthCredentials.tsx => LocalCredentials.tsx} (70%) diff --git a/packages/expo/package.json b/packages/expo/package.json index 36519589f88..61570aebf67 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -56,13 +56,15 @@ "expo-auth-session": "^5.4.0", "expo-web-browser": "^12.8.2", "expo-local-authentication": "^14.0.1", + "expo-secure-store": "^13.0.1", "react-native": "^0.74.2", "react-native-mmkv": "^2.12.2", "typescript": "*" }, "peerDependencies": { - "expo-local-authentication": ">=14", "expo-auth-session": ">=4", + "expo-local-authentication": ">=14", + "expo-secure-store": ">=12", "expo-web-browser": ">=12.5.0", "react": ">=18", "react-native-mmkv": ">=2.12", diff --git a/packages/expo/src/ClerkProvider.tsx b/packages/expo/src/ClerkProvider.tsx index 0cca2fb8ac3..c25b7280663 100644 --- a/packages/expo/src/ClerkProvider.tsx +++ b/packages/expo/src/ClerkProvider.tsx @@ -5,28 +5,15 @@ import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react'; import React from 'react'; import type { TokenCache } from './cache'; -// import { LocalAuthProvider } from './experimental/LocalAuth'; import { isReactNative } from './runtime'; import { getClerkInstance } from './singleton'; export type ClerkProviderProps = ClerkReactProviderProps & { tokenCache?: TokenCache; - // localAuth?: { - // lockTimeout?: number; - // inactiveScreen?: React.ReactNode; - // onLockTimeOutReached?: () => void; - // lockTimeOutScreen?: React.FunctionComponent<{ authenticateWithBiometrics: () => Promise }>; - // }; }; export function ClerkProvider(props: ClerkProviderProps): JSX.Element { - const { - children, - tokenCache, - publishableKey, - // localAuth, - ...rest - } = props; + const { children, tokenCache, publishableKey, ...rest } = props; const pk = publishableKey || process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY || process.env.CLERK_PUBLISHABLE_KEY || ''; return ( diff --git a/packages/expo/src/experimental/LocalAuth.tsx b/packages/expo/src/experimental/LocalAuth.tsx deleted file mode 100644 index 26b24346f5c..00000000000 --- a/packages/expo/src/experimental/LocalAuth.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import type { UserResource } from '@clerk/types'; -import * as LocalAuthentication from 'expo-local-authentication'; -import type { PropsWithChildren } from 'react'; -import React, { createContext, useCallback, useEffect, useRef, useState } from 'react'; -// eslint-disable-next-line import/namespace -import { AppState } from 'react-native'; -import { MMKV } from 'react-native-mmkv'; - -import { getClerkInstance } from '../singleton'; - -export const UserInactivityStore = new MMKV({ - id: 'UserInactivity', -}); - -// Utility type to check if a type is a function -type IsFunction = T extends (...args: any[]) => any ? true : false; - -// Main utility type to exclude function properties -type ExcludeFunctions = { - [K in keyof T]: IsFunction extends true ? never : T[K]; -}; - -export const UserInactivityContext = createContext<{ - localAuthUser: ExcludeFunctions | null; -}>({ - localAuthUser: null, -}); - -type LocalAuthProviderProps = { - lockTimeout?: number; - // onLockTimeOutReached?: () => void; - // inactiveScreen?: React.ReactNode; - lockTimeOutScreen?: React.FunctionComponent<{ - authenticateWithBiometrics: () => Promise; - localAuthUser: ExcludeFunctions; - }>; -}; -// export const useUserInactivity = () => useContext(UserInactivityContext); - -export function LocalAuthProvider(props: PropsWithChildren): JSX.Element { - const { - children, - lockTimeout = 1000 * 10, - // onLockTimeOutReached, - lockTimeOutScreen: LockTimeOutScreen, - } = props; - const lockTimeoutRef = useRef(lockTimeout); - // const onLockTimeOutReachedRef = useRef(onLockTimeOutReached); - const appState = useRef(AppState.currentState); - const [localUser, setLocalUser] = useState | null>(null); - const getElapsed = useCallback(() => { - const hasUser = !!JSON.parse(UserInactivityStore.getString('user') || '')?.id; - const elapsed = Date.now() - (UserInactivityStore.getNumber('startTime') || 0); - - if (!hasUser) { - return false; - } - - return elapsed >= lockTimeoutRef.current; - }, []); - - const [isLocked, setLocked] = useState(getElapsed()); - // const [isInactive, setInactive] = useState(false); - - const authenticateWithBiometrics = useCallback(async () => { - const { success } = await LocalAuthentication.authenticateAsync(); - setLocked(!success); - return success; - }, []); - - const handleActiveState = useCallback( - async (skipElapsed = false) => { - if (!skipElapsed && !getElapsed()) { - return; - } - - console.log('is ellapsed'); - setLocked(true); - // router.replace('/(modals)/lock'); - // if (typeof onLockTimeOutReachedRef.current === 'function') { - // onLockTimeOutReachedRef.current(); - // } else { - await authenticateWithBiometrics(); - }, - [authenticateWithBiometrics, getElapsed], - ); - - useEffect(() => { - setLocalUser(JSON.parse(UserInactivityStore.getString('user') || '') || {}); - void handleActiveState(true); - }, [handleActiveState]); - - useEffect(() => { - const sub = AppState.addEventListener('change', nextAppState => { - if (nextAppState === 'inactive') { - console.log('--- to inactive'); - // setInactive(true); - UserInactivityStore.set('user', JSON.stringify(getClerkInstance().user || '')); - } else { - // setInactive(false); - } - - if (nextAppState === 'background') { - console.log('--- to background'); - UserInactivityStore.set('user', JSON.stringify(getClerkInstance().user || '')); - recordStartTime(); - } else if (nextAppState === 'active' && appState.current.match(/background/i)) { - console.log('--- active'); - - setLocalUser(JSON.parse(UserInactivityStore.getString('user') || '') || {}); - void handleActiveState(); - } - - appState.current = nextAppState; - }); - - return () => { - sub.remove(); - }; - }, [handleActiveState]); - - const recordStartTime = () => { - UserInactivityStore.set('startTime', Date.now()); - }; - - return ( - - {LockTimeOutScreen && isLocked ? ( - - ) : ( - children - )} - - ); -} diff --git a/packages/expo/src/experimental/LocalAuthCredentials.tsx b/packages/expo/src/experimental/LocalCredentials.tsx similarity index 70% rename from packages/expo/src/experimental/LocalAuthCredentials.tsx rename to packages/expo/src/experimental/LocalCredentials.tsx index ac9098abcb1..a9395836997 100644 --- a/packages/expo/src/experimental/LocalAuthCredentials.tsx +++ b/packages/expo/src/experimental/LocalCredentials.tsx @@ -4,37 +4,37 @@ import * as SecureStore from 'expo-secure-store'; import type { PropsWithChildren } from 'react'; import React, { createContext, useContext, useState } from 'react'; -type LocalAuthCredentials = { +type LocalCredentials = { identifier: string; password: string; }; export const LocalAuthContext = createContext<{ - setLocalAuthCredentials: (creds: LocalAuthCredentials) => Promise; - hasLocalAuthCredentials: boolean; - clearLocalAuthAccount: () => void; - authenticateWithLocalAccount: () => Promise; + setCredentials: (creds: LocalCredentials) => Promise; + hasCredentials: boolean; + clearCredentials: () => void; + authenticate: () => Promise; }>({ // @ts-expect-error Initial value cannot return what the type expects - setLocalAuthCredentials: () => {}, - hasLocalAuthCredentials: false, - clearLocalAuthAccount: () => {}, + setCredentials: () => {}, + hasCredentials: false, + clearCredentials: () => {}, // @ts-expect-error Initial value cannot return what the type expects - authenticateWithLocalAccount: () => {}, + authenticate: () => {}, }); -export const useLocalAuthCredentials = () => { +export const useLocalCredentials = () => { return useContext(LocalAuthContext); }; -export function LocalAuthCredentialsProvider(props: PropsWithChildren): JSX.Element { +export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element { const { isLoaded, signIn } = useSignIn(); const { publishableKey } = useClerk(); const key = `__clerk_local_auth_${publishableKey}_identifier`; const pkey = `__clerk_local_auth_${publishableKey}_password`; const [hasLocalAuthCredentials, setHasLocalAuthCredentials] = useState(!!SecureStore.getItem(key)); - const setLocalAuthCredentials = async (creds: LocalAuthCredentials) => { + const setCredentials = async (creds: LocalCredentials) => { if (!SecureStore.canUseBiometricAuthentication()) { return; } @@ -46,12 +46,12 @@ export function LocalAuthCredentialsProvider(props: PropsWithChildren): JSX.Elem setHasLocalAuthCredentials(true); }; - const clearLocalAuthAccount = async () => { + const clearCredentials = async () => { await Promise.all([SecureStore.deleteItemAsync(key), SecureStore.deleteItemAsync(pkey)]); setHasLocalAuthCredentials(false); }; - const authenticateWithLocalAccount = async () => { + const authenticate = async () => { if (!isLoaded) { throw 'not loaded'; } @@ -77,10 +77,10 @@ export function LocalAuthCredentialsProvider(props: PropsWithChildren): JSX.Elem return ( {props.children} diff --git a/packages/expo/src/experimental/index.ts b/packages/expo/src/experimental/index.ts index 6a025b2e517..99a11cc1a38 100644 --- a/packages/expo/src/experimental/index.ts +++ b/packages/expo/src/experimental/index.ts @@ -1,2 +1 @@ -// export { LocalAuthProvider } from './LocalAuth'; -export { LocalAuthCredentialsProvider, useLocalAuthCredentials } from './LocalAuthCredentials'; +export { LocalCredentialsProvider, useLocalCredentials } from './LocalCredentials'; diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts index b73e0edc7eb..a005ed3c2f0 100644 --- a/packages/expo/src/index.ts +++ b/packages/expo/src/index.ts @@ -18,9 +18,8 @@ export { export { isClerkAPIResponseError, isEmailLinkError, isKnownError, isMetamaskError } from '@clerk/clerk-react/errors'; export { - // LocalAuthProvider as __experimental_LocalAuthProvider, - LocalAuthCredentialsProvider as __experimental_LocalAuthCredentialsProvider, - useLocalAuthCredentials as __experimental_useLocalAuthCredentials, + LocalCredentialsProvider as __experimental_LocalCredentialsProvider, + useLocalCredentials as __experimental_useLocalCredentials, } from './experimental'; /** From d01daf824822d4acdf68e7d80fbdfe93f96b90bf Mon Sep 17 00:00:00 2001 From: panteliselef Date: Thu, 4 Jul 2024 00:37:09 +0300 Subject: [PATCH 05/21] feat(clerk-expo): Expose biometricType --- .../src/experimental/LocalCredentials.tsx | 69 +++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/packages/expo/src/experimental/LocalCredentials.tsx b/packages/expo/src/experimental/LocalCredentials.tsx index a9395836997..0c0803b2a36 100644 --- a/packages/expo/src/experimental/LocalCredentials.tsx +++ b/packages/expo/src/experimental/LocalCredentials.tsx @@ -1,19 +1,23 @@ import { useClerk, useSignIn } from '@clerk/clerk-react'; import type { SignInResource } from '@clerk/types'; +import { AuthenticationType, isEnrolledAsync, supportedAuthenticationTypesAsync } from 'expo-local-authentication'; import * as SecureStore from 'expo-secure-store'; import type { PropsWithChildren } from 'react'; -import React, { createContext, useContext, useState } from 'react'; +import React, { createContext, useContext, useEffect, useState } from 'react'; type LocalCredentials = { identifier: string; password: string; }; +type BiometricType = 'fingerprint' | 'face-recognition'; + export const LocalAuthContext = createContext<{ setCredentials: (creds: LocalCredentials) => Promise; hasCredentials: boolean; clearCredentials: () => void; authenticate: () => Promise; + biometryType: BiometricType | null; }>({ // @ts-expect-error Initial value cannot return what the type expects setCredentials: () => {}, @@ -21,29 +25,84 @@ export const LocalAuthContext = createContext<{ clearCredentials: () => {}, // @ts-expect-error Initial value cannot return what the type expects authenticate: () => {}, + biometryType: null, }); export const useLocalCredentials = () => { return useContext(LocalAuthContext); }; +const useEnrolledBiometric = () => { + const [isEnrolled, setIsEnrolled] = useState(false); + + useEffect(() => { + let ignore = false; + + void isEnrolledAsync().then(res => { + if (ignore) { + return; + } + setIsEnrolled(res); + }); + + return () => { + ignore = true; + }; + }, []); + + return isEnrolled; +}; + +const useAuthenticationType = () => { + const [authenticationType, setAuthenticationType] = useState(null); + + useEffect(() => { + let ignore = false; + + void supportedAuthenticationTypesAsync().then(numericTypes => { + if (ignore) { + return; + } + if (numericTypes.length === 0) { + return; + } + + if (numericTypes.includes(AuthenticationType.FINGERPRINT)) { + setAuthenticationType('fingerprint'); + } else { + setAuthenticationType('face-recognition'); + } + }); + + return () => { + ignore = true; + }; + }, []); + + return authenticationType; +}; + export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element { const { isLoaded, signIn } = useSignIn(); const { publishableKey } = useClerk(); const key = `__clerk_local_auth_${publishableKey}_identifier`; const pkey = `__clerk_local_auth_${publishableKey}_password`; const [hasLocalAuthCredentials, setHasLocalAuthCredentials] = useState(!!SecureStore.getItem(key)); + const hasEnrolledBiometric = useEnrolledBiometric(); + const authenticationType = useAuthenticationType(); + + const biometryType = hasEnrolledBiometric ? authenticationType : null; const setCredentials = async (creds: LocalCredentials) => { if (!SecureStore.canUseBiometricAuthentication()) { return; } await SecureStore.setItemAsync(key, creds.identifier); + setHasLocalAuthCredentials(true); await SecureStore.setItemAsync(pkey, creds.password, { keychainAccessible: SecureStore.WHEN_PASSCODE_SET_THIS_DEVICE_ONLY, requireAuthentication: true, }); - setHasLocalAuthCredentials(true); }; const clearCredentials = async () => { @@ -53,14 +112,15 @@ export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element const authenticate = async () => { if (!isLoaded) { + // TODO: improve error throw 'not loaded'; } - const identifier = await SecureStore.getItemAsync(key); + const identifier = await SecureStore.getItemAsync(key).catch(() => null); if (!identifier) { // TODO: improve error throw 'Identifier not found'; } - const password = await SecureStore.getItemAsync(pkey); + const password = await SecureStore.getItemAsync(pkey).catch(() => null); if (!password) { // TODO: improve error @@ -81,6 +141,7 @@ export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element hasCredentials: hasLocalAuthCredentials, clearCredentials, authenticate, + biometryType, }} > {props.children} From 0a068406390549016dbda20dae765ddb761aa38c Mon Sep 17 00:00:00 2001 From: panteliselef Date: Fri, 5 Jul 2024 14:26:09 +0300 Subject: [PATCH 06/21] add react as devDep --- packages/expo/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/expo/package.json b/packages/expo/package.json index 61570aebf67..06174bc0df3 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -59,6 +59,7 @@ "expo-secure-store": "^13.0.1", "react-native": "^0.74.2", "react-native-mmkv": "^2.12.2", + "react": ">=18", "typescript": "*" }, "peerDependencies": { From 42157620b8c87446a75100b8d5825023c3093e52 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Fri, 5 Jul 2024 14:27:16 +0300 Subject: [PATCH 07/21] add changeset --- .changeset/rude-poets-beam.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rude-poets-beam.md diff --git a/.changeset/rude-poets-beam.md b/.changeset/rude-poets-beam.md new file mode 100644 index 00000000000..6fa8730acc3 --- /dev/null +++ b/.changeset/rude-poets-beam.md @@ -0,0 +1,5 @@ +--- +"@clerk/clerk-expo": minor +--- + +Introduce experimental support for LocalAuthentication with `useLocalCredentials`. From ecce02d4d8c93529a9a6a021a5c71585e83c1503 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Fri, 5 Jul 2024 14:31:29 +0300 Subject: [PATCH 08/21] add package-lock.json --- package-lock.json | 2962 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 2898 insertions(+), 64 deletions(-) diff --git a/package-lock.json b/package-lock.json index aca046ff760..0b5f2064ca9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1119,7 +1119,6 @@ "version": "7.20.7", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-plugin-utils": "^7.20.2", @@ -1169,7 +1168,6 @@ "version": "7.23.3", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-default-from": "^7.23.3" @@ -1181,6 +1179,23 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { "version": "7.18.6", "dev": true, @@ -1233,7 +1248,6 @@ "version": "7.18.6", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -1370,7 +1384,6 @@ "version": "7.23.3", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -2582,7 +2595,6 @@ "version": "7.24.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-validator-option": "^7.23.5", @@ -2651,7 +2663,6 @@ "version": "7.23.7", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", @@ -2670,7 +2681,6 @@ "version": "2.1.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "commondir": "^1.0.1", "make-dir": "^2.0.0", @@ -2684,7 +2694,6 @@ "version": "3.0.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "locate-path": "^3.0.0" }, @@ -2696,7 +2705,6 @@ "version": "3.0.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -2709,7 +2717,6 @@ "version": "2.1.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -2722,7 +2729,6 @@ "version": "3.0.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^2.0.0" }, @@ -2734,7 +2740,6 @@ "version": "3.0.0", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -2743,7 +2748,6 @@ "version": "4.0.1", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -2752,7 +2756,6 @@ "version": "3.0.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "find-up": "^3.0.0" }, @@ -2764,7 +2767,6 @@ "version": "5.7.2", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver" } @@ -2773,7 +2775,6 @@ "version": "0.6.1", "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2782,7 +2783,6 @@ "version": "0.5.21", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -7484,7 +7484,6 @@ "version": "1.4.1", "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -7759,6 +7758,18 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jest/environment": { "version": "29.7.0", "devOptional": true, @@ -12023,6 +12034,1034 @@ "tslib": "^2.4.0" } }, + "node_modules/@react-native-community/cli": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-13.6.9.tgz", + "integrity": "sha512-hFJL4cgLPxncJJd/epQ4dHnMg5Jy/7Q56jFvA3MHViuKpzzfTCJCB+pGY54maZbtym53UJON9WTGpM3S81UfjQ==", + "dev": true, + "dependencies": { + "@react-native-community/cli-clean": "13.6.9", + "@react-native-community/cli-config": "13.6.9", + "@react-native-community/cli-debugger-ui": "13.6.9", + "@react-native-community/cli-doctor": "13.6.9", + "@react-native-community/cli-hermes": "13.6.9", + "@react-native-community/cli-server-api": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", + "@react-native-community/cli-types": "13.6.9", + "chalk": "^4.1.2", + "commander": "^9.4.1", + "deepmerge": "^4.3.0", + "execa": "^5.0.0", + "find-up": "^4.1.0", + "fs-extra": "^8.1.0", + "graceful-fs": "^4.1.3", + "prompts": "^2.4.2", + "semver": "^7.5.2" + }, + "bin": { + "rnc-cli": "build/bin.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native-community/cli-clean": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-13.6.9.tgz", + "integrity": "sha512-7Dj5+4p9JggxuVNOjPbduZBAP1SUgNhLKVw5noBUzT/3ZpUZkDM+RCSwyoyg8xKWoE4OrdUAXwAFlMcFDPKykA==", + "dev": true, + "dependencies": { + "@react-native-community/cli-tools": "13.6.9", + "chalk": "^4.1.2", + "execa": "^5.0.0", + "fast-glob": "^3.3.2" + } + }, + "node_modules/@react-native-community/cli-clean/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-clean/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-clean/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native-community/cli-clean/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native-community/cli-config": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-13.6.9.tgz", + "integrity": "sha512-rFfVBcNojcMm+KKHE/xqpqXg8HoKl4EC7bFHUrahMJ+y/tZll55+oX/PGG37rzB8QzP2UbMQ19DYQKC1G7kXeg==", + "dev": true, + "dependencies": { + "@react-native-community/cli-tools": "13.6.9", + "chalk": "^4.1.2", + "cosmiconfig": "^5.1.0", + "deepmerge": "^4.3.0", + "fast-glob": "^3.3.2", + "joi": "^17.2.1" + } + }, + "node_modules/@react-native-community/cli-config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native-community/cli-config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native-community/cli-config/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@react-native-community/cli-config/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@react-native-community/cli-config/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@react-native-community/cli-config/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@react-native-community/cli-debugger-ui": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.9.tgz", + "integrity": "sha512-TkN7IdFmGPPvTpAo3nCAH9uwGCPxWBEAwpqEZDrq0NWllI7Tdie8vDpGdrcuCcKalmhq6OYnkXzeBah7O1Ztpw==", + "dev": true, + "dependencies": { + "serve-static": "^1.13.1" + } + }, + "node_modules/@react-native-community/cli-doctor": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-13.6.9.tgz", + "integrity": "sha512-5quFaLdWFQB+677GXh5dGU9I5eg2z6Vg4jOX9vKnc9IffwyIFAyJfCZHrxLSRPDGNXD7biDQUdoezXYGwb6P/A==", + "dev": true, + "dependencies": { + "@react-native-community/cli-config": "13.6.9", + "@react-native-community/cli-platform-android": "13.6.9", + "@react-native-community/cli-platform-apple": "13.6.9", + "@react-native-community/cli-platform-ios": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", + "chalk": "^4.1.2", + "command-exists": "^1.2.8", + "deepmerge": "^4.3.0", + "envinfo": "^7.10.0", + "execa": "^5.0.0", + "hermes-profile-transformer": "^0.0.6", + "node-stream-zip": "^1.9.1", + "ora": "^5.4.1", + "semver": "^7.5.2", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1", + "yaml": "^2.2.1" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native-community/cli-doctor/node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/ora/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@react-native-community/cli-doctor/node_modules/yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@react-native-community/cli-hermes": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-13.6.9.tgz", + "integrity": "sha512-GvwiwgvFw4Ws+krg2+gYj8sR3g05evmNjAHkKIKMkDTJjZ8EdyxbkifRUs1ZCq3TMZy2oeblZBXCJVOH4W7ZbA==", + "dev": true, + "dependencies": { + "@react-native-community/cli-platform-android": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", + "chalk": "^4.1.2", + "hermes-profile-transformer": "^0.0.6" + } + }, + "node_modules/@react-native-community/cli-hermes/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-hermes/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-hermes/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native-community/cli-hermes/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native-community/cli-platform-android": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.9.tgz", + "integrity": "sha512-9KsYGdr08QhdvT3Ht7e8phQB3gDX9Fs427NJe0xnoBh+PDPTI2BD5ks5ttsH8CzEw8/P6H8tJCHq6hf2nxd9cw==", + "dev": true, + "dependencies": { + "@react-native-community/cli-tools": "13.6.9", + "chalk": "^4.1.2", + "execa": "^5.0.0", + "fast-glob": "^3.3.2", + "fast-xml-parser": "^4.2.4", + "logkitty": "^0.7.1" + } + }, + "node_modules/@react-native-community/cli-platform-android/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-platform-android/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-platform-android/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native-community/cli-platform-android/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native-community/cli-platform-apple": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.9.tgz", + "integrity": "sha512-KoeIHfhxMhKXZPXmhQdl6EE+jGKWwoO9jUVWgBvibpVmsNjo7woaG/tfJMEWfWF3najX1EkQAoJWpCDBMYWtlA==", + "dev": true, + "dependencies": { + "@react-native-community/cli-tools": "13.6.9", + "chalk": "^4.1.2", + "execa": "^5.0.0", + "fast-glob": "^3.3.2", + "fast-xml-parser": "^4.0.12", + "ora": "^5.4.1" + } + }, + "node_modules/@react-native-community/cli-platform-apple/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-platform-apple/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-platform-apple/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native-community/cli-platform-apple/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native-community/cli-platform-apple/node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native-community/cli-platform-apple/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native-community/cli-platform-apple/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native-community/cli-platform-ios": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.9.tgz", + "integrity": "sha512-CiUcHlGs8vE0CAB4oi1f+dzniqfGuhWPNrDvae2nm8dewlahTBwIcK5CawyGezjcJoeQhjBflh9vloska+nlnw==", + "dev": true, + "dependencies": { + "@react-native-community/cli-platform-apple": "13.6.9" + } + }, + "node_modules/@react-native-community/cli-server-api": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-13.6.9.tgz", + "integrity": "sha512-W8FSlCPWymO+tlQfM3E0JmM8Oei5HZsIk5S0COOl0MRi8h0NmHI4WSTF2GCfbFZkcr2VI/fRsocoN8Au4EZAug==", + "dev": true, + "dependencies": { + "@react-native-community/cli-debugger-ui": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", + "compression": "^1.7.1", + "connect": "^3.6.5", + "errorhandler": "^1.5.1", + "nocache": "^3.0.1", + "pretty-format": "^26.6.2", + "serve-static": "^1.13.1", + "ws": "^6.2.2" + } + }, + "node_modules/@react-native-community/cli-server-api/node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@react-native-community/cli-server-api/node_modules/@types/yargs": { + "version": "15.0.19", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", + "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@react-native-community/cli-server-api/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-server-api/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-server-api/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native-community/cli-server-api/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native-community/cli-server-api/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@react-native-community/cli-server-api/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/@react-native-community/cli-server-api/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/@react-native-community/cli-tools": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-13.6.9.tgz", + "integrity": "sha512-OXaSjoN0mZVw3nrAwcY1PC0uMfyTd9fz7Cy06dh+EJc+h0wikABsVRzV8cIOPrVV+PPEEXE0DBrH20T2puZzgQ==", + "dev": true, + "dependencies": { + "appdirsjs": "^1.2.4", + "chalk": "^4.1.2", + "execa": "^5.0.0", + "find-up": "^5.0.0", + "mime": "^2.4.1", + "node-fetch": "^2.6.0", + "open": "^6.2.0", + "ora": "^5.4.1", + "semver": "^7.5.2", + "shell-quote": "^1.7.3", + "sudo-prompt": "^9.0.0" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native-community/cli-tools/node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native-community/cli-tools/node_modules/sudo-prompt": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", + "dev": true + }, + "node_modules/@react-native-community/cli-types": { + "version": "13.6.9", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-13.6.9.tgz", + "integrity": "sha512-RLxDppvRxXfs3hxceW/mShi+6o5yS+kFPnPqZTaMKKR5aSg7LwDpLQW4K2D22irEG8e6RKDkZUeH9aL3vO2O0w==", + "dev": true, + "dependencies": { + "joi": "^17.2.1" + } + }, + "node_modules/@react-native-community/cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native-community/cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native-community/cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native-community/cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native-community/cli/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/@react-native-community/cli/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native-community/cli/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@react-native-community/cli/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native-community/cli/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native-community/cli/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@react-native/assets-registry": { "version": "0.73.1", "dev": true, @@ -12141,6 +13180,151 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@react-native/community-cli-plugin": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.85.tgz", + "integrity": "sha512-ODzND33eA2owAY3g9jgCdqB+BjAh8qJ7dvmSotXgrgDYr3MJMpd8gvHTIPe2fg4Kab+wk8uipRhrE0i0RYMwtQ==", + "dev": true, + "dependencies": { + "@react-native-community/cli-server-api": "13.6.9", + "@react-native-community/cli-tools": "13.6.9", + "@react-native/dev-middleware": "0.74.85", + "@react-native/metro-babel-transformer": "0.74.85", + "chalk": "^4.0.0", + "execa": "^5.1.1", + "metro": "^0.80.3", + "metro-config": "^0.80.3", + "metro-core": "^0.80.3", + "node-fetch": "^2.2.0", + "querystring": "^0.2.1", + "readline": "^1.3.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.85.tgz", + "integrity": "sha512-gUIhhpsYLUTYWlWw4vGztyHaX/kNlgVspSvKe2XaPA7o3jYKUoNLc3Ov7u70u/MBWfKdcEffWq44eSe3j3s5JQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.85.tgz", + "integrity": "sha512-BRmgCK5vnMmHaKRO+h8PKJmHHH3E6JFuerrcfE3wG2eZ1bcSr+QTu8DAlpxsDWvJvHpCi8tRJGauxd+Ssj/c7w==", + "dev": true, + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.74.85", + "@rnx-kit/chromium-edge-launcher": "^1.0.0", + "chrome-launcher": "^0.15.2", + "connect": "^3.6.5", + "debug": "^2.2.0", + "node-fetch": "^2.2.0", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "selfsigned": "^2.4.1", + "serve-static": "^1.13.1", + "temp-dir": "^2.0.0", + "ws": "^6.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@react-native/community-cli-plugin/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/@react-native/community-cli-plugin/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, "node_modules/@react-native/debugger-frontend": { "version": "0.73.3", "dev": true, @@ -12212,11 +13396,164 @@ "async-limiter": "~1.0.0" } }, + "node_modules/@react-native/gradle-plugin": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.85.tgz", + "integrity": "sha512-1VQSLukJzaVMn1MYcs8Weo1nUW8xCas2XU1KuoV7OJPk6xPnEBFJmapmEGP5mWeEy7kcTXJmddEgy1wwW0tcig==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/js-polyfills": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.85.tgz", + "integrity": "sha512-gp4Rg9le3lVZeW7Cie6qLfekvRKZuhJ3LKgi1SFB4N154z1wIclypAJXVXgWBsy8JKJfTwRI+sffC4qZDlvzrg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/metro-babel-transformer": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.85.tgz", + "integrity": "sha512-JIrXqEwhTvWPtGArgMptIPGstMdXQIkwSjKVYt+7VC4a9Pw1GurIWanIJheEW6ZuCVvTc0VZkwglFz9JVjzDjA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.20.0", + "@react-native/babel-preset": "0.74.85", + "hermes-parser": "0.19.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-plugin-codegen": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.85.tgz", + "integrity": "sha512-48TSDclRB5OMXiImiJkLxyCfRyLsqkCgI8buugCZzvXcYslfV7gCvcyFyQldtcOmerV+CK4RAj7QS4hmB5Mr8Q==", + "dev": true, + "dependencies": { + "@react-native/codegen": "0.74.85" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-preset": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.85.tgz", + "integrity": "sha512-yMHUlN8INbK5BBwiBuQMftdWkpm1IgCsoJTKcGD2OpSgZhwwm8RUSvGhdRMzB2w7bsqqBmaEMleGtW6aCR7B9w==", + "dev": true, + "dependencies": { + "@babel/core": "^7.20.0", + "@babel/plugin-proposal-async-generator-functions": "^7.0.0", + "@babel/plugin-proposal-class-properties": "^7.18.0", + "@babel/plugin-proposal-export-default-from": "^7.0.0", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.0", + "@babel/plugin-proposal-numeric-separator": "^7.0.0", + "@babel/plugin-proposal-object-rest-spread": "^7.20.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.0.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-export-default-from": "^7.0.0", + "@babel/plugin-syntax-flow": "^7.18.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0", + "@babel/plugin-syntax-optional-chaining": "^7.0.0", + "@babel/plugin-transform-arrow-functions": "^7.0.0", + "@babel/plugin-transform-async-to-generator": "^7.20.0", + "@babel/plugin-transform-block-scoping": "^7.0.0", + "@babel/plugin-transform-classes": "^7.0.0", + "@babel/plugin-transform-computed-properties": "^7.0.0", + "@babel/plugin-transform-destructuring": "^7.20.0", + "@babel/plugin-transform-flow-strip-types": "^7.20.0", + "@babel/plugin-transform-function-name": "^7.0.0", + "@babel/plugin-transform-literals": "^7.0.0", + "@babel/plugin-transform-modules-commonjs": "^7.0.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0", + "@babel/plugin-transform-parameters": "^7.0.0", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", + "@babel/plugin-transform-react-display-name": "^7.0.0", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/plugin-transform-react-jsx-self": "^7.0.0", + "@babel/plugin-transform-react-jsx-source": "^7.0.0", + "@babel/plugin-transform-runtime": "^7.0.0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0", + "@babel/plugin-transform-spread": "^7.0.0", + "@babel/plugin-transform-sticky-regex": "^7.0.0", + "@babel/plugin-transform-typescript": "^7.5.0", + "@babel/plugin-transform-unicode-regex": "^7.0.0", + "@babel/template": "^7.0.0", + "@react-native/babel-plugin-codegen": "0.74.85", + "babel-plugin-transform-flow-enums": "^0.0.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/codegen": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.85.tgz", + "integrity": "sha512-N7QwoS4Hq/uQmoH83Ewedy6D0M7xbQsOU3OMcQf0eY3ltQ7S2hd9/R4UTalQWRn1OUJfXR6OG12QJ4FStKgV6Q==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.0", + "glob": "^7.1.1", + "hermes-parser": "0.19.1", + "invariant": "^2.2.4", + "jscodeshift": "^0.14.0", + "mkdirp": "^0.5.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@babel/preset-env": "^7.1.6" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@react-native/normalize-color": { "version": "2.1.0", "dev": true, "license": "MIT" }, + "node_modules/@react-native/normalize-colors": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz", + "integrity": "sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==", + "dev": true + }, "node_modules/@react-stately/calendar": { "version": "3.5.1", "license": "Apache-2.0", @@ -13131,6 +14468,72 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@rnx-kit/chromium-edge-launcher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz", + "integrity": "sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==", + "dev": true, + "dependencies": { + "@types/node": "^18.0.0", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=14.15" + } + }, + "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@rollup/plugin-alias": { "version": "5.1.0", "license": "MIT", @@ -15059,6 +16462,15 @@ "node": ">= 6" } }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", @@ -16739,6 +18151,76 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-fragments": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz", + "integrity": "sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==", + "dev": true, + "dependencies": { + "colorette": "^1.0.7", + "slice-ansi": "^2.0.0", + "strip-ansi": "^5.0.0" + } + }, + "node_modules/ansi-fragments/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-fragments/node_modules/astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-fragments/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "node_modules/ansi-fragments/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-fragments/node_modules/slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-fragments/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-html-community": { "version": "0.0.8", "dev": true, @@ -16797,6 +18279,12 @@ "node": ">=8" } }, + "node_modules/appdirsjs": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.7.tgz", + "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==", + "dev": true + }, "node_modules/append-field": { "version": "1.0.0", "dev": true, @@ -17229,7 +18717,6 @@ "version": "0.15.2", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.0.1" }, @@ -18529,8 +20016,7 @@ "node_modules/async-limiter": { "version": "1.0.1", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/async-listen": { "version": "3.0.1", @@ -18683,7 +20169,6 @@ "version": "7.0.0-bridge.0", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@babel/core": "^7.0.0-0" } @@ -18974,7 +20459,6 @@ "version": "0.0.2", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/plugin-syntax-flow": "^7.12.1" } @@ -20028,6 +21512,39 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dev": true, + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dev": true, + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/callsites": { "version": "3.1.0", "license": "MIT", @@ -20370,7 +21887,6 @@ "version": "0.15.2", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", @@ -21428,7 +22944,6 @@ "version": "3.7.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", @@ -21451,7 +22966,6 @@ "version": "2.6.9", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -21459,8 +22973,7 @@ "node_modules/connect/node_modules/ms": { "version": "2.0.0", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/consola": { "version": "3.2.3", @@ -23733,6 +25246,12 @@ "version": "1.0.0", "license": "MIT" }, + "node_modules/denodeify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz", + "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==", + "dev": true + }, "node_modules/denque": { "version": "2.1.0", "license": "Apache-2.0", @@ -24463,6 +25982,19 @@ "stackframe": "^1.3.4" } }, + "node_modules/errorhandler": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", + "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", + "dev": true, + "dependencies": { + "accepts": "~1.3.7", + "escape-html": "~1.0.3" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/es-abstract": { "version": "1.23.3", "license": "MIT", @@ -26246,6 +27778,18 @@ "expo": "*" } }, + "node_modules/expo-local-authentication": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/expo-local-authentication/-/expo-local-authentication-14.0.1.tgz", + "integrity": "sha512-kAwUD1wEqj1fhwQgIHlP4H/JV9AcX+NO3BJwhPM2HuCFS0kgx2wvcHisnKBSTRyl8u5Jt4odzMyQkDJystwUTg==", + "dev": true, + "dependencies": { + "invariant": "^2.2.4" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-modules-autolinking": { "version": "1.10.3", "dev": true, @@ -26357,6 +27901,15 @@ "invariant": "^2.2.4" } }, + "node_modules/expo-secure-store": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-13.0.2.tgz", + "integrity": "sha512-3QYgoneo8p8yeeBPBiAfokNNc2xq6+n8+Ob4fAlErEcf4H7Y72LH+K/dx0nQyWau2ZKZUXBxyyfuHFyVKrEVLg==", + "dev": true, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-web-browser": { "version": "12.8.2", "dev": true, @@ -26718,6 +28271,28 @@ "version": "2.2.0", "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz", + "integrity": "sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "license": "MIT", @@ -26973,7 +28548,6 @@ "version": "1.1.2", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -26991,7 +28565,6 @@ "version": "2.6.9", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -26999,14 +28572,12 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/finalhandler/node_modules/on-finished": { "version": "2.3.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -27144,11 +28715,16 @@ "node": ">=8" } }, + "node_modules/flow-enums-runtime": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", + "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", + "dev": true + }, "node_modules/flow-parser": { "version": "0.206.0", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.4.0" } @@ -29751,6 +31327,42 @@ "tslib": "^2.0.3" } }, + "node_modules/hermes-estree": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.19.1.tgz", + "integrity": "sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g==", + "dev": true + }, + "node_modules/hermes-parser": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.19.1.tgz", + "integrity": "sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A==", + "dev": true, + "dependencies": { + "hermes-estree": "0.19.1" + } + }, + "node_modules/hermes-profile-transformer": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz", + "integrity": "sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==", + "dev": true, + "dependencies": { + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-profile-transformer/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/hexoid": { "version": "1.0.0", "dev": true, @@ -30209,6 +31821,21 @@ "node": ">=10" } }, + "node_modules/image-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "dev": true, + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, "node_modules/immer": { "version": "9.0.21", "dev": true, @@ -31095,6 +32722,15 @@ "node": ">= 0.4" } }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-docker": { "version": "2.2.1", "license": "MIT", @@ -33550,17 +35186,21 @@ "dev": true, "license": "MIT" }, + "node_modules/jsc-android": { + "version": "250231.0.0", + "resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-250231.0.0.tgz", + "integrity": "sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==", + "dev": true + }, "node_modules/jsc-safe-url": { "version": "0.2.4", "dev": true, - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/jscodeshift": { "version": "0.14.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.13.16", "@babel/parser": "^7.13.16", @@ -33593,7 +35233,6 @@ "version": "4.3.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -33608,7 +35247,6 @@ "version": "4.1.2", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -33624,7 +35262,6 @@ "version": "2.0.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -33635,20 +35272,17 @@ "node_modules/jscodeshift/node_modules/color-name": { "version": "1.1.4", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jscodeshift/node_modules/signal-exit": { "version": "3.0.7", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/jscodeshift/node_modules/write-file-atomic": { "version": "2.4.3", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", @@ -34037,7 +35671,6 @@ "version": "1.4.2", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" @@ -34047,7 +35680,6 @@ "version": "2.6.9", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -34055,8 +35687,7 @@ "node_modules/lighthouse-logger/node_modules/ms": { "version": "2.0.0", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lightningcss": { "version": "1.19.0", @@ -34706,6 +36337,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "dev": true + }, "node_modules/lodash.truncate": { "version": "4.4.2", "dev": true, @@ -34878,6 +36515,143 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/logkitty": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz", + "integrity": "sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==", + "dev": true, + "dependencies": { + "ansi-fragments": "^0.2.1", + "dayjs": "^1.8.15", + "yargs": "^15.1.0" + }, + "bin": { + "logkitty": "bin/logkitty.js" + } + }, + "node_modules/logkitty/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/logkitty/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/logkitty/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/logkitty/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/logkitty/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/logkitty/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/logkitty/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/logkitty/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/logkitty/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/logkitty/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -35155,8 +36929,7 @@ "node_modules/marky": { "version": "1.2.5", "dev": true, - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/md5": { "version": "2.3.0", @@ -35478,6 +37251,12 @@ "node": ">= 4.0.0" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "dev": true + }, "node_modules/memoizee": { "version": "0.4.15", "dev": true, @@ -35565,6 +37344,603 @@ "node": ">= 0.6" } }, + "node_modules/metro": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.80.9.tgz", + "integrity": "sha512-Bc57Xf3GO2Xe4UWQsBj/oW6YfLPABEu8jfDVDiNmJvoQW4CO34oDPuYKe4KlXzXhcuNsqOtSxpbjCRRVjhhREg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/core": "^7.20.0", + "@babel/generator": "^7.20.0", + "@babel/parser": "^7.20.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.20.0", + "@babel/types": "^7.20.0", + "accepts": "^1.3.7", + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "connect": "^3.6.5", + "debug": "^2.2.0", + "denodeify": "^1.2.1", + "error-stack-parser": "^2.0.6", + "graceful-fs": "^4.2.4", + "hermes-parser": "0.20.1", + "image-size": "^1.0.2", + "invariant": "^2.2.4", + "jest-worker": "^29.6.3", + "jsc-safe-url": "^0.2.2", + "lodash.throttle": "^4.1.1", + "metro-babel-transformer": "0.80.9", + "metro-cache": "0.80.9", + "metro-cache-key": "0.80.9", + "metro-config": "0.80.9", + "metro-core": "0.80.9", + "metro-file-map": "0.80.9", + "metro-resolver": "0.80.9", + "metro-runtime": "0.80.9", + "metro-source-map": "0.80.9", + "metro-symbolicate": "0.80.9", + "metro-transform-plugins": "0.80.9", + "metro-transform-worker": "0.80.9", + "mime-types": "^2.1.27", + "node-fetch": "^2.2.0", + "nullthrows": "^1.1.1", + "rimraf": "^3.0.2", + "serialize-error": "^2.1.0", + "source-map": "^0.5.6", + "strip-ansi": "^6.0.0", + "throat": "^5.0.0", + "ws": "^7.5.1", + "yargs": "^17.6.2" + }, + "bin": { + "metro": "src/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-babel-transformer": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.80.9.tgz", + "integrity": "sha512-d76BSm64KZam1nifRZlNJmtwIgAeZhZG3fi3K+EmPOlrR8rDtBxQHDSN3fSGeNB9CirdTyabTMQCkCup6BXFSQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.20.0", + "hermes-parser": "0.20.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-babel-transformer/node_modules/hermes-estree": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.20.1.tgz", + "integrity": "sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg==", + "dev": true + }, + "node_modules/metro-babel-transformer/node_modules/hermes-parser": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.20.1.tgz", + "integrity": "sha512-BL5P83cwCogI8D7rrDCgsFY0tdYUtmFP9XaXtl2IQjC+2Xo+4okjfXintlTxcIwl4qeGddEl28Z11kbVIw0aNA==", + "dev": true, + "dependencies": { + "hermes-estree": "0.20.1" + } + }, + "node_modules/metro-cache": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.80.9.tgz", + "integrity": "sha512-ujEdSI43QwI+Dj2xuNax8LMo8UgKuXJEdxJkzGPU6iIx42nYa1byQ+aADv/iPh5sh5a//h5FopraW5voXSgm2w==", + "dev": true, + "dependencies": { + "metro-core": "0.80.9", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-cache-key": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.80.9.tgz", + "integrity": "sha512-hRcYGhEiWIdM87hU0fBlcGr+tHDEAT+7LYNCW89p5JhErFt/QaAkVx4fb5bW3YtXGv5BTV7AspWPERoIb99CXg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-cache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/metro-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/metro-config": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.80.9.tgz", + "integrity": "sha512-28wW7CqS3eJrunRGnsibWldqgwRP9ywBEf7kg+uzUHkSFJNKPM1K3UNSngHmH0EZjomizqQA2Zi6/y6VdZMolg==", + "dev": true, + "dependencies": { + "connect": "^3.6.5", + "cosmiconfig": "^5.0.5", + "jest-validate": "^29.6.3", + "metro": "0.80.9", + "metro-cache": "0.80.9", + "metro-core": "0.80.9", + "metro-runtime": "0.80.9" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-config/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/metro-config/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/metro-config/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/metro-config/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/metro-core": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.80.9.tgz", + "integrity": "sha512-tbltWQn+XTdULkGdzHIxlxk4SdnKxttvQQV3wpqqFbHDteR4gwCyTR2RyYJvxgU7HELfHtrVbqgqAdlPByUSbg==", + "dev": true, + "dependencies": { + "lodash.throttle": "^4.1.1", + "metro-resolver": "0.80.9" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-file-map": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.80.9.tgz", + "integrity": "sha512-sBUjVtQMHagItJH/wGU9sn3k2u0nrCl0CdR4SFMO1tksXLKbkigyQx4cbpcyPVOAmGTVuy3jyvBlELaGCAhplQ==", + "dev": true, + "dependencies": { + "anymatch": "^3.0.3", + "debug": "^2.2.0", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.4", + "invariant": "^2.2.4", + "jest-worker": "^29.6.3", + "micromatch": "^4.0.4", + "node-abort-controller": "^3.1.1", + "nullthrows": "^1.1.1", + "walker": "^1.0.7" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/metro-file-map/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/metro-file-map/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro-file-map/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-file-map/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/metro-file-map/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/metro-minify-terser": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.80.9.tgz", + "integrity": "sha512-FEeCeFbkvvPuhjixZ1FYrXtO0araTpV6UbcnGgDUpH7s7eR5FG/PiJz3TsuuPP/HwCK19cZtQydcA2QrCw446A==", + "dev": true, + "dependencies": { + "terser": "^5.15.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-resolver": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.80.9.tgz", + "integrity": "sha512-wAPIjkN59BQN6gocVsAvvpZ1+LQkkqUaswlT++cJafE/e54GoVkMNCmrR4BsgQHr9DknZ5Um/nKueeN7kaEz9w==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-runtime": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.80.9.tgz", + "integrity": "sha512-8PTVIgrVcyU+X/rVCy/9yxNlvXsBCk5JwwkbAm/Dm+Abo6NBGtNjWF0M1Xo/NWCb4phamNWcD7cHdR91HhbJvg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-source-map": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.80.9.tgz", + "integrity": "sha512-RMn+XS4VTJIwMPOUSj61xlxgBvPeY4G6s5uIn6kt6HB6A/k9ekhr65UkkDD7WzHYs3a9o869qU8tvOZvqeQzgw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.20.0", + "@babel/types": "^7.20.0", + "invariant": "^2.2.4", + "metro-symbolicate": "0.80.9", + "nullthrows": "^1.1.1", + "ob1": "0.80.9", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-symbolicate": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.80.9.tgz", + "integrity": "sha512-Ykae12rdqSs98hg41RKEToojuIW85wNdmSe/eHUgMkzbvCFNVgcC0w3dKZEhSsqQOXapXRlLtHkaHLil0UD/EA==", + "dev": true, + "dependencies": { + "invariant": "^2.2.4", + "metro-source-map": "0.80.9", + "nullthrows": "^1.1.1", + "source-map": "^0.5.6", + "through2": "^2.0.1", + "vlq": "^1.0.0" + }, + "bin": { + "metro-symbolicate": "src/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-transform-plugins": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.80.9.tgz", + "integrity": "sha512-UlDk/uc8UdfLNJhPbF3tvwajyuuygBcyp+yBuS/q0z3QSuN/EbLllY3rK8OTD9n4h00qZ/qgxGv/lMFJkwP4vg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.20.0", + "@babel/generator": "^7.20.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.20.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro-transform-worker": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.80.9.tgz", + "integrity": "sha512-c/IrzMUVnI0hSVVit4TXzt3A1GiUltGVlzCmLJWxNrBGHGrJhvgePj38+GXl1Xf4Fd4vx6qLUkKMQ3ux73bFLQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.20.0", + "@babel/generator": "^7.20.0", + "@babel/parser": "^7.20.0", + "@babel/types": "^7.20.0", + "metro": "0.80.9", + "metro-babel-transformer": "0.80.9", + "metro-cache": "0.80.9", + "metro-cache-key": "0.80.9", + "metro-minify-terser": "0.80.9", + "metro-source-map": "0.80.9", + "metro-transform-plugins": "0.80.9", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/metro/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/metro/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/metro/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/metro/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/metro/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/metro/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/metro/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/metro/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/node_modules/hermes-estree": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.20.1.tgz", + "integrity": "sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg==", + "dev": true + }, + "node_modules/metro/node_modules/hermes-parser": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.20.1.tgz", + "integrity": "sha512-BL5P83cwCogI8D7rrDCgsFY0tdYUtmFP9XaXtl2IQjC+2Xo+4okjfXintlTxcIwl4qeGddEl28Z11kbVIw0aNA==", + "dev": true, + "dependencies": { + "hermes-estree": "0.20.1" + } + }, + "node_modules/metro/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/metro/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/metro/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/metro/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/micromark": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", @@ -36722,6 +39098,15 @@ "tslib": "^2.0.3" } }, + "node_modules/nocache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz", + "integrity": "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/nock": { "version": "13.5.4", "dev": true, @@ -36746,6 +39131,12 @@ "node": ">=10" } }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true + }, "node_modules/node-addon-api": { "version": "7.1.0", "license": "MIT", @@ -36757,7 +39148,6 @@ "version": "0.1.17", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "minimatch": "^3.0.2" }, @@ -36886,6 +39276,19 @@ "version": "2.0.14", "license": "MIT" }, + "node_modules/node-stream-zip": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", + "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", + "dev": true, + "engines": { + "node": ">=0.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/antelle" + } + }, "node_modules/node-watch": { "version": "0.7.3", "dev": true, @@ -37340,6 +39743,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/ob1": { + "version": "0.80.9", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.80.9.tgz", + "integrity": "sha512-v9yOxowkZbxWhKOaaTyLjIm1aLy4ebMNcSn4NYJKOAI/Qv+SkfEfszpLr2GIxsccmb2Y2HA9qtsqiIJ80ucpVA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", @@ -39907,11 +42319,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/querystringify": { "version": "2.2.0", "dev": true, "license": "MIT" }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dev": true, + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "funding": [ @@ -40254,6 +42685,39 @@ "node": ">= 12.13.0" } }, + "node_modules/react-devtools-core": { + "version": "4.28.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.28.5.tgz", + "integrity": "sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==", + "optional": true, + "peer": true, + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "node_modules/react-devtools-core/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "18.3.1", "license": "MIT", @@ -40417,6 +42881,19 @@ "node": ">=0.4.0" } }, + "node_modules/react-shallow-renderer": { + "version": "16.15.0", + "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", + "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-stately": { "version": "3.31.1", "license": "Apache-2.0", @@ -40705,6 +43182,12 @@ "node": ">=8.10.0" } }, + "node_modules/readline": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", + "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==", + "dev": true + }, "node_modules/real-require": { "version": "0.2.0", "license": "MIT", @@ -40716,7 +43199,6 @@ "version": "0.21.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ast-types": "0.15.2", "esprima": "~4.0.0", @@ -40731,7 +43213,6 @@ "version": "0.6.1", "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -41739,10 +44220,12 @@ "license": "MIT" }, "node_modules/selfsigned": { - "version": "2.1.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, - "license": "MIT", "dependencies": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" }, "engines": { @@ -41859,6 +44342,15 @@ "upper-case-first": "^2.0.2" } }, + "node_modules/serialize-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", + "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/serialize-javascript": { "version": "5.0.1", "dev": true, @@ -42064,7 +44556,7 @@ }, "node_modules/shell-quote": { "version": "1.8.1", - "dev": true, + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -42888,7 +45380,6 @@ "version": "0.1.10", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "type-fest": "^0.7.1" }, @@ -42900,7 +45391,6 @@ "version": "0.7.1", "dev": true, "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=8" } @@ -43277,6 +45767,12 @@ "version": "9.0.0", "license": "MIT" }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true + }, "node_modules/strtok3": { "version": "6.3.0", "dev": true, @@ -43856,7 +46352,6 @@ "version": "0.8.4", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "rimraf": "~2.6.2" }, @@ -43868,7 +46363,6 @@ "version": "2.0.0", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -43877,7 +46371,6 @@ "version": "7.2.3", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -43897,7 +46390,6 @@ "version": "2.6.3", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -44165,6 +46657,12 @@ "real-require": "^0.2.0" } }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, "node_modules/throttleit": { "version": "1.0.1", "dev": true, @@ -47008,6 +49506,12 @@ "node": ">=4.0" } }, + "node_modules/vlq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "dev": true + }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "dev": true, @@ -47611,6 +50115,12 @@ "node": ">=0.10.0" } }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "dev": true + }, "node_modules/whatwg-mimetype": { "version": "3.0.0", "dev": true, @@ -49770,7 +52280,12 @@ "@types/react": "*", "@types/react-dom": "*", "expo-auth-session": "^5.4.0", + "expo-local-authentication": "^14.0.1", + "expo-secure-store": "^13.0.1", "expo-web-browser": "^12.8.2", + "react": ">=18", + "react-native": "^0.74.2", + "react-native-mmkv": "^2.12.2", "typescript": "*" }, "engines": { @@ -49778,9 +52293,82 @@ }, "peerDependencies": { "expo-auth-session": ">=4", + "expo-local-authentication": ">=14", + "expo-secure-store": ">=12", "expo-web-browser": ">=12.5.0", "react": ">=18", - "react-dom": ">=18" + "react-dom": ">=18", + "react-native": ">=0.74", + "react-native-mmkv": ">=2.12" + } + }, + "packages/expo/node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "packages/expo/node_modules/@react-native/assets-registry": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.85.tgz", + "integrity": "sha512-59YmIQxfGDw4aP9S/nAM+sjSFdW8fUP6fsqczCcXgL2YVEjyER9XCaUT0J1K+PdHep8pi05KUgIKUds8P3jbmA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "packages/expo/node_modules/@react-native/codegen": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.85.tgz", + "integrity": "sha512-N7QwoS4Hq/uQmoH83Ewedy6D0M7xbQsOU3OMcQf0eY3ltQ7S2hd9/R4UTalQWRn1OUJfXR6OG12QJ4FStKgV6Q==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.0", + "glob": "^7.1.1", + "hermes-parser": "0.19.1", + "invariant": "^2.2.4", + "jscodeshift": "^0.14.0", + "mkdirp": "^0.5.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@babel/preset-env": "^7.1.6" + } + }, + "packages/expo/node_modules/@react-native/virtualized-lists": { + "version": "0.74.85", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.85.tgz", + "integrity": "sha512-jx2Zw0qlZteoQ+0KxRc7s4drsljLBEP534FaNZ950e9+CN9nVkLsV6rigcTjDR8wjKMSBWhKf0C0C3egYz7Ehg==", + "dev": true, + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/react": "^18.2.6", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "packages/expo/node_modules/@types/node": { @@ -49791,6 +52379,70 @@ "undici-types": "~5.26.4" } }, + "packages/expo/node_modules/@types/yargs": { + "version": "15.0.19", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", + "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "packages/expo/node_modules/anser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "dev": true + }, + "packages/expo/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "packages/expo/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "packages/expo/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "packages/expo/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "packages/expo/node_modules/expo-auth-session": { "version": "5.4.0", "dev": true, @@ -49832,10 +52484,192 @@ "invariant": "^2.2.4" } }, + "packages/expo/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/expo/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "packages/expo/node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "packages/expo/node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "packages/expo/node_modules/react-devtools-core": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.3.1.tgz", + "integrity": "sha512-7FSb9meX0btdBQLwdFOwt6bGqvRPabmVMMslv8fgoSPqXyuGpgQe36kx8gR86XPw7aV1yVouTp6fyZ0EH+NfUw==", + "dev": true, + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "packages/expo/node_modules/react-devtools-core/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "packages/expo/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "packages/expo/node_modules/react-native": { + "version": "0.74.3", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.3.tgz", + "integrity": "sha512-UFutCC6WEw6HkxlcpQ2BemKqi0JkwrgDchYB5Svi8Sp4Xwt4HA6LGEjNQgZ+3KM44bjyFRpofQym0uh0jACGng==", + "dev": true, + "dependencies": { + "@jest/create-cache-key-function": "^29.6.3", + "@react-native-community/cli": "13.6.9", + "@react-native-community/cli-platform-android": "13.6.9", + "@react-native-community/cli-platform-ios": "13.6.9", + "@react-native/assets-registry": "0.74.85", + "@react-native/codegen": "0.74.85", + "@react-native/community-cli-plugin": "0.74.85", + "@react-native/gradle-plugin": "0.74.85", + "@react-native/js-polyfills": "0.74.85", + "@react-native/normalize-colors": "0.74.85", + "@react-native/virtualized-lists": "0.74.85", + "abort-controller": "^3.0.0", + "anser": "^1.4.9", + "ansi-regex": "^5.0.0", + "base64-js": "^1.5.1", + "chalk": "^4.0.0", + "event-target-shim": "^5.0.1", + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "jest-environment-node": "^29.6.3", + "jsc-android": "^250231.0.0", + "memoize-one": "^5.0.0", + "metro-runtime": "^0.80.3", + "metro-source-map": "^0.80.3", + "mkdirp": "^0.5.1", + "nullthrows": "^1.1.1", + "pretty-format": "^26.5.2", + "promise": "^8.3.0", + "react-devtools-core": "^5.0.0", + "react-refresh": "^0.14.0", + "react-shallow-renderer": "^16.15.0", + "regenerator-runtime": "^0.13.2", + "scheduler": "0.24.0-canary-efb381bbf-20230505", + "stacktrace-parser": "^0.1.10", + "whatwg-fetch": "^3.0.0", + "ws": "^6.2.2", + "yargs": "^17.6.2" + }, + "bin": { + "react-native": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/react": "^18.2.6", + "react": "18.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "packages/expo/node_modules/react-native-mmkv": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/react-native-mmkv/-/react-native-mmkv-2.12.2.tgz", + "integrity": "sha512-6058Aq0p57chPrUutLGe9fYoiDVDNMU2PKV+lLFUJ3GhoHvUrLdsS1PDSCLr00yqzL4WJQ7TTzH+V8cpyrNcfg==", + "dev": true, + "peerDependencies": { + "react": "*", + "react-native": ">=0.71.0" + } + }, + "packages/expo/node_modules/scheduler": { + "version": "0.24.0-canary-efb381bbf-20230505", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz", + "integrity": "sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "packages/expo/node_modules/tslib": { "version": "2.4.1", "license": "0BSD" }, + "packages/expo/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, "packages/express": { "name": "@clerk/express", "version": "0.0.15", From 84edb49dae303b1d2e46208a658a1c39e375a92e Mon Sep 17 00:00:00 2001 From: panteliselef Date: Fri, 5 Jul 2024 15:35:06 +0300 Subject: [PATCH 09/21] use correct react version for expo --- package-lock.json | 2 +- packages/expo/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0b5f2064ca9..03bacfcd630 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52283,7 +52283,7 @@ "expo-local-authentication": "^14.0.1", "expo-secure-store": "^13.0.1", "expo-web-browser": "^12.8.2", - "react": ">=18", + "react": "18.2.0", "react-native": "^0.74.2", "react-native-mmkv": "^2.12.2", "typescript": "*" diff --git a/packages/expo/package.json b/packages/expo/package.json index 450d0a3761c..ce78f77e2a0 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -59,7 +59,7 @@ "expo-secure-store": "^13.0.1", "react-native": "^0.74.2", "react-native-mmkv": "^2.12.2", - "react": ">=18", + "react": "18.2.0", "typescript": "*" }, "peerDependencies": { From e946474bcb16c69ed3ee08254aaf8aabe142f47d Mon Sep 17 00:00:00 2001 From: panteliselef Date: Fri, 5 Jul 2024 16:03:15 +0300 Subject: [PATCH 10/21] drop react-native-mmkv --- package-lock.json | 14 +------------- packages/expo/package.json | 10 ++++------ 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 03bacfcd630..67fddcb833c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52285,7 +52285,6 @@ "expo-web-browser": "^12.8.2", "react": "18.2.0", "react-native": "^0.74.2", - "react-native-mmkv": "^2.12.2", "typescript": "*" }, "engines": { @@ -52298,8 +52297,7 @@ "expo-web-browser": ">=12.5.0", "react": ">=18", "react-dom": ">=18", - "react-native": ">=0.74", - "react-native-mmkv": ">=2.12" + "react-native": ">=0.74" } }, "packages/expo/node_modules/@jest/types": { @@ -52638,16 +52636,6 @@ } } }, - "packages/expo/node_modules/react-native-mmkv": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/react-native-mmkv/-/react-native-mmkv-2.12.2.tgz", - "integrity": "sha512-6058Aq0p57chPrUutLGe9fYoiDVDNMU2PKV+lLFUJ3GhoHvUrLdsS1PDSCLr00yqzL4WJQ7TTzH+V8cpyrNcfg==", - "dev": true, - "peerDependencies": { - "react": "*", - "react-native": ">=0.71.0" - } - }, "packages/expo/node_modules/scheduler": { "version": "0.24.0-canary-efb381bbf-20230505", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz", diff --git a/packages/expo/package.json b/packages/expo/package.json index ce78f77e2a0..8fad5582a8a 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -54,12 +54,11 @@ "@types/react": "*", "@types/react-dom": "*", "expo-auth-session": "^5.4.0", - "expo-web-browser": "^12.8.2", "expo-local-authentication": "^14.0.1", "expo-secure-store": "^13.0.1", - "react-native": "^0.74.2", - "react-native-mmkv": "^2.12.2", + "expo-web-browser": "^12.8.2", "react": "18.2.0", + "react-native": "^0.74.2", "typescript": "*" }, "peerDependencies": { @@ -68,9 +67,8 @@ "expo-secure-store": ">=12", "expo-web-browser": ">=12.5.0", "react": ">=18", - "react-native-mmkv": ">=2.12", - "react-native": ">=0.74", - "react-dom": ">=18" + "react-dom": ">=18", + "react-native": ">=0.74" }, "engines": { "node": ">=18.17.0" From 0cb780e29f71f6dd498306f7e7102b7a0426cd62 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Thu, 11 Jul 2024 14:51:28 +0300 Subject: [PATCH 11/21] feat(clerk-expo): Add `userOwnsCredentials` --- .../src/experimental/LocalCredentials.tsx | 88 ++++++++++++++++--- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/packages/expo/src/experimental/LocalCredentials.tsx b/packages/expo/src/experimental/LocalCredentials.tsx index 0c0803b2a36..5318e7715a2 100644 --- a/packages/expo/src/experimental/LocalCredentials.tsx +++ b/packages/expo/src/experimental/LocalCredentials.tsx @@ -1,12 +1,19 @@ -import { useClerk, useSignIn } from '@clerk/clerk-react'; +import { useClerk, useSignIn, useUser } from '@clerk/clerk-react'; import type { SignInResource } from '@clerk/types'; import { AuthenticationType, isEnrolledAsync, supportedAuthenticationTypesAsync } from 'expo-local-authentication'; -import * as SecureStore from 'expo-secure-store'; +import { + canUseBiometricAuthentication, + deleteItemAsync, + getItem, + getItemAsync, + setItemAsync, + WHEN_PASSCODE_SET_THIS_DEVICE_ONLY, +} from 'expo-secure-store'; import type { PropsWithChildren } from 'react'; import React, { createContext, useContext, useEffect, useState } from 'react'; type LocalCredentials = { - identifier: string; + identifier?: string; password: string; }; @@ -15,6 +22,7 @@ type BiometricType = 'fingerprint' | 'face-recognition'; export const LocalAuthContext = createContext<{ setCredentials: (creds: LocalCredentials) => Promise; hasCredentials: boolean; + userOwnsCredentials: boolean | null; clearCredentials: () => void; authenticate: () => Promise; biometryType: BiometricType | null; @@ -22,6 +30,7 @@ export const LocalAuthContext = createContext<{ // @ts-expect-error Initial value cannot return what the type expects setCredentials: () => {}, hasCredentials: false, + userOwnsCredentials: null, clearCredentials: () => {}, // @ts-expect-error Initial value cannot return what the type expects authenticate: () => {}, @@ -82,32 +91,88 @@ const useAuthenticationType = () => { return authenticationType; }; +const useUserOwnsCredentials = ({ storeKey }: { storeKey: string }) => { + const { user } = useUser(); + const [userOwnsCredentials, setUserOwnsCredentials] = useState(false); + + const getUserCredentials = (storedIdentifier: string | null): boolean => { + if (!user || !storedIdentifier) { + return false; + } + + const identifiers = [ + user.emailAddresses.map(e => e.emailAddress), + user.phoneNumbers.map(p => p.phoneNumber), + ].flat(); + + if (user.username) { + identifiers.push(user.username); + } + return identifiers.includes(storedIdentifier); + }; + + useEffect(() => { + let ignore = false; + getItemAsync(storeKey) + .catch(() => null) + .then(res => { + if (ignore) { + return; + } + setUserOwnsCredentials(getUserCredentials(res)); + }); + + return () => { + ignore = true; + }; + }, [storeKey, user]); + + return [userOwnsCredentials, setUserOwnsCredentials] as const; +}; + export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element { const { isLoaded, signIn } = useSignIn(); const { publishableKey } = useClerk(); + const key = `__clerk_local_auth_${publishableKey}_identifier`; const pkey = `__clerk_local_auth_${publishableKey}_password`; - const [hasLocalAuthCredentials, setHasLocalAuthCredentials] = useState(!!SecureStore.getItem(key)); + const [hasLocalAuthCredentials, setHasLocalAuthCredentials] = useState(!!getItem(key)); + const [userOwnsCredentials, setUserOwnsCredentials] = useUserOwnsCredentials({ storeKey: key }); const hasEnrolledBiometric = useEnrolledBiometric(); const authenticationType = useAuthenticationType(); const biometryType = hasEnrolledBiometric ? authenticationType : null; const setCredentials = async (creds: LocalCredentials) => { - if (!SecureStore.canUseBiometricAuthentication()) { + if (!canUseBiometricAuthentication()) { return; } - await SecureStore.setItemAsync(key, creds.identifier); + + if (creds.identifier && !creds.password) { + throw 'when setting identifier the password is required'; + } + + if (creds.identifier) { + await setItemAsync(key, creds.identifier); + } + + const storedIdentifier = await getItemAsync(key).catch(() => null); + + if (!storedIdentifier) { + throw 'an identifier should already be set in order to update its password'; + } + setHasLocalAuthCredentials(true); - await SecureStore.setItemAsync(pkey, creds.password, { - keychainAccessible: SecureStore.WHEN_PASSCODE_SET_THIS_DEVICE_ONLY, + await setItemAsync(pkey, creds.password, { + keychainAccessible: WHEN_PASSCODE_SET_THIS_DEVICE_ONLY, requireAuthentication: true, }); }; const clearCredentials = async () => { - await Promise.all([SecureStore.deleteItemAsync(key), SecureStore.deleteItemAsync(pkey)]); + await Promise.all([deleteItemAsync(key), deleteItemAsync(pkey)]); setHasLocalAuthCredentials(false); + setUserOwnsCredentials(false); }; const authenticate = async () => { @@ -115,12 +180,12 @@ export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element // TODO: improve error throw 'not loaded'; } - const identifier = await SecureStore.getItemAsync(key).catch(() => null); + const identifier = await getItemAsync(key).catch(() => null); if (!identifier) { // TODO: improve error throw 'Identifier not found'; } - const password = await SecureStore.getItemAsync(pkey).catch(() => null); + const password = await getItemAsync(pkey).catch(() => null); if (!password) { // TODO: improve error @@ -139,6 +204,7 @@ export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element value={{ setCredentials, hasCredentials: hasLocalAuthCredentials, + userOwnsCredentials, clearCredentials, authenticate, biometryType, From 388877aad1e3d15121874ea8ba39db7a4cef1180 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Thu, 11 Jul 2024 14:53:17 +0300 Subject: [PATCH 12/21] Mark deps as optional --- packages/expo/package.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/expo/package.json b/packages/expo/package.json index 8fad5582a8a..59aad9e8dc8 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -70,6 +70,14 @@ "react-dom": ">=18", "react-native": ">=0.74" }, + "peerDependenciesMeta": { + "expo-secure-store": { + "optional": true + }, + "expo-local-authentication": { + "optional": true + } + }, "engines": { "node": ">=18.17.0" }, From f3232164c8c7cff0d367eeaa7966d0d88b8a0227 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Sat, 13 Jul 2024 21:14:09 +0300 Subject: [PATCH 13/21] feat(clerk-expo): Support SDK 50 For SDK 50 `expo-secure-store` is missing `canUseBiometricAuthentication` but its implementation for iOS is identical to `isEnrolledAsync` from `expo-local-authentication` --- package-lock.json | 8 ++++++++ packages/expo/src/experimental/LocalCredentials.tsx | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 349c7124cd3..f9603b24ea8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52298,6 +52298,14 @@ "react": ">=18", "react-dom": ">=18", "react-native": ">=0.74" + }, + "peerDependenciesMeta": { + "expo-local-authentication": { + "optional": true + }, + "expo-secure-store": { + "optional": true + } } }, "packages/expo/node_modules/@jest/types": { diff --git a/packages/expo/src/experimental/LocalCredentials.tsx b/packages/expo/src/experimental/LocalCredentials.tsx index 5318e7715a2..5dedf3a4e96 100644 --- a/packages/expo/src/experimental/LocalCredentials.tsx +++ b/packages/expo/src/experimental/LocalCredentials.tsx @@ -2,7 +2,6 @@ import { useClerk, useSignIn, useUser } from '@clerk/clerk-react'; import type { SignInResource } from '@clerk/types'; import { AuthenticationType, isEnrolledAsync, supportedAuthenticationTypesAsync } from 'expo-local-authentication'; import { - canUseBiometricAuthentication, deleteItemAsync, getItem, getItemAsync, @@ -144,7 +143,7 @@ export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element const biometryType = hasEnrolledBiometric ? authenticationType : null; const setCredentials = async (creds: LocalCredentials) => { - if (!canUseBiometricAuthentication()) { + if (!(await isEnrolledAsync())) { return; } From a184992e7e2c20d2bef9408f0e0ada25d7e2adf0 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 30 Jul 2024 12:12:17 +0300 Subject: [PATCH 14/21] revert husky deletion --- .husky/pre-commit | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000000..5d2103ba288 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,42 @@ +#!/bin/bash +. "$(dirname "$0")/_/husky.sh" + +############################### +## Determine Staged Packages +############################### +# +#staged_files=$(git diff --name-only --cached --diff-filter=ACM) +# +## Initialize an empty string to hold the unique folder paths +#unique_folders="" +# +## Loop through each staged file +#for file in $staged_files; do +# # Extract the first two folders from the file path +# folder=$(echo $file | awk -F'/' '{print $1 "/" $2}') +# +# # Filter files which end with .mjs, .js, .jsx, .ts, or .tsx [NOTE: Should match ./.lintstagedrc.json] +# if [[ $file =~ \.(mjs|js|jsx|ts|tsx)$ ]]; then +# # Check if this folder is already in the list of unique folders +# if [[ $folder == packages/* ]] && [[ ! " $unique_folders " =~ "$folder" ]]; then +# # Append the folder to the list of unique folders +# unique_folders="$unique_folders --filter={./$folder}^..." +# fi +# fi +#done +# +############################### +## Build Staged Packages +############################### +# +#if [ -n "$unique_folders" ]; then +# npx turbo run build --output-logs=errors-only $unique_folders +#else +# echo "SKIPPING: No packages to build" +#fi + +############################## +# Run Lint Staged +############################## + +npx lint-staged From 2b021d2a22ee271e161e2392021062f5f8c3bfad Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 30 Jul 2024 12:17:30 +0300 Subject: [PATCH 15/21] update changeset --- .changeset/rude-poets-beam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/rude-poets-beam.md b/.changeset/rude-poets-beam.md index 6fa8730acc3..1d5fef5332c 100644 --- a/.changeset/rude-poets-beam.md +++ b/.changeset/rude-poets-beam.md @@ -2,4 +2,4 @@ "@clerk/clerk-expo": minor --- -Introduce experimental support for LocalAuthentication with `useLocalCredentials`. +Introduce support for LocalAuthentication with `useLocalCredentials`. From 086e8929f0b24ffe96e95d7b8082cb0e36528de8 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 30 Jul 2024 21:24:43 +0300 Subject: [PATCH 16/21] Drop provider and add web support --- package-lock.json | 33 ++++++++ packages/expo/src/experimental/index.ts | 1 - packages/expo/src/hooks/index.ts | 1 + .../src/hooks/useLocalCredentials/index.tsx | 1 + .../src/hooks/useLocalCredentials/shared.ts | 31 +++++++ .../useLocalCredentials.ts} | 80 ++++++------------- .../useLocalCredentials.web.ts | 6 ++ packages/expo/src/index.ts | 5 -- 8 files changed, 96 insertions(+), 62 deletions(-) delete mode 100644 packages/expo/src/experimental/index.ts create mode 100644 packages/expo/src/hooks/useLocalCredentials/index.tsx create mode 100644 packages/expo/src/hooks/useLocalCredentials/shared.ts rename packages/expo/src/{experimental/LocalCredentials.tsx => hooks/useLocalCredentials/useLocalCredentials.ts} (69%) create mode 100644 packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.web.ts diff --git a/package-lock.json b/package-lock.json index eafdc1a6318..35ae424d77f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27527,6 +27527,18 @@ "expo": "*" } }, + "node_modules/expo-local-authentication": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/expo-local-authentication/-/expo-local-authentication-13.8.0.tgz", + "integrity": "sha512-h0YA7grVdo3834AS70EUCsalaXrrEnoq+yTvIhRTxiPmzWxUv7rNo5ff+XsIEYNElKPmT/wh/xPV1yo3l3fhGg==", + "dev": true, + "dependencies": { + "invariant": "^2.2.4" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-modules-autolinking": { "version": "1.10.3", "dev": true, @@ -27638,6 +27650,15 @@ "invariant": "^2.2.4" } }, + "node_modules/expo-secure-store": { + "version": "12.8.1", + "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-12.8.1.tgz", + "integrity": "sha512-Ju3jmkHby4w7rIzdYAt9kQyQ7HhHJ0qRaiQOInknhOLIltftHjEgF4I1UmzKc7P5RCfGNmVbEH729Pncp/sHXQ==", + "dev": true, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-web-browser": { "version": "12.8.2", "dev": true, @@ -52655,6 +52676,8 @@ "@types/react": "*", "@types/react-dom": "*", "expo-auth-session": "^5.4.0", + "expo-local-authentication": "^13.5.0", + "expo-secure-store": "^12.4.0", "expo-web-browser": "^12.8.2", "react-native": "^0.73.9", "typescript": "*" @@ -52664,10 +52687,20 @@ }, "peerDependencies": { "expo-auth-session": ">=5", + "expo-local-authentication": ">=13.5.0", + "expo-secure-store": ">=12.4.0", "expo-web-browser": ">=12.5.0", "react": ">=18", "react-dom": ">=18", "react-native": ">=0.73" + }, + "peerDependenciesMeta": { + "expo-local-authentication": { + "optional": true + }, + "expo-secure-store": { + "optional": true + } } }, "packages/expo/node_modules/@jest/types": { diff --git a/packages/expo/src/experimental/index.ts b/packages/expo/src/experimental/index.ts deleted file mode 100644 index 99a11cc1a38..00000000000 --- a/packages/expo/src/experimental/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { LocalCredentialsProvider, useLocalCredentials } from './LocalCredentials'; diff --git a/packages/expo/src/hooks/index.ts b/packages/expo/src/hooks/index.ts index 969e644bfb9..d5bd5f1a91b 100644 --- a/packages/expo/src/hooks/index.ts +++ b/packages/expo/src/hooks/index.ts @@ -12,3 +12,4 @@ export { } from '@clerk/clerk-react'; export * from './useOAuth'; +export * from './useLocalCredentials'; diff --git a/packages/expo/src/hooks/useLocalCredentials/index.tsx b/packages/expo/src/hooks/useLocalCredentials/index.tsx new file mode 100644 index 00000000000..3548abc9206 --- /dev/null +++ b/packages/expo/src/hooks/useLocalCredentials/index.tsx @@ -0,0 +1 @@ +export { useLocalCredentials } from './useLocalCredentials'; diff --git a/packages/expo/src/hooks/useLocalCredentials/shared.ts b/packages/expo/src/hooks/useLocalCredentials/shared.ts new file mode 100644 index 00000000000..39689c0375e --- /dev/null +++ b/packages/expo/src/hooks/useLocalCredentials/shared.ts @@ -0,0 +1,31 @@ +import type { SignInResource } from '@clerk/types'; + +type LocalCredentials = { + identifier?: string; + password: string; +}; + +type BiometricType = 'fingerprint' | 'face-recognition'; + +type LocalCredentialsReturn = { + setCredentials: (creds: LocalCredentials) => Promise; + hasCredentials: boolean; + userOwnsCredentials: boolean | null; + clearCredentials: () => Promise; + authenticate: () => Promise; + biometryType: BiometricType | null; +}; + +const LocalCredentialsInitValues: LocalCredentialsReturn = { + setCredentials: () => Promise.resolve(), + hasCredentials: false, + userOwnsCredentials: null, + clearCredentials: () => Promise.resolve(), + // @ts-expect-error Initial value cannot return what the type expects + authenticate: () => Promise.resolve({}), + biometryType: null, +}; + +export { LocalCredentialsInitValues }; + +export type { LocalCredentials, BiometricType, LocalCredentialsReturn }; diff --git a/packages/expo/src/experimental/LocalCredentials.tsx b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts similarity index 69% rename from packages/expo/src/experimental/LocalCredentials.tsx rename to packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts index 5dedf3a4e96..eff2d846483 100644 --- a/packages/expo/src/experimental/LocalCredentials.tsx +++ b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts @@ -1,5 +1,4 @@ import { useClerk, useSignIn, useUser } from '@clerk/clerk-react'; -import type { SignInResource } from '@clerk/types'; import { AuthenticationType, isEnrolledAsync, supportedAuthenticationTypesAsync } from 'expo-local-authentication'; import { deleteItemAsync, @@ -8,37 +7,10 @@ import { setItemAsync, WHEN_PASSCODE_SET_THIS_DEVICE_ONLY, } from 'expo-secure-store'; -import type { PropsWithChildren } from 'react'; -import React, { createContext, useContext, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; -type LocalCredentials = { - identifier?: string; - password: string; -}; - -type BiometricType = 'fingerprint' | 'face-recognition'; - -export const LocalAuthContext = createContext<{ - setCredentials: (creds: LocalCredentials) => Promise; - hasCredentials: boolean; - userOwnsCredentials: boolean | null; - clearCredentials: () => void; - authenticate: () => Promise; - biometryType: BiometricType | null; -}>({ - // @ts-expect-error Initial value cannot return what the type expects - setCredentials: () => {}, - hasCredentials: false, - userOwnsCredentials: null, - clearCredentials: () => {}, - // @ts-expect-error Initial value cannot return what the type expects - authenticate: () => {}, - biometryType: null, -}); - -export const useLocalCredentials = () => { - return useContext(LocalAuthContext); -}; +import { errorThrower } from '../../utils'; +import type { BiometricType, LocalCredentials, LocalCredentialsReturn } from './shared'; const useEnrolledBiometric = () => { const [isEnrolled, setIsEnrolled] = useState(false); @@ -129,7 +101,7 @@ const useUserOwnsCredentials = ({ storeKey }: { storeKey: string }) => { return [userOwnsCredentials, setUserOwnsCredentials] as const; }; -export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element { +export const useLocalCredentials = (): LocalCredentialsReturn => { const { isLoaded, signIn } = useSignIn(); const { publishableKey } = useClerk(); @@ -148,7 +120,9 @@ export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element } if (creds.identifier && !creds.password) { - throw 'when setting identifier the password is required'; + return errorThrower.throw( + `useLocalCredentials: setCredentials() A password is required when specifying an identifier.`, + ); } if (creds.identifier) { @@ -158,7 +132,9 @@ export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element const storedIdentifier = await getItemAsync(key).catch(() => null); if (!storedIdentifier) { - throw 'an identifier should already be set in order to update its password'; + return errorThrower.throw( + `useLocalCredentials: setCredentials() an identifier should already be set in order to update its password.`, + ); } setHasLocalAuthCredentials(true); @@ -176,19 +152,18 @@ export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element const authenticate = async () => { if (!isLoaded) { - // TODO: improve error - throw 'not loaded'; + return errorThrower.throw( + `useLocalCredentials: authenticate() Clerk has not loaded yet. Wait for clerk to load before calling this function`, + ); } const identifier = await getItemAsync(key).catch(() => null); if (!identifier) { - // TODO: improve error - throw 'Identifier not found'; + return errorThrower.throw(`useLocalCredentials: authenticate() the identifier could not be found`); } const password = await getItemAsync(pkey).catch(() => null); if (!password) { - // TODO: improve error - throw 'password not found'; + return errorThrower.throw(`useLocalCredentials: authenticate() cannot retrieve a password for that identifier`); } return signIn.create({ @@ -197,19 +172,12 @@ export function LocalCredentialsProvider(props: PropsWithChildren): JSX.Element password, }); }; - - return ( - - {props.children} - - ); -} + return { + setCredentials, + hasCredentials: hasLocalAuthCredentials, + userOwnsCredentials, + clearCredentials, + authenticate, + biometryType, + }; +}; diff --git a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.web.ts b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.web.ts new file mode 100644 index 00000000000..2ff0a589abd --- /dev/null +++ b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.web.ts @@ -0,0 +1,6 @@ +import type { LocalCredentialsReturn } from './shared'; +import { LocalCredentialsInitValues } from './shared'; + +export const useLocalCredentials = (): LocalCredentialsReturn => { + return LocalCredentialsInitValues; +}; diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts index 3f4e4813854..acb5c041391 100644 --- a/packages/expo/src/index.ts +++ b/packages/expo/src/index.ts @@ -2,11 +2,6 @@ import { setErrorThrowerOptions } from '@clerk/clerk-react/internal'; export { isClerkAPIResponseError, isEmailLinkError, isKnownError, isMetamaskError } from '@clerk/clerk-react/errors'; -export { - LocalCredentialsProvider as __experimental_LocalCredentialsProvider, - useLocalCredentials as __experimental_useLocalCredentials, -} from './experimental'; - /** * @deprecated Use `getClerkInstance()` instead. */ From c7fddbf6b17128824837f1341a60e7aa5fa6f34c Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 6 Aug 2024 22:47:08 +0300 Subject: [PATCH 17/21] Prioritize face recognition over fingerprint --- .../src/hooks/useLocalCredentials/useLocalCredentials.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts index eff2d846483..7bae40bb7fe 100644 --- a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts +++ b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts @@ -47,10 +47,13 @@ const useAuthenticationType = () => { return; } - if (numericTypes.includes(AuthenticationType.FINGERPRINT)) { - setAuthenticationType('fingerprint'); - } else { + if ( + numericTypes.includes(AuthenticationType.IRIS) || + numericTypes.includes(AuthenticationType.FACIAL_RECOGNITION) + ) { setAuthenticationType('face-recognition'); + } else { + setAuthenticationType('fingerprint'); } }); From 4c2a0f85652256a74a193dfc0c0a2b845c60e019 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 7 Aug 2024 11:17:04 +0300 Subject: [PATCH 18/21] Add jsdoc --- .../src/hooks/useLocalCredentials/shared.ts | 10 ++++-- .../useLocalCredentials.ts | 35 +++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/expo/src/hooks/useLocalCredentials/shared.ts b/packages/expo/src/hooks/useLocalCredentials/shared.ts index 39689c0375e..53f13242e45 100644 --- a/packages/expo/src/hooks/useLocalCredentials/shared.ts +++ b/packages/expo/src/hooks/useLocalCredentials/shared.ts @@ -1,7 +1,13 @@ import type { SignInResource } from '@clerk/types'; type LocalCredentials = { + /** + * The identifier of the credentials to be stored on the device. It can be a username, email, phone number, etc. + */ identifier?: string; + /** + * The password for the identifier to be stored on the device. If an identifier already exists on the device passing only password would update the password for the stored identifier. + */ password: string; }; @@ -13,7 +19,7 @@ type LocalCredentialsReturn = { userOwnsCredentials: boolean | null; clearCredentials: () => Promise; authenticate: () => Promise; - biometryType: BiometricType | null; + biometricType: BiometricType | null; }; const LocalCredentialsInitValues: LocalCredentialsReturn = { @@ -23,7 +29,7 @@ const LocalCredentialsInitValues: LocalCredentialsReturn = { clearCredentials: () => Promise.resolve(), // @ts-expect-error Initial value cannot return what the type expects authenticate: () => Promise.resolve({}), - biometryType: null, + biometricType: null, }; export { LocalCredentialsInitValues }; diff --git a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts index 7bae40bb7fe..c0a5f0fbf6f 100644 --- a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts +++ b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts @@ -1,4 +1,5 @@ import { useClerk, useSignIn, useUser } from '@clerk/clerk-react'; +import type { SignInResource } from '@clerk/types'; import { AuthenticationType, isEnrolledAsync, supportedAuthenticationTypesAsync } from 'expo-local-authentication'; import { deleteItemAsync, @@ -104,6 +105,10 @@ const useUserOwnsCredentials = ({ storeKey }: { storeKey: string }) => { return [userOwnsCredentials, setUserOwnsCredentials] as const; }; +/** + * Exposes utilities that allow for storing and accessing an identifier, and it's password securely on the device. + * In order to access the stored credentials, the end user will be prompted to verify themselves via biometrics. + */ export const useLocalCredentials = (): LocalCredentialsReturn => { const { isLoaded, signIn } = useSignIn(); const { publishableKey } = useClerk(); @@ -115,7 +120,7 @@ export const useLocalCredentials = (): LocalCredentialsReturn => { const hasEnrolledBiometric = useEnrolledBiometric(); const authenticationType = useAuthenticationType(); - const biometryType = hasEnrolledBiometric ? authenticationType : null; + const biometricType = hasEnrolledBiometric ? authenticationType : null; const setCredentials = async (creds: LocalCredentials) => { if (!(await isEnrolledAsync())) { @@ -153,7 +158,7 @@ export const useLocalCredentials = (): LocalCredentialsReturn => { setUserOwnsCredentials(false); }; - const authenticate = async () => { + const authenticate = async (): Promise => { if (!isLoaded) { return errorThrower.throw( `useLocalCredentials: authenticate() Clerk has not loaded yet. Wait for clerk to load before calling this function`, @@ -176,11 +181,35 @@ export const useLocalCredentials = (): LocalCredentialsReturn => { }); }; return { + /** + * Stores the provided credentials on the device if the device has enrolled biometrics. + * The end user needs to have a passcode set in order for the credentials to be stored, and those credentials will be removed if the passcode gets removed. + * @param credentials An [`LocalCredentials`](#localcredentials) object. + * @return A promise that will reject if value cannot be stored on the device. + */ setCredentials, + /** + * A Boolean that indicates if there are any credentials stored on the device. + */ hasCredentials: hasLocalAuthCredentials, + /** + * A Boolean that indicates if the stored credentials belong to the signed in uer. When there is no signed-in user the value will always be `false`. + */ userOwnsCredentials, + /** + * Removes the stored credentials from the device. + * @return A promise that will reject if value cannot be deleted from the device. + */ clearCredentials, + /** + * Attempts to read the stored credentials and creates a sign in attempt with the password strategy. + * @return A promise with a SignInResource if the stored credentials were accessed, otherwise the promise will reject. + */ authenticate, - biometryType, + /** + * Indicates the supported enrolled biometric authenticator type. + * Can be `facial-recognition`, `fingerprint` or null. + */ + biometricType, }; }; From 76706a0cfb437fdebbc22c27ab57bb07b61dded6 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 7 Aug 2024 11:18:45 +0300 Subject: [PATCH 19/21] Update packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts Co-authored-by: Bryce Kalow --- .../expo/src/hooks/useLocalCredentials/useLocalCredentials.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts index c0a5f0fbf6f..d8e10d13750 100644 --- a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts +++ b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts @@ -180,6 +180,7 @@ export const useLocalCredentials = (): LocalCredentialsReturn => { password, }); }; + return { /** * Stores the provided credentials on the device if the device has enrolled biometrics. From 9b7a4098829c8aed26afe4890db7130fcbd65123 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 7 Aug 2024 11:19:55 +0300 Subject: [PATCH 20/21] update error --- .../expo/src/hooks/useLocalCredentials/useLocalCredentials.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts index d8e10d13750..15045c55588 100644 --- a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts +++ b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts @@ -171,7 +171,7 @@ export const useLocalCredentials = (): LocalCredentialsReturn => { const password = await getItemAsync(pkey).catch(() => null); if (!password) { - return errorThrower.throw(`useLocalCredentials: authenticate() cannot retrieve a password for that identifier`); + return errorThrower.throw(`useLocalCredentials: authenticate() cannot retrieve a password for ${identifier}`); } return signIn.create({ @@ -180,7 +180,7 @@ export const useLocalCredentials = (): LocalCredentialsReturn => { password, }); }; - + return { /** * Stores the provided credentials on the device if the device has enrolled biometrics. From 35e1326fca02e714b3bf84e26f15240898557461 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 7 Aug 2024 19:02:24 +0300 Subject: [PATCH 21/21] Update packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts Co-authored-by: Bryce Kalow --- .../expo/src/hooks/useLocalCredentials/useLocalCredentials.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts index 15045c55588..472cb04cd85 100644 --- a/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts +++ b/packages/expo/src/hooks/useLocalCredentials/useLocalCredentials.ts @@ -185,7 +185,7 @@ export const useLocalCredentials = (): LocalCredentialsReturn => { /** * Stores the provided credentials on the device if the device has enrolled biometrics. * The end user needs to have a passcode set in order for the credentials to be stored, and those credentials will be removed if the passcode gets removed. - * @param credentials An [`LocalCredentials`](#localcredentials) object. + * @param credentials A [`LocalCredentials`](#localcredentials) object. * @return A promise that will reject if value cannot be stored on the device. */ setCredentials,