From 4ca9b73d75ee9bb58d04bef4d96d46a29e220431 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Thu, 12 Mar 2026 16:36:13 -0700 Subject: [PATCH] fix(platform): build react-native-platform-sdk + fix admin-web typecheck errors - react-native-platform-sdk: add tsconfig.json + 13 source files (core, auth, telemetry, feature-flags, kill-switch, broadcasts, surveys) - react-native-platform-sdk: React hooks + providers wrapping platform-service APIs via fetch - admin-web: fix ThemeEditor.tsx + themes/active/route.ts lysnrai token type access - tracker-web: product-context import path fix (pre-existing) --- .../src/app/api/themes/active/route.ts | 4 +- .../admin-web/src/components/ThemeEditor.tsx | 11 +- .../tracker-web/src/lib/product-constants.ts | 17 + .../tracker-web/src/lib/product-context.tsx | 2 +- .../react-native-platform-sdk/package.json | 15 +- .../react-native-platform-sdk/src/auth.ts | 1 + .../src/auth/index.ts | 184 ++++++++ .../src/broadcasts.ts | 7 + .../src/broadcasts/index.ts | 104 +++++ .../react-native-platform-sdk/src/core.ts | 40 ++ .../src/feature-flags.ts | 1 + .../src/feature-flags/index.ts | 83 ++++ .../src/kill-switch.ts | 1 + .../src/kill-switch/index.ts | 72 ++++ .../react-native-platform-sdk/src/surveys.ts | 7 + .../src/surveys/index.ts | 108 +++++ .../src/telemetry.ts | 6 + .../src/telemetry/index.ts | 87 ++++ .../react-native-platform-sdk/tsconfig.json | 12 + pnpm-lock.yaml | 407 +++--------------- 20 files changed, 814 insertions(+), 355 deletions(-) create mode 100644 dashboards/tracker-web/src/lib/product-constants.ts create mode 100644 packages/react-native-platform-sdk/src/auth.ts create mode 100644 packages/react-native-platform-sdk/src/auth/index.ts create mode 100644 packages/react-native-platform-sdk/src/broadcasts.ts create mode 100644 packages/react-native-platform-sdk/src/broadcasts/index.ts create mode 100644 packages/react-native-platform-sdk/src/core.ts create mode 100644 packages/react-native-platform-sdk/src/feature-flags.ts create mode 100644 packages/react-native-platform-sdk/src/feature-flags/index.ts create mode 100644 packages/react-native-platform-sdk/src/kill-switch.ts create mode 100644 packages/react-native-platform-sdk/src/kill-switch/index.ts create mode 100644 packages/react-native-platform-sdk/src/surveys.ts create mode 100644 packages/react-native-platform-sdk/src/surveys/index.ts create mode 100644 packages/react-native-platform-sdk/src/telemetry.ts create mode 100644 packages/react-native-platform-sdk/src/telemetry/index.ts create mode 100644 packages/react-native-platform-sdk/tsconfig.json diff --git a/dashboards/admin-web/src/app/api/themes/active/route.ts b/dashboards/admin-web/src/app/api/themes/active/route.ts index d6e52406..c7a1c126 100644 --- a/dashboards/admin-web/src/app/api/themes/active/route.ts +++ b/dashboards/admin-web/src/app/api/themes/active/route.ts @@ -25,7 +25,9 @@ export async function GET(req: NextRequest) { const defaultDesktopExtras = { idle: tokensJson.color.semantic.dark.success, - listening: tokensJson.color.lysnrai.hotkeyActive, + listening: + (tokensJson.color as unknown as Record>).lysnrai + ?.hotkeyActive ?? '#5A8CFF', processing: tokensJson.color.semantic.dark.warning, offline: tokensJson.color.semantic.dark.textSecondary, }; diff --git a/dashboards/admin-web/src/components/ThemeEditor.tsx b/dashboards/admin-web/src/components/ThemeEditor.tsx index ac54d12c..6aa8b860 100644 --- a/dashboards/admin-web/src/components/ThemeEditor.tsx +++ b/dashboards/admin-web/src/components/ThemeEditor.tsx @@ -71,7 +71,12 @@ function PlatformSection({ /> >).lysnrai + ?.hotkeyActive ?? + '#5A8CFF') + } onChange={v => onChange('listening', v)} /> >).lysnrai + ?.hotkeyActive ?? '#5A8CFF', processing: tokensJson.color.semantic.dark.warning, offline: tokensJson.color.semantic.dark.textSecondary, } diff --git a/dashboards/tracker-web/src/lib/product-constants.ts b/dashboards/tracker-web/src/lib/product-constants.ts new file mode 100644 index 00000000..3138bf8c --- /dev/null +++ b/dashboards/tracker-web/src/lib/product-constants.ts @@ -0,0 +1,17 @@ +/** + * Client-safe product constants — no Node.js imports. + * + * Use this in 'use client' components. For server-side code that needs + * loadProductIdentity(), import from product-config.ts instead. + */ + +export const PRODUCT_ID = process.env.NEXT_PUBLIC_PRODUCT_ID || process.env.PRODUCT_ID || 'lysnrai'; +export const DISPLAY_NAME = process.env.NEXT_PUBLIC_DISPLAY_NAME || 'LysnrAI'; + +/** All known products in the ByteLyst ecosystem. */ +export const KNOWN_PRODUCTS = [ + { id: 'lysnrai', name: 'LysnrAI', icon: 'Mic' }, + { id: 'chronomind', name: 'ChronoMind', icon: 'Clock' }, + { id: 'nomgap', name: 'NomGap', icon: 'Apple' }, + { id: 'mindlyst', name: 'MindLyst', icon: 'Brain' }, +] as const; diff --git a/dashboards/tracker-web/src/lib/product-context.tsx b/dashboards/tracker-web/src/lib/product-context.tsx index 832735de..8040c986 100644 --- a/dashboards/tracker-web/src/lib/product-context.tsx +++ b/dashboards/tracker-web/src/lib/product-context.tsx @@ -1,7 +1,7 @@ 'use client'; import { createContext, useContext, useState, useCallback, type ReactNode } from 'react'; -import { KNOWN_PRODUCTS, PRODUCT_ID } from '@/lib/product-config'; +import { KNOWN_PRODUCTS, PRODUCT_ID } from '@/lib/product-constants'; const STORAGE_KEY = 'tracker_selected_product'; diff --git a/packages/react-native-platform-sdk/package.json b/packages/react-native-platform-sdk/package.json index 4eae2450..51821ee6 100644 --- a/packages/react-native-platform-sdk/package.json +++ b/packages/react-native-platform-sdk/package.json @@ -37,25 +37,20 @@ }, "scripts": { "build": "tsc", - "test": "jest", + "test": "vitest run", "lint": "eslint src/**/*.ts", "typecheck": "tsc --noEmit" }, - "dependencies": { - "@bytelyst/api-client": "workspace:*", - "@bytelyst/auth": "workspace:*", - "@bytelyst/config": "workspace:*" - }, + "dependencies": {}, "peerDependencies": { "react": ">=18.0.0", "react-native": ">=0.72.0", "expo": ">=49.0.0" }, "devDependencies": { - "@types/react": "^18.2.0", - "@types/react-native": "^0.72.0", - "typescript": "^5.3.0", - "eslint": "^8.54.0" + "@types/react": "^19.0.0", + "typescript": "^5.7.0", + "vitest": "^3.0.0" }, "keywords": [ "react-native", diff --git a/packages/react-native-platform-sdk/src/auth.ts b/packages/react-native-platform-sdk/src/auth.ts new file mode 100644 index 00000000..9ec3a881 --- /dev/null +++ b/packages/react-native-platform-sdk/src/auth.ts @@ -0,0 +1 @@ +export { useAuth, AuthProvider, type AuthState, type AuthContextType } from './auth/index.js'; diff --git a/packages/react-native-platform-sdk/src/auth/index.ts b/packages/react-native-platform-sdk/src/auth/index.ts new file mode 100644 index 00000000..28cf29ac --- /dev/null +++ b/packages/react-native-platform-sdk/src/auth/index.ts @@ -0,0 +1,184 @@ +/** + * Auth module — React context + hook for authentication in React Native apps. + */ + +import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'; +import type { PlatformSDK } from '../core.js'; + +export interface AuthState { + isAuthenticated: boolean; + isLoading: boolean; + userId: string | null; + email: string | null; + error: string | null; +} + +export interface AuthContextType extends AuthState { + login: (email: string, password: string) => Promise; + loginWithGoogle: (idToken: string) => Promise; + loginWithApple: (idToken: string) => Promise; + logout: () => Promise; + refreshSession: () => Promise; +} + +const AuthContext = createContext(null); + +export function useAuth(): AuthContextType { + const ctx = useContext(AuthContext); + if (!ctx) throw new Error('useAuth must be used within an AuthProvider'); + return ctx; +} + +interface AuthProviderProps { + sdk: PlatformSDK; + children: React.ReactNode; + /** Called when tokens are received — persist to secure storage */ + onTokens?: (access: string, refresh: string) => void; + /** Called on logout — clear secure storage */ + onLogout?: () => void; +} + +export function AuthProvider({ + sdk, + children, + onTokens, + onLogout, +}: AuthProviderProps): React.JSX.Element { + const [state, setState] = useState({ + isAuthenticated: false, + isLoading: true, + userId: null, + email: null, + error: null, + }); + + const handleTokenResponse = useCallback( + async (res: Response) => { + if (!res.ok) { + const body = (await res.json().catch(() => ({ message: 'Login failed' }))) as { + message?: string; + }; + throw new Error(body.message ?? `HTTP ${res.status}`); + } + const data = (await res.json()) as { + accessToken?: string; + refreshToken?: string; + user?: { id?: string; email?: string }; + }; + if (data.accessToken && data.refreshToken) { + onTokens?.(data.accessToken, data.refreshToken); + } + setState({ + isAuthenticated: true, + isLoading: false, + userId: data.user?.id ?? null, + email: data.user?.email ?? null, + error: null, + }); + }, + [onTokens] + ); + + const login = useCallback( + async (email: string, password: string) => { + setState(s => ({ ...s, isLoading: true, error: null })); + try { + const res = await sdk.fetch('/auth/login', { + method: 'POST', + body: JSON.stringify({ email, password }), + }); + await handleTokenResponse(res); + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : 'Login failed'; + setState(s => ({ ...s, isLoading: false, error: msg })); + } + }, + [sdk, handleTokenResponse] + ); + + const loginWithGoogle = useCallback( + async (idToken: string) => { + setState(s => ({ ...s, isLoading: true, error: null })); + try { + const res = await sdk.fetch('/auth/oauth/google', { + method: 'POST', + body: JSON.stringify({ idToken }), + }); + await handleTokenResponse(res); + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : 'Google login failed'; + setState(s => ({ ...s, isLoading: false, error: msg })); + } + }, + [sdk, handleTokenResponse] + ); + + const loginWithApple = useCallback( + async (idToken: string) => { + setState(s => ({ ...s, isLoading: true, error: null })); + try { + const res = await sdk.fetch('/auth/oauth/apple', { + method: 'POST', + body: JSON.stringify({ idToken }), + }); + await handleTokenResponse(res); + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : 'Apple login failed'; + setState(s => ({ ...s, isLoading: false, error: msg })); + } + }, + [sdk, handleTokenResponse] + ); + + const logout = useCallback(async () => { + try { + await sdk.fetch('/auth/logout', { method: 'POST' }); + } catch { + /* best-effort */ + } + onLogout?.(); + setState({ + isAuthenticated: false, + isLoading: false, + userId: null, + email: null, + error: null, + }); + }, [sdk, onLogout]); + + const refreshSession = useCallback(async () => { + setState(s => ({ ...s, isLoading: true })); + try { + const res = await sdk.fetch('/auth/me'); + if (res.ok) { + const data = (await res.json()) as { id?: string; email?: string }; + setState({ + isAuthenticated: true, + isLoading: false, + userId: data.id ?? null, + email: data.email ?? null, + error: null, + }); + } else { + setState(s => ({ ...s, isAuthenticated: false, isLoading: false })); + } + } catch { + setState(s => ({ ...s, isLoading: false })); + } + }, [sdk]); + + useEffect(() => { + refreshSession(); + }, [refreshSession]); + + const value: AuthContextType = { + ...state, + login, + loginWithGoogle, + loginWithApple, + logout, + refreshSession, + }; + + return React.createElement(AuthContext.Provider, { value }, children); +} diff --git a/packages/react-native-platform-sdk/src/broadcasts.ts b/packages/react-native-platform-sdk/src/broadcasts.ts new file mode 100644 index 00000000..2dfce29e --- /dev/null +++ b/packages/react-native-platform-sdk/src/broadcasts.ts @@ -0,0 +1,7 @@ +export { + useBroadcasts, + BroadcastProvider, + InAppMessageBanner, + BroadcastModal, + type InAppMessage, +} from './broadcasts/index.js'; diff --git a/packages/react-native-platform-sdk/src/broadcasts/index.ts b/packages/react-native-platform-sdk/src/broadcasts/index.ts new file mode 100644 index 00000000..e9179435 --- /dev/null +++ b/packages/react-native-platform-sdk/src/broadcasts/index.ts @@ -0,0 +1,104 @@ +/** + * Broadcasts module — React context + hook for in-app messages in React Native apps. + */ + +import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'; +import type { PlatformSDK } from '../core.js'; + +export interface InAppMessage { + id: string; + title: string; + body: string; + type: 'info' | 'warning' | 'critical'; + action?: { label: string; url: string }; + dismissible: boolean; + expiresAt?: string; +} + +interface BroadcastContextType { + messages: InAppMessage[]; + dismiss: (id: string) => void; + refresh: () => Promise; +} + +const BroadcastContext = createContext(null); + +export function useBroadcasts(): BroadcastContextType { + const ctx = useContext(BroadcastContext); + if (!ctx) throw new Error('useBroadcasts must be used within a BroadcastProvider'); + return ctx; +} + +interface BroadcastProviderProps { + sdk: PlatformSDK; + /** Poll interval in ms (default: 300000 = 5 min) */ + pollInterval?: number; + children: React.ReactNode; +} + +export function BroadcastProvider({ + sdk, + pollInterval = 300_000, + children, +}: BroadcastProviderProps): React.JSX.Element { + const [messages, setMessages] = useState([]); + + const refresh = useCallback(async () => { + try { + const res = await sdk.fetch('/api/broadcasts/active'); + if (res.ok) { + const data = (await res.json()) as InAppMessage[]; + setMessages(data); + } + } catch { + /* silent */ + } + }, [sdk]); + + const dismiss = useCallback( + (id: string) => { + setMessages(prev => prev.filter(m => m.id !== id)); + // Fire-and-forget dismiss on server + sdk.fetch(`/api/broadcasts/${id}/dismiss`, { method: 'POST' }).catch(() => {}); + }, + [sdk] + ); + + useEffect(() => { + refresh(); + const id = setInterval(refresh, pollInterval); + return () => clearInterval(id); + }, [refresh, pollInterval]); + + const value: BroadcastContextType = { messages, dismiss, refresh }; + return React.createElement(BroadcastContext.Provider, { value }, children); +} + +// MARK: - UI Components + +interface InAppMessageBannerProps { + message: InAppMessage; + onDismiss: () => void; +} + +/** + * Placeholder banner component — product apps should implement their own + * styled version using this as a reference. Returns null (render-only hook). + */ +export function InAppMessageBanner(_props: InAppMessageBannerProps): React.JSX.Element | null { + // Product apps implement their own styled component + return null; +} + +interface BroadcastModalProps { + message: InAppMessage | null; + onDismiss: () => void; +} + +/** + * Placeholder modal component — product apps should implement their own + * styled version. Returns null. + */ +export function BroadcastModal(_props: BroadcastModalProps): React.JSX.Element | null { + return null; +} diff --git a/packages/react-native-platform-sdk/src/core.ts b/packages/react-native-platform-sdk/src/core.ts new file mode 100644 index 00000000..b1e3c4fe --- /dev/null +++ b/packages/react-native-platform-sdk/src/core.ts @@ -0,0 +1,40 @@ +/** + * Core SDK factory — creates and configures platform clients for React Native. + */ + +export interface PlatformSDKConfig { + /** Platform-service base URL (e.g. https://api.bytelyst.com) */ + baseURL: string; + /** Product ID (e.g. 'nomgap', 'flowmonk') */ + productId: string; + /** Function that returns the current access token */ + getAccessToken: () => string | null; +} + +export interface PlatformSDK { + config: PlatformSDKConfig; + /** Generic authenticated fetch against platform-service */ + fetch: (path: string, init?: RequestInit) => Promise; +} + +/** + * Create a configured platform SDK instance. + * Pass to providers to wire up auth, telemetry, flags, etc. + */ +export function createRNPlatformSDK(config: PlatformSDKConfig): PlatformSDK { + const platformFetch = async (path: string, init?: RequestInit): Promise => { + const url = `${config.baseURL}${path}`; + const token = config.getAccessToken(); + const headers: Record = { + 'Content-Type': 'application/json', + 'x-product-id': config.productId, + ...((init?.headers as Record) ?? {}), + }; + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + return globalThis.fetch(url, { ...init, headers }); + }; + + return { config, fetch: platformFetch }; +} diff --git a/packages/react-native-platform-sdk/src/feature-flags.ts b/packages/react-native-platform-sdk/src/feature-flags.ts new file mode 100644 index 00000000..15f9fc38 --- /dev/null +++ b/packages/react-native-platform-sdk/src/feature-flags.ts @@ -0,0 +1 @@ +export { useFeatureFlags, FeatureFlagProvider, type FeatureFlag } from './feature-flags/index.js'; diff --git a/packages/react-native-platform-sdk/src/feature-flags/index.ts b/packages/react-native-platform-sdk/src/feature-flags/index.ts new file mode 100644 index 00000000..f96a3c21 --- /dev/null +++ b/packages/react-native-platform-sdk/src/feature-flags/index.ts @@ -0,0 +1,83 @@ +/** + * Feature Flags module — React context + hook for feature flags in React Native apps. + */ + +import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'; +import type { PlatformSDK } from '../core.js'; + +export interface FeatureFlag { + key: string; + enabled: boolean; + value?: unknown; +} + +interface FeatureFlagContextType { + flags: Map; + isEnabled: (key: string) => boolean; + getValue: (key: string, fallback: T) => T; + refresh: () => Promise; +} + +const FeatureFlagContext = createContext(null); + +export function useFeatureFlags(): FeatureFlagContextType { + const ctx = useContext(FeatureFlagContext); + if (!ctx) throw new Error('useFeatureFlags must be used within a FeatureFlagProvider'); + return ctx; +} + +interface FeatureFlagProviderProps { + sdk: PlatformSDK; + /** Poll interval in ms (default: 60000) */ + pollInterval?: number; + children: React.ReactNode; +} + +export function FeatureFlagProvider({ + sdk, + pollInterval = 60_000, + children, +}: FeatureFlagProviderProps): React.JSX.Element { + const [flags, setFlags] = useState>(new Map()); + + const refresh = useCallback(async () => { + try { + const res = await sdk.fetch('/api/flags/poll'); + if (res.ok) { + const data = (await res.json()) as FeatureFlag[]; + const map = new Map(); + for (const flag of data) { + map.set(flag.key, flag); + } + setFlags(map); + } + } catch { + /* fail-open: keep existing flags */ + } + }, [sdk]); + + const isEnabled = useCallback( + (key: string): boolean => { + return flags.get(key)?.enabled ?? false; + }, + [flags] + ); + + const getValue = useCallback( + (key: string, fallback: T): T => { + const flag = flags.get(key); + if (!flag?.enabled) return fallback; + return (flag.value as T) ?? fallback; + }, + [flags] + ); + + useEffect(() => { + refresh(); + const id = setInterval(refresh, pollInterval); + return () => clearInterval(id); + }, [refresh, pollInterval]); + + const value: FeatureFlagContextType = { flags, isEnabled, getValue, refresh }; + return React.createElement(FeatureFlagContext.Provider, { value }, children); +} diff --git a/packages/react-native-platform-sdk/src/kill-switch.ts b/packages/react-native-platform-sdk/src/kill-switch.ts new file mode 100644 index 00000000..ad836e4a --- /dev/null +++ b/packages/react-native-platform-sdk/src/kill-switch.ts @@ -0,0 +1 @@ +export { useKillSwitch, KillSwitchProvider, type KillSwitchState } from './kill-switch/index.js'; diff --git a/packages/react-native-platform-sdk/src/kill-switch/index.ts b/packages/react-native-platform-sdk/src/kill-switch/index.ts new file mode 100644 index 00000000..01ae08b9 --- /dev/null +++ b/packages/react-native-platform-sdk/src/kill-switch/index.ts @@ -0,0 +1,72 @@ +/** + * Kill Switch module — React context + hook for kill switch in React Native apps. + * Fail-open: if the check fails, the app is assumed to be enabled. + */ + +import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'; +import type { PlatformSDK } from '../core.js'; + +export interface KillSwitchState { + disabled: boolean; + reason?: string; + isLoading: boolean; +} + +interface KillSwitchContextType extends KillSwitchState { + check: () => Promise; +} + +const KillSwitchContext = createContext(null); + +export function useKillSwitch(): KillSwitchContextType { + const ctx = useContext(KillSwitchContext); + if (!ctx) throw new Error('useKillSwitch must be used within a KillSwitchProvider'); + return ctx; +} + +interface KillSwitchProviderProps { + sdk: PlatformSDK; + /** Poll interval in ms (default: 300000 = 5 min) */ + pollInterval?: number; + children: React.ReactNode; +} + +export function KillSwitchProvider({ + sdk, + pollInterval = 300_000, + children, +}: KillSwitchProviderProps): React.JSX.Element { + const [state, setState] = useState({ + disabled: false, + isLoading: true, + }); + + const check = useCallback(async () => { + try { + const res = await sdk.fetch('/api/flags/kill-switch'); + if (res.ok) { + const data = (await res.json()) as { disabled?: boolean; reason?: string }; + setState({ + disabled: data.disabled ?? false, + reason: data.reason, + isLoading: false, + }); + } else { + // Fail-open + setState(s => ({ ...s, disabled: false, isLoading: false })); + } + } catch { + // Fail-open on network error + setState(s => ({ ...s, disabled: false, isLoading: false })); + } + }, [sdk]); + + useEffect(() => { + check(); + const id = setInterval(check, pollInterval); + return () => clearInterval(id); + }, [check, pollInterval]); + + const value: KillSwitchContextType = { ...state, check }; + return React.createElement(KillSwitchContext.Provider, { value }, children); +} diff --git a/packages/react-native-platform-sdk/src/surveys.ts b/packages/react-native-platform-sdk/src/surveys.ts new file mode 100644 index 00000000..3d2b7f05 --- /dev/null +++ b/packages/react-native-platform-sdk/src/surveys.ts @@ -0,0 +1,7 @@ +export { + useSurveys, + SurveyProvider, + SurveyModal, + type ActiveSurvey, + type Question, +} from './surveys/index.js'; diff --git a/packages/react-native-platform-sdk/src/surveys/index.ts b/packages/react-native-platform-sdk/src/surveys/index.ts new file mode 100644 index 00000000..98d8cd21 --- /dev/null +++ b/packages/react-native-platform-sdk/src/surveys/index.ts @@ -0,0 +1,108 @@ +/** + * Surveys module — React context + hook for in-app surveys in React Native apps. + */ + +import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'; +import type { PlatformSDK } from '../core.js'; + +export interface Question { + id: string; + text: string; + type: 'rating' | 'text' | 'choice'; + options?: string[]; + required: boolean; +} + +export interface ActiveSurvey { + id: string; + title: string; + description?: string; + questions: Question[]; + expiresAt?: string; +} + +interface SurveyContextType { + activeSurvey: ActiveSurvey | null; + submit: (surveyId: string, answers: Record) => Promise; + dismiss: (surveyId: string) => void; + refresh: () => Promise; +} + +const SurveyContext = createContext(null); + +export function useSurveys(): SurveyContextType { + const ctx = useContext(SurveyContext); + if (!ctx) throw new Error('useSurveys must be used within a SurveyProvider'); + return ctx; +} + +interface SurveyProviderProps { + sdk: PlatformSDK; + /** Poll interval in ms (default: 600000 = 10 min) */ + pollInterval?: number; + children: React.ReactNode; +} + +export function SurveyProvider({ + sdk, + pollInterval = 600_000, + children, +}: SurveyProviderProps): React.JSX.Element { + const [activeSurvey, setActiveSurvey] = useState(null); + + const refresh = useCallback(async () => { + try { + const res = await sdk.fetch('/api/surveys/active'); + if (res.ok) { + const data = (await res.json()) as ActiveSurvey | null; + setActiveSurvey(data ?? null); + } + } catch { + /* silent */ + } + }, [sdk]); + + const submit = useCallback( + async (surveyId: string, answers: Record) => { + await sdk.fetch(`/api/surveys/${surveyId}/respond`, { + method: 'POST', + body: JSON.stringify({ answers }), + }); + setActiveSurvey(null); + }, + [sdk] + ); + + const dismiss = useCallback( + (surveyId: string) => { + setActiveSurvey(null); + sdk.fetch(`/api/surveys/${surveyId}/dismiss`, { method: 'POST' }).catch(() => {}); + }, + [sdk] + ); + + useEffect(() => { + refresh(); + const id = setInterval(refresh, pollInterval); + return () => clearInterval(id); + }, [refresh, pollInterval]); + + const value: SurveyContextType = { activeSurvey, submit, dismiss, refresh }; + return React.createElement(SurveyContext.Provider, { value }, children); +} + +// MARK: - UI Components + +interface SurveyModalProps { + survey: ActiveSurvey | null; + onSubmit: (answers: Record) => void; + onDismiss: () => void; +} + +/** + * Placeholder survey modal — product apps should implement their own + * styled version. Returns null. + */ +export function SurveyModal(_props: SurveyModalProps): React.JSX.Element | null { + return null; +} diff --git a/packages/react-native-platform-sdk/src/telemetry.ts b/packages/react-native-platform-sdk/src/telemetry.ts new file mode 100644 index 00000000..82a64208 --- /dev/null +++ b/packages/react-native-platform-sdk/src/telemetry.ts @@ -0,0 +1,6 @@ +export { + useTelemetry, + TelemetryProvider, + type TelemetryEvent, + type TelemetryConfig, +} from './telemetry/index.js'; diff --git a/packages/react-native-platform-sdk/src/telemetry/index.ts b/packages/react-native-platform-sdk/src/telemetry/index.ts new file mode 100644 index 00000000..ebcd035f --- /dev/null +++ b/packages/react-native-platform-sdk/src/telemetry/index.ts @@ -0,0 +1,87 @@ +/** + * Telemetry module — React context + hook for event tracking in React Native apps. + */ + +import React, { createContext, useContext, useCallback, useRef, useEffect } from 'react'; +import type { PlatformSDK } from '../core.js'; + +export interface TelemetryEvent { + name: string; + properties?: Record; + timestamp?: string; +} + +export interface TelemetryConfig { + /** Flush interval in ms (default: 30000) */ + flushInterval?: number; + /** Max batch size before auto-flush (default: 20) */ + maxBatchSize?: number; +} + +interface TelemetryContextType { + track: (name: string, properties?: Record) => void; + flush: () => Promise; +} + +const TelemetryContext = createContext(null); + +export function useTelemetry(): TelemetryContextType { + const ctx = useContext(TelemetryContext); + if (!ctx) throw new Error('useTelemetry must be used within a TelemetryProvider'); + return ctx; +} + +interface TelemetryProviderProps { + sdk: PlatformSDK; + config?: TelemetryConfig; + children: React.ReactNode; +} + +export function TelemetryProvider({ + sdk, + config, + children, +}: TelemetryProviderProps): React.JSX.Element { + const queue = useRef([]); + const flushInterval = config?.flushInterval ?? 30_000; + const maxBatchSize = config?.maxBatchSize ?? 20; + + const flush = useCallback(async () => { + if (queue.current.length === 0) return; + const batch = queue.current.splice(0); + try { + await sdk.fetch('/api/telemetry/batch', { + method: 'POST', + body: JSON.stringify({ events: batch }), + }); + } catch { + // Re-queue on failure (best-effort) + queue.current.unshift(...batch); + } + }, [sdk]); + + const track = useCallback( + (name: string, properties?: Record) => { + queue.current.push({ + name, + properties, + timestamp: new Date().toISOString(), + }); + if (queue.current.length >= maxBatchSize) { + flush(); + } + }, + [maxBatchSize, flush] + ); + + useEffect(() => { + const id = setInterval(flush, flushInterval); + return () => { + clearInterval(id); + flush(); + }; + }, [flush, flushInterval]); + + const value: TelemetryContextType = { track, flush }; + return React.createElement(TelemetryContext.Provider, { value }, children); +} diff --git a/packages/react-native-platform-sdk/tsconfig.json b/packages/react-native-platform-sdk/tsconfig.json new file mode 100644 index 00000000..055a6d07 --- /dev/null +++ b/packages/react-native-platform-sdk/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "lib": ["ES2022"], + "jsx": "react-jsx", + "skipLibCheck": true + }, + "include": ["src"], + "exclude": ["src/**/*.test.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8bde2803..0705b63a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -544,37 +544,25 @@ importers: packages/react-native-platform-sdk: dependencies: - '@bytelyst/api-client': - specifier: workspace:* - version: link:../api-client - '@bytelyst/auth': - specifier: workspace:* - version: link:../auth - '@bytelyst/config': - specifier: workspace:* - version: link:../config expo: specifier: '>=49.0.0' - version: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + version: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react: specifier: '>=18.0.0' version: 19.2.4 react-native: specifier: '>=0.72.0' - version: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + version: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) devDependencies: '@types/react': - specifier: ^18.2.0 - version: 18.3.28 - '@types/react-native': - specifier: ^0.72.0 - version: 0.72.8(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4)) - eslint: - specifier: ^8.54.0 - version: 8.57.1 + specifier: ^19.0.0 + version: 19.2.14 typescript: - specifier: ^5.3.0 + specifier: ^5.7.0 version: 5.9.3 + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.11)(happy-dom@18.0.1)(jiti@2.6.1)(jsdom@28.0.0(@noble/hashes@1.8.0))(lightningcss@1.31.1)(msw@2.12.10(@types/node@22.19.11)(typescript@5.9.3))(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) packages/speech: devDependencies: @@ -2261,13 +2249,6 @@ packages: } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - '@eslint/eslintrc@2.1.4': - resolution: - { - integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - '@eslint/eslintrc@3.3.3': resolution: { @@ -2287,13 +2268,6 @@ packages: eslint: optional: true - '@eslint/js@8.57.1': - resolution: - { - integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - '@eslint/js@9.39.2': resolution: { @@ -2696,14 +2670,6 @@ packages: } engines: { node: '>=18.18.0' } - '@humanwhocodes/config-array@0.13.0': - resolution: - { - integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==, - } - engines: { node: '>=10.10.0' } - deprecated: Use @eslint/config-array instead - '@humanwhocodes/module-importer@1.0.1': resolution: { @@ -2711,13 +2677,6 @@ packages: } engines: { node: '>=12.22' } - '@humanwhocodes/object-schema@2.0.3': - resolution: - { - integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==, - } - deprecated: Use @eslint/object-schema instead - '@humanwhocodes/retry@0.4.3': resolution: { @@ -4520,14 +4479,6 @@ packages: integrity: sha512-/UPaQ4jl95soXnLDEJ6Cs6lnRXhwbxtT4KbZz+AFDees7prMV2NOLcHfCnzmTabf5Y3oxENMVBL666n4GMLcTA==, } - '@react-native/virtualized-lists@0.72.8': - resolution: - { - integrity: sha512-J3Q4Bkuo99k7mu+jPS9gSUSgq+lLRSI/+ahXNwV92XgJ/8UgOTxu2LPwhJnBk/sQKxq7E8WkZBnBiozukQMqrw==, - } - peerDependencies: - react-native: '*' - '@react-native/virtualized-lists@0.84.1': resolution: { @@ -5198,12 +5149,6 @@ packages: integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==, } - '@types/prop-types@15.7.15': - resolution: - { - integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==, - } - '@types/react-dom@19.2.3': resolution: { @@ -5212,18 +5157,6 @@ packages: peerDependencies: '@types/react': ^19.2.0 - '@types/react-native@0.72.8': - resolution: - { - integrity: sha512-St6xA7+EoHN5mEYfdWnfYt0e8u6k2FR0P9s2arYgakQGFgU1f9FlPrIEcj0X24pLCF5c5i3WVuLCUdiCYHmOoA==, - } - - '@types/react@18.3.28': - resolution: - { - integrity: sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==, - } - '@types/react@19.2.14': resolution: { @@ -7334,13 +7267,6 @@ packages: } engines: { node: '>=0.10.0' } - doctrine@3.0.0: - resolution: - { - integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==, - } - engines: { node: '>=6.0.0' } - dom-accessibility-api@0.5.16: resolution: { @@ -7704,13 +7630,6 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-scope@7.2.2: - resolution: - { - integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - eslint-scope@8.4.0: resolution: { @@ -7739,15 +7658,6 @@ packages: } engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - eslint@8.57.1: - resolution: - { - integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - eslint@9.39.2: resolution: { @@ -7768,13 +7678,6 @@ packages: } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - espree@9.6.1: - resolution: - { - integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - esprima@4.0.1: resolution: { @@ -8187,13 +8090,6 @@ packages: } engines: { node: '>=18' } - file-entry-cache@6.0.1: - resolution: - { - integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==, - } - engines: { node: ^10.12.0 || >=12.0.0 } - file-entry-cache@8.0.0: resolution: { @@ -8243,13 +8139,6 @@ packages: } engines: { node: '>=10' } - flat-cache@3.2.0: - resolution: - { - integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==, - } - engines: { node: ^10.12.0 || >=12.0.0 } - flat-cache@4.0.1: resolution: { @@ -8575,13 +8464,6 @@ packages: } deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - globals@13.24.0: - resolution: - { - integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==, - } - engines: { node: '>=8' } - globals@14.0.0: resolution: { @@ -8623,12 +8505,6 @@ packages: integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, } - graphemer@1.4.0: - resolution: - { - integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==, - } - graphql@16.13.0: resolution: { @@ -9255,13 +9131,6 @@ packages: } engines: { node: '>=12' } - is-path-inside@3.0.3: - resolution: - { - integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==, - } - engines: { node: '>=8' } - is-plain-obj@4.1.0: resolution: { @@ -13088,12 +12957,6 @@ packages: } engines: { node: '>=18' } - text-table@0.2.0: - resolution: - { - integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==, - } - thread-stream@4.0.0: resolution: { @@ -13307,13 +13170,6 @@ packages: } engines: { node: '>=4' } - type-fest@0.20.2: - resolution: - { - integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==, - } - engines: { node: '>=10' } - type-fest@0.21.3: resolution: { @@ -15257,11 +15113,6 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': - dependencies: - eslint: 8.57.1 - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': dependencies: eslint: 9.39.2(jiti@2.6.1) @@ -15285,20 +15136,6 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.12.6 - debug: 4.4.3 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 @@ -15317,8 +15154,6 @@ snapshots: optionalDependencies: eslint: 9.39.2(jiti@2.6.1) - '@eslint/js@8.57.1': {} - '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -15333,7 +15168,7 @@ snapshots: '@noble/hashes': 1.8.0 optional: true - '@expo/cli@55.0.14(@expo/dom-webview@55.0.3)(expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4))(expo@55.0.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@expo/cli@55.0.14(@expo/dom-webview@55.0.3)(expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(expo@55.0.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: '@expo/code-signing-certificates': 0.0.6 '@expo/config': 55.0.8(typescript@5.9.3) @@ -15342,7 +15177,7 @@ snapshots: '@expo/env': 2.1.1 '@expo/image-utils': 0.8.12 '@expo/json-file': 10.0.12 - '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) '@expo/metro': 54.2.0 '@expo/metro-config': 55.0.9(expo@55.0.4)(typescript@5.9.3) '@expo/osascript': 2.4.2 @@ -15350,7 +15185,7 @@ snapshots: '@expo/plist': 0.5.2 '@expo/prebuild-config': 55.0.8(expo@55.0.4)(typescript@5.9.3) '@expo/require-utils': 55.0.2(typescript@5.9.3) - '@expo/router-server': 55.0.9(expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4))(expo-server@55.0.6)(expo@55.0.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@expo/router-server': 55.0.9(expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(expo-server@55.0.6)(expo@55.0.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@expo/schema-utils': 55.0.2 '@expo/spawn-async': 1.7.2 '@expo/ws-tunnel': 1.0.6 @@ -15367,7 +15202,7 @@ snapshots: connect: 3.7.0 debug: 4.4.3 dnssd-advertise: 1.1.3 - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) expo-server: 55.0.6 fetch-nodeshim: 0.4.8 getenv: 2.0.0 @@ -15394,7 +15229,7 @@ snapshots: ws: 8.19.0 zod: 3.25.76 optionalDependencies: - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) transitivePeerDependencies: - '@expo/dom-webview' - '@expo/metro-runtime' @@ -15456,18 +15291,18 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/devtools@55.0.2(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)': + '@expo/devtools@55.0.2(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': dependencies: chalk: 4.1.2 optionalDependencies: react: 19.2.4 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) - '@expo/dom-webview@55.0.3(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)': + '@expo/dom-webview@55.0.3(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': dependencies: - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react: 19.2.4 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) '@expo/env@2.1.1': dependencies: @@ -15516,13 +15351,13 @@ snapshots: - supports-color - typescript - '@expo/log-box@55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)': + '@expo/log-box@55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': dependencies: - '@expo/dom-webview': 55.0.3(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + '@expo/dom-webview': 55.0.3(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) anser: 1.4.10 - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react: 19.2.4 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) stacktrace-parser: 0.1.11 '@expo/metro-config@55.0.9(expo@55.0.4)(typescript@5.9.3)': @@ -15547,7 +15382,7 @@ snapshots: postcss: 8.4.49 resolve-from: 5.0.0 optionalDependencies: - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) transitivePeerDependencies: - bufferutil - supports-color @@ -15603,7 +15438,7 @@ snapshots: '@expo/json-file': 10.0.12 '@react-native/normalize-colors': 0.83.2 debug: 4.4.3 - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) resolve-from: 5.0.0 semver: 7.7.4 xml2js: 0.6.0 @@ -15621,12 +15456,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/router-server@55.0.9(expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4))(expo-server@55.0.6)(expo@55.0.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@expo/router-server@55.0.9(expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(expo-server@55.0.6)(expo@55.0.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: debug: 4.4.3 - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-constants: 55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(typescript@5.9.3) - expo-font: 55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo-constants: 55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) + expo-font: 55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) expo-server: 55.0.6 react: 19.2.4 optionalDependencies: @@ -15644,11 +15479,11 @@ snapshots: '@expo/sudo-prompt@9.3.2': {} - '@expo/vector-icons@15.1.1(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)': + '@expo/vector-icons@15.1.1(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': dependencies: - expo-font: 55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + expo-font: 55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) react: 19.2.4 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) '@expo/ws-tunnel@1.0.6': {} @@ -15757,18 +15592,8 @@ snapshots: '@humanfs/core': 0.19.1 '@humanwhocodes/retry': 0.4.3 - '@humanwhocodes/config-array@0.13.0': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.3': {} - '@humanwhocodes/retry@0.4.3': {} '@img/colour@1.0.0': @@ -17163,20 +16988,14 @@ snapshots: '@react-native/normalize-colors@0.84.1': {} - '@react-native/virtualized-lists@0.72.8(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))': - dependencies: - invariant: 2.2.4 - nullthrows: 1.1.1 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) - - '@react-native/virtualized-lists@0.84.1(@types/react@18.3.28)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)': + '@react-native/virtualized-lists@0.84.1(@types/react@19.2.14)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 19.2.4 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) optionalDependencies: - '@types/react': 18.3.28 + '@types/react': 19.2.14 '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.3)(redux@5.0.1))(react@19.2.3)': dependencies: @@ -17499,24 +17318,10 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/prop-types@15.7.15': {} - '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 - '@types/react-native@0.72.8(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))': - dependencies: - '@react-native/virtualized-lists': 0.72.8(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4)) - '@types/react': 19.2.14 - transitivePeerDependencies: - - react-native - - '@types/react@18.3.28': - dependencies: - '@types/prop-types': 15.7.15 - csstype: 3.2.3 - '@types/react@19.2.14': dependencies: csstype: 3.2.3 @@ -18269,7 +18074,7 @@ snapshots: resolve-from: 5.0.0 optionalDependencies: '@babel/runtime': 7.28.6 - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) transitivePeerDependencies: - '@babel/core' - supports-color @@ -18842,10 +18647,6 @@ snapshots: dependencies: esutils: 2.0.3 - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - dom-accessibility-api@0.5.16: {} dompurify@3.3.1: @@ -19274,11 +19075,6 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -19290,49 +19086,6 @@ snapshots: eslint-visitor-keys@5.0.0: {} - eslint@8.57.1: - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - '@eslint-community/regexpp': 4.12.2 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.7.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.1 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - eslint@9.39.2(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) @@ -19380,12 +19133,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 - espree@9.6.1: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 3.4.3 - esprima@4.0.1: {} esquery@1.7.0: @@ -19463,42 +19210,42 @@ snapshots: expect-type@1.3.0: {} - expo-asset@55.0.8(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + expo-asset@55.0.8(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3): dependencies: '@expo/image-utils': 0.8.12 - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-constants: 55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(typescript@5.9.3) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo-constants: 55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) react: 19.2.4 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) transitivePeerDependencies: - supports-color - typescript - expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(typescript@5.9.3): + expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3): dependencies: '@expo/config': 55.0.8(typescript@5.9.3) '@expo/env': 2.1.1 - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) transitivePeerDependencies: - supports-color - typescript - expo-file-system@55.0.10(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4)): + expo-file-system@55.0.10(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4)): dependencies: - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) - expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4): + expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): dependencies: - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) fontfaceobserver: 2.3.0 react: 19.2.4 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) expo-keep-awake@55.0.4(expo@55.0.4)(react@19.2.4): dependencies: - expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo: 55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react: 19.2.4 expo-modules-autolinking@55.0.8(typescript@5.9.3): @@ -19511,43 +19258,43 @@ snapshots: - supports-color - typescript - expo-modules-core@55.0.13(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4): + expo-modules-core@55.0.13(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4): dependencies: invariant: 2.2.4 react: 19.2.4 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) expo-server@55.0.6: {} - expo@55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + expo@55.0.4(@babel/core@7.29.0)(@expo/dom-webview@55.0.3)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3): dependencies: '@babel/runtime': 7.28.6 - '@expo/cli': 55.0.14(@expo/dom-webview@55.0.3)(expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4))(expo@55.0.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@expo/cli': 55.0.14(@expo/dom-webview@55.0.3)(expo-constants@55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3))(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(expo@55.0.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@expo/config': 55.0.8(typescript@5.9.3) '@expo/config-plugins': 55.0.6 - '@expo/devtools': 55.0.2(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + '@expo/devtools': 55.0.2(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) '@expo/fingerprint': 0.16.5 '@expo/local-build-cache-provider': 55.0.6(typescript@5.9.3) - '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) '@expo/metro': 54.2.0 '@expo/metro-config': 55.0.9(expo@55.0.4)(typescript@5.9.3) - '@expo/vector-icons': 15.1.1(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + '@expo/vector-icons': 15.1.1(expo-font@55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) '@ungap/structured-clone': 1.3.0 babel-preset-expo: 55.0.10(@babel/core@7.29.0)(@babel/runtime@7.28.6)(expo@55.0.4)(react-refresh@0.14.2) - expo-asset: 55.0.8(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - expo-constants: 55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(typescript@5.9.3) - expo-file-system: 55.0.10(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4)) - expo-font: 55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + expo-asset: 55.0.8(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + expo-constants: 55.0.7(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(typescript@5.9.3) + expo-file-system: 55.0.10(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4)) + expo-font: 55.0.4(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) expo-keep-awake: 55.0.4(expo@55.0.4)(react@19.2.4) expo-modules-autolinking: 55.0.8(typescript@5.9.3) - expo-modules-core: 55.0.13(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + expo-modules-core: 55.0.13(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) pretty-format: 29.7.0 react: 19.2.4 - react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4) react-refresh: 0.14.2 whatwg-url-minimum: 0.1.1 optionalDependencies: - '@expo/dom-webview': 55.0.3(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + '@expo/dom-webview': 55.0.3(expo@55.0.4)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) transitivePeerDependencies: - '@babel/core' - bufferutil @@ -19713,10 +19460,6 @@ snapshots: dependencies: is-unicode-supported: 2.1.0 - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -19764,12 +19507,6 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flat-cache@3.2.0: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - rimraf: 3.0.2 - flat-cache@4.0.1: dependencies: flatted: 3.3.3 @@ -19959,10 +19696,6 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - globals@14.0.0: {} globals@16.4.0: {} @@ -19985,8 +19718,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - graphql@16.13.0: {} gzip-size@4.1.0: @@ -20312,8 +20043,6 @@ snapshots: is-obj@3.0.0: {} - is-path-inside@3.0.3: {} - is-plain-obj@4.1.0: {} is-potential-custom-element-name@1.0.1: @@ -22385,7 +22114,7 @@ snapshots: transitivePeerDependencies: - supports-color - react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4): + react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4): dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native/assets-registry': 0.84.1 @@ -22394,7 +22123,7 @@ snapshots: '@react-native/gradle-plugin': 0.84.1 '@react-native/js-polyfills': 0.84.1 '@react-native/normalize-colors': 0.84.1 - '@react-native/virtualized-lists': 0.84.1(@types/react@18.3.28)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.28)(react@19.2.4))(react@19.2.4) + '@react-native/virtualized-lists': 0.84.1(@types/react@19.2.14)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -22424,7 +22153,7 @@ snapshots: ws: 7.5.10 yargs: 17.7.2 optionalDependencies: - '@types/react': 18.3.28 + '@types/react': 19.2.14 transitivePeerDependencies: - '@babel/core' - '@react-native-community/cli' @@ -23278,8 +23007,6 @@ snapshots: glob: 10.5.0 minimatch: 9.0.5 - text-table@0.2.0: {} - thread-stream@4.0.0: dependencies: real-require: 0.2.0 @@ -23381,8 +23108,6 @@ snapshots: type-detect@4.0.8: {} - type-fest@0.20.2: {} - type-fest@0.21.3: {} type-fest@0.7.1: {}