diff --git a/.opencode/skills/react-native-best-practices/POWER.md b/.opencode/skills/react-native-best-practices/POWER.md
new file mode 100644
index 0000000..bedabb4
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/POWER.md
@@ -0,0 +1,161 @@
+---
+name: react-native-best-practices
+description: Provides React Native performance optimization guidelines for FPS, TTI, bundle size, memory leaks, re-renders, and animations. Applies to tasks involving Hermes optimization, JS thread blocking, bridge overhead, FlashList, native modules, or debugging jank and frame drops.
+license: MIT
+author: Callstack
+keywords: ["react-native", "expo", "performance", "optimization", "profiling"]
+---
+
+# Onboarding
+
+## Step 1: Validate React Native Setup
+
+Before applying performance optimizations, ensure:
+- **Expo CLI** or **React Native CLI** is installed
+ - Verify with: `npx expo --version` and `npx react-native --version`
+- Metro bundler is running (**apply only for** bundle analysis)
+- React Native DevTools is available (**apply only for** profiling)
+ - Press 'j' in Metro terminal or shake device → "Open DevTools"
+
+## Security Guardrails
+
+- Review shell commands before running them and prefer version-pinned tooling from trusted sources.
+- Do not pipe remote install scripts directly into a shell.
+- Treat third-party packages as normal supply-chain dependencies that require provenance and version review.
+- If using Re.Pack code splitting, only load first-party chunks from trusted HTTPS origins tied to the current release.
+
+# When to Load Reference Files
+
+Load specific reference files from `references/` based on the task:
+
+## JavaScript/React Performance (`js-*`)
+
+- **Debugging slow/janky UI or animations** → `references/js-measure-fps.md`
+- **Investigating re-render issues** → `references/js-profile-react.md` → `references/js-react-compiler.md`
+- **Optimizing list scrolling** → `references/js-lists-flatlist-flashlist.md`
+- **Reducing re-renders with state management** → `references/js-atomic-state.md`
+- **Using Concurrent React features** → `references/js-concurrent-react.md`
+- **Enabling automatic memoization** → `references/js-react-compiler.md`
+- **Optimizing animations** → `references/js-animations-reanimated.md`
+- **Fixing TextInput lag** → `references/js-uncontrolled-components.md`
+- **Hunting JavaScript memory leaks** → `references/js-memory-leaks.md`
+
+## Native Performance (`native-*`)
+
+- **Measuring startup time (TTI)** → `references/native-measure-tti.md`
+- **Building native modules** → `references/native-turbo-modules.md`
+- **Understanding native threading** → `references/native-threading-model.md`
+- **Profiling native code** → `references/native-profiling.md`
+- **Setting up native tooling** → `references/native-platform-setup.md`
+- **Debugging view hierarchy** → `references/native-view-flattening.md`
+- **Native memory patterns** → `references/native-memory-patterns.md`
+- **Hunting native memory leaks** → `references/native-memory-leaks.md`
+- **Choosing native SDKs vs polyfills** → `references/native-sdks-over-polyfills.md`
+- **Fixing Android 16KB alignment** → `references/native-android-16kb-alignment.md`
+
+## Bundle & App Size (`bundle-*`)
+
+- **Analyzing bundle size** → `references/bundle-analyze-js.md`
+- **Analyzing app size** → `references/bundle-analyze-app.md`
+- **Fixing barrel imports** → `references/bundle-barrel-exports.md`
+- **Enabling tree shaking** → `references/bundle-tree-shaking.md`
+- **Android code shrinking** → `references/bundle-r8-android.md`
+- **Optimizing Hermes bundle loading** → `references/bundle-hermes-mmap.md`
+- **Managing native assets** → `references/bundle-native-assets.md`
+- **Evaluating library size** → `references/bundle-library-size.md`
+- **Code splitting** → `references/bundle-code-splitting.md`
+
+## Problem → Reference Mapping
+
+Use this quick lookup when debugging specific issues:
+
+| Problem | Start With |
+|---------|-----------|
+| App feels slow/janky | `references/js-measure-fps.md` → `references/js-profile-react.md` |
+| Too many re-renders | `references/js-profile-react.md` → `references/js-react-compiler.md` |
+| Slow startup (TTI) | `references/native-measure-tti.md` → `references/bundle-analyze-js.md` |
+| Large app size | `references/bundle-analyze-app.md` → `references/bundle-r8-android.md` |
+| Memory growing | `references/js-memory-leaks.md` or `references/native-memory-leaks.md` |
+| Animation drops frames | `references/js-animations-reanimated.md` |
+| List scroll jank | `references/js-lists-flatlist-flashlist.md` |
+| TextInput lag | `references/js-uncontrolled-components.md` |
+| Native module slow | `references/native-turbo-modules.md` → `references/native-threading-model.md` |
+| Native library alignment issue | `references/native-android-16kb-alignment.md` |
+
+## Quick Reference Commands
+
+### FPS & Re-renders
+```bash
+# Open React Native DevTools
+# Press 'j' in Metro, or shake device → "Open DevTools"
+```
+
+Baseline runtime metrics should come from the target interaction itself:
+- Capture commit timeline, re-render counts, slow components, and heaviest-commit breakdown.
+- Treat component tree depth and count as supporting context only.
+
+**Common fixes:**
+- Replace ScrollView with FlatList/FlashList for lists
+- Use React Compiler for automatic memoization
+- Use atomic state (Jotai/Zustand) to reduce re-renders
+- Use `useDeferredValue` for expensive computations
+
+**Review guardrails:**
+- Check library versions before suggesting API-specific fixes. FlashList v2 deprecates `estimatedItemSize`.
+- Do not suggest `useMemo` or `useCallback` dependency changes without a reproducible correctness issue or profiling evidence.
+- Do not report stale closures unless the stale read path or repro is clear.
+
+### Analyze Bundle Size
+```bash
+npx react-native bundle \
+ --entry-file index.js \
+ --bundle-output output.js \
+ --platform ios \
+ --sourcemap-output output.js.map \
+ --dev false --minify true
+
+npx source-map-explorer output.js --no-border-checks
+```
+
+**Common fixes:**
+- Avoid barrel imports (import directly from source)
+- Remove unnecessary Intl polyfills only after checking Hermes API and method coverage
+- Enable tree shaking (Expo SDK 52+ or Re.Pack)
+- Enable R8 for Android native code shrinking
+
+### Measure TTI
+- Use `react-native-performance` for markers
+- Only measure cold starts (exclude warm/hot/prewarm)
+
+**Common fixes:**
+- Disable JS bundle compression on Android (enables Hermes mmap)
+- Use native navigation (react-native-screens)
+- Preload commonly-used expensive screens before navigating to them
+
+### Native Performance
+
+**Profile native:**
+- iOS: Xcode Instruments → Time Profiler
+- Android: Android Studio → CPU Profiler
+
+**Common fixes:**
+- Use background threads for heavy native work
+- Prefer async over sync Turbo Module methods
+- Use C++ for cross-platform performance-critical code
+
+## Priority Guidelines
+
+Apply optimizations in this order:
+
+| Priority | Category | Impact | Prefix |
+|----------|----------|--------|--------|
+| 1 | FPS & Re-renders | CRITICAL | `js-*` |
+| 2 | Bundle Size | CRITICAL | `bundle-*` |
+| 3 | TTI Optimization | HIGH | `native-*`, `bundle-*` |
+| 4 | Native Performance | HIGH | `native-*` |
+| 5 | Memory Management | MEDIUM-HIGH | `js-*`, `native-*` |
+| 6 | Animations | MEDIUM | `js-*` |
+
+## Attribution
+
+Based on "The Ultimate Guide to React Native Optimization" by Callstack.
diff --git a/.opencode/skills/react-native-best-practices/SKILL.md b/.opencode/skills/react-native-best-practices/SKILL.md
new file mode 100644
index 0000000..bde6607
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/SKILL.md
@@ -0,0 +1,253 @@
+---
+name: react-native-best-practices
+description: Provides React Native performance optimization guidelines for FPS, TTI, bundle size, memory leaks, re-renders, and animations. Applies to tasks involving Hermes optimization, JS thread blocking, bridge overhead, FlashList, native modules, or debugging jank and frame drops.
+license: MIT
+metadata:
+ author: Callstack
+ tags: react-native, expo, performance, optimization, profiling
+---
+
+# React Native Best Practices
+
+## Overview
+
+Performance optimization guide for React Native applications, covering JavaScript/React, Native (iOS/Android), and bundling optimizations. Based on Callstack's "Ultimate Guide to React Native Optimization".
+
+## Skill Format
+
+Each reference file follows a hybrid format for fast lookup and deep understanding:
+
+- **Quick Pattern**: Incorrect/Correct code snippets for immediate pattern matching
+- **Quick Command**: Shell commands for process/measurement skills
+- **Quick Config**: Configuration snippets for setup-focused skills
+- **Quick Reference**: Summary tables for conceptual skills
+- **Deep Dive**: Full context with When to Use, Prerequisites, Step-by-Step, Common Pitfalls
+
+**Impact ratings**: CRITICAL (fix immediately), HIGH (significant improvement), MEDIUM (worthwhile optimization)
+
+## When to Apply
+
+Reference these guidelines when:
+- Debugging slow/janky UI or animations
+- Investigating memory leaks (JS or native)
+- Optimizing app startup time (TTI)
+- Reducing bundle or app size
+- Writing native modules (Turbo Modules)
+- Profiling React Native performance
+- Reviewing React Native code for performance
+
+## Security Notes
+
+- Treat shell commands in these references as local developer operations. Review them before running, prefer version-pinned tooling, and avoid piping remote scripts directly to a shell.
+- Treat third-party libraries and plugins as dependencies that still require normal supply-chain controls: pin versions, verify provenance, and update through your standard review process.
+- Treat Re.Pack code splitting as first-party artifact delivery only. Remote chunks must come from trusted HTTPS origins you control and be pinned to the current app release.
+
+## Priority-Ordered Guidelines
+
+| Priority | Category | Impact | Prefix |
+|----------|----------|--------|--------|
+| 1 | FPS & Re-renders | CRITICAL | `js-*` |
+| 2 | Bundle Size | CRITICAL | `bundle-*` |
+| 3 | TTI Optimization | HIGH | `native-*`, `bundle-*` |
+| 4 | Native Performance | HIGH | `native-*` |
+| 5 | Memory Management | MEDIUM-HIGH | `js-*`, `native-*` |
+| 6 | Animations | MEDIUM | `js-*` |
+
+## Quick Reference
+
+### Optimization Workflow
+
+Follow this cycle for any performance issue: **Measure → Optimize → Re-measure → Validate**
+
+1. **Measure**: Capture baseline metrics before changes. For runtime issues, prefer commit timeline, re-render counts, slow components, heaviest-commit breakdown, and startup/TTI when available. Component tree depth or count are optional context, not substitutes.
+2. **Optimize**: Apply the targeted fix from the relevant reference
+3. **Re-measure**: Run the same measurement to get updated metrics
+4. **Validate**: Confirm improvement (e.g., FPS 45→60, TTI 3.2s→1.8s, bundle 2.1MB→1.6MB)
+
+If metrics did not improve, revert and try the next suggested fix.
+
+### Review Guardrails
+
+- Check library versions before suggesting API-specific fixes. Example: FlashList v2 deprecates `estimatedItemSize`, so do not flag it as missing there.
+- Do not suggest `useMemo` or `useCallback` dependency changes unless behavior is demonstrably incorrect or profiling shows wasted work tied to that value.
+- Do not report stale closures speculatively. Show the stale read path, a repro, or profiler evidence before calling it out.
+- When profiling a flow, measure the target interaction itself. Do not treat component tree depth or component count as the main performance evidence.
+
+### Critical: FPS & Re-renders
+
+**Profile first:**
+```bash
+# Open React Native DevTools
+# Press 'j' in Metro, or shake device → "Open DevTools"
+```
+
+**Common fixes:**
+- Replace ScrollView with FlatList/FlashList for lists
+- Use React Compiler for automatic memoization
+- Use atomic state (Jotai/Zustand) to reduce re-renders
+- Use `useDeferredValue` for expensive computations
+
+### Critical: Bundle Size
+
+**Analyze bundle:**
+```bash
+npx react-native bundle \
+ --entry-file index.js \
+ --bundle-output output.js \
+ --platform ios \
+ --sourcemap-output output.js.map \
+ --dev false --minify true
+
+npx source-map-explorer output.js --no-border-checks
+```
+
+**Verify improvement after optimization:**
+```bash
+# Record baseline size before changes
+ls -lh output.js # e.g., Before: 2.1 MB
+
+# After applying fixes, re-bundle and compare
+npx react-native bundle --entry-file index.js --bundle-output output.js \
+ --platform ios --dev false --minify true
+ls -lh output.js # e.g., After: 1.6 MB (24% reduction)
+```
+
+**Common fixes:**
+- Avoid barrel imports (import directly from source)
+- Remove unnecessary Intl polyfills only after checking Hermes API and method coverage
+- Enable tree shaking (Expo SDK 52+ or Re.Pack)
+- Enable R8 for Android native code shrinking
+
+### High: TTI Optimization
+
+**Measure TTI:**
+- Use `react-native-performance` for markers
+- Only measure cold starts (exclude warm/hot/prewarm)
+
+**Common fixes:**
+- Disable JS bundle compression on Android (enables Hermes mmap)
+- Use native navigation (react-native-screens)
+- Preload commonly-used expensive screens before navigating to them
+
+### High: Native Performance
+
+**Profile native:**
+- iOS: Xcode Instruments → Time Profiler
+- Android: Android Studio → CPU Profiler
+
+**Common fixes:**
+- Use background threads for heavy native work
+- Prefer async over sync Turbo Module methods
+- Use C++ for cross-platform performance-critical code
+
+## References
+
+Full documentation with code examples in [references/][references]:
+
+### JavaScript/React (`js-*`)
+
+| File | Impact | Description |
+|------|--------|-------------|
+| [js-lists-flatlist-flashlist.md][js-lists-flatlist-flashlist] | CRITICAL | Replace ScrollView with virtualized lists |
+| [js-profile-react.md][js-profile-react] | MEDIUM | React DevTools profiling |
+| [js-measure-fps.md][js-measure-fps] | HIGH | FPS monitoring and measurement |
+| [js-memory-leaks.md][js-memory-leaks] | MEDIUM | JS memory leak hunting |
+| [js-atomic-state.md][js-atomic-state] | HIGH | Jotai/Zustand patterns |
+| [js-concurrent-react.md][js-concurrent-react] | HIGH | useDeferredValue, useTransition |
+| [js-react-compiler.md][js-react-compiler] | HIGH | Automatic memoization |
+| [js-animations-reanimated.md][js-animations-reanimated] | MEDIUM | Reanimated worklets |
+| [js-bottomsheet.md][js-bottomsheet] | HIGH | Bottom sheet optimization |
+| [js-uncontrolled-components.md][js-uncontrolled-components] | HIGH | TextInput optimization |
+
+### Native (`native-*`)
+
+| File | Impact | Description |
+|------|--------|-------------|
+| [native-turbo-modules.md][native-turbo-modules] | HIGH | Building fast native modules |
+| [native-sdks-over-polyfills.md][native-sdks-over-polyfills] | HIGH | Native vs JS libraries |
+| [native-measure-tti.md][native-measure-tti] | HIGH | TTI measurement setup |
+| [native-threading-model.md][native-threading-model] | HIGH | Turbo Module threads |
+| [native-profiling.md][native-profiling] | MEDIUM | Xcode/Android Studio profiling |
+| [native-platform-setup.md][native-platform-setup] | MEDIUM | iOS/Android tooling guide |
+| [native-view-flattening.md][native-view-flattening] | MEDIUM | View hierarchy debugging |
+| [native-memory-patterns.md][native-memory-patterns] | MEDIUM | C++/Swift/Kotlin memory |
+| [native-memory-leaks.md][native-memory-leaks] | MEDIUM | Native memory leak hunting |
+| [native-android-16kb-alignment.md][native-android-16kb-alignment] | CRITICAL | Third-party library alignment for Google Play |
+
+### Bundling (`bundle-*`)
+
+| File | Impact | Description |
+|------|--------|-------------|
+| [bundle-barrel-exports.md][bundle-barrel-exports] | CRITICAL | Avoid barrel imports |
+| [bundle-analyze-js.md][bundle-analyze-js] | CRITICAL | JS bundle visualization |
+| [bundle-tree-shaking.md][bundle-tree-shaking] | HIGH | Dead code elimination |
+| [bundle-analyze-app.md][bundle-analyze-app] | HIGH | App size analysis |
+| [bundle-r8-android.md][bundle-r8-android] | HIGH | Android code shrinking |
+| [bundle-hermes-mmap.md][bundle-hermes-mmap] | HIGH | Disable bundle compression |
+| [bundle-native-assets.md][bundle-native-assets] | HIGH | Asset catalog setup |
+| [bundle-library-size.md][bundle-library-size] | MEDIUM | Evaluate dependencies |
+| [bundle-code-splitting.md][bundle-code-splitting] | MEDIUM | Re.Pack code splitting |
+
+
+## Searching References
+
+```bash
+# Find patterns by keyword
+grep -l "reanimated" references/
+grep -l "flatlist" references/
+grep -l "memory" references/
+grep -l "profil" references/
+grep -l "tti" references/
+grep -l "bundle" references/
+```
+
+## Problem → Skill Mapping
+
+| Problem | Start With |
+|---------|------------|
+| App feels slow/janky | [js-measure-fps.md][js-measure-fps] → [js-profile-react.md][js-profile-react] |
+| Too many re-renders | [js-profile-react.md][js-profile-react] → [js-react-compiler.md][js-react-compiler] |
+| Slow startup (TTI) | [native-measure-tti.md][native-measure-tti] → [bundle-analyze-js.md][bundle-analyze-js] |
+| Large app size | [bundle-analyze-app.md][bundle-analyze-app] → [bundle-r8-android.md][bundle-r8-android] |
+| Memory growing | [js-memory-leaks.md][js-memory-leaks] or [native-memory-leaks.md][native-memory-leaks] |
+| Animation drops frames | [js-animations-reanimated.md][js-animations-reanimated] |
+| Bottom sheet jank/re-renders | [js-bottomsheet.md][js-bottomsheet] → [js-animations-reanimated.md][js-animations-reanimated] |
+| List scroll jank | [js-lists-flatlist-flashlist.md][js-lists-flatlist-flashlist] |
+| TextInput lag | [js-uncontrolled-components.md][js-uncontrolled-components] |
+| Native module slow | [native-turbo-modules.md][native-turbo-modules] → [native-threading-model.md][native-threading-model] |
+| Native library alignment issue | [native-android-16kb-alignment.md][native-android-16kb-alignment] |
+
+[references]: references/
+[js-lists-flatlist-flashlist]: references/js-lists-flatlist-flashlist.md
+[js-profile-react]: references/js-profile-react.md
+[js-measure-fps]: references/js-measure-fps.md
+[js-memory-leaks]: references/js-memory-leaks.md
+[js-atomic-state]: references/js-atomic-state.md
+[js-concurrent-react]: references/js-concurrent-react.md
+[js-react-compiler]: references/js-react-compiler.md
+[js-animations-reanimated]: references/js-animations-reanimated.md
+[js-bottomsheet]: references/js-bottomsheet.md
+[js-uncontrolled-components]: references/js-uncontrolled-components.md
+[native-turbo-modules]: references/native-turbo-modules.md
+[native-sdks-over-polyfills]: references/native-sdks-over-polyfills.md
+[native-measure-tti]: references/native-measure-tti.md
+[native-threading-model]: references/native-threading-model.md
+[native-profiling]: references/native-profiling.md
+[native-platform-setup]: references/native-platform-setup.md
+[native-view-flattening]: references/native-view-flattening.md
+[native-memory-patterns]: references/native-memory-patterns.md
+[native-memory-leaks]: references/native-memory-leaks.md
+[native-android-16kb-alignment]: references/native-android-16kb-alignment.md
+[bundle-barrel-exports]: references/bundle-barrel-exports.md
+[bundle-analyze-js]: references/bundle-analyze-js.md
+[bundle-tree-shaking]: references/bundle-tree-shaking.md
+[bundle-analyze-app]: references/bundle-analyze-app.md
+[bundle-r8-android]: references/bundle-r8-android.md
+[bundle-hermes-mmap]: references/bundle-hermes-mmap.md
+[bundle-native-assets]: references/bundle-native-assets.md
+[bundle-library-size]: references/bundle-library-size.md
+[bundle-code-splitting]: references/bundle-code-splitting.md
+
+## Attribution
+
+Based on "The Ultimate Guide to React Native Optimization" by Callstack.
diff --git a/.opencode/skills/react-native-best-practices/agents/openai.yaml b/.opencode/skills/react-native-best-practices/agents/openai.yaml
new file mode 100644
index 0000000..48c283d
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/agents/openai.yaml
@@ -0,0 +1,4 @@
+interface:
+ display_name: "React Native Best Practices"
+ short_description: "React Native performance optimization guide"
+ default_prompt: "Use $react-native-best-practices to diagnose and improve React Native performance."
diff --git a/.opencode/skills/react-native-best-practices/references/bundle-analyze-app.md b/.opencode/skills/react-native-best-practices/references/bundle-analyze-app.md
new file mode 100644
index 0000000..7113685
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/bundle-analyze-app.md
@@ -0,0 +1,211 @@
+---
+title: Analyze App Bundle Size
+impact: HIGH
+tags: app-size, ruler, emerge-tools, thinning
+---
+
+# Skill: Analyze App Bundle Size
+
+Measure iOS and Android app download/install sizes using Ruler, App Store Connect, and Emerge Tools.
+
+## Quick Command
+
+```bash
+# Android (Ruler)
+cd android && ./gradlew analyzeReleaseBundle
+
+# iOS (Xcode export with thinning)
+cd ios && xcodebuild -exportArchive \
+ -archivePath MyApp.xcarchive \
+ -exportPath ./export \
+ -exportOptionsPlist ExportOptions.plist
+# Check: App Thinning Size Report.txt
+```
+
+## When to Use
+
+- App download size is too large
+- Users complain about storage usage
+- App approaching store limits
+- Comparing releases for size regression
+
+> **Note**: This skill involves interpreting visual size reports (Ruler, Emerge Tools X-Ray). AI agents cannot yet process screenshots autonomously. Use this as a guide while reviewing the reports manually, or await MCP-based visual feedback integration (see roadmap).
+
+## Key Metrics
+
+| Metric | Description | User Impact |
+|--------|-------------|-------------|
+| Download Size | Compressed, transferred over network | Download time, data usage |
+| Install Size | Uncompressed, on device storage | Storage space |
+
+**Google finding**: Every 6 MB increase reduces installs by 1%.
+
+## Android: Ruler (Spotify)
+
+### Setup
+
+Add to `android/build.gradle`:
+
+```groovy
+buildscript {
+ dependencies {
+ classpath("com.spotify.ruler:ruler-gradle-plugin:2.0.0-beta-3")
+ }
+}
+```
+
+Add to `android/app/build.gradle`:
+
+```groovy
+apply plugin: "com.spotify.ruler"
+
+ruler {
+ abi.set("arm64-v8a") // Target architecture
+ locale.set("en")
+ screenDensity.set(480)
+ sdkVersion.set(34)
+}
+```
+
+### Analyze
+
+```bash
+cd android
+./gradlew analyzeReleaseBundle
+```
+
+Opens HTML report with:
+- Download size
+- Install size
+- Component breakdown (biggest → smallest)
+
+### CI Size Validation
+
+```groovy
+ruler {
+ verification {
+ downloadSizeThreshold = 20 * 1024 * 1024 // 20 MB
+ installSizeThreshold = 50 * 1024 * 1024 // 50 MB
+ }
+}
+```
+
+Build fails if thresholds exceeded.
+
+## iOS: Xcode App Thinning
+
+### Via App Store Connect (Most Accurate)
+
+After uploading to TestFlight:
+1. Open App Store Connect
+2. Go to your build
+3. View size table by device variant
+
+**Note**: TestFlight builds include debug data, App Store builds slightly larger due to DRM.
+
+### Via Xcode Export
+
+1. Archive app: **Product → Archive**
+2. In Organizer, click **Distribute App**
+3. Select **Custom**
+4. Choose **App Thinning: All compatible device variants**
+
+Or in `ExportOptions.plist`:
+
+```xml
+thinning
+<thin-for-all-variants>
+```
+
+### Output
+
+Creates folder with:
+- **Universal IPA**: All variants combined
+- **Thinned IPAs**: One per device variant
+- **App Thinning Size Report.txt**:
+
+```
+Variant: SampleApp-.ipa
+App + On Demand Resources size: 3.5 MB compressed, 10.6 MB uncompressed
+App size: 3.5 MB compressed, 10.6 MB uncompressed
+```
+
+- Compressed = Download size
+- Uncompressed = Install size
+
+## Emerge Tools (Cross-Platform)
+
+Third-party service with visual analysis.
+
+### Upload
+
+Upload IPA, APK, or AAB through their web interface or CI integration.
+
+### Features
+
+
+
+- **X-Ray**: Treemap visualization (like source-map-explorer for binaries)
+ - Shows Frameworks (hermes.framework), Mach-O sections (TEXT, DATA), etc.
+ - Color-coded: Binaries, Localizations, Fonts, Asset Catalogs, Videos, CoreML Models
+ - Visible components: `main.jsbundle` (JS code), RCT modules, DYLD sections
+- **Breakdown**: Component-by-component size
+- **Insights**: Automated suggestions (use with caution)
+
+**Caution**: Some suggestions may not apply to React Native (e.g., "remove Hermes").
+
+## Size Comparison
+
+| Tool | Platform | Accuracy | CI Integration |
+|------|----------|----------|----------------|
+| Ruler | Android | High | Yes (Gradle) |
+| App Store Connect | iOS | Highest | No |
+| Xcode Export | iOS | High | Yes (xcodebuild) |
+| Emerge Tools | Both | High | Yes (API) |
+
+## Typical React Native App Sizes
+
+| Component | Approximate Size |
+|-----------|------------------|
+| Hermes engine | ~2-3 MB |
+| React Native core | ~3-5 MB |
+| JavaScript bundle | 1-10 MB |
+| Assets (images, etc.) | Varies |
+
+**Baseline empty app**: ~6-10 MB download
+
+## Optimization Impact Example
+
+| Optimization | Size Reduction |
+|--------------|----------------|
+| Enable R8 (Android) | ~30% |
+| Remove unused polyfills | 400+ KB |
+| Asset catalog (iOS) | 10-50% of assets |
+| Tree shaking | 10-15% |
+
+## Quick Commands
+
+```bash
+# Android release bundle size
+cd android && ./gradlew bundleRelease
+# Check: android/app/build/outputs/bundle/release/
+
+# iOS archive
+cd ios && xcodebuild -workspace ios/MyApp.xcworkspace \
+ -scheme MyApp \
+ -configuration Release \
+ -archivePath MyApp.xcarchive \
+ archive
+
+# Export with thinning report
+cd ios && xcodebuild -exportArchive \
+ -archivePath MyApp.xcarchive \
+ -exportPath ./export \
+ -exportOptionsPlist ExportOptions.plist
+```
+
+## Related Skills
+
+- [bundle-r8-android.md](./bundle-r8-android.md) - Reduce Android size
+- [bundle-native-assets.md](./bundle-native-assets.md) - Optimize asset delivery
+- [bundle-analyze-js.md](./bundle-analyze-js.md) - JS bundle analysis
diff --git a/.opencode/skills/react-native-best-practices/references/bundle-analyze-js.md b/.opencode/skills/react-native-best-practices/references/bundle-analyze-js.md
new file mode 100644
index 0000000..531a4ea
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/bundle-analyze-js.md
@@ -0,0 +1,262 @@
+---
+title: Analyze JS Bundle Size
+impact: CRITICAL
+tags: bundle, analysis, source-map-explorer, expo-atlas
+---
+
+# Skill: Analyze JS Bundle Size
+
+Use source-map-explorer and Expo Atlas to visualize what's in your JavaScript bundle.
+
+## Quick Command
+
+```bash
+# React Native CLI
+npx react-native bundle \
+ --entry-file index.js \
+ --bundle-output output.js \
+ --platform ios \
+ --sourcemap-output output.js.map \
+ --dev false --minify true && \
+npx source-map-explorer output.js --no-border-checks
+
+# Expo
+EXPO_UNSTABLE_ATLAS=true npx expo export --platform ios && npx expo-atlas
+```
+
+## When to Use
+
+- JS bundle seems too large
+- Want to identify heavy dependencies
+- Investigating startup time issues
+- Before/after optimization comparison
+
+> **Note**: This skill involves interpreting visual treemap output (source-map-explorer, Expo Atlas). AI agents cannot yet process screenshots autonomously. Use this as a guide while reviewing the visualization manually, or await MCP-based visual feedback integration (see roadmap).
+
+## Understanding Hermes Bytecode
+
+Modern React Native (0.70+) uses Hermes bytecode, not raw JavaScript:
+- Skips parsing at runtime
+- Still benefits from smaller bundles
+- Heavy imports still execute on startup
+
+**Impact of bundle size:**
+- Larger bytecode = longer download from store
+- More imports on init path = slower TTI
+
+## Method 1: source-map-explorer
+
+### Generate Bundle with Source Map
+
+**React Native CLI:**
+
+```bash
+npx react-native bundle \
+ --entry-file index.js \
+ --bundle-output output.js \
+ --platform ios \
+ --sourcemap-output output.js.map \
+ --dev false \
+ --minify true
+```
+
+**Expo (SDK 51+):**
+
+```bash
+npx expo export --platform ios --source-maps --output-dir dist
+# Bundle at: dist/ios/_expo/static/js/ios/*.js
+# Source map at: dist/ios/_expo/static/js/ios/*.map
+```
+
+### Analyze
+
+```bash
+npx source-map-explorer output.js --no-border-checks
+```
+
+**Note**: `--no-border-checks` needed due to Metro's non-standard source maps.
+
+Opens browser with treemap visualization:
+
+
+
+The treemap shows:
+- **Hierarchy**: `node_modules/` → `react-native/` → `Libraries/` → individual files
+- **Size**: Box area proportional to file size (KB shown in labels)
+- **Major components visible**:
+ - `react-native` (724.18 KB, 80.5%)
+ - `Renderer` (208.44 KB) - ReactNativeRenderer-prod.js, ReactFabric-prod.js
+ - `Components` (125.29 KB) - Touchable, ScrollView, etc.
+ - `Animated` (79.48 KB) - Animation system
+ - `virtualized-lists` (57.57 KB) - FlatList internals
+
+Click on any section to drill down into that directory.
+
+**Limitation**: May lose ~30% info due to mapping issues.
+
+## Method 2: Expo Atlas
+
+More accurate for Expo projects (or with workaround for bare RN).
+
+### For Expo Projects
+
+```bash
+# Start with Atlas enabled
+EXPO_UNSTABLE_ATLAS=true npx expo start --no-dev
+
+# Or export
+EXPO_UNSTABLE_ATLAS=true npx expo export
+```
+
+Then launch UI:
+
+```bash
+npx expo-atlas
+```
+
+
+
+Expo Atlas provides more accurate visualization for Expo projects, with similar treemap interface showing module sizes and dependencies.
+
+### For Non-Expo Projects
+
+Use `expo-atlas-without-expo` package.
+
+## Method 3: Re.Pack Bundle Analysis (Webpack/Rspack)
+
+If using Re.Pack:
+
+### webpack-bundle-analyzer
+
+```bash
+rspack build --analyze
+```
+
+### bundle-stats / statoscope
+
+```bash
+# Generate stats
+npx react-native bundle \
+ --platform android \
+ --entry-file index.js \
+ --dev false \
+ --minify true \
+ --json stats.json
+
+# Analyze
+npx bundle-stats --html --json stats.json
+```
+
+### Rsdoctor
+
+```javascript
+// rspack.config.js
+const { RsdoctorRspackPlugin } = require('@rsdoctor/rspack-plugin');
+
+module.exports = {
+ plugins: [
+ process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
+ ].filter(Boolean),
+};
+```
+
+Run with:
+
+```bash
+RSDOCTOR=true npx react-native start
+```
+
+## What to Look For
+
+### Red Flags
+
+| Finding | Problem | Solution |
+|---------|---------|----------|
+| Entire library imported | Barrel exports | Use direct imports |
+| Duplicate packages | Multiple versions | Dedupe in package.json |
+| Dev dependencies in bundle | Incorrect imports | Check conditional imports |
+| Large polyfills | Unnecessary for Hermes | Remove (see native-sdks-over-polyfills.md) |
+| Moment.js with locales | Bloated date library | Switch to date-fns or dayjs |
+
+### Common Offenders
+
+- **Lodash full import**: Use `lodash-es` or specific imports
+- **Moment.js**: Replace with `date-fns` or `dayjs`
+- **Intl polyfills**: Check Hermes API and method coverage before removing them
+- **AWS SDK**: Import specific services only
+
+## Code Examples
+
+### Identify Barrel Import Impact
+
+```tsx
+// BAD: Imports entire library through barrel
+import { format } from 'date-fns';
+
+// In bundle: All of date-fns loaded
+
+// GOOD: Direct import
+import format from 'date-fns/format';
+
+// In bundle: Only format function
+```
+
+## Comparing Bundles
+
+### source-map-explorer
+
+```bash
+# Generate baseline
+npx react-native bundle ... --bundle-output baseline.js --sourcemap-output baseline.js.map
+
+# Make changes, generate new bundle
+npx react-native bundle ... --bundle-output current.js --sourcemap-output current.js.map
+
+# Compare manually in browser
+```
+
+### Re.Pack (automated)
+
+```bash
+npx bundle-stats compare baseline-stats.json current-stats.json
+```
+
+## Quick Commands
+
+**React Native CLI:**
+
+```bash
+# iOS bundle analysis
+npx react-native bundle \
+ --entry-file index.js \
+ --bundle-output ios-bundle.js \
+ --platform ios \
+ --sourcemap-output ios-bundle.js.map \
+ --dev false \
+ --minify true && \
+npx source-map-explorer ios-bundle.js --no-border-checks
+
+# Android bundle analysis
+npx react-native bundle \
+ --entry-file index.js \
+ --bundle-output android-bundle.js \
+ --platform android \
+ --sourcemap-output android-bundle.js.map \
+ --dev false \
+ --minify true && \
+npx source-map-explorer android-bundle.js --no-border-checks
+```
+
+**Expo:**
+
+```bash
+# Use Expo Atlas (recommended for Expo projects)
+EXPO_UNSTABLE_ATLAS=true npx expo export --platform ios
+npx expo-atlas
+```
+
+## Related Skills
+
+- [bundle-barrel-exports.md](./bundle-barrel-exports.md) - Fix barrel import issues
+- [bundle-tree-shaking.md](./bundle-tree-shaking.md) - Enable dead code elimination
+- [bundle-library-size.md](./bundle-library-size.md) - Check library sizes before adding
diff --git a/.opencode/skills/react-native-best-practices/references/bundle-barrel-exports.md b/.opencode/skills/react-native-best-practices/references/bundle-barrel-exports.md
new file mode 100644
index 0000000..42b37f2
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/bundle-barrel-exports.md
@@ -0,0 +1,248 @@
+---
+title: Avoid Barrel Exports
+impact: CRITICAL
+tags: bundle, imports, barrel, tree-shaking
+---
+
+# Skill: Avoid Barrel Exports
+
+Refactor barrel imports (index files) to reduce bundle size and improve startup time.
+
+## Quick Pattern
+
+**Incorrect:**
+
+```tsx
+import { Button } from './components';
+// Loads ALL exports from components/index.ts
+```
+
+**Correct:**
+
+```tsx
+import Button from './components/Button';
+// Loads only Button
+```
+
+## When to Use
+
+- Bundle contains unused code from libraries
+- Circular dependency warnings in Metro
+- Hot Module Replacement (HMR) breaks frequently
+- TTI is slow due to module evaluation
+
+## What Are Barrel Exports?
+
+```tsx
+// components/index.ts (barrel file)
+export { Button } from './Button';
+export { Card } from './Card';
+export { Modal } from './Modal';
+export { Sidebar } from './Sidebar';
+
+// Usage (barrel import)
+import { Button } from './components';
+```
+
+## Problems with Barrel Imports
+
+### 1. Bundle Size Overhead
+
+Metro includes **all exports** even if you use one:
+
+```tsx
+// Only need Button, but entire barrel is bundled
+import { Button } from './components';
+// Card, Modal, Sidebar also included!
+```
+
+### 2. Runtime Overhead
+
+All modules evaluate before returning your import:
+
+```tsx
+import { Button } from './components';
+// JavaScript must evaluate:
+// - Button.tsx
+// - Card.tsx
+// - Modal.tsx
+// - Sidebar.tsx
+// Even though you only use Button
+```
+
+### 3. Circular Dependencies
+
+Barrel files make cycles easier to create accidentally:
+
+```
+Warning: Require cycle:
+ components/index.ts -> Button.tsx -> utils/index.ts -> components/index.ts
+```
+
+Breaks HMR, causes unpredictable behavior.
+
+## Solution 1: Direct Imports
+
+Replace barrel imports with direct paths:
+
+```tsx
+// BEFORE: Barrel import
+import { Button, Card } from './components';
+
+// AFTER: Direct imports
+import Button from './components/Button';
+import Card from './components/Card';
+```
+
+### Enforce with ESLint
+
+```bash
+npm install -D eslint-plugin-no-barrel-files
+```
+
+```javascript
+// eslint.config.js
+import noBarrelFiles from 'eslint-plugin-no-barrel-files';
+
+export default [
+ {
+ plugins: { 'no-barrel-files': noBarrelFiles },
+ rules: {
+ 'no-barrel-files/no-barrel-files': 'error',
+ },
+ },
+];
+```
+
+## Solution 2: Tree Shaking (Automatic)
+
+Enable tree shaking to automatically remove unused barrel exports.
+
+### Expo SDK 52+
+
+```tsx
+// metro.config.js
+const { getDefaultConfig } = require('expo/metro-config');
+const config = getDefaultConfig(__dirname);
+
+config.transformer.getTransformOptions = async () => ({
+ transform: {
+ experimentalImportSupport: true,
+ },
+});
+
+module.exports = config;
+```
+
+```bash
+# .env
+EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH=1
+EXPO_UNSTABLE_TREE_SHAKING=1
+```
+
+### metro-serializer-esbuild
+
+```bash
+npm install @rnx-kit/metro-serializer-esbuild
+```
+
+### Re.Pack (Webpack/Rspack)
+
+Tree shaking built-in.
+
+## Real-World Example: date-fns
+
+```tsx
+// BAD: Imports entire library
+import { format, addDays, isToday } from 'date-fns';
+
+// GOOD: Direct imports
+import format from 'date-fns/format';
+import addDays from 'date-fns/addDays';
+import isToday from 'date-fns/isToday';
+```
+
+## Library-Specific Solutions
+
+Some libraries provide Babel plugins:
+
+### React Native Paper
+
+```javascript
+// babel.config.js
+module.exports = {
+ plugins: [
+ 'react-native-paper/babel', // Auto-transforms imports
+ ],
+};
+```
+
+Transforms:
+```tsx
+import { Button } from 'react-native-paper';
+// Into:
+import Button from 'react-native-paper/lib/module/components/Button';
+```
+
+## Refactoring Strategy
+
+### Step 1: Identify Barrel Files
+
+Look for `index.ts` files with multiple exports:
+
+```bash
+grep -r "export \* from" src/
+grep -r "export { .* } from" src/
+```
+
+### Step 2: Update Imports
+
+```tsx
+// Find all usages
+// VS Code: Cmd+Shift+F for "from './components'"
+
+// Replace each with direct import
+import Button from './components/Button';
+```
+
+### Step 3: (Optional) Keep Barrel for External API
+
+If your package is consumed by others:
+
+```tsx
+// Keep index.ts for package API
+// components/index.ts
+export { Button } from './Button';
+
+// Internal code uses direct imports
+// src/screens/Home.tsx
+import Button from '../components/Button';
+```
+
+## Migration Script Example
+
+```bash
+# Use codemod or search-replace
+# Find: import { (\w+) } from '\.\/components';
+# Replace: import $1 from './components/$1';
+```
+
+## Verification
+
+After refactoring:
+
+1. Run bundle analysis (see [bundle-analyze-js.md](./bundle-analyze-js.md))
+2. Compare sizes before/after
+3. Check for circular dependency warnings
+
+## Common Pitfalls
+
+- **Breaking external consumers**: If publishing a library, keep barrel for public API
+- **IDE auto-imports**: Configure IDE to prefer direct imports
+- **Inconsistent patterns**: Enforce with ESLint across team
+
+## Related Skills
+
+- [bundle-analyze-js.md](./bundle-analyze-js.md) - Verify impact
+- [bundle-tree-shaking.md](./bundle-tree-shaking.md) - Automatic solution
+- [bundle-library-size.md](./bundle-library-size.md) - Check library patterns
diff --git a/.opencode/skills/react-native-best-practices/references/bundle-code-splitting.md b/.opencode/skills/react-native-best-practices/references/bundle-code-splitting.md
new file mode 100644
index 0000000..9eb18f2
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/bundle-code-splitting.md
@@ -0,0 +1,247 @@
+---
+title: Remote Code Loading
+impact: MEDIUM
+tags: code-splitting, repack, lazy-loading, chunks
+---
+
+# Skill: Remote Code Loading
+
+Set up code splitting with Re.Pack for on-demand bundle loading from trusted, first-party assets.
+
+## Quick Pattern
+
+**Before (static import):**
+
+```jsx
+import SettingsScreen from './screens/SettingsScreen';
+```
+
+**After (lazy loaded chunk):**
+
+```jsx
+const SettingsScreen = React.lazy(() =>
+ import(/* webpackChunkName: "settings" */ './screens/SettingsScreen')
+);
+
+}>
+
+
+```
+
+## When to Use
+
+Consider code splitting when:
+- **Not using Hermes** (JSC/V8 benefits more)
+- App size exceeds 200 MB (Play Store limit)
+- Building micro-frontend architecture
+- Loading features based on user permissions
+- Other optimizations exhausted
+
+**Note**: Hermes already uses memory mapping for efficient bundle reading. Benefits of code splitting are minimal with Hermes or even counterproductive in some cases.
+
+## Security Model
+
+Remote chunks are executable application code. Only load chunks that you build and publish yourself.
+
+Keep these guardrails in place:
+- Serve chunks only from a first-party, HTTPS-only origin you control
+- Resolve `scriptId` through a fixed allowlist or release manifest
+- Fail closed if a chunk is missing or unexpected
+- Do not load chunks from user-controlled input, query params, or third-party domains
+
+## Prerequisites
+
+- Re.Pack installed (replaces Metro)
+
+```bash
+npx @callstack/repack-init
+```
+
+## Step-by-Step Instructions
+
+### 1. Initialize Re.Pack
+
+```bash
+npx @callstack/repack-init
+```
+
+Follow prompts to migrate from Metro. Check [migration guide](https://re-pack.dev/docs/getting-started/quick-start).
+
+### 2. Create Split Point with React.lazy
+
+```tsx
+// BEFORE: Static import
+import SettingsScreen from './screens/SettingsScreen';
+
+// AFTER: Dynamic import (creates split point)
+const SettingsScreen = React.lazy(() =>
+ import(/* webpackChunkName: "settings" */ './screens/SettingsScreen')
+);
+```
+
+### 3. Wrap with Suspense
+
+```tsx
+import React, { Suspense } from 'react';
+
+const App = () => {
+ return (
+ }>
+
+
+ );
+};
+```
+
+### 4. Configure Chunk Loading
+
+```jsx
+// index.js (before AppRegistry)
+import { ScriptManager, Script } from '@callstack/repack/client';
+
+const CHUNK_URLS = {
+ settings: 'https://assets.example.com/app/v42/settings.chunk.bundle',
+};
+
+ScriptManager.shared.addResolver((scriptId) => ({
+ url: __DEV__ ? Script.getDevServerURL(scriptId) : getChunkUrl(scriptId),
+}));
+
+function getChunkUrl(scriptId) {
+ const url = CHUNK_URLS[scriptId];
+
+ if (!url) {
+ throw new Error(`Unknown chunk: ${scriptId}`);
+ }
+
+ return url;
+}
+
+AppRegistry.registerComponent(appName, () => App);
+```
+
+### 5. Build and Deploy Chunks
+
+Build generates:
+- `index.bundle` - Main bundle
+- `settings.chunk.bundle` - Lazy-loaded chunk
+
+Deploy chunks to a first-party CDN with versioned paths, and keep the allowlist or manifest in sync with the app release.
+
+## Complete Example
+
+```tsx
+// App.tsx
+import React, { Suspense, useState } from 'react';
+import { Button, View, ActivityIndicator } from 'react-native';
+
+// Lazy load heavy feature
+const HeavyFeature = React.lazy(() =>
+ import(/* webpackChunkName: "heavy-feature" */ './HeavyFeature')
+);
+
+const App = () => {
+ const [showFeature, setShowFeature] = useState(false);
+
+ return (
+
+
+ );
+};
+```
+
+## Module Federation (Advanced)
+
+For micro-frontend architecture:
+
+```tsx
+// Host app loads remote module
+const RemoteModule = React.lazy(() =>
+ import('remote-app/Module')
+);
+```
+
+Enables:
+- Independent team deployments
+- Shared dependencies
+- Runtime composition
+
+**Complexity warning**: Only use when organizational benefits outweigh overhead. Federation increases the trust boundary, so keep the same first-party origin and allowlist rules as above.
+
+### Version Management
+
+Consider [Zephyr Cloud](https://zephyr-cloud.io/) for:
+- Sub-second deployments
+- Version management
+- Re.Pack integration
+
+## Caching Strategy
+
+```tsx
+ScriptManager.shared.addResolver((scriptId) => ({
+ url: getChunkUrl(scriptId),
+ cache: {
+ // Enable caching
+ enabled: true,
+ // Cache location
+ path: `${FileSystem.cacheDirectory}/chunks/`,
+ },
+}));
+```
+
+## When NOT to Use
+
+| Scenario | Why Not |
+|----------|---------|
+| Using Hermes | mmap already efficient |
+| Small app | Overhead not worth it |
+| Simple navigation | Native navigation better |
+| Quick iteration needed | Added complexity |
+
+## Hermes Memory Mapping
+
+Hermes reads bytecode lazily via mmap:
+- Only loads executed code into memory
+- No parse step needed
+- Code splitting provides marginal benefit
+
+## Verification
+
+```tsx
+// Check if chunk loaded correctly
+ScriptManager.shared.on('loading', (scriptId) => {
+ console.log(`Loading: ${scriptId}`);
+});
+
+ScriptManager.shared.on('loaded', (scriptId) => {
+ console.log(`Loaded: ${scriptId}`);
+});
+
+ScriptManager.shared.on('error', (scriptId, error) => {
+ console.error(`Failed: ${scriptId}`, error);
+});
+```
+
+## Common Pitfalls
+
+- **Forgetting Suspense**: Lazy components need fallback
+- **Wrong CDN path**: Chunks 404 in production
+- **No caching**: Re-downloads on every load
+- **Too many chunks**: Network overhead exceeds savings
+- **Untrusted chunk source**: Remote JS from third-party or user-controlled origins is equivalent to remote code execution
+
+## Related Skills
+
+- [bundle-tree-shaking.md](./bundle-tree-shaking.md) - Re.Pack tree shaking
+- [bundle-analyze-js.md](./bundle-analyze-js.md) - Measure chunk sizes
+- [native-measure-tti.md](./native-measure-tti.md) - Verify TTI impact
diff --git a/.opencode/skills/react-native-best-practices/references/bundle-hermes-mmap.md b/.opencode/skills/react-native-best-practices/references/bundle-hermes-mmap.md
new file mode 100644
index 0000000..3de25c3
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/bundle-hermes-mmap.md
@@ -0,0 +1,167 @@
+---
+title: Disable JS Bundle Compression
+impact: HIGH
+tags: android, hermes, mmap, tti, startup
+---
+
+# Skill: Disable JS Bundle Compression
+
+Disable Android JS bundle compression to enable Hermes memory mapping for faster startup.
+
+## Quick Config
+
+```groovy
+// android/app/build.gradle
+android {
+ androidResources {
+ noCompress += ["bundle"]
+ }
+}
+```
+
+**Note**: Default in React Native 0.79+. Only needed for 0.78 and earlier.
+
+## When to Use
+
+- Android app using Hermes
+- Want faster TTI (Time to Interactive)
+- Willing to trade install size for startup speed
+- React Native version is 0.78 or earlier, skip otherwise (see applicability)
+
+## Background
+
+Android compresses most files in APK/AAB by default, including `index.android.bundle`.
+
+**Problem**: Compressed files can't be memory-mapped (mmap).
+
+**Impact**: Hermes must decompress before reading, losing one of its key optimizations.
+
+## How Hermes Memory Mapping Works
+
+Without compression:
+1. Hermes opens bytecode file
+2. OS memory-maps directly to disk
+3. Only pages actually accessed are loaded
+4. **Result**: Fast startup, low memory
+
+With compression:
+1. Android decompresses entire bundle
+2. Loaded into memory
+3. Then Hermes processes
+4. **Result**: Slower startup, higher memory
+
+## Step-by-Step Implementation
+
+### Edit build.gradle
+
+In `android/app/build.gradle`:
+
+```groovy
+android {
+ androidResources {
+ noCompress += ["bundle"]
+ }
+}
+```
+
+### Full Context
+
+```groovy
+android {
+ namespace "com.myapp"
+ defaultConfig {
+ applicationId "com.myapp"
+ // ...
+ }
+
+ androidResources {
+ noCompress += ["bundle"]
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ // ...
+ }
+ }
+}
+```
+
+### Rebuild
+
+```bash
+cd android
+./gradlew clean
+./gradlew bundleRelease
+# or
+./gradlew assembleRelease
+```
+
+## Trade-offs
+
+| Metric | Without Change | With Change |
+|--------|----------------|-------------|
+| Download size | Same | Same |
+| Install size | Smaller | **+8% larger** |
+| TTI | Slower | **-16% faster** |
+
+**Real example**: 75.9 MB install → 82 MB install, but 450ms faster startup.
+
+## Applicability
+
+**React Native 0.78 and earlier**: Apply this optimization manually.
+
+**React Native 0.79+**: Skip this—bundle compression is disabled by default.
+
+## Verification
+
+### Check APK Contents
+
+```bash
+# Unzip APK
+unzip app-release.apk -d apk-contents
+
+# Check if bundle is compressed
+file apk-contents/assets/index.android.bundle
+# Should show: "data" (not "gzip compressed")
+```
+
+### Measure TTI Impact
+
+Use performance markers (see [native-measure-tti.md](./native-measure-tti.md)) to compare before/after.
+
+## Multiple File Types
+
+If you have other files that benefit from mmap:
+
+```groovy
+androidResources {
+ noCompress += ["bundle", "hbc", "data"]
+}
+```
+
+## Common Pitfalls
+
+- **Not rebuilding**: Change requires clean build
+- **Wrong config location**: Must be in `android` block
+- **Ignoring size increase**: Monitor user feedback on install size
+- **Already default**: Check if React Native version includes this
+
+## Expo Notes
+
+For Expo projects, run `npx expo prebuild` first to generate `android/` folder, then apply the `build.gradle` changes. Add `android/` to version control or use a [config plugin](https://docs.expo.dev/config-plugins/introduction/) for persistent changes.
+
+## Should You Enable This?
+
+| Scenario | Recommendation |
+|----------|---------------|
+| Startup-critical app | ✅ Enable |
+| Storage-sensitive users | ⚠️ Test impact |
+| Already fast TTI | Maybe not worth it |
+| Large JS bundle | ✅ Bigger benefit |
+
+## Related Skills
+
+- [native-measure-tti.md](./native-measure-tti.md) - Measure TTI improvement
+- [bundle-analyze-app.md](./bundle-analyze-app.md) - Check size impact
+- [bundle-r8-android.md](./bundle-r8-android.md) - Offset size increase
diff --git a/.opencode/skills/react-native-best-practices/references/bundle-library-size.md b/.opencode/skills/react-native-best-practices/references/bundle-library-size.md
new file mode 100644
index 0000000..678afe6
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/bundle-library-size.md
@@ -0,0 +1,177 @@
+---
+title: Determine Library Size
+impact: MEDIUM
+tags: dependencies, bundlephobia, library-size
+---
+
+# Skill: Determine Library Size
+
+Evaluate third-party library size impact before adding to your project.
+
+## Quick Command
+
+```bash
+# Check size before installing
+# Visit: https://bundlephobia.com/package/[package-name]
+
+# Or use CLI
+npx bundle-phobia-cli
+```
+
+## When to Use
+
+- Evaluating new dependencies
+- Comparing alternative libraries
+- Auditing existing dependencies
+- Investigating bundle bloat
+
+## Tools Overview
+
+| Tool | Type | Best For |
+|------|------|----------|
+| bundlephobia.com | Web | Quick size check |
+| pkg-size.dev | Web | Backup/alternative |
+| Import Cost (VS Code) | IDE extension | Real-time feedback |
+
+## bundlephobia.com
+
+### Usage
+
+Visit [bundlephobia.com](https://bundlephobia.com) and enter package name.
+
+### Shows
+
+- **Minified size**: Raw JS size
+- **Minified + Gzipped**: Network transfer size
+- **Download time**: Estimated on various connections
+- **Dependencies**: What else gets pulled in
+- **Composition**: Breakdown by dependency
+
+### Example Analysis
+
+```
+react-native-paper
+├── Minified: 312 kB
+├── Gzipped: 78 kB
+└── Dependencies: 12 packages
+ ├── @callstack/react-theme-provider
+ ├── color
+ └── ...
+```
+
+## pkg-size.dev
+
+Backup when bundlephobia fails.
+
+Visit [pkg-size.dev](https://pkg-size.dev) with package name.
+
+**Difference**: Actually installs package in web container, may be more accurate for edge cases.
+
+## Import Cost (VS Code Extension)
+
+### Install
+
+Search "Import Cost" in VS Code extensions or:
+
+```bash
+code --install-extension wix.vscode-import-cost
+```
+
+### Usage
+
+Shows inline size next to imports:
+
+```tsx
+import React from 'react'; // 6.5K (gzipped)
+import { View, Text } from 'react-native'; // 0B (native)
+import lodash from 'lodash'; // 71.5K (gzipped: 24.7K)
+import get from 'lodash/get'; // 8K (gzipped: 2.9K)
+```
+
+### Limitations
+
+- Uses Webpack internally (not Metro)
+- May fail on React Native-specific packages
+- Doesn't account for tree shaking
+
+## Comparison Workflow
+
+### Before Adding Dependency
+
+1. Check on bundlephobia:
+ ```
+ https://bundlephobia.com/package/[package-name]
+ ```
+
+2. Compare alternatives:
+ ```
+ moment (289 kB) vs date-fns (75 kB) vs dayjs (6 kB)
+ ```
+
+3. Check what you actually need:
+ - Full library import vs specific functions
+ - Native alternative available?
+
+### After Adding
+
+1. Analyze bundle (see [bundle-analyze-js.md](./bundle-analyze-js.md))
+2. Verify actual impact matches expected
+3. Check for duplicate dependencies
+
+## Size Guidelines
+
+| Size (gzipped) | Assessment | Action |
+|----------------|------------|--------|
+| < 5 KB | Small | Generally fine |
+| 5-20 KB | Medium | Evaluate necessity |
+| 20-50 KB | Large | Look for alternatives |
+| > 50 KB | Very large | Strong justification needed |
+
+## Common Large Dependencies
+
+| Library | Size (gzipped) | Alternative |
+|---------|----------------|-------------|
+| moment | ~70 KB | dayjs (~3 KB) |
+| lodash (full) | ~25 KB | lodash-es + direct imports |
+| aws-sdk (full) | 200+ KB | @aws-sdk/client-* |
+| crypto-js | ~15 KB | react-native-quick-crypto |
+
+## Quick Size Check Script
+
+```bash
+# Check size before installing
+npx bundle-phobia-cli
+
+# Or use npm directly (less accurate)
+npm pack --dry-run 2>&1 | grep "total files"
+```
+
+## Decision Matrix
+
+| Factor | Keep JS Library | Use Native Alternative |
+|--------|-----------------|------------------------|
+| Size | > 50 KB | < 50 KB |
+| Platform coverage | Both platforms | Single platform OK |
+| Performance | Not critical | Critical path |
+| Functionality | Simple | Complex computation |
+
+## Code Example: Optimizing Imports
+
+```tsx
+// BAD: Full library (71.5 KB)
+import _ from 'lodash';
+_.get(obj, 'path.to.value');
+
+// BETTER: Specific import (8 KB)
+import get from 'lodash/get';
+get(obj, 'path.to.value');
+
+// BEST: Native JS (0 KB)
+obj?.path?.to?.value;
+```
+
+## Related Skills
+
+- [bundle-analyze-js.md](./bundle-analyze-js.md) - Verify actual bundle impact
+- [bundle-barrel-exports.md](./bundle-barrel-exports.md) - Optimize how you import
+- [native-sdks-over-polyfills.md](./native-sdks-over-polyfills.md) - Native alternatives to JS libs
diff --git a/.opencode/skills/react-native-best-practices/references/bundle-native-assets.md b/.opencode/skills/react-native-best-practices/references/bundle-native-assets.md
new file mode 100644
index 0000000..2ca55d2
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/bundle-native-assets.md
@@ -0,0 +1,214 @@
+---
+title: Native Assets
+impact: HIGH
+tags: assets, images, asset-catalog, app-thinning
+---
+
+# Skill: Native Assets
+
+Configure platform-specific asset delivery to reduce app download size.
+
+## Quick Config
+
+**iOS Asset Catalog (Build Phase):**
+
+```bash
+# Add to "Bundle React Native code and images" build phase
+export EXTRA_PACKAGER_ARGS="--asset-catalog-dest ./"
+```
+
+**Android**: Automatic via AAB — Play Store delivers correct density per device.
+
+## When to Use
+
+- Images bloating app size
+- Different device densities need different assets
+- Want to leverage App Store/Play Store optimization
+- Using high-resolution images
+
+## Concept: Size Suffixes
+
+React Native convention for multiple resolutions:
+
+```
+assets/
+├── image.jpg # 1x resolution (base)
+├── image@2x.jpg # 2x resolution
+└── image@3x.jpg # 3x resolution
+```
+
+```tsx
+// React Native selects best one for device
+
+```
+
+## Android: Automatic Optimization
+
+Android handles this automatically.
+
+### How It Works
+
+1. Build AAB:
+ ```bash
+ cd android && ./gradlew bundleRelease
+ ```
+
+2. Metro places images in density folders:
+ ```
+ android/app/build/outputs/bundle/release/
+ └── base/
+ └── res/
+ ├── drawable-mdpi-v4/ # 1x
+ ├── drawable-hdpi-v4/ # 1.5x
+ ├── drawable-xhdpi-v4/ # 2x
+ ├── drawable-xxhdpi-v4/ # 3x
+ └── drawable-xxxhdpi-v4/ # 4x
+ ```
+
+3. Play Store delivers only needed density per device.
+
+**No configuration required** for Android.
+
+## iOS: Asset Catalog Setup
+
+iOS requires explicit configuration.
+
+### Step 1: Create Asset Catalog
+
+Create folder in `ios/`:
+
+```
+ios/RNAssets.xcassets/
+```
+
+**Important**: Must be named exactly `RNAssets.xcassets` (hardcoded in React Native).
+
+### Step 2: Configure Build Phase
+
+In Xcode:
+1. Open project settings
+2. Go to **Build Phases**
+3. Find **"Bundle React Native code and images"**
+4. Add before line 8:
+
+```bash
+export EXTRA_PACKAGER_ARGS="--asset-catalog-dest ./"
+```
+
+### Step 3: Build
+
+Run build to populate asset catalog:
+
+```bash
+npx react-native run-ios --mode Release
+```
+
+Or manually:
+
+```bash
+npx react-native bundle \
+ --entry-file index.js \
+ --bundle-output ios-bundle.js \
+ --platform ios \
+ --dev false \
+ --asset-catalog-dest ios \
+ --assets-dest ios/assets
+```
+
+### Step 4: Verify
+
+After build, `RNAssets.xcassets` contains:
+
+```
+ios/RNAssets.xcassets/
+└── assets_image_image.imageset/
+ ├── Contents.json
+ ├── image.jpg
+ ├── image@2x.jpg
+ └── image@3x.jpg
+```
+
+App Store then delivers only needed resolution.
+
+## Before/After Comparison
+
+### Without Asset Catalog (All Variants)
+
+```
+App bundle contains:
+├── image.jpg (100 KB)
+├── image@2x.jpg (300 KB)
+└── image@3x.jpg (600 KB)
+Total: 1 MB
+```
+
+### With Asset Catalog (Device-Specific)
+
+```
+iPhone 15 Pro receives:
+└── image@3x.jpg (600 KB)
+Total: 600 KB (40% smaller)
+```
+
+## Asset Optimization Tips
+
+### 1. Compress Images
+
+Use tools before adding to project:
+
+```bash
+# ImageOptim (macOS)
+# TinyPNG (web)
+# sharp (programmatic)
+
+npx sharp-cli input.jpg -o output.jpg --quality 80
+```
+
+### 2. Use Appropriate Formats
+
+| Format | Best For |
+|--------|----------|
+| JPEG | Photos |
+| PNG | Icons, transparency |
+| WebP | Both (smaller) |
+| SVG | Vector icons |
+
+### 3. Consider react-native-fast-image
+
+Caching and better image handling:
+
+```bash
+npm install react-native-fast-image
+```
+
+## Verification
+
+### iOS App Thinning Report
+
+After export, check `App Thinning Size Report.txt`:
+
+```
+Variant: MyApp-.ipa
+Supported variant descriptors: iPhone15,2 ...
+App size: 3.5 MB compressed, 10.6 MB uncompressed
+```
+
+### Use Emerge Tools
+
+Upload IPA to see asset breakdown.
+
+## Common Pitfalls
+
+- **Wrong folder name**: Must be `RNAssets.xcassets` exactly
+- **Missing build phase config**: Assets not processed
+- **Not using size suffixes**: All variants included anyway
+- **Forgetting to rebuild**: Changes need fresh build
+
+## Future Note
+
+As of January 2025, Asset Catalog is not default. May become default in future React Native versions.
+
+## Related Skills
+
+- [bundle-analyze-app.md](./bundle-analyze-app.md) - Verify asset impact
+- [bundle-r8-android.md](./bundle-r8-android.md) - Android code optimization
diff --git a/.opencode/skills/react-native-best-practices/references/bundle-r8-android.md b/.opencode/skills/react-native-best-practices/references/bundle-r8-android.md
new file mode 100644
index 0000000..cf07950
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/bundle-r8-android.md
@@ -0,0 +1,225 @@
+---
+title: R8 Code Shrinking
+impact: HIGH
+tags: android, r8, proguard, minify, shrink
+---
+
+# Skill: R8 Code Shrinking
+
+Enable R8 for Android to shrink, optimize, and obfuscate native code.
+
+## Quick Config
+
+```groovy
+// android/app/build.gradle
+def enableProguardInReleaseBuilds = true
+
+android {
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ }
+ }
+}
+```
+
+## When to Use
+
+- Android app size too large
+- Want to obfuscate code for security
+- Building release APK/AAB
+
+## What is R8?
+
+R8 replaces ProGuard in Android:
+- **Shrinks**: Removes unused code
+- **Optimizes**: Improves bytecode
+- **Obfuscates**: Renames classes/methods
+
+**Compatibility**: Uses ProGuard configuration format.
+
+## Step-by-Step Instructions
+
+### 1. Enable R8
+
+Edit `android/app/build.gradle`:
+
+```groovy
+def enableProguardInReleaseBuilds = true
+```
+
+This sets `minifyEnabled = true` for release builds.
+
+### 2. Enable Resource Shrinking (Optional)
+
+Further reduces size by removing unused resources:
+
+```groovy
+android {
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true // Requires minifyEnabled
+
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+```
+
+### 3. Configure ProGuard Rules (If Needed)
+
+Edit `android/app/proguard-rules.pro`. React Native defaults are usually sufficient—only add rules when specific libraries break after enabling R8.
+
+**Only add if using Firebase (`@react-native-firebase/*`):**
+
+```proguard
+-keep class io.invertase.firebase.** { *; }
+-dontwarn io.invertase.firebase.**
+```
+
+**Only add if using Retrofit:**
+
+```proguard
+-keepattributes Signature
+-keepattributes *Annotation*
+-keep class retrofit2.** { *; }
+-dontwarn retrofit2.**
+```
+
+See [Common Library Rules](#common-library-rules) and [Troubleshooting](#troubleshooting) for more examples.
+
+### 4. Build and Test
+
+```bash
+cd android
+./gradlew assembleRelease
+# or
+./gradlew bundleRelease
+```
+
+**Critical**: Test thoroughly! R8 can remove code it thinks is unused.
+
+## ProGuard Rules Reference
+
+| Rule | Effect |
+|------|--------|
+| `-keep class X` | Don't remove class X |
+| `-keepclassmembers` | Keep members but allow rename |
+| `-keepnames` | Keep names but allow removal if unused |
+| `-dontwarn X` | Suppress warnings for X |
+| `-dontobfuscate` | Disable obfuscation |
+
+### Keep Entire Package
+
+```proguard
+-keep class com.mypackage.** { *; }
+```
+
+### Keep Classes with Annotation
+
+```proguard
+-keep @interface com.facebook.proguard.annotations.DoNotStrip
+-keep @com.facebook.proguard.annotations.DoNotStrip class *
+-keepclassmembers class * {
+ @com.facebook.proguard.annotations.DoNotStrip *;
+}
+```
+
+## Disable Obfuscation (If Needed)
+
+```proguard
+# proguard-rules.pro
+-dontobfuscate
+```
+
+Use when:
+- Debugging crashes (stack traces more readable)
+- Library requires class names
+
+## Size Impact
+
+Example from guide:
+- **Without R8**: 9.5 MB
+- **With R8**: 6.3 MB
+- **Savings**: 33%
+
+Larger apps may see 20-30% reduction.
+
+## Troubleshooting
+
+### App Crashes After R8
+
+Usually means needed class was removed.
+
+**Debug steps**:
+
+1. Check crash log for class name
+2. Add keep rule:
+ ```proguard
+ -keep class com.example.CrashedClass { *; }
+ ```
+3. Rebuild and test
+
+### Library Specific Rules
+
+Many libraries provide ProGuard rules. Check:
+- Library README
+- Library's `consumer-proguard-rules.pro`
+- Stack Overflow for library + proguard
+
+### Common Library Rules
+
+```proguard
+# Hermes (usually auto-included)
+-keep class com.facebook.hermes.unicode.** { *; }
+
+# React Native
+-keep class com.facebook.react.** { *; }
+
+# Gson
+-keepattributes Signature
+-keep class com.google.gson.** { *; }
+
+# OkHttp
+-dontwarn okhttp3.**
+-dontwarn okio.**
+```
+
+## Verification
+
+### Check APK Size
+
+```bash
+# Build
+./gradlew assembleRelease
+
+# Check size
+ls -la android/app/build/outputs/apk/release/
+```
+
+### Use Ruler for Detailed Analysis
+
+See [bundle-analyze-app.md](./bundle-analyze-app.md).
+
+### Verify Obfuscation
+
+Decompile APK to check class names are obfuscated:
+
+```bash
+# Using jadx or similar
+jadx android/app/build/outputs/apk/release/app-release.apk
+```
+
+## Common Pitfalls
+
+- **Not testing release build**: Always QA with R8 enabled
+- **Missing library rules**: Check library docs
+- **Over-keeping**: Too many keep rules negates benefits
+- **Reflection**: Code using reflection may break
+
+## Related Skills
+
+- [bundle-analyze-app.md](./bundle-analyze-app.md) - Measure size impact
+- [bundle-native-assets.md](./bundle-native-assets.md) - Further size reduction
diff --git a/.opencode/skills/react-native-best-practices/references/bundle-tree-shaking.md b/.opencode/skills/react-native-best-practices/references/bundle-tree-shaking.md
new file mode 100644
index 0000000..a0b0600
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/bundle-tree-shaking.md
@@ -0,0 +1,214 @@
+---
+title: Tree Shaking
+impact: HIGH
+tags: bundle, tree-shaking, dead-code, metro, repack
+---
+
+# Skill: Tree Shaking
+
+Enable dead code elimination to remove unused exports from your JavaScript bundle.
+
+## Quick Config
+
+```bash
+# .env (Expo SDK 52+)
+EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH=1
+EXPO_UNSTABLE_TREE_SHAKING=1
+```
+
+```javascript
+// metro.config.js
+config.transformer.getTransformOptions = async () => ({
+ transform: { experimentalImportSupport: true },
+});
+```
+
+```javascript
+// babel.config.js (non-Expo projects must set `disableImportExportTransform`)
+module.exports = {
+ presets: [
+ [
+ 'module:@react-native/babel-preset',
+ { disableImportExportTransform: true },
+ ],
+ ],
+};
+```
+
+## When to Use
+
+- Bundle includes unused library code
+- Want automatic barrel export optimization
+- Using Expo SDK 52+ (recommended approach)
+- Project already uses Re.Pack (`@callstack/repack`)
+
+## Platform Support
+
+| Bundler | Tree Shaking | Notes |
+| -------------- | --------------- | ---------------------------- |
+| Metro | ❌ No | Use metro-serializer-esbuild |
+| Expo (SDK 52+) | ✅ Experimental | Requires config |
+| Re.Pack | ✅ Yes | Built-in via Webpack/Rspack |
+
+## Setup: Expo SDK 52+
+
+### 1. Enable Import Support
+
+```javascript
+// metro.config.js
+const { getDefaultConfig } = require('expo/metro-config');
+
+const config = getDefaultConfig(__dirname);
+
+config.transformer.getTransformOptions = async () => ({
+ transform: {
+ experimentalImportSupport: true,
+ },
+});
+
+module.exports = config;
+```
+
+### 2. Enable Tree Shaking
+
+Create/edit `.env`:
+
+```bash
+EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH=1
+EXPO_UNSTABLE_TREE_SHAKING=1
+```
+
+**Note**: Only applies in production builds.
+
+## Setup: Metro with ESBuild
+
+```bash
+npm install @rnx-kit/metro-serializer-esbuild
+```
+
+Configure in metro.config.js.
+
+## Setup: Re.Pack (Only If Already Installed)
+
+> **Important**: Re.Pack (`@callstack/repack`) is a custom bundler using Rspack/Webpack. Only apply these instructions if the project already has Re.Pack configured. Do not suggest migrating a codebase to Re.Pack—it's rarely necessary and requires significant setup.
+
+**If project has `@callstack/repack` in dependencies:**
+
+Tree shaking is enabled by default with Rspack. Verify in config:
+
+```javascript
+// rspack.config.js or webpack.config.js
+module.exports = {
+ optimization: {
+ usedExports: true, // Mark unused exports
+ minimize: true, // Remove during minification
+ },
+};
+```
+
+## Platform Shaking
+
+Code inside `Platform.OS` and `Platform.select` checks is removed for other platforms:
+
+```tsx
+// IMPORTANT: import Platform directly from 'react-native'
+import { Platform } from 'react-native';
+
+if (Platform.OS === 'ios') {
+ // Removed from Android bundle
+}
+
+if (Platform.select({ ios: true, android: false }) === 'ios') {
+ // Removed from Android bundle
+}
+```
+
+**Critical**: Must use direct import. This does NOT work:
+
+```tsx
+import * as RN from 'react-native';
+if (RN.Platform.OS === 'ios') {
+ // NOT removed - optimization fails
+}
+```
+
+For non-Expo projects, requires both `experimentalImportSupport: true` in Metro config and `disableImportExportTransform: true` in Babel config.
+
+Impact: Savings from enabling platform shaking on a bare React Native Community CLI project are:
+- 5% smaller Hermes bytecode (2.79 MB → 2.64 MB)
+- 15% smaller minified JS bundle (1 MB → 0.85 MB)
+
+## Requirements for Tree Shaking
+
+### ESM Imports Required
+
+```tsx
+// ✅ ESM - Tree shakeable
+import { foo } from './module';
+
+// ❌ CommonJS - Not tree shakeable
+const { foo } = require('./module');
+```
+
+### Side Effects Declaration
+
+Libraries must declare side-effect-free in `package.json`:
+
+```json
+{
+ "sideEffects": false
+}
+```
+
+Or specify files with side effects:
+
+```json
+{
+ "sideEffects": ["*.css", "./src/polyfills.js"]
+}
+```
+
+## Size Impact
+
+| Bundle Type | Metro (MB) | Re.Pack (MB) | Change |
+| ----------------- | ---------- | ------------ | -------- |
+| Production | 35.63 | 38.48 | +8% |
+| Prod Minified | 15.54 | 13.36 | **-14%** |
+| Prod HBC | 21.79 | 19.35 | **-11%** |
+| Prod Minified HBC | 21.62 | 19.05 | **-12%** |
+
+**Expected improvement**: 10-15% bundle size reduction.
+
+## Verification
+
+1. Build production bundle (see [bundle-analyze-js.md](./bundle-analyze-js.md))
+2. Analyze with source-map-explorer (see [bundle-analyze-js.md](./bundle-analyze-js.md))
+3. Search for functions you know are unused
+4. If found → tree shaking not working
+
+### Test Example
+
+```tsx
+// test-treeshake.js
+export const usedFunction = () => 'used';
+export const unusedFunction = () => 'unused'; // Should be removed
+
+// app.js
+import { usedFunction } from './test-treeshake';
+```
+
+After building, search bundle for `unusedFunction`. Should not exist.
+
+## Common Pitfalls
+
+- **Not using production build**: Tree shaking only in prod
+- **CommonJS modules**: Need ESM for full effectiveness
+- **Side effects not declared**: Library may not be shakeable
+- **Dynamic imports**: `require(variable)` prevents analysis
+- **Babel/Metro config mismatch**: `disableImportExportTransform` must match `experimentalImportSupport`
+
+## Related Skills
+
+- [bundle-analyze-js.md](./bundle-analyze-js.md) - Verify tree shaking effect
+- [bundle-barrel-exports.md](./bundle-barrel-exports.md) - Manual alternative
+- [bundle-code-splitting.md](./bundle-code-splitting.md) - Re.Pack code splitting
diff --git a/.opencode/skills/react-native-best-practices/references/images/bundle-treemap-source-map-explorer.png b/.opencode/skills/react-native-best-practices/references/images/bundle-treemap-source-map-explorer.png
new file mode 100644
index 0000000..b8c93e1
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/bundle-treemap-source-map-explorer.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/controlled-textinput-pingpong.png b/.opencode/skills/react-native-best-practices/references/images/controlled-textinput-pingpong.png
new file mode 100644
index 0000000..0de7141
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/controlled-textinput-pingpong.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/devtools-flamegraph.png b/.opencode/skills/react-native-best-practices/references/images/devtools-flamegraph.png
new file mode 100644
index 0000000..10b5afe
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/devtools-flamegraph.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/emerge-xray-ios.png b/.opencode/skills/react-native-best-practices/references/images/emerge-xray-ios.png
new file mode 100644
index 0000000..3f737a0
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/emerge-xray-ios.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/expo-atlas-treemap.png b/.opencode/skills/react-native-best-practices/references/images/expo-atlas-treemap.png
new file mode 100644
index 0000000..bfa214e
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/expo-atlas-treemap.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/flashlight-flatlist-vs-flashlist.png b/.opencode/skills/react-native-best-practices/references/images/flashlight-flatlist-vs-flashlist.png
new file mode 100644
index 0000000..2ef15f6
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/flashlight-flatlist-vs-flashlist.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/fps-drop-graph.png b/.opencode/skills/react-native-best-practices/references/images/fps-drop-graph.png
new file mode 100644
index 0000000..64843da
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/fps-drop-graph.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/memory-heap-snapshot.png b/.opencode/skills/react-native-best-practices/references/images/memory-heap-snapshot.png
new file mode 100644
index 0000000..5c8a5ce
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/memory-heap-snapshot.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/tti-warm-start-diagram.png b/.opencode/skills/react-native-best-practices/references/images/tti-warm-start-diagram.png
new file mode 100644
index 0000000..dcb5256
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/tti-warm-start-diagram.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/view-hierarchy-flattening.png b/.opencode/skills/react-native-best-practices/references/images/view-hierarchy-flattening.png
new file mode 100644
index 0000000..d3dbc26
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/view-hierarchy-flattening.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/xcode-instruments-templates.png b/.opencode/skills/react-native-best-practices/references/images/xcode-instruments-templates.png
new file mode 100644
index 0000000..00a7dcd
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/xcode-instruments-templates.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/images/xcode-thread-view.png b/.opencode/skills/react-native-best-practices/references/images/xcode-thread-view.png
new file mode 100644
index 0000000..ce2c180
Binary files /dev/null and b/.opencode/skills/react-native-best-practices/references/images/xcode-thread-view.png differ
diff --git a/.opencode/skills/react-native-best-practices/references/js-animations-reanimated.md b/.opencode/skills/react-native-best-practices/references/js-animations-reanimated.md
new file mode 100644
index 0000000..f116790
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-animations-reanimated.md
@@ -0,0 +1,255 @@
+---
+title: High-Performance Animations
+impact: MEDIUM
+tags: reanimated, animations, worklets, ui-thread
+---
+
+# Skill: High-Performance Animations
+
+Use React Native Reanimated for smooth 60+ FPS animations.
+
+## Quick Pattern
+
+**Incorrect (JS thread - blocks on heavy work):**
+
+```jsx
+const opacity = useRef(new Animated.Value(0)).current;
+Animated.timing(opacity, { toValue: 1 }).start();
+```
+
+**Correct (UI thread - smooth even during JS work):**
+
+```jsx
+const opacity = useSharedValue(0);
+const style = useAnimatedStyle(() => ({ opacity: opacity.value }));
+opacity.value = withTiming(1);
+```
+
+## When to Use
+
+- Animations drop frames or feel janky
+- UI freezes during animations
+- Need gesture-driven animations
+- Want animations to run during heavy JS work
+
+## Prerequisites
+
+- `react-native-reanimated` (v4+) and `react-native-worklets` installed
+
+```bash
+npm install react-native-reanimated react-native-worklets
+```
+
+Add to `babel.config.js`:
+
+```javascript
+module.exports = {
+ plugins: ['react-native-worklets/plugin'], // Must be last
+};
+```
+
+> **Note**: Reanimated 4 requires React Native's **New Architecture** (Fabric + TurboModules). The Legacy Architecture is no longer supported. If upgrading from v3, see the migration notes at the end of this document.
+
+## Key Concepts
+
+### Main Thread vs JS Thread
+
+- **Main/UI Thread**: Handles native rendering (60+ FPS target)
+- **JS Thread**: Runs React and your JavaScript
+
+**Problem**: Heavy JS work blocks animations running on JS thread.
+
+**Solution**: Run animations on UI thread with Reanimated worklets.
+
+## Step-by-Step Instructions
+
+### 1. Basic Animated Style (UI Thread)
+
+```jsx
+import Animated, {
+ useSharedValue,
+ useAnimatedStyle,
+ withTiming
+} from 'react-native-reanimated';
+
+const FadeInView = () => {
+ const opacity = useSharedValue(0);
+
+ // This runs on UI thread - won't be blocked by JS
+ const animatedStyle = useAnimatedStyle(() => {
+ return { opacity: opacity.value };
+ });
+
+ useEffect(() => {
+ opacity.value = withTiming(1, { duration: 500 });
+ }, []);
+
+ return ;
+};
+```
+
+### 2. Run Code on UI Thread with `scheduleOnUI`
+
+```jsx
+import { scheduleOnUI } from 'react-native-worklets';
+
+const triggerAnimation = () => {
+ scheduleOnUI(() => {
+ 'worklet';
+ console.log('Running on UI thread');
+ // Direct UI manipulations here
+ });
+};
+```
+
+### 3. Call JS from UI Thread with `scheduleOnRN`
+
+```jsx
+import { scheduleOnRN } from 'react-native-worklets';
+
+// Regular JS function
+const trackAnalytics = (value) => {
+ analytics.track('animation_complete', { value });
+};
+
+const AnimatedComponent = () => {
+ const progress = useSharedValue(0);
+
+ const animatedStyle = useAnimatedStyle(() => {
+ // When animation completes, call JS function
+ if (progress.value === 1) {
+ scheduleOnRN(trackAnalytics, progress.value);
+ }
+ return { opacity: progress.value };
+ });
+
+ return ;
+};
+```
+
+### 4. Animation with Callback
+
+```jsx
+import { scheduleOnRN } from 'react-native-worklets';
+
+const AnimatedButton = () => {
+ const scale = useSharedValue(1);
+
+ const onComplete = () => {
+ console.log('Animation finished!');
+ };
+
+ const handlePress = () => {
+ scale.value = withTiming(
+ 1.2,
+ { duration: 200 },
+ (finished) => {
+ if (finished) {
+ scheduleOnRN(onComplete);
+ }
+ }
+ );
+ };
+
+ const animatedStyle = useAnimatedStyle(() => ({
+ transform: [{ scale: scale.value }],
+ }));
+
+ return (
+
+
+ Press Me
+
+
+ );
+};
+```
+
+## When to Use What
+
+| Thread | Best For |
+|--------|----------|
+| **UI Thread** (worklets) | Visual animations, transforms, gestures |
+| **JS Thread** | State updates, data processing, API calls |
+
+| Hook/API | Use Case |
+|----------|----------|
+| `useAnimatedStyle` | Animated styles (auto UI thread) |
+| `scheduleOnUI` | Manual UI thread execution (from `react-native-worklets`) |
+| `scheduleOnRN` | Call JS functions from worklets (from `react-native-worklets`) |
+| `useTransition` | Alternative for React state-driven delays |
+
+## Common Pitfalls
+
+- **Accessing React state in worklets**: Use `useSharedValue` instead of `useState` for animated values
+- **Not using Animated components**: Must use `Animated.View`, `Animated.Text`, etc.
+- **Heavy computation in useAnimatedStyle**: Keep worklets fast
+- **Forgetting 'worklet' directive**: Required for inline worklet functions
+
+```jsx
+// BAD: Regular function in useAnimatedStyle
+const style = useAnimatedStyle(() => {
+ heavyComputation(); // Blocks UI thread!
+ return { opacity: 1 };
+});
+
+// GOOD: Keep worklets fast
+const style = useAnimatedStyle(() => {
+ return { opacity: opacity.value }; // Just read value
+});
+```
+
+## Migrating from Reanimated 3.x to 4.x
+
+If you're upgrading from Reanimated 3.x, here are the key changes.
+
+> **Can't upgrade to v4?** If your project is blocked from migrating to New Architecture (e.g., incompatible native libraries, complex native code, or timeline constraints), keep using existing APIs and leverage native drivers where applicable. Avoid introducing legacy Reanimated 3.x or older to reduce future migration complexity.
+
+### Breaking Changes
+
+| Old API (v3) | New API (v4) | Package |
+|--------------|--------------|---------|
+| `runOnUI(() => {...})()` | `scheduleOnUI(() => {...})` | `react-native-worklets` |
+| `runOnJS(fn)(args)` | `scheduleOnRN(fn, args)` | `react-native-worklets` |
+| `executeOnUIRuntimeSync` | `runOnUISync` | `react-native-worklets` |
+| `runOnRuntime` | `scheduleOnRuntime` | `react-native-worklets` |
+| `useScrollViewOffset` | `useScrollOffset` | `react-native-reanimated` |
+| `useWorkletCallback` | Use `useCallback` with `'worklet';` directive | React |
+
+### Removed APIs
+
+- `useAnimatedGestureHandler` - Migrate to the Gesture API from `react-native-gesture-handler` v2+
+- `addWhitelistedNativeProps` / `addWhitelistedUIProps` - No longer needed
+- `combineTransition` - Use `EntryExitTransition.entering(...).exiting(...)` instead
+
+### withSpring Changes
+
+```jsx
+// Before (v3)
+withSpring(value, {
+ restDisplacementThreshold: 0.01,
+ restSpeedThreshold: 0.01,
+ duration: 300,
+});
+
+// After (v4)
+withSpring(value, {
+ energyThreshold: 0.01, // Replaces both threshold parameters
+ duration: 200, // Duration is now "perceptual" (~1.5x actual time)
+});
+```
+
+### Migration Checklist
+
+1. **Enable New Architecture** - Reanimated 4 only supports Fabric + TurboModules
+2. **Install `react-native-worklets`** - Required new dependency
+3. **Update Babel plugin** - Change `'react-native-reanimated/plugin'` to `'react-native-worklets/plugin'`
+4. **Update imports** - Move worklet functions to `react-native-worklets`
+5. **Update API calls** - New functions take callback + args directly (not curried)
+6. **Rebuild native apps** - Required after adding `react-native-worklets`
+
+## Related Skills
+
+- [js-measure-fps.md](./js-measure-fps.md) - Verify animation frame rate
+- [js-bottomsheet.md](./js-bottomsheet.md) - Keep bottom sheet visual state on the UI thread
+- [js-concurrent-react.md](./js-concurrent-react.md) - React-level deferral with useTransition
diff --git a/.opencode/skills/react-native-best-practices/references/js-atomic-state.md b/.opencode/skills/react-native-best-practices/references/js-atomic-state.md
new file mode 100644
index 0000000..f243c34
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-atomic-state.md
@@ -0,0 +1,246 @@
+---
+title: Atomic State Management
+impact: HIGH
+tags: state, jotai, zustand, re-renders, context
+---
+
+# Skill: Atomic State Management
+
+Use atomic state libraries (Jotai, Zustand) to reduce unnecessary re-renders without manual memoization.
+
+## Quick Pattern
+
+**Before (Context - all consumers re-render):**
+
+```jsx
+const { filter, todos } = useContext(TodoContext);
+// Re-renders when ANY state changes
+```
+
+**After (Zustand - only subscribed state):**
+
+```jsx
+const filter = useTodoStore((s) => s.filter);
+// Only re-renders when filter changes
+```
+
+## When to Use
+
+- Global state changes cause widespread re-renders
+- Using React Context for app state
+- Components re-render even when their data hasn't changed
+- Want to avoid manual `useMemo`/`useCallback` everywhere
+- Not ready to adopt React Compiler
+
+## Prerequisites
+
+- State management library: `jotai` or `zustand`
+
+```bash
+npm install jotai
+# or
+npm install zustand
+```
+
+## Problem Description
+
+With traditional React state or Context:
+
+```jsx
+// When filter OR todos change, EVERYTHING re-renders
+const App = () => {
+ const [filter, setFilter] = useState('all');
+ const [todos, setTodos] = useState([]);
+
+ return (
+ <>
+
+
+ >
+ );
+};
+```
+
+Changing a todo re-renders FilterMenu even though it doesn't use todos.
+
+## Step-by-Step Instructions
+
+### Using Jotai
+
+#### 1. Define Atoms
+
+```jsx
+import { atom } from 'jotai';
+
+// Each atom is an independent piece of state
+const filterAtom = atom('all');
+const todosAtom = atom([]);
+
+// Derived atom (computed value)
+const filteredTodosAtom = atom((get) => {
+ const filter = get(filterAtom);
+ const todos = get(todosAtom);
+
+ if (filter === 'active') return todos.filter(t => !t.completed);
+ if (filter === 'completed') return todos.filter(t => t.completed);
+ return todos;
+});
+```
+
+#### 2. Use Atoms in Components
+
+```jsx
+import { useAtom, useAtomValue, useSetAtom } from 'jotai';
+
+// Only re-renders when filterAtom changes
+const FilterMenu = () => {
+ const [filter, setFilter] = useAtom(filterAtom);
+
+ return (
+
+ {['all', 'active', 'completed'].map((f) => (
+ setFilter(f)}>
+ {f}
+
+ ))}
+
+ );
+};
+
+// Only re-renders when todosAtom changes
+const TodoItem = ({ id }) => {
+ const setTodos = useSetAtom(todosAtom); // Only setter, no re-render on read
+
+ const toggleTodo = () => {
+ setTodos((prev) =>
+ prev.map((t) => t.id === id ? { ...t, completed: !t.completed } : t)
+ );
+ };
+
+ return ...;
+};
+```
+
+### Using Zustand
+
+#### 1. Create Store
+
+```jsx
+import { create } from 'zustand';
+
+const useTodoStore = create((set, get) => ({
+ filter: 'all',
+ todos: [],
+
+ setFilter: (filter) => set({ filter }),
+
+ toggleTodo: (id) => set((state) => ({
+ todos: state.todos.map((t) =>
+ t.id === id ? { ...t, completed: !t.completed } : t
+ ),
+ })),
+
+ // Selector for derived state
+ getFilteredTodos: () => {
+ const { filter, todos } = get();
+ if (filter === 'active') return todos.filter(t => !t.completed);
+ if (filter === 'completed') return todos.filter(t => t.completed);
+ return todos;
+ },
+}));
+```
+
+#### 2. Use Selectors
+
+```jsx
+// Only re-renders when filter changes
+const FilterMenu = () => {
+ const filter = useTodoStore((state) => state.filter);
+ const setFilter = useTodoStore((state) => state.setFilter);
+
+ return (
+
+ {['all', 'active', 'completed'].map((f) => (
+ setFilter(f)}>
+ {f}
+
+ ))}
+
+ );
+};
+
+// Only re-renders when todos change
+const TodoList = () => {
+ const todos = useTodoStore((state) => state.todos);
+ return todos.map((todo) => );
+};
+```
+
+## Code Examples
+
+### Before: Context-Based (Many Re-renders)
+
+```jsx
+const TodoContext = createContext();
+
+const TodoProvider = ({ children }) => {
+ const [state, setState] = useState({ filter: 'all', todos: [] });
+ return (
+
+ {children}
+
+ );
+};
+
+// Every component using this context re-renders on ANY state change
+const FilterMenu = () => {
+ const { state, setState } = useContext(TodoContext);
+ // Re-renders when todos change too!
+};
+```
+
+### After: Atomic (Targeted Re-renders)
+
+```jsx
+// Jotai version - only affected components re-render
+const filterAtom = atom('all');
+const todosAtom = atom([]);
+
+const FilterMenu = () => {
+ const [filter, setFilter] = useAtom(filterAtom);
+ // Only re-renders when filter changes
+};
+
+const TodoList = () => {
+ const todos = useAtomValue(todosAtom);
+ // Only re-renders when todos change
+};
+```
+
+## Comparison
+
+| Feature | Context | Jotai | Zustand |
+|---------|---------|-------|---------|
+| Re-render scope | All consumers | Atom subscribers | Selector subscribers |
+| Derived state | Manual | Built-in atoms | Selectors |
+| DevTools | React DevTools | Jotai DevTools | Zustand DevTools |
+| Bundle size | 0 KB | ~3 KB | ~2 KB |
+| Learning curve | Low | Medium | Low |
+
+## When to Use Which
+
+- **Jotai**: Fine-grained state, many small atoms, derived/async atoms
+- **Zustand**: Simpler mental model, single store, familiar Redux-like pattern
+- **React Compiler**: If available, may eliminate need for these libraries
+
+## Common Pitfalls
+
+- **Over-atomizing**: Don't create an atom for every variable. Group related state.
+- **Missing selectors in Zustand**: Always use selectors to prevent unnecessary re-renders.
+- **Derived state without memoization**: Use derived atoms (Jotai) or memoized selectors.
+
+## Related Skills
+
+- [js-bottomsheet.md](./js-bottomsheet.md) - Avoid context-driven bottom sheet subtree re-renders
+- [js-react-compiler.md](./js-react-compiler.md) - Automatic memoization alternative
+- [js-profile-react.md](./js-profile-react.md) - Verify re-render reduction
diff --git a/.opencode/skills/react-native-best-practices/references/js-bottomsheet.md b/.opencode/skills/react-native-best-practices/references/js-bottomsheet.md
new file mode 100644
index 0000000..05e7587
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-bottomsheet.md
@@ -0,0 +1,325 @@
+---
+title: Bottom Sheet
+impact: HIGH
+tags: bottom-sheet, gorhom, re-renders, shared-values, gestures, context, scrollable, modal, keyboard
+---
+
+# Skill: Bottom Sheet Best Practices
+
+Optimize `@gorhom/bottom-sheet` for smooth 60 FPS by keeping gesture/scroll-driven state on the UI thread.
+
+## Quick Pattern
+
+**Incorrect (can re-enter JS repeatedly during interaction — full subtree re-render):**
+
+```jsx
+const handleAnimate = useCallback((fromIndex, toIndex) => {
+ setIsExpanded(toIndex > 0); // re-renders entire tree
+}, []);
+
+
+
+
+```
+
+**Correct (stays on UI thread — zero re-renders):**
+
+```jsx
+const animatedIndex = useSharedValue(0);
+
+const overlayStyle = useAnimatedStyle(() => ({
+ opacity: withTiming(animatedIndex.value > 0 ? 0.5 : 0),
+}));
+
+
+
+
+
+```
+
+## When to Use
+
+- Implementing or optimizing a bottom sheet with `@gorhom/bottom-sheet`
+- Bottom sheet gestures cause jank or dropped frames
+- Scroll inside bottom sheet triggers excessive re-renders
+- Context provider wrapping bottom sheet re-renders the entire subtree
+- Visual-only state (shadow, opacity, footer visibility) managed with `useState`
+- Need to choose between `BottomSheet` and `BottomSheetModal`
+- Scrollable content inside bottom sheet doesn't coordinate with gestures
+- Keyboard doesn't interact properly with the sheet
+
+## Prerequisites
+
+- Check the official [`@gorhom/bottom-sheet` versioning / compatibility table](https://github.com/gorhom/react-native-bottom-sheet#versioning) first.
+- If your app is on `@gorhom/bottom-sheet` below v5, upgrade to v5 before applying the patterns in this skill.
+- `@gorhom/bottom-sheet` v5 is the current maintained line and is built for `react-native-reanimated` v3.
+- `react-native-reanimated` v4 may work in some apps, but the bottom-sheet docs do not officially guarantee it. Decide explicitly whether to stay on v3 or try v4 and validate thoroughly on device.
+- `react-native-gesture-handler` v2+
+
+```bash
+npm install @gorhom/bottom-sheet@^5 react-native-reanimated@^3 react-native-gesture-handler
+```
+
+> **Note**: In v5, `enableDynamicSizing` defaults to `true`. If you need fixed snap-point indexing or do not want the library to insert a dynamic snap point based on content height, set `enableDynamicSizing={false}` explicitly.
+
+## Problem Description
+
+Bottom-sheet gesture, animation, and scroll callbacks that update React state can re-render the sheet subtree during interaction. In practice, callbacks like `onAnimate` may run repeatedly as the sheet retargets animations, which can cause visible jank if they drive expensive React updates.
+
+## Step-by-Step Instructions
+
+### 1. Convert Gesture-Driven State to SharedValue
+
+Avoid React state for gesture-driven visual state. Update a shared value and consume it via `useAnimatedStyle`.
+
+**Before:**
+
+```jsx
+const [shadowOpacity, setShadowOpacity] = useState(0);
+
+const handleAnimate = useCallback((fromIndex, toIndex) => {
+ setShadowOpacity(toIndex > 0 ? 0.3 : 0);
+}, []);
+
+
+
+
+
+
+```
+
+**After:**
+
+```jsx
+const animatedIndex = useSharedValue(0);
+
+const shadowStyle = useAnimatedStyle(() => ({
+ shadowOpacity: withTiming(animatedIndex.value > 0 ? 0.3 : 0),
+}));
+
+
+
+
+
+
+```
+
+### 2. Drive Sheet-Index Visibility via `useAnimatedReaction`
+
+Toggling content based on sheet index via `{showFooter && }` causes mount/unmount cycles on every snap. Instead, always mount, animate visibility from `animatedIndex`, and bridge only the minimal boolean needed for `pointerEvents`/accessibility — scoped to a wrapper so the full tree doesn't re-render.
+
+**Before:**
+
+```jsx
+const [showFooter, setShowFooter] = useState(false);
+
+// re-mounts footer on every toggle
+{showFooter && }
+```
+
+**After:**
+
+```jsx
+const SheetVisibilityWrapper = ({ animatedIndex, threshold = 1, children }) => {
+ const [isInteractive, setIsInteractive] = useState(false);
+
+ const style = useAnimatedStyle(() => ({
+ opacity: withTiming(animatedIndex.value >= threshold ? 1 : 0),
+ transform: [{ translateY: withTiming(animatedIndex.value >= threshold ? 0 : 50) }],
+ }));
+
+ useAnimatedReaction(
+ () => animatedIndex.value >= threshold,
+ (visible, prev) => {
+ if (visible !== prev) runOnJS(setIsInteractive)(visible);
+ }
+ );
+
+ return (
+
+ {children}
+
+ );
+};
+
+// Usage:
+
+
+
+```
+
+### 3. Keep Scroll-Driven Logic off the JS Thread
+
+`BottomSheetScrollView` ignores `scrollEventThrottle`, so setting it is not an optimization. Keep JS `onScroll` work minimal, or move scroll-driven logic to `useAnimatedScrollHandler` (see [js-animations-reanimated.md](./js-animations-reanimated.md)) so it stays on the UI thread:
+
+```jsx
+const scrollHandler = useAnimatedScrollHandler((event) => {
+ scrollY.value = event.contentOffset.y;
+});
+
+
+
+
+```
+
+### 4. Use Library-Provided Components and Props
+
+**Scrollables** — always use these instead of React Native built-ins inside a bottom sheet:
+
+```jsx
+import {
+ BottomSheetScrollView,
+ BottomSheetFlatList,
+ BottomSheetSectionList,
+} from '@gorhom/bottom-sheet';
+
+// FlashList v2: BottomSheetFlashList is deprecated.
+// Create the scroll component, then pass it to FlashList.
+import { useBottomSheetScrollableCreator } from '@gorhom/bottom-sheet';
+import { FlashList } from '@shopify/flash-list';
+
+const BottomSheetFlashListScrollComponent = useBottomSheetScrollableCreator();
+
+
+ item.id}
+ renderItem={renderItem}
+ renderScrollComponent={BottomSheetFlashListScrollComponent}
+ />
+
+```
+
+**Key props:**
+
+| Prop | Purpose |
+|------|---------|
+| `containerHeight` | Provide to skip extra measurement re-render on mount |
+| `enableDynamicSizing={false}` | Use when you want fixed snap-point indexing and do not want a dynamic content-height snap point inserted |
+| `animatedIndex` | SharedValue for continuous index tracking on UI thread |
+| `animatedPosition` | SharedValue for continuous position tracking on UI thread |
+| `onChange` | Fires on snap **completion** only (discrete) — use for analytics/side effects |
+| `onAnimate` | Fires before each animation start/retarget — use sparingly, because it can run repeatedly during interaction |
+
+### 5. BottomSheetModal Setup
+
+```jsx
+import {
+ BottomSheetModal,
+ BottomSheetModalProvider,
+} from '@gorhom/bottom-sheet';
+
+const App = () => (
+
+
+
+
+
+);
+```
+
+**iOS layering fix** — use `FullWindowOverlay` to render above native navigation:
+
+```jsx
+import { FullWindowOverlay } from 'react-native-screens';
+
+ {props.children}}
+>
+```
+
+### 6. Keyboard Handling
+
+```jsx
+
+
+
+```
+
+| `keyboardBehavior` | Effect |
+|--------------------|--------|
+| `extend` | Sheet grows to accommodate keyboard |
+| `fillParent` | Sheet fills parent when keyboard appears |
+| `interactive` | Sheet follows keyboard position interactively |
+
+> Prefer `BottomSheetTextInput` inside a bottom sheet. If you need a custom input, copy the focus/blur handlers from the library's `BottomSheetTextInput` implementation so keyboard handling still works correctly.
+
+## Derived Animations with `animatedPosition`
+
+Use the `animatedPosition` shared value for smooth derived UI that stays on the UI thread:
+
+```jsx
+const animatedPosition = useSharedValue(0);
+
+const backdropStyle = useAnimatedStyle(() => ({
+ opacity: interpolate(
+ animatedPosition.value,
+ [0, 300],
+ [0.5, 0],
+ Extrapolation.CLAMP
+ ),
+}));
+
+
+
+
+
+```
+
+## Native Alternative: react-native-true-sheet
+
+If your app already runs on **New Architecture (Fabric)**, consider `@lodev09/react-native-true-sheet` — a fully native bottom sheet that sidesteps JS re-render problems entirely.
+
+| Scenario | Recommendation |
+|----------|---------------|
+| Need deep JS customization (custom gestures, animated derived UI) | `@gorhom/bottom-sheet` |
+| Standard sheet with native feel + accessibility | `react-native-true-sheet` |
+| Legacy Architecture (no Fabric) | `@gorhom/bottom-sheet` (true-sheet v3+ requires Fabric) |
+| Web support needed | Either (true-sheet uses `@gorhom/bottom-sheet` on web internally) |
+
+**Advantages**: zero JS overhead (sheet lives in native land — no SharedValue plumbing needed), built-in keyboard handling, native screen reader support, side sheet on tablets, iOS 26+ Liquid Glass support, React Navigation sheet navigator integration.
+
+**Requirements**: New Architecture (Fabric) for v3+, use v2.x for Legacy Architecture.
+
+```bash
+npm install @lodev09/react-native-true-sheet
+```
+
+> If requirements are met and you don't need the fine-grained Reanimated-driven customization described in this skill, `react-native-true-sheet` is the simpler and more performant choice.
+
+## Common Pitfalls
+
+- **Using `onChange` for continuous position tracking** — it fires on snap completion only (discrete). Use `animatedPosition` or `animatedIndex` shared values instead.
+- **Forgetting `pointerEvents='none'` on always-mounted hidden elements** — invisible elements still capture touches.
+- **Missing accessibility attributes on hidden elements** — add `accessibilityElementsHidden` and `importantForAccessibility='no-hide-descendants'`.
+- **Bundling independent state values in one context** — see [js-atomic-state.md](./js-atomic-state.md) for splitting patterns.
+- **Assuming `enableDynamicSizing` must be disabled whenever you pass `snapPoints`** — it does not have to be, but leaving it enabled can insert an additional snap point and change indexing.
+- **Using React Native `ScrollView`/`FlatList` inside bottom sheet** — gestures won't coordinate. Use `BottomSheetScrollView`, `BottomSheetFlatList`, etc.
+- **Using React Native touchables on Android** — import `TouchableOpacity`, `TouchableHighlight`, or `TouchableWithoutFeedback` from `@gorhom/bottom-sheet`.
+- **Not providing `containerHeight`** — causes an extra re-render on mount for measurement.
+- **Using a custom `TextInput` without porting the library's focus/blur handlers** — keyboard handling will be incomplete. Prefer `BottomSheetTextInput` unless you need a custom input.
+
+## Related Skills
+
+- [js-animations-reanimated.md](./js-animations-reanimated.md) — SharedValue and useAnimatedStyle fundamentals
+- [js-atomic-state.md](./js-atomic-state.md) — Context splitting and atomic state patterns
+- [js-profile-react.md](./js-profile-react.md) — Profiling to measure re-render reduction
+- [js-measure-fps.md](./js-measure-fps.md) — Verify FPS improvement after optimization
diff --git a/.opencode/skills/react-native-best-practices/references/js-concurrent-react.md b/.opencode/skills/react-native-best-practices/references/js-concurrent-react.md
new file mode 100644
index 0000000..87b7dc1
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-concurrent-react.md
@@ -0,0 +1,236 @@
+---
+title: Concurrent React
+impact: HIGH
+tags: useDeferredValue, useTransition, suspense, concurrent
+---
+
+# Skill: Concurrent React
+
+Use `useDeferredValue` and `useTransition` to improve perceived performance by prioritizing critical updates.
+
+## Quick Pattern
+
+**Incorrect (blocks input on every keystroke):**
+
+```jsx
+const [query, setQuery] = useState('');
+
+ // Blocks typing
+```
+
+**Correct (input stays responsive):**
+
+```jsx
+const [query, setQuery] = useState('');
+const deferredQuery = useDeferredValue(query);
+
+ // Deferred update
+```
+
+## When to Use
+
+- Search/filter inputs feel laggy with large result sets
+- Expensive computations block UI interactions
+- Loading states appear too frequently
+- Want to show stale content while loading new content
+- Need to prioritize user input over background updates
+
+## Prerequisites
+
+- React Native with New Architecture enabled (default in RN 0.76+)
+- React 18+ features (`useDeferredValue`, `useTransition`, `Suspense`)
+
+## Concept Overview
+
+**Concurrent React** allows updates to be:
+- **Paused**: Low-priority work can wait
+- **Interrupted**: User input takes priority
+- **Abandoned**: Outdated updates can be skipped
+
+## Step-by-Step Instructions
+
+### Pattern 1: Defer Expensive Rendering with `useDeferredValue`
+
+Use when a value drives expensive computation but you want input to stay responsive.
+
+```jsx
+import { useState, useDeferredValue } from 'react';
+
+const SearchScreen = () => {
+ const [query, setQuery] = useState('');
+ const deferredQuery = useDeferredValue(query);
+
+ // query updates immediately (input stays responsive)
+ // deferredQuery updates when React has time
+
+ return (
+
+
+ {/* ExpensiveList receives deferred value */}
+
+
+ );
+};
+```
+
+### Pattern 2: Show Stale Content While Loading
+
+```jsx
+const SearchWithStaleIndicator = () => {
+ const [query, setQuery] = useState('');
+ const deferredQuery = useDeferredValue(query);
+ const isStale = query !== deferredQuery;
+
+ return (
+
+
+
+
+
+ {isStale && }
+
+ );
+};
+```
+
+### Pattern 3: Transition Non-Critical Updates with `useTransition`
+
+Use when you have multiple state updates and want to mark some as low-priority.
+
+```jsx
+import { useState, useTransition } from 'react';
+
+const TransitionExample = () => {
+ const [count, setCount] = useState(0);
+ const [heavyData, setHeavyData] = useState(null);
+ const [isPending, startTransition] = useTransition();
+
+ const handleIncrement = () => {
+ // High priority - updates immediately
+ setCount(c => c + 1);
+
+ // Low priority - can be interrupted
+ startTransition(() => {
+ setHeavyData(computeExpensiveData());
+ });
+ };
+
+ return (
+
+ Count: {count}
+ {isPending ? : }
+
+
+ );
+};
+```
+
+### Pattern 4: Suspense for Data Fetching
+
+```jsx
+import { Suspense, useDeferredValue } from 'react';
+
+const DataScreen = () => {
+ const [query, setQuery] = useState('');
+ const deferredQuery = useDeferredValue(query);
+
+ return (
+
+
+ }>
+
+
+
+ );
+};
+```
+
+## Code Examples
+
+### Slow Component Optimization
+
+```jsx
+// Without Concurrent React - UI freezes
+const SlowSearch = () => {
+ const [query, setQuery] = useState('');
+
+ return (
+ <>
+
+ {/* Blocks every keystroke */}
+ >
+ );
+};
+
+// With Concurrent React - UI stays responsive
+const FastSearch = () => {
+ const [query, setQuery] = useState('');
+ const deferredQuery = useDeferredValue(query);
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+// Important: Wrap SlowComponent in memo to prevent re-renders from parent
+const SlowComponent = memo(({ query }) => {
+ // Expensive computation here
+});
+```
+
+### Automatic Batching (React 18+)
+
+React 18 automatically batches state updates:
+
+```jsx
+// Before React 18 - 2 re-renders
+setTimeout(() => {
+ setCount(c => c + 1);
+ setFlag(f => !f);
+ // Rendered twice
+}, 1000);
+
+// React 18+ - 1 re-render (automatic batching)
+setTimeout(() => {
+ setCount(c => c + 1);
+ setFlag(f => !f);
+ // Rendered once!
+}, 1000);
+```
+
+## When to Use Which
+
+| Scenario | Solution |
+|----------|----------|
+| Single value drives expensive render | `useDeferredValue` |
+| Multiple state updates, some non-critical | `useTransition` |
+| Need loading indicator for transition | `useTransition` (has `isPending`) |
+| Data fetching with loading states | `Suspense` + `useDeferredValue` |
+| Simple parent-to-child value deferral | `useDeferredValue` |
+
+## Important Considerations
+
+1. **Wrap expensive components in `memo()`**: Without memoization, the component re-renders from parent anyway.
+
+2. **Use with New Architecture**: Concurrent features require New Architecture in React Native.
+
+3. **Don't overuse**: Only defer truly expensive work. Adding complexity for fast components is counterproductive.
+
+## Common Pitfalls
+
+- **Forgetting memo()**: `useDeferredValue` is useless if child re-renders from parent
+- **Using for simple state**: Overhead isn't worth it for cheap updates
+- **Expecting faster computation**: These hooks don't make code faster, they prioritize what runs when
+
+## Related Skills
+
+- [js-profile-react.md](./js-profile-react.md) - Identify slow components
+- [js-react-compiler.md](./js-react-compiler.md) - Automatic memoization
+- [js-lists-flatlist-flashlist.md](./js-lists-flatlist-flashlist.md) - For list-specific optimizations
diff --git a/.opencode/skills/react-native-best-practices/references/js-lists-flatlist-flashlist.md b/.opencode/skills/react-native-best-practices/references/js-lists-flatlist-flashlist.md
new file mode 100644
index 0000000..8dd5267
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-lists-flatlist-flashlist.md
@@ -0,0 +1,244 @@
+---
+title: Higher-Order Lists
+impact: CRITICAL
+tags: lists, flatlist, flashlist, scrollview, virtualization
+---
+
+# Skill: Higher-Order Lists
+
+Replace ScrollView with FlatList or FlashList for performant large list rendering.
+
+## Quick Pattern
+
+**Incorrect:**
+
+```jsx
+
+ {items.map((item) => )}
+
+```
+
+**Correct:**
+
+```jsx
+ item.id}
+ renderItem={({ item }) => }
+/>
+```
+
+## When to Use
+
+- Rendering more than 10-20 items in a list
+- List scrolling is choppy or laggy
+- App freezes when loading list data
+- Memory usage spikes with long lists
+
+## Prerequisites
+
+- `@shopify/flash-list` for FlashList (recommended)
+- Understanding of list virtualization
+
+## Version Guardrail
+
+- FlashList v1: `estimatedItemSize` is part of the optimization guidance.
+- FlashList v2 and newer: `estimatedItemSize`, `estimatedListSize`, and `estimatedFirstItemOffset` are deprecated and no longer used. Do not flag them as missing.
+- Before suggesting a FlashList fix, confirm the installed major version and tailor the advice. See [FlashList v2 changes](https://shopify.github.io/flash-list/docs/v2-changes/).
+
+## Step-by-Step Instructions
+
+### 1. Identify the Problem
+
+
+
+The FPS graph shows a severe performance problem during list rendering:
+- FPS starts at ~60 (smooth)
+- Drops to ~3 FPS during heavy list operation
+- Recovers after rendering completes
+
+```jsx
+// BAD: ScrollView renders ALL items at once
+const BadList = ({ items }) => (
+
+ {items.map((item, index) => (
+
+ {item}
+
+ ))}
+
+);
+```
+
+With 5000 items, this creates 5000 views immediately, causing:
+- Multi-second freeze
+- FPS drop to 0
+- High memory usage
+
+### 2. Replace with FlatList
+
+```jsx
+import { FlatList } from 'react-native';
+
+const BetterList = ({ items }) => {
+ const renderItem = ({ item }) => (
+
+ {item}
+
+ );
+
+ return (
+ index.toString()}
+ />
+ );
+};
+```
+
+FlatList only renders visible items + buffer (windowing).
+
+### 3. Optimize FlatList with getItemLayout
+
+For fixed-height items, skip layout measurement:
+
+```jsx
+const ITEM_HEIGHT = 50;
+
+const OptimizedList = ({ items }) => {
+ const renderItem = ({ item }) => (
+
+ {item}
+
+ );
+
+ const getItemLayout = (_, index) => ({
+ length: ITEM_HEIGHT,
+ offset: ITEM_HEIGHT * index,
+ index,
+ });
+
+ return (
+ index.toString()}
+ getItemLayout={getItemLayout}
+ />
+ );
+};
+```
+
+### 4. Upgrade to FlashList (Best Performance)
+
+```bash
+npm install @shopify/flash-list
+```
+
+```jsx
+import { FlashList } from '@shopify/flash-list';
+
+const BestList = ({ items }) => {
+ const renderItem = ({ item }) => (
+
+ {item}
+
+ );
+
+ return (
+ item.id}
+ />
+ );
+};
+```
+
+For FlashList v1, add `estimatedItemSize` with a realistic average item height. For FlashList v2+, skip that prop and focus on stable keys, lightweight item components, and `getItemType` when item shapes differ.
+
+**FlashList advantages:**
+- Recycles views instead of creating new ones
+- 78/100 vs 25/100 performance score in benchmarks
+- Smoother scrolling at ~54 FPS vs lower for FlatList
+
+## Code Examples
+
+### Variable Height Items (FlashList v1)
+
+```jsx
+// Calculate average for estimatedItemSize
+// Items are 50px, 100px, 150px
+// Average: (50 + 100 + 150) / 3 = 100px
+
+
+```
+
+### Mixed Item Types
+
+```jsx
+ {
+ if (item.type === 'header') return ;
+ if (item.type === 'product') return ;
+ return ;
+ }}
+ getItemType={(item) => item.type} // Helps recycling
+/>
+```
+
+If the project is still on FlashList v1, keep `estimatedItemSize` alongside `getItemType`.
+
+### FlatList Optimizations (if not using FlashList)
+
+```jsx
+ item.id}
+ extraData={selectedId} // Only when selection changes
+/>
+```
+
+## Performance Comparison
+
+| Component | 5000 Items Load | Scroll FPS | Memory |
+|-----------|-----------------|------------|--------|
+| ScrollView | 1-3 seconds freeze | < 30 | High |
+| FlatList | ~100ms | ~45 | Medium |
+| FlashList | ~50ms | ~54 | Low |
+
+## Decision Matrix
+
+| Scenario | Recommendation |
+|----------|---------------|
+| < 20 static items | ScrollView OK |
+| 20-100 items | FlatList minimum |
+| > 100 items | FlashList |
+| Complex item layouts | FlashList with `getItemType` |
+| Fixed height items | FlatList: `getItemLayout`; FlashList v1: `estimatedItemSize`; FlashList v2+: stable item structure |
+
+## Common Pitfalls
+
+- **Inline renderItem functions**: Causes re-renders. Define outside or use `useCallback`.
+- **Missing keyExtractor**: Use unique IDs, not array index when possible.
+- **Assuming all FlashList versions need `estimatedItemSize`**: FlashList v2 ignores it. Check the installed version before suggesting it.
+- **Heavy item components**: Keep list items light. Move side effects out.
+
+## Related Skills
+
+- [js-profile-react.md](./js-profile-react.md) - Profile list rendering
+- [js-measure-fps.md](./js-measure-fps.md) - Measure scroll performance
diff --git a/.opencode/skills/react-native-best-practices/references/js-measure-fps.md b/.opencode/skills/react-native-best-practices/references/js-measure-fps.md
new file mode 100644
index 0000000..dacf2ac
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-measure-fps.md
@@ -0,0 +1,178 @@
+---
+title: Measure JS FPS
+impact: HIGH
+tags: fps, performance, monitoring, flashlight
+---
+
+# Skill: Measure JS FPS
+
+Monitor and measure JavaScript frame rate to quantify app smoothness and identify performance regressions.
+
+## Quick Command
+
+```bash
+# Method 1: Built-in Perf Monitor
+# Shake device → Dev Menu → "Perf Monitor"
+
+# Method 2: Flashlight (Android, detailed reports)
+# Install Flashlight from an official, verified release channel first.
+flashlight measure
+```
+
+## When to Use
+
+- Animations feel choppy or janky
+- Scrolling is not smooth
+- Need baseline FPS metrics before/after optimization
+- Want to compare performance across builds
+
+## Prerequisites
+
+- React Native app running on device/simulator
+- For Flashlight: Android device (iOS not supported)
+
+> **Note**: This skill involves interpreting visual output (FPS graphs, performance overlays). AI agents cannot yet process screenshots autonomously. Use this as a guide while reviewing metrics manually, or await MCP-based visual feedback integration (see roadmap).
+
+## Step-by-Step Instructions
+
+### Method 1: React Perf Monitor (Quick Check)
+
+1. Open Dev Menu:
+ - iOS Simulator: `Ctrl + Cmd + Z` or Device > Shake
+ - Android Emulator: `Cmd + M` (Mac) / `Ctrl + M` (Windows)
+
+2. Select **"Perf Monitor"**
+
+3. Observe the overlay showing:
+ - **UI (Main) thread FPS** - Native rendering
+ - **JS thread FPS** - JavaScript execution
+ - **RAM usage**
+
+4. Hide with "Hide Perf Monitor" from Dev Menu
+
+**Interpretation:**
+- **60 FPS** = Smooth (16.6ms per frame)
+- **< 60 FPS** = Dropping frames
+- **120 FPS** target for high refresh rate devices (8.3ms per frame)
+
+### Method 2: Flashlight (Automated Benchmarking)
+
+> Android only. Provides detailed reports and JSON export.
+
+
+
+Flashlight shows comparative performance data:
+- **Score** (0-100): Overall performance rating (higher is better)
+- **Average FPS**: Target 60 FPS for smooth scrolling
+- **FPS Graph**: Real-time frame rate over test duration
+- **CPU/RAM metrics**: Resource consumption
+
+The image shows FlatList (score: 3) vs FlashList (score: 67) - a dramatic difference visible in both the score and FPS graph.
+
+**Installation:**
+
+Install Flashlight from the vendor's official release channel before using it. Prefer a package manager or a version-pinned binary with checksum/signature verification. Do not pipe a remote install script directly into a shell.
+
+**Usage:**
+
+```bash
+# Start measuring (app must be running on Android)
+flashlight measure
+```
+
+**Features:**
+- Real-time FPS graph
+- Average FPS calculation
+- CPU and RAM metrics
+- Overall performance score
+- JSON export for CI comparison
+
+### Important: Disable Dev Mode
+
+**Always disable development mode for accurate measurements:**
+
+**Android:**
+1. Open Dev Menu
+2. Settings > JS Dev Mode → **OFF**
+
+**iOS (React Native CLI):**
+```bash
+# Run Metro in production mode
+npx react-native start --reset-cache
+# Then build release variant
+```
+
+**Expo:**
+```bash
+# Start Metro without dev mode
+npx expo start --no-dev --minify
+# For accurate measurements, use EAS Build for release testing
+```
+
+## Code Examples
+
+### Identify FPS Drop Source
+
+If **UI FPS drops but JS FPS is fine:**
+- Native rendering issue
+- Too many views/complex layouts
+- Heavy native animations
+
+If **JS FPS drops but UI FPS is fine:**
+- JavaScript computation blocking
+- Expensive React re-renders
+- Look for `longRunningFunction` patterns
+
+If **Both drop:**
+- Mixed issue, start with JS profiling
+
+### Target Frame Budgets
+
+```javascript
+// 60 FPS = 16.6ms per frame
+const FRAME_BUDGET_60 = 16.6;
+
+// 120 FPS = 8.3ms per frame
+const FRAME_BUDGET_120 = 8.3;
+
+// If your function takes longer, it will drop frames
+const longRunningFunction = () => {
+ let i = 0;
+ while (i < 1000000000) { // This blocks for seconds!
+ i++;
+ }
+};
+```
+
+## Interpreting Results
+
+| FPS Range | User Perception | Action |
+|-----------|-----------------|--------|
+| 55-60 | Smooth | Acceptable |
+| 45-55 | Slight stutter | Investigate |
+| 30-45 | Noticeable jank | Optimize required |
+| < 30 | Very choppy | Critical fix needed |
+
+## Flashlight CI Integration
+
+```bash
+# Export measurements to JSON
+flashlight measure --output results.json
+
+# Compare builds
+flashlight compare baseline.json current.json
+```
+
+## Common Pitfalls
+
+- **Measuring in dev mode**: Results will be artificially slow
+- **Not using real device**: Simulators don't reflect real performance
+- **Ignoring UI thread**: React Native has two threads - JS issues don't always show on UI thread
+- **Single measurement**: Run multiple times, FPS varies
+
+## Related Skills
+
+- [js-profile-react.md](./js-profile-react.md) - Find what's causing FPS drops
+- [js-animations-reanimated.md](./js-animations-reanimated.md) - Fix animation-related drops
+- [js-bottomsheet.md](./js-bottomsheet.md) - Measure bottom sheet gesture and snap performance
+- [js-lists-flatlist-flashlist.md](./js-lists-flatlist-flashlist.md) - Fix scroll-related drops
diff --git a/.opencode/skills/react-native-best-practices/references/js-memory-leaks.md b/.opencode/skills/react-native-best-practices/references/js-memory-leaks.md
new file mode 100644
index 0000000..d086e07
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-memory-leaks.md
@@ -0,0 +1,205 @@
+---
+title: Hunt JS Memory Leaks
+impact: MEDIUM
+tags: memory, leaks, profiling, cleanup
+---
+
+# Skill: Hunt JS Memory Leaks
+
+Find and fix JavaScript memory leaks using React Native DevTools memory profiling.
+
+## Quick Pattern
+
+**Incorrect (listener not cleaned up):**
+
+```jsx
+useEffect(() => {
+ const sub = EventEmitter.addListener('event', handler);
+ // Missing cleanup!
+}, []);
+```
+
+**Correct (proper cleanup):**
+
+```jsx
+useEffect(() => {
+ const sub = EventEmitter.addListener('event', handler);
+ return () => sub.remove();
+}, []);
+```
+
+## When to Use
+
+- App memory usage grows over time
+- App crashes after extended use
+- Navigating between screens increases memory
+- Suspecting event listeners or timers not cleaned up
+
+## Prerequisites
+
+- React Native DevTools accessible
+- App running in development mode
+
+## Step-by-Step Instructions
+
+### 1. Open Memory Profiler
+
+1. Launch React Native DevTools (press `j` in Metro)
+2. Go to **Memory** tab
+3. Select **"Allocation instrumentation on timeline"**
+
+### 2. Record Memory Allocations
+
+1. Click **"Start"** at the bottom
+2. Perform actions that might leak (navigate, trigger events, etc.)
+3. Wait 10-30 seconds
+4. Click **"Stop"**
+
+### 3. Analyze the Timeline
+
+**Key indicators:**
+- **Blue bars** = Memory allocated
+- **Gray bars** = Memory freed (garbage collected)
+- **Blue bars that stay blue** = Potential leak!
+
+### 4. Investigate Leaking Objects
+
+
+
+The Memory tab shows:
+- **Timeline** (top): Blue bars = allocations, select time range to filter
+- **Summary view** (bottom): Lists constructors with allocation counts
+
+**Key columns:**
+- **Constructor**: Object type (e.g., `JSObject`, `Function`, `(string)`)
+- **Count**: Number of instances (×85000 = 85,000 objects)
+- **Shallow Size**: Memory of the object itself
+- **Retained Size**: Memory freed if object is deleted (including references)
+
+**Red flag**: Large retained size % with small shallow size % = closures or references holding large objects.
+
+**To investigate:**
+1. Click on a blue spike in the timeline
+2. Look at the Constructor list below
+3. Check **Shallow size** vs **Retained size**
+4. Expand constructors to see individual allocations
+5. Click to see the exact source location
+
+### 5. Verify the Fix
+
+After fixing, re-profile. All bars should turn gray (except the most recent).
+
+## Code Examples
+
+### Common Leak Patterns
+
+**1. Listeners Not Cleaned Up:**
+
+```jsx
+// BAD: Memory leak
+const BadEventComponent = () => {
+ useEffect(() => {
+ const subscription = EventEmitter.addListener('myEvent', handleEvent);
+ // Missing cleanup!
+ }, []);
+
+ return Listening...;
+};
+
+// GOOD: Proper cleanup
+const GoodEventComponent = () => {
+ useEffect(() => {
+ const subscription = EventEmitter.addListener('myEvent', handleEvent);
+ return () => subscription.remove(); // Cleanup!
+ }, []);
+
+ return Listening...;
+};
+```
+
+**2. Timers Not Cleared:**
+
+```jsx
+// BAD: Memory leak
+const BadTimerComponent = () => {
+ useEffect(() => {
+ const timer = setInterval(() => {
+ setCount(prev => prev + 1);
+ }, 1000);
+ // Missing cleanup!
+ }, []);
+};
+
+// GOOD: Proper cleanup
+const GoodTimerComponent = () => {
+ useEffect(() => {
+ const timer = setInterval(() => {
+ setCount(prev => prev + 1);
+ }, 1000);
+ return () => clearInterval(timer); // Cleanup!
+ }, []);
+};
+```
+
+**3. Closures Capturing Large Objects:**
+
+```jsx
+// BAD: Closure captures entire array
+class BadClosureExample {
+ private largeData = new Array(1000000).fill('data');
+
+ createLeakyFunction() {
+ return () => this.largeData.length; // Captures this.largeData
+ }
+}
+
+// GOOD: Only capture what's needed
+class GoodClosureExample {
+ private largeData = new Array(1000000).fill('data');
+
+ createEfficientFunction() {
+ const length = this.largeData.length; // Extract value
+ return () => length; // Only captures primitive
+ }
+}
+```
+
+**4. Global Arrays Growing:**
+
+```jsx
+// BAD: Global array never cleared
+let leakyClosures = [];
+
+const createLeak = () => {
+ const data = generateLargeData();
+ leakyClosures.push(() => data); // Keeps growing!
+};
+
+// GOOD: Clear when done or use WeakRef
+const createNoLeak = () => {
+ const data = generateLargeData();
+ const closure = () => data;
+ // Use it and let it be garbage collected
+ return closure;
+};
+```
+
+## Memory Profiler Metrics
+
+| Metric | Meaning |
+|--------|---------|
+| **Shallow size** | Memory held by the object itself |
+| **Retained size** | Memory freed if object is deleted (includes references) |
+
+**Large retained size with small shallow size** = Object holding references to other large objects (common in closures).
+
+## Common Pitfalls
+
+- **Not forcing GC**: GC runs periodically. Allocate something else to trigger collection before concluding there's a leak.
+- **Ignoring gray bars**: Gray = properly collected. Only blue bars that persist are leaks.
+- **Missing useEffect cleanup**: Most common React Native leak source.
+
+## Related Skills
+
+- [native-memory-leaks.md](./native-memory-leaks.md) - Native-side memory leaks
+- [js-profile-react.md](./js-profile-react.md) - General profiling
diff --git a/.opencode/skills/react-native-best-practices/references/js-profile-react.md b/.opencode/skills/react-native-best-practices/references/js-profile-react.md
new file mode 100644
index 0000000..ec4edc5
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-profile-react.md
@@ -0,0 +1,174 @@
+---
+title: Profile React Performance
+impact: MEDIUM
+tags: profiling, devtools, re-renders, flamegraph
+---
+
+# Skill: Profile React Performance
+
+Identify unnecessary re-renders and performance bottlenecks in React Native apps using React Native DevTools.
+
+## Quick Command
+
+```bash
+# Open React Native DevTools (press 'j' in Metro terminal)
+# Or shake device → "Open DevTools"
+# Go to Profiler tab → Start profiling → Perform actions → Stop
+```
+
+For targeted audits, profile the exact flow under review. Baseline output should include commit timeline, re-render counts, slow components, and a breakdown of the heaviest commit.
+
+## When to Use
+
+- App feels sluggish or janky during interactions
+- Need to identify which components re-render unnecessarily
+- Investigating slow list scrolling or form inputs
+- Before applying memoization or state management changes
+
+## Prerequisites
+
+- React Native DevTools accessible (press `j` in Metro or use Dev Menu)
+- App running in development mode
+- React DevTools version 6.0.1+ for React Compiler support
+
+> **Note**: This skill involves interpreting visual profiler output (flame graphs, component highlighting). AI agents cannot yet process screenshots autonomously. Use this as a guide while reviewing the profiler UI manually, or await MCP-based visual feedback integration (see roadmap).
+
+## Step-by-Step Instructions
+
+### 1. Open React Native DevTools
+
+```bash
+# Option A: Press 'j' in Metro terminal (works with both RN CLI and Expo)
+# Option B: Shake device / Cmd+D (iOS) / Cmd+M (Android) → "Open DevTools"
+# Expo: Also accessible via Expo DevTools in browser
+```
+
+### 2. Configure Profiler Settings
+
+1. Go to **Profiler** tab
+2. Click gear icon (⚙️) for settings
+3. Enable:
+ - "Highlight updates when components render"
+ - "Record why each component rendered while profiling"
+
+### 3. Record a Profiling Session
+
+```
+1. Click "Start profiling" (blue circle) or "Reload and start profiling"
+2. Perform the exact interaction or navigation flow you want to analyze
+3. Click "Stop profiling"
+```
+
+**Use "Reload and start profiling"** for startup performance analysis.
+
+For AI-agent workflows, treat this as a required sequence:
+
+1. Start profiling.
+2. Drive the audited flow, not just app startup or idle state.
+3. Stop profiling.
+4. Inspect commit timeline, re-renders, slow components, and the heaviest commit before proposing fixes.
+
+### 4. Analyze the Flame Graph
+
+
+
+The flame graph shows component render hierarchy with timing:
+
+**Color indicators:**
+- **Yellow components**: Most time spent rendering (focus here)
+- **Green components**: Fast/memoized
+- **Gray components**: Did not render
+
+**Right panel shows "Why did this render?":**
+- Props changed (shows which prop, e.g., `children`, `onPress`)
+- Rendered at timestamps with duration (e.g., "3.7s for 0.9ms")
+
+**Click on a component to see:**
+- Why it rendered (hook change, props change, parent re-render)
+- Render duration
+- Child components affected
+
+### 5. Use Ranked View for Bottom-Up Analysis
+
+Click "Ranked" tab to see components sorted by render time (slowest first).
+
+### 6. Profile JavaScript CPU
+
+For non-React performance issues:
+
+1. Go to **JavaScript Profiler** tab (enable in settings if hidden)
+2. Click "Start" to record
+3. Perform actions
+4. Click "Stop"
+5. Use **Heavy (Bottom Up)** view to find slowest functions
+
+## Code Examples
+
+### Before: Unnecessary Re-renders
+
+```jsx
+const App = () => {
+ const [count, setCount] = useState(0);
+
+ return (
+
+ {count}
+ {/* Button re-renders on every count change */}
+
+ );
+};
+
+const Button = ({onPress, title}) => (
+
+ {title}
+
+);
+```
+
+### After: Memoized
+
+```jsx
+const App = () => {
+ const [count, setCount] = useState(0);
+ const onPressHandler = useCallback(() => setCount(c => c + 1), []);
+
+ return (
+
+ {count}
+
+
+ );
+};
+
+const Button = memo(({onPress, title}) => (
+
+ {title}
+
+));
+```
+
+## Interpreting Results
+
+| Symptom | Likely Cause | Solution |
+|---------|--------------|----------|
+| Many yellow components | Cascading re-renders | Add memoization or use React Compiler |
+| "Props changed" on callbacks | Inline functions recreated | Use `useCallback` |
+| "Parent component rendered" | State too high in tree | Move state down or use atomic state |
+| Long JS thread block | Heavy computation | Move to background or use `useDeferredValue` |
+
+Only propose callback or dependency-array changes when the profiler or a reproducible bug shows they matter. Do not infer stale closures from a snippet alone.
+
+## Common Pitfalls
+
+- **Profiling in dev mode**: Always disable JS Dev Mode for accurate measurements (Settings > JS Dev Mode on Android)
+- **Not using production builds**: Some issues only appear with minified code
+- **Ignoring "Why did this render?"**: This tells you exactly what to fix
+- **Using component tree depth or count as the main baseline**: These are secondary context, not the core performance signal
+
+## Related Skills
+
+- [js-react-compiler.md](./js-react-compiler.md) - Automatic memoization
+- [js-atomic-state.md](./js-atomic-state.md) - Reduce re-renders with Jotai/Zustand
+- [js-bottomsheet.md](./js-bottomsheet.md) - Profile bottom sheet callback-driven re-renders
+- [js-measure-fps.md](./js-measure-fps.md) - Quantify frame rate impact
diff --git a/.opencode/skills/react-native-best-practices/references/js-react-compiler.md b/.opencode/skills/react-native-best-practices/references/js-react-compiler.md
new file mode 100644
index 0000000..f7ccda0
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-react-compiler.md
@@ -0,0 +1,368 @@
+---
+title: React Compiler
+impact: HIGH
+tags: memoization, react-compiler, memo, useMemo, useCallback
+---
+
+# Skill: React Compiler
+
+Set up React Compiler to automatically memoize components and eliminate unnecessary re-renders.
+
+## Quick Pattern
+
+**Before (manual memoization):**
+
+```jsx
+const MemoizedButton = memo(({ onPress }) => );
+const handler = useCallback(() => doSomething(), []);
+```
+
+**After (automatic with React Compiler):**
+
+```jsx
+// No memo/useCallback needed - compiler handles it
+const Button = ({ onPress }) => ;
+const handler = () => doSomething();
+```
+
+## When to Use
+
+- Want automatic performance optimization without manual `memo`/`useMemo`/`useCallback`
+- Codebase follows Rules of React
+- React Native 0.76+ or Expo SDK 52+
+- Ready to remove boilerplate memoization code
+
+## Prerequisites
+
+- React 17+ (React 19 recommended for best compatibility)
+- Babel-based build system
+- Code follows [Rules of React](https://react.dev/reference/rules)
+
+## Step-by-Step Instructions
+
+### Step 1: Check Compatibility
+
+Before enabling the compiler, verify your project is compatible:
+
+```bash
+npx react-compiler-healthcheck@latest
+```
+
+This checks if your app follows the Rules of React and identifies potential issues.
+
+### Step 2: Install React Compiler
+
+#### Expo Projects
+
+**SDK 54 and later** (simplified setup):
+
+```bash
+npx expo install babel-plugin-react-compiler
+```
+
+**SDK 52-53**:
+
+```bash
+npx expo install babel-plugin-react-compiler@beta react-compiler-runtime@beta
+```
+
+Then enable in your app config:
+
+```json
+// app.json
+{
+ "expo": {
+ "experiments": {
+ "reactCompiler": true
+ }
+ }
+}
+```
+
+#### React Native (without Expo)
+
+```bash
+npm install -D babel-plugin-react-compiler@latest
+```
+
+For React Native < 0.78 (React < 19), also install the runtime:
+
+```bash
+npm install react-compiler-runtime@beta
+```
+
+### Step 3: Configure Babel (React Native without Expo)
+
+For non-Expo React Native projects, configure Babel manually:
+
+```javascript
+// babel.config.js
+const ReactCompilerConfig = {
+ target: '19', // Use '18' for React Native < 0.78
+};
+
+module.exports = function (api) {
+ api.cache(true);
+ return {
+ presets: ['module:@react-native/babel-preset'],
+ plugins: [
+ ['babel-plugin-react-compiler', ReactCompilerConfig], // Must run first!
+ // ... other plugins
+ ],
+ };
+};
+```
+
+> **Important**: React Compiler must run **first** in your Babel plugin pipeline. The compiler needs the original source information for proper analysis.
+
+### Step 4: Set Up ESLint (Recommended)
+
+The ESLint plugin helps identify code that can't be optimized and enforces the Rules of React.
+
+#### Expo Projects
+
+```bash
+npx expo lint # Ensures ESLint is set up
+npx expo install eslint-plugin-react-compiler -- -D
+```
+
+Configure ESLint:
+
+```javascript
+// .eslintrc.js
+const { defineConfig } = require('eslint/config');
+const expoConfig = require('eslint-config-expo/flat');
+const reactCompiler = require('eslint-plugin-react-compiler');
+
+module.exports = defineConfig([
+ expoConfig,
+ reactCompiler.configs.recommended,
+ {
+ ignores: ['dist/*'],
+ },
+]);
+```
+
+#### React Native (without Expo)
+
+```bash
+npm install -D eslint-plugin-react-hooks@latest
+```
+
+The compiler rules are available in the `recommended-latest` preset. Follow the [eslint-plugin-react-hooks installation instructions](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks).
+
+### Step 5: Verify Optimizations
+
+Open React DevTools. Optimized components show a `Memo ✨` badge.
+
+You can also verify by checking build output—compiled code includes automatic memoization:
+
+```javascript
+import { c as _c } from 'react/compiler-runtime';
+
+export default function MyApp() {
+ const $ = _c(1);
+ let t0;
+ if ($[0] === Symbol.for('react.memo_cache_sentinel')) {
+ t0 = Hello World
;
+ $[0] = t0;
+ } else {
+ t0 = $[0];
+ }
+ return t0;
+}
+```
+
+**Note**: React Native 0.76+ includes DevTools with Memo badge support by default. For older versions or third-party debuggers with version mismatches, you may need to override `react-devtools-core` in `package.json`.
+
+## Incremental Adoption
+
+You can incrementally adopt React Compiler using two strategies:
+
+### Strategy 1: Limit to Specific Directories
+
+Configure the Babel plugin to only run on specific files, e.g. `src/path/to/dir` in the following examples:
+
+**Expo** (create `babel.config.js` with `npx expo customize babel.config.js`):
+
+```javascript
+// babel.config.js
+module.exports = function (api) {
+ api.cache(true);
+ return {
+ presets: [
+ [
+ 'babel-preset-expo',
+ {
+ 'react-compiler': {
+ sources: (filename) => {
+ return filename.includes('src/path/to/dir');
+ },
+ },
+ },
+ ],
+ ],
+ };
+};
+```
+
+**React Native (without Expo)**:
+
+```javascript
+// babel.config.js
+const ReactCompilerConfig = {
+ target: '19',
+ sources: (filename) => {
+ return filename.includes('src/path/to/dir');
+ },
+};
+
+module.exports = function (api) {
+ api.cache(true);
+ return {
+ presets: ['module:@react-native/babel-preset'],
+ plugins: [['babel-plugin-react-compiler', ReactCompilerConfig]],
+ };
+};
+```
+
+After changing `babel.config.js`, restart Metro with cache cleared:
+
+```bash
+# Expo
+npx expo start --clear
+
+# React Native CLI
+npx react-native start --reset-cache
+```
+
+### Strategy 2: Opt Out Specific Components
+
+Use the `"use no memo"` directive to skip optimization for specific components or files:
+
+```jsx
+function ProblematicComponent() {
+ 'use no memo';
+
+ return Will not be optimized;
+}
+```
+
+This is useful for temporarily opting out components that cause issues. Fix the underlying problem and remove the directive once resolved.
+
+## How It Works
+
+The compiler transforms your code to automatically cache values:
+
+**Before (your code):**
+
+```jsx
+export default function MyApp() {
+ const [value, setValue] = useState('');
+ return (
+ setValue(value)}>Hello World
+ );
+}
+```
+
+**After (compiled output):**
+
+```jsx
+import { c as _c } from 'react/compiler-runtime';
+
+export default function MyApp() {
+ const $ = _c(2); // Cache with 2 slots
+ const [value, setValue] = useState('');
+
+ let t0;
+ if ($[0] !== value) {
+ t0 = (
+ setValue(value)}>Hello World
+ );
+ $[0] = value;
+ $[1] = t0;
+ } else {
+ t0 = $[1]; // Return cached JSX
+ }
+ return t0;
+}
+```
+
+## Code Examples
+
+### React Compiler Playground
+
+Test transformations at [React Playground](https://playground.react.dev/).
+
+### What Gets Optimized
+
+```jsx
+// Components - auto-memoized
+const Button = ({ onPress, label }) => (
+
+ {label}
+
+);
+
+// Callbacks - auto-cached (no useCallback needed)
+const handlePress = () => {
+ console.log('pressed');
+};
+
+// Expensive computations - auto-cached (no useMemo needed)
+const filtered = items.filter((item) => item.active);
+```
+
+### What Breaks Compilation
+
+```jsx
+// BAD: Mutating props
+const BadComponent = ({ items }) => {
+ items.push('new item'); // Mutation!
+ return
;
+};
+
+// BAD: Mutating during render
+const BadMutation = () => {
+ const [items, setItems] = useState([]);
+ items.push('new'); // Mutation during render!
+ return
;
+};
+
+// BAD: Non-idempotent render
+let counter = 0;
+const BadRender = () => {
+ counter++; // Side effect during render!
+ return {counter};
+};
+```
+
+## Should You Remove Manual Memoization?
+
+Improvements are primarily automatic. You can remove instances of `useCallback`, `useMemo`, and `React.memo` in favor of automatic memoization once the compiler is working correctly in your project.
+
+**Note**: Class components will not be optimized. Migrate to function components for full benefits.
+
+Expo's implementation only runs on application code (not node_modules), and only when bundling for the client (disabled in server rendering).
+
+## Expected Performance Improvements
+
+Testing on Expensify app showed:
+
+- **4.3% improvement** in Chat Finder TTI
+- Significant reduction in cascading re-renders
+- Most impact on apps without existing manual optimization
+
+Already heavily optimized apps may see marginal gains.
+
+## Common Pitfalls
+
+- **Not fixing ESLint errors first**: When ESLint reports an error, the compiler skips that component—this is safe but means you miss optimization
+- **Expecting it to fix bad patterns**: Compiler optimizes good code, doesn't fix bad code
+- **Forgetting shallow comparison**: Like `memo`, compiler uses shallow comparison for objects/arrays
+- **Not running healthcheck**: Always run `npx react-compiler-healthcheck@latest` before enabling
+
+## Related Skills
+
+- [js-profile-react.md](./js-profile-react.md) - Verify optimization impact
+- [js-atomic-state.md](./js-atomic-state.md) - Alternative for state-related re-renders
diff --git a/.opencode/skills/react-native-best-practices/references/js-uncontrolled-components.md b/.opencode/skills/react-native-best-practices/references/js-uncontrolled-components.md
new file mode 100644
index 0000000..d2a2f81
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/js-uncontrolled-components.md
@@ -0,0 +1,216 @@
+---
+title: Uncontrolled Components
+impact: HIGH
+tags: textinput, forms, controlled, uncontrolled
+---
+
+# Skill: Uncontrolled Components
+
+Fix TextInput synchronization and flickering issues by using uncontrolled component pattern.
+
+## Quick Pattern
+
+**Before (controlled - may flicker on legacy arch):**
+
+```jsx
+
+```
+
+**After (uncontrolled - native owns state):**
+
+```jsx
+
+```
+
+## When to Use
+
+- TextInput flickers or shows wrong characters during fast typing
+- Text input lags behind user input on low-end devices
+- Using legacy (non-New Architecture) React Native
+- Need maximum input responsiveness
+
+## Prerequisites
+
+- Understanding of React controlled vs uncontrolled components
+- TextInput component in use
+
+## Problem Description
+
+
+
+The diagram shows what happens when typing "TEST" with a controlled `TextInput`:
+
+1. User types "T" → `onChangeText('T')` fires
+2. React calls `setValue('T')` → native updates to "T"
+3. User types "E" → `onChangeText('TE')` fires
+4. React calls `setValue('TE')` → native updates to "TE"
+5. ...continues for each character
+
+**The problem**: Each character requires a round-trip between native and JavaScript. On legacy architecture, if React state update is slow, native may show intermediate states (flicker).
+
+**New Architecture note:** This issue is largely resolved in New Architecture, but uncontrolled pattern still provides best performance.
+
+## Step-by-Step Instructions
+
+### 1. Identify Controlled TextInput
+
+```jsx
+// Controlled - value prop syncs state to native
+const ControlledInput = () => {
+ const [value, setValue] = useState('');
+
+ return (
+
+ );
+};
+```
+
+### 2. Convert to Uncontrolled
+
+Remove the `value` prop to make it uncontrolled:
+
+```jsx
+// Uncontrolled - native owns the state
+const UncontrolledInput = () => {
+ const [value, setValue] = useState('');
+
+ return (
+
+ );
+};
+```
+
+### 3. Use Ref for Programmatic Control
+
+If you need to read/set value programmatically:
+
+```jsx
+const UncontrolledWithRef = () => {
+ const inputRef = useRef(null);
+
+ const clearInput = () => {
+ inputRef.current?.clear();
+ };
+
+ const getValue = () => {
+ // Use onChangeText to track value, or native methods
+ };
+
+ return (
+ console.log('Current:', text)}
+ />
+ );
+};
+```
+
+## Code Examples
+
+### Full Migration Example
+
+**Before (Controlled):**
+
+```jsx
+const SearchInput = () => {
+ const [query, setQuery] = useState('');
+ const [results, setResults] = useState([]);
+
+ const handleChange = (text) => {
+ setQuery(text);
+ fetchResults(text).then(setResults);
+ };
+
+ return (
+
+
+
+
+ );
+};
+```
+
+**After (Uncontrolled):**
+
+```jsx
+const SearchInput = () => {
+ const [query, setQuery] = useState('');
+ const [results, setResults] = useState([]);
+
+ const handleChange = (text) => {
+ setQuery(text);
+ fetchResults(text).then(setResults);
+ };
+
+ return (
+
+
+
+
+ );
+};
+```
+
+### When You Need Value Control
+
+For input masking or validation that modifies input:
+
+```jsx
+// Option 1: Accept the controlled behavior (may flicker)
+const MaskedInput = () => {
+ const [value, setValue] = useState('');
+
+ const handleChange = (text) => {
+ // Phone mask: (123) 456-7890
+ const masked = maskPhone(text);
+ setValue(masked);
+ };
+
+ return (
+
+ );
+};
+
+// Option 2: Use a native masked input library
+// react-native-masked-text handles this natively
+```
+
+## Decision Matrix
+
+| Scenario | Recommendation |
+|----------|---------------|
+| Simple text input | Uncontrolled |
+| Search/filter input | Uncontrolled |
+| Form with validation on submit | Uncontrolled |
+| Input masking (phone, credit card) | Controlled or native library |
+| Character-by-character validation | Controlled |
+| New Architecture app | Either works well |
+
+## Common Pitfalls
+
+- **Forgetting `defaultValue`**: Without it, input starts empty
+- **Trying to clear with state**: Use `ref.current.clear()` instead
+- **Mixing patterns**: Don't use both `value` and `defaultValue`
+
+## Related Skills
+
+- [js-profile-react.md](./js-profile-react.md) - Profile input performance
+- [js-concurrent-react.md](./js-concurrent-react.md) - Defer expensive search operations
diff --git a/.opencode/skills/react-native-best-practices/references/native-android-16kb-alignment.md b/.opencode/skills/react-native-best-practices/references/native-android-16kb-alignment.md
new file mode 100644
index 0000000..58ca1b2
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-android-16kb-alignment.md
@@ -0,0 +1,113 @@
+---
+title: Android 16 KB Page Size Alignment
+impact: CRITICAL
+tags: android, native, 16kb, alignment, page-size, google-play, third-party
+---
+
+# Android 16 KB page size alignment
+
+---
+
+## Quick Reference
+
+| Item | Details |
+| ---------------------- | ---------------------------------------------------- |
+| Google Play deadline | November 1, 2025 for apps targeting Android 15+ |
+| React Native support | Built-in since React Native 0.79 |
+| What to check | Third-party native libraries (`.so` files) |
+| Official documentation | [developer.android.com/guide/practices/page-sizes][] |
+
+[developer.android.com/guide/practices/page-sizes]: https://developer.android.com/guide/practices/page-sizes
+
+---
+
+## Quick Command
+
+Verify APK alignment using Android's official `zipalign` tool:
+
+```bash
+zipalign -c -P 16 -v 4 app-release.apk
+```
+
+If any 64-bit libraries (`arm64-v8a`, `x86_64`) show misalignment, they need updating.
+
+For deeper ELF-level inspection, use Android's [check_elf_alignment.sh][] script.
+
+[check_elf_alignment.sh]: https://cs.android.com/android/platform/superproject/main/+/main:system/extras/tools/check_elf_alignment.sh
+
+---
+
+## When to Check
+
+React Native 0.79+ builds core binaries with correct alignment. However, **third-party
+native libraries** may still be misaligned. Check alignment when:
+
+* Adding or updating SDKs with native code
+* Preparing a release for Google Play
+* Investigating crashes on Android 15+ devices with 16 KB page size
+
+---
+
+## CI Integration
+
+Add alignment check to your release pipeline to catch issues before submission, example:
+
+```bash
+zipalign -c -P 16 -v 4 app-release.apk 2>&1 | tee alignment.log
+if grep -q "Verification FAILED" alignment.log; then exit 1; fi
+```
+
+## Step-by-Step
+
+1. Build your release APK or AAB
+2. Run `zipalign` verification (see Quick Command)
+3. If misaligned libraries are found, trace them to source packages (see below)
+4. Update, replace, or remove the affected dependencies
+
+For runtime testing, use the [16KB Android Emulator image][] or enable
+"Boot with 16KB page size" on Pixel 8/8a/9 devices.
+
+[16KB Android Emulator image]: https://developer.android.com/guide/practices/page-sizes#set-up-the-android-emulator-with-a-16-kb-based-system-image
+
+---
+
+## Tracing Misaligned Libraries
+
+When `zipalign` reports a misaligned library like `libfoo.so`, find its source package:
+
+```bash
+# Find the .so file in node_modules
+find node_modules -name "libfoo.so" 2>/dev/null
+
+# Or search gradle files for references
+grep -r "foo" node_modules/*/android --include="*.gradle" 2>/dev/null
+```
+
+Once identified, update the dependency or contact the vendor for a 16KB-compatible build.
+
+---
+
+## Common Pitfalls
+
+* Waiting for Play Store rejection instead of checking in CI
+* Assuming a React Native upgrade rebuilds third-party native binaries
+* Only checking 32-bit ABIs (`armeabi-v7a`, `x86`) — these are not affected
+* Using `zipalign` without the `-P 16` flag (checks 4 KB, not 16 KB)
+* Validating only debug builds
+
+---
+
+## Fixing Alignment Issues
+
+Alignment issues require **rebuilding** the native library with a compatible toolchain.
+Repackaging alone does not fix them.
+
+See [official remediation steps][] for detailed guidance.
+
+[official remediation steps]: https://developer.android.com/guide/practices/page-sizes#build-app-16kb
+
+---
+
+## Related Skills
+
+* [native-profiling.md](./native-profiling.md) — Native debugging tools
diff --git a/.opencode/skills/react-native-best-practices/references/native-measure-tti.md b/.opencode/skills/react-native-best-practices/references/native-measure-tti.md
new file mode 100644
index 0000000..a3981c2
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-measure-tti.md
@@ -0,0 +1,262 @@
+---
+title: Measure TTI (Time to Interactive)
+impact: HIGH
+tags: tti, startup, performance, markers
+---
+
+# Skill: Measure TTI (Time to Interactive)
+
+Set up performance markers to measure app startup time and track TTI improvements.
+
+## Quick Command
+
+```bash
+npm install react-native-performance
+```
+
+```tsx
+// Mark when screen is interactive
+import performance from 'react-native-performance';
+
+useEffect(() => {
+ performance.mark('screenInteractive');
+}, []);
+```
+
+## When to Use
+
+- App startup feels slow
+- Need baseline metrics for optimization
+- Setting up performance monitoring
+- Comparing TTI across releases
+
+## Prerequisites
+
+- `react-native-performance` library (recommended)
+
+```bash
+npm install react-native-performance
+```
+
+> **Note**: This skill involves interpreting visual timeline diagrams and profiler output. AI agents cannot yet process screenshots autonomously. Use this as a guide while reviewing metrics manually, or await MCP-based visual feedback integration (see roadmap).
+
+## Understanding TTI
+
+**Time to Interactive**: Time from app icon tap to displaying usable content.
+
+### Startup Types
+
+| Type | Description | Measure? |
+|------|-------------|----------|
+| Cold | App not in memory, full init | ✅ Yes |
+| Warm | Process exists, activity recreated | ❌ Skip |
+| Hot | App in background, resumed | ❌ Skip |
+| Prewarmed (iOS) | iOS pre-initialized app | ❌ Filter out |
+
+**Only measure cold starts** for consistent metrics.
+
+## React Native Startup Pipeline
+
+
+
+The diagram shows a warm start (app was in memory):
+
+**UI Thread:**
+1. `init native process` → `init native app`
+2. Gap while user is away (e.g., "5h break from using the app")
+3. `JS bundle load` → `RootView render`
+
+**JS Thread (runs in parallel):**
+- `init entrypoint` → `registerComponent`
+
+**Pipeline markers:**
+```
+1. Native Process Init (nativeLaunchStart → nativeLaunchEnd)
+2. Native App Init (appCreationStart → appCreationEnd)
+3. JS Bundle Load (runJSBundleStart → runJSBundleEnd)
+4. RN Root View Render (contentAppeared)
+5. React App Interactive (screenInteractive) ← This is TTI
+```
+
+## Step-by-Step Implementation
+
+### 1. Detect Cold Start
+
+**iOS (Swift):**
+
+```swift
+let isColdStart = ProcessInfo.processInfo.environment["ActivePrewarm"] != "1"
+```
+
+**Android (Kotlin):**
+
+```kotlin
+class MainApplication : Application() {
+ var isColdStart = false
+
+ override fun onCreate() {
+ super.onCreate()
+
+ var firstPostEnqueued = true
+ Handler().post { firstPostEnqueued = false }
+
+ registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
+ override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
+ unregisterActivityLifecycleCallbacks(this)
+ if (firstPostEnqueued && savedInstanceState == null) {
+ isColdStart = true
+ }
+ }
+ // ... other callbacks
+ })
+ }
+}
+```
+
+### 2. Check Foreground State
+
+Only measure when app starts in foreground.
+
+**iOS:**
+
+```swift
+var isForegroundProcess = false
+
+override func application(_ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ if application.applicationState == .active {
+ isForegroundProcess = true
+ }
+ return true
+}
+```
+
+**Android:**
+
+```kotlin
+private fun isForegroundProcess(): Boolean {
+ val processInfo = ActivityManager.RunningAppProcessInfo()
+ ActivityManager.getMyMemoryState(processInfo)
+ return processInfo.importance == IMPORTANCE_FOREGROUND
+}
+```
+
+### 3. Set Up Performance Markers
+
+Using `react-native-performance`:
+
+**Native (iOS):**
+
+```swift
+import ReactNativePerformance
+
+RNPerformance.sharedInstance().mark("appCreationStart")
+// ... app init ...
+RNPerformance.sharedInstance().mark("appCreationEnd")
+```
+
+**Native (Android):**
+
+```kotlin
+import com.oblador.performance.RNPerformance
+
+RNPerformance.getInstance().mark("appCreationStart")
+// ... app init ...
+RNPerformance.getInstance().mark("appCreationEnd")
+```
+
+### 4. Mark Screen Interactive (JavaScript)
+
+```tsx
+import performance from 'react-native-performance';
+
+export default function HomeScreen() {
+ useEffect(() => {
+ // Mark when meaningful content is displayed
+ performance.mark('screenInteractive');
+ }, []);
+
+ return ;
+}
+```
+
+### 5. Collect and Report Metrics
+
+```tsx
+import performance from 'react-native-performance';
+
+const collectTTIMetrics = () => {
+ const entries = performance.getEntriesByType('mark');
+
+ // Calculate durations
+ const metrics = {
+ nativeInit: getMarkDuration('nativeLaunchStart', 'nativeLaunchEnd'),
+ appCreation: getMarkDuration('appCreationStart', 'appCreationEnd'),
+ jsBundleLoad: getMarkDuration('runJSBundleStart', 'runJSBundleEnd'),
+ tti: getMarkDuration('nativeLaunchStart', 'screenInteractive'),
+ };
+
+ // Send to analytics
+ analytics.track('app_performance', metrics);
+};
+```
+
+## Built-in Markers
+
+`react-native-performance` provides automatic markers:
+
+| Marker | Description |
+|--------|-------------|
+| `nativeLaunchStart` | Process start (pre-main) |
+| `nativeLaunchEnd` | Native init complete |
+| `runJSBundleStart` | JS bundle loading starts |
+| `runJSBundleEnd` | JS bundle loaded |
+| `contentAppeared` | RN root view rendered |
+
+## Listening to Native Events
+
+**iOS (JS Bundle Load):**
+
+```swift
+NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(onJSLoad),
+ name: NSNotification.Name("RCTJavaScriptDidLoadNotification"),
+ object: nil
+)
+```
+
+**Android (JS Bundle Load):**
+
+```kotlin
+ReactMarker.addListener { name ->
+ when (name) {
+ RUN_JS_BUNDLE_START -> { /* mark start */ }
+ RUN_JS_BUNDLE_END -> { /* mark end */ }
+ CONTENT_APPEARED -> { /* mark content */ }
+ }
+}
+```
+
+## Target Metrics
+
+| Metric | Good | Acceptable | Needs Work |
+|--------|------|------------|------------|
+| TTI | < 2s | 2-4s | > 4s |
+| JS Bundle Load | < 500ms | 500ms-1s | > 1s |
+| Native Init | < 500ms | 500ms-1s | > 1s |
+
+**Note**: Targets vary by app complexity and device tier.
+
+## Common Pitfalls
+
+- **Including prewarmed starts**: iOS prewarming skews metrics
+- **Measuring warm/hot starts**: Only cold starts are meaningful
+- **Wrong screenInteractive placement**: Mark when truly interactive, not just mounted
+- **Not filtering background launches**: Push notifications can start app in background
+
+## Related Skills
+
+- [bundle-analyze-js.md](./bundle-analyze-js.md) - Reduce JS bundle load time
+- [native-profiling.md](./native-profiling.md) - Profile native init
+- [bundle-hermes-mmap.md](./bundle-hermes-mmap.md) - Improve Android TTI
diff --git a/.opencode/skills/react-native-best-practices/references/native-memory-leaks.md b/.opencode/skills/react-native-best-practices/references/native-memory-leaks.md
new file mode 100644
index 0000000..a946afb
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-memory-leaks.md
@@ -0,0 +1,240 @@
+---
+title: Hunt Native Memory Leaks
+impact: MEDIUM
+tags: memory, leaks, xcode, instruments, profiler
+---
+
+# Skill: Hunt Native Memory Leaks
+
+Find native memory leaks using Xcode Leaks and Android Studio Memory Profiler.
+
+## Quick Command
+
+```bash
+# iOS: Profile with Leaks instrument
+# Xcode → Product → Profile (Cmd+I) → Leaks template
+
+# Android: Memory Profiler
+# Android Studio → Run → Profile → Track Memory Consumption
+```
+
+## When to Use
+
+- App memory grows despite JS profiler showing no leaks
+- Native modules suspected of leaking
+- Activity recreation causes memory growth (Android)
+- C++/Swift/Kotlin code under investigation
+
+## iOS: Xcode Leaks
+
+### Quick Check: Memory Report
+
+1. Run app via Xcode
+2. Open **Debug Navigator** (side panel)
+3. Click **Memory**
+4. Watch graph for continuous growth
+
+### Deep Analysis: Instruments Leaks
+
+
+
+1. **Xcode → Product → Profile** (or Cmd+I)
+2. Select **Leaks** template (highlighted with orange triangle icon in the grid)
+3. Click **Choose**
+4. Click **Record** (red circle)
+5. Use the app, perform suspect actions
+6. Stop recording
+
+The template picker shows all available Instruments:
+- **Leaks**: Memory leak detection (what we need)
+- **Allocations**: All memory allocations over time
+- **Time Profiler**: CPU usage profiling
+- **Zombies**: Detect messages to deallocated objects
+
+### Analyzing Results
+
+**Red markers** = Leaked memory detected
+
+Click on leak to see:
+- **Leaked Object**: Type and size
+- **Responsible Library**: Which code leaked
+- **Responsible Frame**: Exact function
+- **Stack Trace**: Full call path (right panel)
+
+**Double-click function** to see source code.
+
+### Common iOS Leak: Missing delete
+
+```cpp
+// BAD: Memory leak
+void createNewStrings() {
+ std::string* str = new std::string("Hello");
+ // Forgot delete str;
+}
+
+// GOOD: Fixed
+void createNewStrings() {
+ std::string* str = new std::string("Hello");
+ // ... use str ...
+ delete str;
+}
+
+// BETTER: Use smart pointers
+void createNewStrings() {
+ auto str = std::make_unique("Hello");
+ // Automatically deleted
+}
+```
+
+## Android: Memory Profiler
+
+### Launch Profiler
+
+1. **Run → Profile** (or click Profile in toolbar)
+2. Or: **View → Tool Windows → Profiler**
+3. Select **"Track Memory Consumption"**
+
+### Recording
+
+1. Start the app
+2. Perform actions that might leak
+3. Watch memory graph for growth patterns
+
+### Analyzing Allocations
+
+Memory profiler shows:
+- **Allocations count**: Objects created
+- **Deallocations count**: Objects freed
+- **Live objects**: Still in memory
+
+**If allocations >> deallocations**, you have a leak.
+
+### Common Android Leak: Listener Not Removed
+
+```kotlin
+// BAD: Leaks MainActivity on config change
+class MainActivity : AppCompatActivity(), Callback {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ EventManager.addListener(this)
+ // Never removed!
+ }
+}
+
+// GOOD: Remove listener
+class MainActivity : AppCompatActivity(), Callback {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ EventManager.addListener(this)
+ }
+
+ override fun onDestroy() {
+ EventManager.removeListener(this)
+ super.onDestroy()
+ }
+}
+```
+
+### Activity Recreation Test
+
+Android recreates activities on:
+- Screen rotation
+- Dark mode change
+- Locale change
+
+**Test**: Rotate device multiple times, check if old activities are freed.
+
+React Native note: RN opts out via `android:configChanges` in manifest, but native code might not.
+
+## Debugging Workflow
+
+### iOS
+
+1. Profile with Instruments Leaks
+2. Trigger suspect actions repeatedly
+3. Wait for red leak markers
+4. Click to identify responsible frame
+5. Fix and re-test
+
+### Android
+
+1. Profile memory consumption
+2. Trigger suspect actions (rotate, navigate)
+3. Check allocation/deallocation counts
+4. Look for classes with 0 deallocations
+5. Fix and re-test
+
+## Code Fixes by Pattern
+
+### Reference Cycle (Swift)
+
+```swift
+// BAD
+class Parent {
+ var child: Child?
+}
+class Child {
+ var parent: Parent? // Strong reference cycle
+}
+
+// GOOD
+class Parent {
+ var child: Child?
+}
+class Child {
+ weak var parent: Parent? // Weak breaks cycle
+}
+```
+
+### Missing Cleanup (C++)
+
+```cpp
+// BAD
+void process() {
+ auto* data = new LargeData();
+ if (error) return; // Leak!
+ delete data;
+}
+
+// GOOD: RAII with unique_ptr
+void process() {
+ auto data = std::make_unique();
+ if (error) return; // Automatically cleaned up
+}
+```
+
+### Global Singleton Holding References (Kotlin)
+
+```kotlin
+// BAD: Holds strong references
+object Cache {
+ private val items = mutableMapOf()
+}
+
+// GOOD: Use weak references
+object Cache {
+ private val items = mutableMapOf>()
+}
+```
+
+## Verification
+
+After fixing:
+1. Re-run profiler
+2. Perform same actions
+3. Verify:
+ - iOS: No red leak markers
+ - Android: Allocations ≈ Deallocations
+
+## Common Pitfalls
+
+- **Testing in debug mode**: Some leaks only appear in release
+- **Not waiting for GC**: Force GC before concluding no leak
+- **Ignoring small leaks**: They add up over time
+- **Missing cleanup in invalidate()**: Turbo Modules need proper cleanup
+
+## Related Skills
+
+- [native-memory-patterns.md](./native-memory-patterns.md) - Understanding memory patterns
+- [js-memory-leaks.md](./js-memory-leaks.md) - JS-side leaks
+- [native-threading-model.md](./native-threading-model.md) - Module invalidation
diff --git a/.opencode/skills/react-native-best-practices/references/native-memory-patterns.md b/.opencode/skills/react-native-best-practices/references/native-memory-patterns.md
new file mode 100644
index 0000000..2505b8f
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-memory-patterns.md
@@ -0,0 +1,274 @@
+---
+title: Native Memory Management
+impact: MEDIUM
+tags: memory, c++, swift, kotlin, arc, smart-pointers
+---
+
+# Skill: Native Memory Management
+
+Understand memory management patterns in C++, Swift, and Kotlin for React Native native modules.
+
+## Quick Reference
+
+| Pattern | Languages | Mechanism |
+|---------|-----------|-----------|
+| Reference Counting | Swift, Obj-C, C++ (smart ptrs) | Count refs, free at zero |
+| Garbage Collection | Kotlin/Java, JavaScript | GC scans and frees unreachable |
+| Manual | C, C++ (raw pointers) | Explicit new/delete |
+
+**Key rule**: Use `std::unique_ptr`/`std::shared_ptr` in C++, `weak` for delegates in Swift.
+
+## When to Use
+
+- Writing native modules with manual memory management
+- Debugging native memory leaks
+- Interfacing C++ with Swift/Kotlin
+- Understanding reference counting vs garbage collection
+
+## Memory Management Patterns
+
+| Pattern | Languages | Mechanism |
+|---------|-----------|-----------|
+| Reference Counting | Swift, Obj-C, C++ (smart pointers) | Count refs, free at zero |
+| Garbage Collection | Kotlin/Java, JavaScript | GC scans and frees unreachable |
+| Manual | C, C++ (raw pointers) | Explicit new/delete |
+
+## C++ Smart Pointers
+
+### `std::unique_ptr` - Single Owner
+
+```cpp
+#include
+
+void takeOwnership(std::unique_ptr s) {
+ std::cout << *s;
+ // Automatically deleted when function ends
+}
+
+int main() {
+ auto str = std::make_unique("Hello");
+
+ // Can only be moved, not copied
+ takeOwnership(std::move(str));
+ // str is now empty
+
+ return 0;
+}
+```
+
+### `std::shared_ptr` - Multiple Owners
+
+```cpp
+void useShared(std::shared_ptr s) {
+ std::cout << *s; // Reference count temporarily +1
+}
+
+void useReference(const std::shared_ptr& s) {
+ std::cout << *s; // No ref count change (passed by reference)
+}
+
+int main() {
+ auto str = std::make_shared("Hello");
+
+ useShared(str); // Copies pointer, ref count +1
+ useReference(str); // No copy, ref count unchanged
+
+ std::cout << *str; // Still valid
+ return 0;
+}
+```
+
+### `std::weak_ptr` - Non-Owning Reference
+
+```cpp
+void useWeak(std::weak_ptr weak) {
+ if (auto shared = weak.lock()) { // Check if still exists
+ std::cout << *shared;
+ } else {
+ std::cout << "Object destroyed";
+ }
+}
+
+int main() {
+ auto str = std::make_shared("Hello");
+ std::weak_ptr weak = str; // No ref count increase
+
+ useWeak(weak); // Works
+ str.reset(); // Destroys object
+ useWeak(weak); // "Object destroyed"
+
+ return 0;
+}
+```
+
+## Swift ARC (Automatic Reference Counting)
+
+```swift
+class Person {
+ let name: String
+ init(name: String) { self.name = name }
+ deinit { print("Deallocated") }
+}
+
+do {
+ let person1 = Person(name: "John") // Ref count: 1
+
+ do {
+ let person2 = person1 // Ref count: 2
+ } // person2 out of scope, ref count: 1
+
+} // person1 out of scope, ref count: 0, "Deallocated"
+```
+
+### Breaking Reference Cycles with `weak`
+
+```swift
+// BAD: Reference cycle (memory leak)
+class A {
+ var b: B?
+}
+class B {
+ var a: A? // Strong reference creates cycle
+}
+
+// GOOD: Use weak to break cycle
+class A {
+ var b: B?
+}
+class B {
+ weak var a: A? // Weak reference, doesn't prevent deallocation
+}
+```
+
+## Kotlin/Android GC
+
+### WeakHashMap for Caches
+
+```kotlin
+val weakMap = WeakHashMap()
+
+run {
+ weakMap[String("temp")] = "value"
+ println(weakMap.size) // 1
+}
+
+System.gc() // Force garbage collection
+Thread.sleep(100)
+
+println(weakMap.size) // 0 (key was collected)
+```
+
+### WeakReference for Callbacks
+
+```kotlin
+class DataManager {
+ // Weak references to listeners prevent memory leaks
+ private val listeners = mutableListOf>()
+
+ fun addListener(listener: DataListener) {
+ listeners.add(WeakReference(listener))
+ }
+
+ fun notifyListeners(data: String) {
+ listeners.forEach { ref ->
+ ref.get()?.onDataChanged(data)
+ }
+ }
+}
+```
+
+## Common Memory Leak Sources
+
+### 1. Forgetting to Delete (C++)
+
+```cpp
+// BAD: Memory leak
+int main() {
+ std::string* str = new std::string("Hello");
+ // Forgot to delete!
+ return 0;
+}
+
+// GOOD: Use smart pointers or stack allocation
+int main() {
+ auto str = std::make_unique("Hello");
+ // Automatically deleted
+ return 0;
+}
+```
+
+### 2. Reference Cycles (Swift/C++)
+
+```cpp
+// BAD: Cycle
+class A { std::shared_ptr b; };
+class B { std::shared_ptr a; };
+
+// GOOD: Break with weak_ptr
+class A { std::shared_ptr b; };
+class B { std::weak_ptr a; };
+```
+
+### 3. Unremoved Listeners (Kotlin)
+
+```kotlin
+// BAD: Listener never removed
+class MyClass {
+ private val listener = object : Callback {
+ override fun onEvent() { /* ... */ }
+ }
+
+ init {
+ EventManager.addListener(listener)
+ // Never removed!
+ }
+}
+
+// GOOD: Implement cleanup
+class MyClass : AutoCloseable {
+ private val listener = object : Callback {
+ override fun onEvent() { /* ... */ }
+ }
+
+ init {
+ EventManager.addListener(listener)
+ }
+
+ override fun close() {
+ EventManager.removeListener(listener)
+ }
+}
+```
+
+## Swift `Unmanaged` (Advanced)
+
+For C interop, manually manage reference counts:
+
+```swift
+let obj = MyObject() // Ref count: 1
+
+// Increment manually
+let unmanaged = Unmanaged.passRetained(obj) // Ref count: 2
+
+// Decrement and get object
+let retrieved = unmanaged.takeRetainedValue() // Ref count: 1
+
+// Get raw pointer for C
+let pointer = unmanaged.toOpaque()
+```
+
+**Rule**: Match `passRetained` with `takeRetainedValue`, `passUnretained` with `takeUnretainedValue`.
+
+## Best Practices Summary
+
+| Language | Best Practice |
+|----------|---------------|
+| C++ | Use smart pointers (`shared_ptr`, `unique_ptr`) |
+| Swift | Use `weak` for delegates, breaking cycles |
+| Kotlin | Implement `AutoCloseable`, use `WeakReference` |
+| All | Prefer stack over heap when possible |
+
+## Related Skills
+
+- [native-memory-leaks.md](./native-memory-leaks.md) - Find leaks with profilers
+- [native-turbo-modules.md](./native-turbo-modules.md) - Build memory-safe modules
diff --git a/.opencode/skills/react-native-best-practices/references/native-platform-setup.md b/.opencode/skills/react-native-best-practices/references/native-platform-setup.md
new file mode 100644
index 0000000..2235e49
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-platform-setup.md
@@ -0,0 +1,110 @@
+---
+title: Platform Differences
+impact: MEDIUM
+tags: ios, android, xcode, gradle, cocoapods
+---
+
+# Skill: Platform Differences
+
+Navigate iOS and Android tooling, dependency management, and build systems in React Native.
+
+## Quick Reference
+
+| Platform | IDE | Package Manager | Build System |
+|----------|-----|-----------------|--------------|
+| JavaScript | VS Code | npm/yarn/pnpm/bun | Metro |
+| iOS | Xcode | CocoaPods | xcodebuild |
+| Android | Android Studio | Gradle | Gradle |
+
+```bash
+# Common commands
+bundle install # Install ruby bundler
+cd ios && bundle exec pod install # Install CocoaPods deps
+cd android && ./gradlew clean # Clean Android build
+xed ios/ # Open Xcode
+```
+
+## When to Use
+
+- Setting up native development environment
+- Adding native dependencies
+- Debugging platform-specific issues
+- Understanding build processes
+
+## Dependency Management
+
+### JavaScript (npm/yarn/pnpm/bun)
+
+Infer package manager from lockfile: `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `bun.lockb`.
+
+### iOS (CocoaPods)
+
+```bash
+# Install pods after npm install
+bundle install
+cd ios && bundle exec pod install
+
+# Key files
+ios/Podfile # Pod dependencies
+ios/Pods/ # Installed pods (gitignored)
+ios/*.xcworkspace # Open this in Xcode (not .xcodeproj)
+Gemfile # Ruby/CocoaPods version
+```
+
+
+### Android (Gradle)
+
+```bash
+# Sync after adding dependencies
+cd android && ./gradlew clean
+
+# Key files
+android/build.gradle # Project-level config
+android/app/build.gradle # App dependencies
+android/gradle.properties # Build flags
+android/gradlew # Gradle wrapper
+```
+
+## Common Commands
+
+```bash
+# iOS
+bundle install # Install ruby bundler
+cd ios && bundle exec pod install # Install pods
+xcrun simctl list # List simulators
+
+# Android
+cd android && ./gradlew clean # Clean build
+./gradlew tasks # List available tasks
+./gradlew assembleRelease # Build release APK
+
+# React Native CLI
+npx react-native start # Start Metro
+npx react-native run-ios # Run on iOS
+npx react-native run-android # Run on Android
+npx react-native build-ios # Build for iOS
+npx react-native build-android # Build for Android
+
+# Expo
+npx expo start # Start Metro (Expo)
+npx expo run:ios # Run on iOS (dev client)
+npx expo run:android # Run on Android (dev client)
+npx expo prebuild # Generate native projects
+```
+
+## Troubleshooting
+
+| Issue | Solution |
+|-------|----------|
+| Pod install fails | `cd ios && bundle exec pod install --repo-update` |
+| Xcode build fails | `cd ios && xcodebuild clean` |
+| Android Gradle sync fails | `./gradlew clean` then sync |
+| Can't find simulator | `xcrun simctl list` to verify name |
+| Metro cache issues | `npx react-native start --reset-cache` |
+| React Native cache issues | `npx react-native clean` |
+
+## Related Skills
+
+- [native-profiling.md](./native-profiling.md) - Use IDE profilers
+- [native-turbo-modules.md](./native-turbo-modules.md) - Build native modules
+- [upgrading-react-native.md](../../upgrading-react-native/references/upgrading-react-native.md) - Upgrade React Native safely
diff --git a/.opencode/skills/react-native-best-practices/references/native-profiling.md b/.opencode/skills/react-native-best-practices/references/native-profiling.md
new file mode 100644
index 0000000..05609c3
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-profiling.md
@@ -0,0 +1,176 @@
+---
+title: Profile Native Code
+impact: MEDIUM
+tags: xcode, instruments, android-studio, profiler
+---
+
+# Skill: Profile Native Code
+
+Use Xcode Instruments and Android Studio Profiler to identify native performance bottlenecks.
+
+## Quick Command
+
+```bash
+# iOS: Open Instruments
+# Xcode → Open Developer Tool → Instruments → Time Profiler
+
+# Android: Open Profiler
+# Android Studio → View → Tool Windows → Profiler
+```
+
+## When to Use
+
+- App is slow but JS profiler shows no issues
+- Investigating native module performance
+- Startup feels slow (native init)
+- Battery drain concerns
+- Need CPU/memory breakdown by thread
+
+> **Note**: This skill involves interpreting visual profiler output (Xcode Instruments, Android Studio Profiler). AI agents cannot yet process screenshots autonomously. Use this as a guide while reviewing the profiler UI manually, or await MCP-based visual feedback integration (see roadmap).
+
+## iOS Profiling with Xcode
+
+### Quick Check: Debug Navigator
+
+1. Run app via Xcode
+2. Open Debug Navigator (side panel)
+3. View real-time: CPU, Memory, Disk, Network
+
+**CPU percentage can exceed 100%** (multi-core usage).
+
+### Deep Profiling: Instruments
+
+1. Open: **Xcode → Open Developer Tool → Instruments**
+2. Select **Time Profiler**
+3. Choose target device and app
+4. Click record (red circle)
+5. Perform actions in app
+6. Stop recording
+
+### Analyzing Time Profiler Results
+
+**Key views:**
+- **Flame Graph**: Visual call stack over time
+- **Call Tree**: Hierarchical function breakdown
+- **Ranked**: Functions sorted by time (Bottom-Up)
+
+**Useful filters:**
+- Hide System Libraries
+- Invert Call Tree (bottom-up view)
+- Filter by thread (main, JS, etc.)
+
+**Identifying problems:**
+- **Microhang**: Brief UI unresponsiveness
+- **Hang**: Full UI thread block (critical)
+- Yellow = most time spent
+
+### Thread Breakdown
+
+Pin threads to compare:
+- **Main thread** (SampleApp): UI rendering
+- **JavaScript thread**: React/JS execution
+- **Background threads**: Native modules
+
+**Pro tip**: JS thread blocking ≠ UI block (React Native design benefit).
+
+## Android Profiling with Android Studio
+
+### Launch Profiler
+
+1. **View → Tool Windows → Profiler**
+2. Or: Click "Profile" in toolbar
+
+### CPU Profiling
+
+1. Select **"Find CPU Hotspots"**
+2. Click **"Start profiler task"**
+3. Interact with app
+4. Stop to analyze
+
+### Analyzing Results
+
+**Flame Graph:**
+- Zoom with scroll/pinch
+- Click to expand call stacks
+- Filter by keyword (e.g., "hermes")
+
+**Views:**
+- **Top Down**: From entry points down
+- **Bottom Up**: From slowest functions up
+- **Flame Chart**: Timeline visualization
+
+### Reading the Call Stack
+
+Example analysis:
+```
+JS Thread activity after button press:
+- Event handler on main thread
+- Triggers JS work via sync JSI calls
+- Hermes processes React reconciliation
+- ~30% time in "commit" phase (Yoga layout)
+```
+
+## Code Example: What to Look For
+
+### 5000 Views in ScrollView (Bad)
+
+Profiler shows:
+- 240ms+ JS thread work
+- Many 1ms Hermes spikes
+- Exceeds 16.6ms frame budget
+- Result: Dropped frames, UI jank
+
+### Using FlatList (Better)
+
+Profiler shows:
+- Minimal JS work (windowed rendering)
+- Smooth main thread
+- Stays within frame budget
+
+## Platform Tools Summary
+
+| Tool | Platform | Use Case |
+|------|----------|----------|
+| Time Profiler | iOS | CPU hotspots |
+| Leaks | iOS | Memory leaks |
+| Hangs | iOS | UI thread blocks |
+| CPU Profiler | Android | CPU hotspots |
+| Memory Profiler | Android | Memory tracking |
+| Perfetto | Android | Advanced trace analysis |
+
+## Perfetto (Advanced Android)
+
+Export traces from Android Studio and analyze at [ui.perfetto.dev](https://ui.perfetto.dev/):
+
+- Cross-process analysis
+- Custom trace events
+- Additional visualizations
+
+## Pro Tips
+
+1. **Profile on low-end devices**: Issues appear more clearly
+2. **Use release builds**: Debug builds have overhead
+3. **Compare before/after**: Export traces for comparison
+4. **Filter by thread**: Focus on relevant work
+5. **Look for patterns**: Spikes correlating with interactions
+
+## Expo Notes
+
+- **Expo Go**: Cannot profile native code directly; JS profiling only
+- **Dev Client / Prebuild**: Full native profiling supported via Xcode/Android Studio
+- Run `npx expo prebuild` to generate native projects, then profile as bare React Native
+
+## Common Findings
+
+| Symptom | Likely Cause |
+|---------|--------------|
+| Main thread hangs | Heavy UI work, blocked operations |
+| JS thread spikes | React re-renders, heavy computation |
+| Background thread busy | Native module work |
+| Memory climbing | Leak (see memory profiling skills) |
+
+## Related Skills
+
+- [native-measure-tti.md](./native-measure-tti.md) - Profile startup specifically
+- [native-memory-leaks.md](./native-memory-leaks.md) - Memory profiling
+- [js-profile-react.md](./js-profile-react.md) - JS/React profiling
diff --git a/.opencode/skills/react-native-best-practices/references/native-sdks-over-polyfills.md b/.opencode/skills/react-native-best-practices/references/native-sdks-over-polyfills.md
new file mode 100644
index 0000000..3bac263
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-sdks-over-polyfills.md
@@ -0,0 +1,192 @@
+---
+title: Native SDKs
+impact: HIGH
+tags: polyfills, intl, crypto, navigation, native
+---
+
+# Skill: Native SDKs
+
+Replace web polyfills and JS navigators with native React Native implementations for better performance.
+
+## Quick Pattern
+
+**Before (JS polyfills - 430+ KB):**
+
+```tsx
+import '@formatjs/intl-datetimeformat/polyfill';
+import CryptoJS from 'crypto-js';
+import { createStackNavigator } from '@react-navigation/stack';
+```
+
+**After (native implementations):**
+
+```tsx
+// Hermes has native Intl.DateTimeFormat support, so this polyfill is often unnecessary
+import { createHash } from 'react-native-quick-crypto'; // 58x faster
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+```
+
+## When to Use
+
+- Large JS bundle from polyfills
+- Navigation feels non-native
+- Crypto operations are slow
+- Internationalization bloating bundle
+
+## Step-by-Step Instructions
+
+### 1. Remove Unnecessary Intl Polyfills
+
+Hermes supports many `Intl` APIs natively, but not every constructor and method combination across platforms. Audit the exact APIs and methods you use before removing polyfills:
+
+```tsx
+// BEFORE: All these polyfills (430+ KB)
+import '@formatjs/intl-getcanonicallocales/polyfill';
+import '@formatjs/intl-locale/polyfill';
+import '@formatjs/intl-numberformat/polyfill';
+import '@formatjs/intl-numberformat/locale-data/en';
+import '@formatjs/intl-datetimeformat/polyfill';
+import '@formatjs/intl-datetimeformat/locale-data/en';
+import '@formatjs/intl-pluralrules/polyfill';
+import '@formatjs/intl-pluralrules/locale-data/en';
+import '@formatjs/intl-relativetimeformat/polyfill';
+import '@formatjs/intl-relativetimeformat/locale-data/en';
+import '@formatjs/intl-displaynames/polyfill';
+```
+
+**Hermes Support (as of March 2026):**
+
+| API | Hermes | Keep Polyfill? |
+|-----|--------|----------------|
+| `Intl.Collator` | ✅ | No |
+| `Intl.DateTimeFormat` | ✅ | No |
+| `Intl.NumberFormat` | ⚠️ Partial | Maybe |
+| `Intl.getCanonicalLocales()` | ✅ | No |
+| `Intl.supportedValuesOf()` | ✅ | No |
+| `Intl.Locale` | ❌ | Yes |
+| `Intl.PluralRules` | ❌ | Yes |
+| `Intl.RelativeTimeFormat` | ❌ | Yes |
+| `Intl.DisplayNames` | ❌ | Yes |
+| `Intl.ListFormat` | ❌ | Yes |
+| `Intl.Segmenter` | ❌ | Yes |
+
+`Intl.NumberFormat` is not fully covered on Hermes across platforms. In particular, `Intl.NumberFormat.prototype.formatToParts()` still has an iOS gap, so keep `@formatjs/intl-numberformat` if your app relies on that method.
+
+```tsx
+// AFTER: Keep only the polyfills your app still needs
+import '@formatjs/intl-locale/polyfill';
+import '@formatjs/intl-pluralrules/polyfill';
+import '@formatjs/intl-pluralrules/locale-data/en';
+import '@formatjs/intl-relativetimeformat/polyfill';
+import '@formatjs/intl-relativetimeformat/locale-data/en';
+import '@formatjs/intl-displaynames/polyfill';
+```
+
+If you use `Intl.NumberFormat.prototype.formatToParts()` on Hermes/iOS, also keep:
+
+```tsx
+import '@formatjs/intl-numberformat/polyfill';
+import '@formatjs/intl-numberformat/locale-data/en';
+```
+
+### 2. Use Native Crypto
+
+Replace JS crypto with native C++ implementation:
+
+```bash
+npm install react-native-quick-crypto
+```
+
+**Performance**: Up to 58x faster than `crypto-js`.
+
+```tsx
+// BEFORE: Slow JS implementation
+import CryptoJS from 'crypto-js';
+
+// AFTER: Native C++ implementation
+import { createHash } from 'react-native-quick-crypto';
+```
+
+Essential for:
+- Web3 wallet seed generation
+- CSPRNG (Cryptographically Secure Random Numbers)
+- Any heavy cryptographic operations
+
+### 3. Use Native Stack Navigator
+
+```bash
+npm install @react-navigation/native-stack react-native-screens
+```
+
+```tsx
+// BEFORE: JS-based stack (more flexible, less native)
+import { createStackNavigator } from '@react-navigation/stack';
+const Stack = createStackNavigator();
+
+// AFTER: Native stack (native feel, better performance)
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+const Stack = createNativeStackNavigator();
+
+// Usage is nearly identical
+
+
+
+
+```
+
+**Benefits:**
+- Native navigation animations
+- Platform-specific headers (large titles on iOS)
+- Lower memory usage
+- Offloads work from JS thread
+
+### 4. Use Native Bottom Tabs
+
+```bash
+npm install @bottom-tabs/react-navigation react-native-bottom-tabs
+```
+
+```tsx
+// BEFORE: JS tabs
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+const Tabs = createBottomTabNavigator();
+
+// AFTER: Native tabs
+import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation';
+const Tabs = createNativeBottomTabNavigator();
+
+
+
+
+
+```
+
+## Recommended Native Libraries
+
+| Category | Library | Description |
+|----------|---------|-------------|
+| Navigation | `react-native-screens` | Native screen containers |
+| Menus | `zeego` | Native menus (Radix-like API) |
+| Slider | `@react-native-community/slider` | Native slider |
+| Date Picker | `react-native-date-picker` | Native date/time picker |
+| Image | `react-native-fast-image` | Native image caching |
+
+## Decision Matrix
+
+| Scenario | Use Native? | Tradeoff |
+|----------|-------------|----------|
+| Standard navigation | ✅ Yes | Slight API differences |
+| Custom transition animations | ⚠️ Maybe | Native is more limited |
+| Platform-consistent UI | ✅ Yes | Less customization |
+| Unique/branded design | ⚠️ Consider JS | Native may not support |
+
+## Common Pitfalls
+
+- **Assuming constructor support means full method coverage**: Check the specific Hermes API and methods you call
+- **Ignoring migration effort**: Native navigators have slightly different APIs
+- **Over-customizing native components**: If design requires heavy customization, JS might be better
+
+## Related Skills
+
+- [bundle-analyze-js.md](./bundle-analyze-js.md) - Measure polyfill impact
+- [bundle-library-size.md](./bundle-library-size.md) - Compare library sizes
diff --git a/.opencode/skills/react-native-best-practices/references/native-threading-model.md b/.opencode/skills/react-native-best-practices/references/native-threading-model.md
new file mode 100644
index 0000000..0c35815
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-threading-model.md
@@ -0,0 +1,234 @@
+---
+title: Threading Model
+impact: HIGH
+tags: threads, turbo-modules, fabric, async, sync
+---
+
+# Skill: Threading Model
+
+Understand which threads Turbo Modules and Fabric use for initialization, method calls, and view updates.
+
+## Quick Reference
+
+| Action | iOS Thread | Android Thread |
+|--------|------------|----------------|
+| Module init | Main | JS (lazy) / Native (eager) |
+| Sync method | JS | JS |
+| Async method | Native modules | Native modules |
+| View init/props | Main | Main |
+| Yoga layout | JS | JS |
+
+**Key rule**: Sync methods block JS thread. Keep under 16ms or make async.
+
+## When to Use
+
+- Building native modules
+- Debugging threading issues
+- Accessing UI from native code
+- Understanding async vs sync method behavior
+
+## Available Threads
+
+| Thread | Name in Debugger | Purpose |
+|--------|------------------|---------|
+| Main/UI | Main thread | UI rendering, UIKit/Android Views |
+| JavaScript | `mqt_v_js` | JS execution, React |
+| Native Modules | `mqt_v_native` | Async Turbo Module calls |
+| Custom | Various | Your background threads |
+
+## Turbo Modules Threading
+
+### Initialization
+
+| Platform | Thread | Notes |
+|----------|--------|-------|
+| iOS | Main thread | Assumes UIKit access needed |
+| Android (lazy) | JS thread | Default behavior |
+| Android (eager) | Native modules thread | When `needsEagerInit = true` |
+
+**iOS**: React Native runs `init` on main thread assuming UIKit access.
+
+**Android Eager Loading:**
+
+```kotlin
+// ReactModuleInfo constructor params:
+// canOverrideExistingModule, needsEagerInit, isCxxModule, isTurboModule
+ReactModuleInfo(
+ AwesomeModule.NAME,
+ AwesomeModule.NAME,
+ false,
+ true, // needsEagerInit = true → runs on native modules thread
+ false,
+ true
+)
+```
+
+### Synchronous Method Calls
+
+**Always run on JS thread** - blocks until return.
+
+```swift
+// iOS - runs on JS thread
+@objc func multiply(_ a: Double, b: Double) -> NSNumber {
+ // This blocks JS for entire duration!
+ return a * b as NSNumber
+}
+```
+
+**Danger**: Long sync operations freeze the app:
+
+```swift
+// BAD: Blocks JS for 20 seconds
+@objc func multiply(_ a: Double, b: Double) -> NSNumber {
+ Thread.sleep(forTimeInterval: 20) // App frozen!
+ return a * b as NSNumber
+}
+```
+
+### Asynchronous Method Calls
+
+**Run on Native Modules thread** - doesn't block JS.
+
+```swift
+// iOS - runs on mqt_v_native thread
+@objc func asyncOperation(
+ _ a: Double,
+ resolve: @escaping RCTPromiseResolveBlock,
+ reject: RCTPromiseRejectBlock
+) {
+ // Already on background thread
+ resolve(a * 2)
+}
+```
+
+```kotlin
+// Android - runs on native modules thread
+override fun asyncOperation(a: Double, promise: Promise?) {
+ // Already on background thread
+ promise?.resolve(a * 2)
+}
+```
+
+### Module Invalidation
+
+Called when React Native instance is torn down (e.g., Metro reload):
+
+| Platform | Thread |
+|----------|--------|
+| iOS | Native modules thread |
+| Android | ReactHost thread pool |
+
+**iOS**: Implement `RCTInvalidating` protocol.
+
+## Fabric (Native Views) Threading
+
+### View Lifecycle
+
+| Operation | Thread |
+|-----------|--------|
+| View init | Main thread |
+| Prop updates | Main thread |
+| Layout (Yoga) | JS thread |
+
+Views always manipulate UI on main thread (UIKit/Android requirement).
+
+### Yoga Layout
+
+Layout calculations happen on JS thread:
+
+```
+JS Thread: Calculate Yoga tree → Shadow tree
+Main Thread: Apply layout to native views
+```
+
+## Moving Work to Background
+
+### iOS: DispatchQueue
+
+```swift
+@objc func heavyWork(
+ resolve: @escaping RCTPromiseResolveBlock,
+ reject: RCTPromiseRejectBlock
+) {
+ DispatchQueue.global().async {
+ // Heavy computation here
+ let result = self.compute()
+ resolve(result)
+ }
+}
+```
+
+### Android: Coroutines
+
+```kotlin
+class MyModule(reactContext: ReactApplicationContext) :
+ NativeMyModuleSpec(reactContext) {
+
+ private val moduleScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
+
+ override fun heavyWork(promise: Promise?) {
+ moduleScope.launch {
+ // Heavy computation here
+ val result = compute()
+ promise?.resolve(result)
+ }
+ }
+
+ override fun invalidate() {
+ super.invalidate()
+ moduleScope.cancel() // Important: cancel to prevent leaks
+ }
+}
+```
+
+## Thread Safety Checklist
+
+| Scenario | Safe? | Solution |
+|----------|-------|----------|
+| Sync method accessing shared state | ⚠️ | Use locks/synchronized |
+| Async method accessing UI | ❌ | Dispatch to main thread |
+| Multiple async calls to same resource | ⚠️ | Queue or mutex |
+| Accessing JS from background | ❌ | Use CallInvoker |
+
+### Accessing UI from Background (iOS)
+
+```swift
+DispatchQueue.global().async {
+ let result = self.heavyComputation()
+
+ DispatchQueue.main.async {
+ // Safe to update UI here
+ self.updateUI(with: result)
+ }
+}
+```
+
+### Accessing UI from Background (Android)
+
+```kotlin
+moduleScope.launch(Dispatchers.Default) {
+ val result = heavyComputation()
+
+ withContext(Dispatchers.Main) {
+ // Safe to update UI here
+ updateUI(result)
+ }
+}
+```
+
+## Summary Table
+
+| Action | iOS Thread | Android Thread |
+|--------|------------|----------------|
+| Module init | Main | JS (lazy) / Native (eager) |
+| Sync method | JS | JS |
+| Async method | Native modules | Native modules |
+| View init | Main | Main |
+| Prop update | Main | Main |
+| Yoga layout | JS | JS |
+| Invalidate | Native modules | ReactHost pool |
+
+## Related Skills
+
+- [native-turbo-modules.md](./native-turbo-modules.md) - Implement background threads
+- [native-profiling.md](./native-profiling.md) - Debug thread issues
diff --git a/.opencode/skills/react-native-best-practices/references/native-turbo-modules.md b/.opencode/skills/react-native-best-practices/references/native-turbo-modules.md
new file mode 100644
index 0000000..96e735c
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-turbo-modules.md
@@ -0,0 +1,292 @@
+---
+title: Fast Native Modules
+impact: HIGH
+tags: turbo-modules, native, swift, kotlin, c++
+---
+
+# Skill: Fast Native Modules
+
+Build performant Turbo Modules using modern languages and background threading.
+
+## Quick Pattern
+
+**Incorrect (sync method blocks JS thread):**
+
+```swift
+@objc func heavyWork() -> NSNumber {
+ Thread.sleep(forTimeInterval: 2) // Blocks JS for 2s!
+ return 42
+}
+```
+
+**Correct (async on background thread):**
+
+```swift
+@objc func heavyWork(
+ resolve: @escaping RCTPromiseResolveBlock,
+ reject: RCTPromiseRejectBlock
+) {
+ DispatchQueue.global().async {
+ let result = self.compute()
+ resolve(result)
+ }
+}
+```
+
+## When to Use
+
+- Creating new native modules
+- Optimizing existing module performance
+- Heavy computation needs to run off JS thread
+- Cross-platform C++ code needed
+
+## Prerequisites
+
+- React Native Builder Bob for scaffolding
+
+```bash
+npx create-react-native-library@latest my-library
+```
+
+## Step-by-Step Instructions
+
+### 1. Scaffold with Builder Bob
+
+```bash
+npx create-react-native-library@latest awesome-library
+# Follow prompts: choose Turbo Module, select languages
+```
+
+Creates ready-to-publish library with:
+- iOS (Obj-C/Swift) support
+- Android (Kotlin) support
+- TypeScript definitions
+- Codegen setup
+
+For local modules:
+
+```bash
+npx create-react-native-library@latest awesome-library --local
+```
+
+### 2. Enable Swift in iOS Module
+
+Update `awesome-library.podspec`:
+
+```diff
+- s.source_files = "ios/**/*.{h,m,mm,cpp}"
++ s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"
+```
+
+Create Swift file in Xcode (accept bridging header prompt).
+
+Update header file for Swift compatibility:
+
+```objc
+// AwesomeLibrary.h
+#import
+
+#if __cplusplus
+#import "ReactCodegen/RNAwesomeLibrarySpec/RNAwesomeLibrarySpec.h"
+#endif
+
+@interface AwesomeLibrary : NSObject
+#if __cplusplus
+
+#endif
+@end
+```
+
+Import header in bridging header:
+
+```objc
+// AwesomeLibrary-Bridging-Header.h
+#import "AwesomeLibrary.h"
+```
+
+Implement in Swift:
+
+```swift
+// AwesomeLibrary.swift
+import Foundation
+
+extension AwesomeLibrary {
+ @objc func multiply(_ a: Double, b: Double) -> NSNumber {
+ return (a * b) as NSNumber
+ }
+}
+```
+
+Bridge in Obj-C++:
+
+```objc
+// AwesomeLibrary.mm
+#import "AwesomeLibrary.h"
+
+#if __has_include("awesome_library/awesome_library-Swift.h")
+#import "awesome_library/awesome_library-Swift.h"
+#else
+#import "awesome_library-Swift.h"
+#endif
+
+@implementation AwesomeLibrary
+RCT_EXPORT_MODULE()
+RCT_EXTERN_METHOD(multiply:(double)a b:(double)b);
+@end
+```
+
+### 3. Run on Background Thread (iOS)
+
+```swift
+@objc func heavyOperation(
+ _ input: Double,
+ resolve: @escaping RCTPromiseResolveBlock,
+ reject: RCTPromiseRejectBlock
+) {
+ DispatchQueue.global().async {
+ // Heavy work on background thread
+ let result = self.expensiveComputation(input)
+ resolve(result)
+ }
+}
+```
+
+### 4. Run on Background Thread (Android)
+
+```kotlin
+class AwesomeLibraryModule(reactContext: ReactApplicationContext) :
+ NativeAwesomeLibrarySpec(reactContext) {
+
+ private val moduleScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
+
+ override fun heavyOperation(input: Double, promise: Promise?) {
+ moduleScope.launch {
+ // Heavy work on coroutine
+ val result = expensiveComputation(input)
+ promise?.resolve(result)
+ }
+ }
+
+ override fun invalidate() {
+ super.invalidate()
+ moduleScope.cancel() // Prevent memory leaks!
+ }
+}
+```
+
+### 5. Use C++ for Cross-Platform Code
+
+Create C++ Turbo Module for shared logic:
+
+```cpp
+// MyCppModule.h
+#pragma once
+
+#include
+
+namespace facebook::react {
+
+class MyCppModule : public TurboModule {
+public:
+ MyCppModule(std::shared_ptr jsInvoker);
+
+ double multiply(double a, double b);
+};
+
+} // namespace facebook::react
+```
+
+Register for iOS auto-linking:
+
+```objc
+// MyCppModuleRegistration.mm
+#include
+
+@implementation MyCppModuleRegistration
+
++ (void)load {
+ facebook::react::registerCxxModuleToGlobalModuleMap(
+ std::string(facebook::react::MyCppModule::kModuleName),
+ [&](std::shared_ptr jsInvoker) {
+ return std::make_shared(jsInvoker);
+ }
+ );
+}
+
+@end
+```
+
+## Threading Summary
+
+| Method Type | Default Thread | Best Practice |
+|-------------|----------------|---------------|
+| Sync | JS thread | Keep fast (<16ms) |
+| Async | Native modules thread | OK for moderate work |
+| Heavy async | Custom background | Use DispatchQueue/Coroutines |
+
+## Language Interop Costs
+
+| Interface | Overhead | Notes |
+|-----------|----------|-------|
+| Obj-C ↔ C++ | ~0 | Compile-time |
+| Swift ↔ C++ | ~0 | Swift 5.9+ interop |
+| Kotlin ↔ C++ (JNI) | Medium | Per-call lookup |
+| C++ Turbo Module | Low | JSI direct access |
+
+**Tip**: C++ Turbo Modules skip JNI at runtime since JS holds direct C++ function references via JSI.
+
+## Code Example: Complete Async Operation
+
+```typescript
+// TypeScript interface
+export interface Spec extends TurboModule {
+ multiply(a: number, b: number): number; // Sync
+ heavyOperation(input: number): Promise; // Async
+}
+```
+
+```kotlin
+// Android implementation
+override fun heavyOperation(input: Double, promise: Promise?) {
+ moduleScope.launch {
+ try {
+ val result = withContext(Dispatchers.Default) {
+ // Simulate heavy work
+ delay(1000)
+ input * 2
+ }
+ promise?.resolve(result)
+ } catch (e: Exception) {
+ promise?.reject("ERROR", e.message)
+ }
+ }
+}
+```
+
+```swift
+// iOS implementation
+@objc func heavyOperation(
+ _ input: Double,
+ resolve: @escaping RCTPromiseResolveBlock,
+ reject: @escaping RCTPromiseRejectBlock
+) {
+ DispatchQueue.global(qos: .userInitiated).async {
+ // Simulate heavy work
+ Thread.sleep(forTimeInterval: 1.0)
+ let result = input * 2
+ resolve(result)
+ }
+}
+```
+
+## Common Pitfalls
+
+- **Sync methods that block**: Keep under 16ms or make async
+- **Forgetting to cancel coroutine scope**: Causes memory leaks
+- **Not handling errors in async**: Always try/catch with reject
+- **Accessing UI from background**: Dispatch to main thread
+
+## Related Skills
+
+- [native-threading-model.md](./native-threading-model.md) - Thread details
+- [native-memory-patterns.md](./native-memory-patterns.md) - Memory in native code
diff --git a/.opencode/skills/react-native-best-practices/references/native-view-flattening.md b/.opencode/skills/react-native-best-practices/references/native-view-flattening.md
new file mode 100644
index 0000000..9253041
--- /dev/null
+++ b/.opencode/skills/react-native-best-practices/references/native-view-flattening.md
@@ -0,0 +1,201 @@
+---
+title: View Flattening
+impact: MEDIUM
+tags: views, flattening, collapsable, hierarchy
+---
+
+# Skill: View Flattening
+
+Understand and debug React Native's view flattening optimization.
+
+## Quick Pattern
+
+**Problem (children get flattened unexpectedly):**
+
+```jsx
+
+ // May be flattened, breaking native component
+
+
+```
+
+**Solution (prevent flattening):**
+
+```jsx
+
+
+
+
+```
+
+## When to Use
+
+- Native component receives unexpected number of children
+- Layout debugging with native components
+- Building native components that accept children
+- Understanding React Native rendering
+
+> **Note**: This skill involves interpreting visual view hierarchy tools (Xcode Debug View Hierarchy, Android Layout Inspector). AI agents cannot yet process screenshots autonomously. Use this as a guide while reviewing the hierarchy manually, or await MCP-based visual feedback integration (see roadmap).
+
+## What is View Flattening?
+
+React Native's renderer automatically removes "layout-only" views that:
+- Only affect layout (no visual rendering)
+- Don't need to exist in native view hierarchy
+
+**Benefits**: Reduced memory, faster rendering, shallower view tree.
+
+## The Problem with Native Components
+
+```tsx
+// You expect 3 children
+
+
+
+
+
+```
+
+If `Child1` is flattened, its internal views become direct children:
+
+```tsx
+// Native side receives 5 views instead of 3!
+
+ // Was inside Child1
+ // Was inside Child1
+ // Was inside Child1
+
+
+
+```
+
+## Preventing Flattening with `collapsable`
+
+```tsx
+
+
+
+
+
+```
+
+Now native side always receives exactly 3 children.
+
+## Debugging View Hierarchy
+
+
+
+Use native debugging tools to see the actual view hierarchy:
+
+### Xcode (iOS)
+
+1. Run app via Xcode
+2. Click **"Debug View Hierarchy"** in debug toolbar (shown in image)
+3. Inspect 3D view of native hierarchy
+
+**React Native components map to:**
+- `` → `RCTViewComponentView`
+- `` → `RCTTextView`
+
+### Android Studio
+
+1. Run app via Android Studio
+2. **View → Tool Windows → Layout Inspector**
+3. Select running process
+
+**React Native components map to:**
+- `` → `ReactViewGroup`
+- `` → `ReactTextView`
+
+## Code Examples
+
+### When Flattening Breaks Your Component
+
+```tsx
+// Your native component expects exactly 2 tabs
+const NativeTabBar = requireNativeComponent('RCTTabBar');
+
+// BAD: TabContent might get flattened
+const MyTabs = () => (
+
+
+ Home content
+
+
+ Profile content
+
+
+);
+
+// GOOD: Prevent flattening
+const MyTabs = () => (
+
+
+ Home content
+
+
+ Profile content
+
+
+);
+```
+
+### Wrapper Component with collapsable
+
+```tsx
+// Wrapper that prevents flattening
+const NativeChildWrapper = ({ children, ...props }) => (
+
+ {children}
+
+);
+
+// Usage
+
+
+
+
+
+```
+
+## When Views Get Flattened
+
+Views are considered "layout-only" when they:
+- Have no `backgroundColor`
+- Have no `borderWidth`, `borderColor`
+- Have no `shadowColor`, `elevation`
+- Don't handle events (no `onPress`, etc.)
+- Don't use `opacity` < 1
+- Don't have `overflow: 'hidden'`
+
+## Forcing a View to Stay
+
+Besides `collapsable={false}`, these also prevent flattening:
+
+```tsx
+// Any of these prevent flattening
+
+
+
+ {}} />
+```
+
+But `collapsable={false}` is the cleanest solution.
+
+## Debugging Checklist
+
+1. **Check native child count**: Log received children in native code
+2. **Use Layout Inspector**: Visual hierarchy debugging
+3. **Add collapsable={false}**: Test if flattening is the issue
+4. **Check wrapper components**: Intermediate views may be flattened
+
+## Common Pitfalls
+
+- **Assuming JS children = native children**: Flattening changes this
+- **Not documenting native component requirements**: If your native component expects specific child count, document it
+- **Over-using collapsable={false}**: Only use when necessary (loses optimization benefits)
+
+## Related Skills
+
+- [native-platform-setup.md](./native-platform-setup.md) - IDE setup for debugging
+- [native-profiling.md](./native-profiling.md) - Performance impact analysis
diff --git a/.opencode/skills/upgrading-react-native/SKILL.md b/.opencode/skills/upgrading-react-native/SKILL.md
new file mode 100644
index 0000000..524d4d1
--- /dev/null
+++ b/.opencode/skills/upgrading-react-native/SKILL.md
@@ -0,0 +1,82 @@
+---
+name: upgrading-react-native
+description: Upgrades React Native apps to newer versions by applying rn-diff-purge template diffs, updating package.json dependencies, migrating native iOS and Android configuration, resolving CocoaPods and Gradle changes, and handling breaking API updates. Use when upgrading React Native, bumping RN version, updating from RN 0.x to 0.y, or migrating Expo SDK alongside a React Native upgrade.
+license: MIT
+metadata:
+ author: Callstack
+ tags: react-native, upgrade, upgrade-helper, npm, changelog, cocoapods, ios, android
+---
+
+# Upgrading React Native
+
+## Overview
+
+Covers the full React Native upgrade workflow: template diffs via Upgrade Helper, dependency updates, Expo SDK steps, and common pitfalls.
+
+## Typical Upgrade Sequence
+
+1. **Route**: Choose the right upgrade path via [upgrading-react-native.md][upgrading-react-native]
+2. **Diff**: Fetch the canonical template diff using Upgrade Helper via [upgrade-helper-core.md][upgrade-helper-core]
+3. **Dependencies**: Assess and update third-party packages via [upgrading-dependencies.md][upgrading-dependencies]
+4. **React**: Align React version if upgraded via [react.md][react]
+5. **Expo** (if applicable): Apply Expo SDK layer via [expo-sdk-upgrade.md][expo-sdk-upgrade]
+6. **Verify**: Run post-upgrade checks via [upgrade-verification.md][upgrade-verification]
+
+```bash
+# Quick start: detect current version and fetch diff
+npm pkg get dependencies.react-native --prefix "$APP_DIR"
+npm view react-native dist-tags.latest
+
+# Example: upgrading from 0.76.9 to 0.78.2
+# 1. Fetch the template diff
+curl -L -f -o /tmp/rn-diff.diff \
+ "https://raw.githubusercontent.com/react-native-community/rn-diff-purge/diffs/diffs/0.76.9..0.78.2.diff" \
+ && echo "Diff downloaded OK" || echo "ERROR: diff not found, check versions"
+# 2. Review changed files
+grep -n "^diff --git" /tmp/rn-diff.diff
+# 3. Update package.json, apply native changes, then install + rebuild
+npm install --prefix "$APP_DIR"
+cd "$APP_DIR/ios" && pod install
+# 4. Validate: both platforms must build successfully
+npx react-native build-android --mode debug --no-packager
+xcodebuild -workspace "$APP_DIR/ios/App.xcworkspace" -scheme App -sdk iphonesimulator build
+```
+
+## When to Apply
+
+Reference these guidelines when:
+- Moving a React Native app to a newer version
+- Reconciling native config changes from Upgrade Helper
+- Validating release notes for breaking changes
+
+## Quick Reference
+
+| File | Description |
+|------|-------------|
+| [upgrading-react-native.md][upgrading-react-native] | Router: choose the right upgrade path |
+| [upgrade-helper-core.md][upgrade-helper-core] | Core Upgrade Helper workflow and reliability gates |
+| [upgrading-dependencies.md][upgrading-dependencies] | Dependency compatibility checks and migration planning |
+| [react.md][react] | React and React 19 upgrade alignment rules |
+| [expo-sdk-upgrade.md][expo-sdk-upgrade] | Expo SDK-specific upgrade layer (conditional) |
+| [upgrade-verification.md][upgrade-verification] | Manual post-upgrade verification checklist |
+| [monorepo-singlerepo-targeting.md][monorepo-singlerepo-targeting] | Monorepo and single-repo app targeting and command scoping |
+
+## Problem → Skill Mapping
+
+| Problem | Start With |
+|---------|------------|
+| Need to upgrade React Native | [upgrade-helper-core.md][upgrade-helper-core] |
+| Need dependency risk triage and migration options | [upgrading-dependencies.md][upgrading-dependencies] |
+| Need React/React 19 package alignment | [react.md][react] |
+| Need workflow routing first | [upgrading-react-native.md][upgrading-react-native] |
+| Need Expo SDK-specific steps | [expo-sdk-upgrade.md][expo-sdk-upgrade] |
+| Need manual regression validation | [upgrade-verification.md][upgrade-verification] |
+| Need repo/app command scoping | [monorepo-singlerepo-targeting.md][monorepo-singlerepo-targeting] |
+
+[upgrading-react-native]: references/upgrading-react-native.md
+[upgrade-helper-core]: references/upgrade-helper-core.md
+[upgrading-dependencies]: references/upgrading-dependencies.md
+[react]: references/react.md
+[expo-sdk-upgrade]: references/expo-sdk-upgrade.md
+[upgrade-verification]: references/upgrade-verification.md
+[monorepo-singlerepo-targeting]: references/monorepo-singlerepo-targeting.md
diff --git a/.opencode/skills/upgrading-react-native/agents/openai.yaml b/.opencode/skills/upgrading-react-native/agents/openai.yaml
new file mode 100644
index 0000000..c5152f6
--- /dev/null
+++ b/.opencode/skills/upgrading-react-native/agents/openai.yaml
@@ -0,0 +1,4 @@
+interface:
+ display_name: "Upgrade React Native"
+ short_description: "React Native upgrade workflow updated templates, dependencies, and common pitfalls"
+ default_prompt: "Use $upgrading-react-native to plan and execute a React Native upgrade."
diff --git a/.opencode/skills/upgrading-react-native/references/expo-sdk-upgrade.md b/.opencode/skills/upgrading-react-native/references/expo-sdk-upgrade.md
new file mode 100644
index 0000000..4753bd4
--- /dev/null
+++ b/.opencode/skills/upgrading-react-native/references/expo-sdk-upgrade.md
@@ -0,0 +1,65 @@
+---
+title: Expo SDK Upgrade Layer
+impact: HIGH
+tags: expo, sdk, react-native, dependencies
+---
+
+# Skill: Expo SDK Upgrade Layer
+
+Expo-specific add-on to the core Upgrade Helper workflow. Use only when `expo` exists in app `package.json`.
+
+## Quick Commands
+
+```bash
+npm pkg get dependencies.expo devDependencies.expo --prefix "$APP_DIR"
+cd "$APP_DIR" && npx expo install --fix
+cd "$APP_DIR" && npx expo-doctor
+```
+
+## When to Apply
+
+- `expo` or `expo-updates` is present in the target app package
+- RN upgrade is paired with Expo SDK upgrade
+
+## Official Expo Reference
+
+- Follow Expo's official upgrade skill as a primary guide:
+ - [Expo Upgrading Expo Skill][expo-upgrading-expo-skill]
+- Important for this workflow: skip `app.json` changes, because this is not an Expo Managed project.
+
+## Pre-Upgrade Audit (Required)
+
+- Confirm SDK version and target path.
+- Inventory dependencies and native modules.
+- Review config plugins and prebuild behavior.
+- Review native build setup (Gradle, iOS settings, CI/EAS config).
+- Identify critical app flows for regression testing before changes.
+
+## Workflow Additions
+
+1. Run Expo compatibility alignment.
+ - `npx expo install --fix` (source of truth for SDK-compatible versions).
+ - Treat `expo-modules` package versions as SDK-coupled; align them with Expo recommendations.
+2. Run health checks.
+ - `npx expo-doctor`; resolve blocking issues first.
+3. If native folders are part of workflow, reconcile prebuild output.
+ - `npx expo prebuild --clean` (when applicable).
+4. Handle React 19 pairing.
+ - Run [react.md](react.md).
+5. Run [upgrade-verification.md](upgrade-verification.md) for manual regression checks and release gates.
+
+## Notes
+
+- Use `npx expo ...`; do not require global `expo-cli`.
+- Some package bumps may be optional if not SDK-bound; validate before deferring.
+- Read Expo and React Native release notes deeply before editing code, then map each breaking change to a concrete code/task item.
+
+## Related Skills
+
+- [upgrading-react-native.md](upgrading-react-native.md) - Routing and mode selection
+- [upgrade-helper-core.md](upgrade-helper-core.md) - Base upgrade workflow
+- [react.md](react.md) - React and React 19 alignment
+- [upgrade-verification.md](upgrade-verification.md) - Manual post-upgrade validation
+- [monorepo-singlerepo-targeting.md](monorepo-singlerepo-targeting.md) - Repo/app selection and command scoping
+
+[expo-upgrading-expo-skill]: https://github.com/expo/skills/blob/main/plugins/upgrading-expo/skills/upgrading-expo/SKILL.md
diff --git a/.opencode/skills/upgrading-react-native/references/monorepo-singlerepo-targeting.md b/.opencode/skills/upgrading-react-native/references/monorepo-singlerepo-targeting.md
new file mode 100644
index 0000000..8c26c84
--- /dev/null
+++ b/.opencode/skills/upgrading-react-native/references/monorepo-singlerepo-targeting.md
@@ -0,0 +1,34 @@
+---
+title: Monorepo vs Single-App Targeting
+impact: HIGH
+tags: monorepo, workspace, react-native, app-selection
+---
+
+# Skill: Monorepo vs Single-App Targeting
+
+Small instruction set for selecting the correct app package and running upgrade commands in the right scope.
+
+## Quick Commands
+
+```bash
+APP_DIR=apps/mobile
+npm pkg get dependencies.react-native devDependencies.react-native --prefix "$APP_DIR"
+```
+
+## Rules
+
+1. Pick one target app package before any upgrade command.
+2. Run all app-specific commands with `--prefix "$APP_DIR"` or from `cd "$APP_DIR"`.
+3. Use `APP_DIR=.` for single-package repos.
+4. Never apply diffs to workspace root when RN app lives in a subpackage.
+
+## Verification
+
+- `react-native` is present in `APP_DIR/package.json`.
+- `ios/` and `android/` paths used for build/pods are under `APP_DIR`.
+
+## Related Skills
+
+- [upgrading-react-native.md](upgrading-react-native.md) - Routing and mode selection
+- [upgrade-helper-core.md](upgrade-helper-core.md) - Base upgrade workflow
+- [expo-sdk-upgrade.md](expo-sdk-upgrade.md) - Expo-specific steps
diff --git a/.opencode/skills/upgrading-react-native/references/react.md b/.opencode/skills/upgrading-react-native/references/react.md
new file mode 100644
index 0000000..33e9144
--- /dev/null
+++ b/.opencode/skills/upgrading-react-native/references/react.md
@@ -0,0 +1,42 @@
+---
+title: React Upgrade Layer
+impact: HIGH
+tags: react, react-19, rntl, types
+---
+
+# Skill: React Upgrade Layer
+
+React-specific upgrade rules to run when `react` changes during a React Native or Expo upgrade.
+
+## When to Apply
+
+- `react` version changes (major, minor, or patch).
+- React Native target implies a newer React pairing.
+- Tests/types break after React upgrade.
+
+## React Pairing Rules
+
+1. Keep companion packages aligned with installed React version:
+ - `react-test-renderer`
+ - `@types/react`
+ - `react-dom` (if used by the app)
+2. Prefer matching React major and minor exactly; patch should also match when available.
+3. Do not leave these packages on older `x.y` after upgrading `react`.
+
+## React 19 Rules
+
+1. Upgrade `@testing-library/react-native` to `v13+`.
+2. Follow:
+ - [Expo React 19 Reference][expo-react-19-reference]
+ - [RNTL LLM Docs][rntl-llm-docs]
+3. Expect type-level breakages from deprecated API removals; fix code and mocks accordingly.
+
+## Related Skills
+
+- [upgrade-helper-core.md](upgrade-helper-core.md) - Core RN upgrade workflow
+- [upgrading-dependencies.md](upgrading-dependencies.md) - Dependency compatibility triage
+- [expo-sdk-upgrade.md](expo-sdk-upgrade.md) - Expo-specific upgrade layer
+- [upgrade-verification.md](upgrade-verification.md) - Post-upgrade manual validation
+
+[expo-react-19-reference]: https://github.com/expo/skills/blob/main/plugins/upgrading-expo/skills/upgrading-expo/references/react-19.md
+[rntl-llm-docs]: https://oss.callstack.com/react-native-testing-library/llms.txt
diff --git a/.opencode/skills/upgrading-react-native/references/upgrade-helper-core.md b/.opencode/skills/upgrading-react-native/references/upgrade-helper-core.md
new file mode 100644
index 0000000..3bbcb7d
--- /dev/null
+++ b/.opencode/skills/upgrading-react-native/references/upgrade-helper-core.md
@@ -0,0 +1,122 @@
+---
+title: Upgrade Helper Core Workflow
+impact: HIGH
+tags: react-native, upgrade-helper, rn-diff-purge, ios, android
+---
+
+# Skill: Upgrade Helper Core Workflow
+
+Reliable, framework-agnostic workflow for React Native upgrades using Upgrade Helper and rn-diff-purge.
+
+Run shared environment checks first in [upgrading-react-native.md](upgrading-react-native.md) under `Prerequisites (All Upgrade Paths)`.
+
+## Quick Commands
+
+```bash
+npm pkg get dependencies.react-native devDependencies.react-native --prefix "$APP_DIR"
+npm view react-native dist-tags.latest
+curl -L "https://raw.githubusercontent.com/react-native-community/rn-diff-purge/master/RELEASES"
+curl -L -o /tmp/rn-diff-...diff "https://raw.githubusercontent.com/react-native-community/rn-diff-purge/diffs/diffs/...diff"
+grep -n "^diff --git" /tmp/rn-diff-...diff
+```
+
+## Upgrade Helper API (Inline Reference)
+
+- List supported versions:
+ - `https://raw.githubusercontent.com/react-native-community/rn-diff-purge/master/RELEASES`
+- Fetch raw unified diff:
+ - `https://raw.githubusercontent.com/react-native-community/rn-diff-purge/diffs/diffs/...diff`
+- GitHub compare view:
+ - `https://github.com/react-native-community/rn-diff-purge/compare/release/..release/`
+- Upgrade Helper UI:
+ - `https://react-native-community.github.io/upgrade-helper/?from=&to=`
+- Path mapping note:
+ - Diff paths are prefixed with `RnDiffApp/`; remap to your app paths and package names.
+
+## Inputs
+
+- `APP_DIR`: app package path (`.` for single-package repos)
+- `current_version`: current React Native version
+- `target_version`: target React Native version (latest by default)
+
+## Reliable Workflow
+
+1. Detect app and versions.
+ - Read `react-native` from `APP_DIR/package.json`.
+ - Resolve target via `npm view react-native dist-tags.latest` unless user provides one.
+2. Validate `target_version` exists.
+ - Check `RELEASES` from rn-diff-purge and confirm `target_version` is listed.
+ - If missing, stop and ask user to choose one of available versions.
+3. Collect canonical sources.
+ - Upgrade Helper URL.
+ - rn-diff-purge raw diff.
+4. Fetch diff with fallback.
+ - Try exact raw diff: `..`.
+ - If 404, try nearest available patch versions and report what was attempted.
+ - If no available pair works, stop and ask user for target adjustment.
+5. Build dependency baseline from rn-diff-purge first.
+ - Start with the `RnDiffApp/package.json` diff for the exact version pair.
+ - Do not manually install RN packages one-by-one before this baseline is captured.
+6. Publish a short execution plan before edits.
+ - Include ordered phases: dependency baseline, one-pass install, native/tooling merges, verification.
+ - If dependency migrations are ambiguous, ask for user confirmation before modifying package choices.
+7. Run dependency risk planning.
+ - Use [upgrading-dependencies.md](upgrading-dependencies.md).
+ - Fold approved migrations into the same dependency update pass.
+8. Apply dependency updates in one pass.
+ - Update `APP_DIR/package.json` (and lockfile) from the baseline plus approved migrations.
+ - Run exactly one install command with the repo's package manager (`npm install`, `yarn install`, `pnpm install`, or `bun install`).
+ - Avoid piecemeal installs such as repeated `npm install ` unless explicitly requested.
+9. Build a change checklist from diff.
+ - Group by JS/TS, iOS, Android, tooling.
+ - Skip template-only UI (`App.tsx`) unless explicitly requested.
+ - Skip template-only dependencies (`@react-native/new-app-screen`) unless they exist in the app.
+10. Apply diff safely.
+ - Treat `RnDiffApp` as placeholder; remap app/package names.
+ - Merge, do not overwrite project-specific customizations.
+11. Sync native deps.
+ - Run iOS pods in `APP_DIR/ios`.
+12. Validate and gate completion.
+ - iOS build passes.
+ - Android build passes.
+ - tests/typecheck/lint pass or failures are documented with next actions.
+ - If `react` was upgraded, run [react.md](react.md).
+ - If `target_version >= 0.81` and tests fail due to missing modules, add proper mocks.
+ - Example (`BackHandler` mock removal): https://github.com/facebook/react-native/issues/52667#issuecomment-3094788618
+ - Run [upgrade-verification.md](upgrade-verification.md) before closing the upgrade.
+
+## Stop Conditions
+
+- Missing `react-native` dependency in target package.
+- Diff source unavailable and no fallback available.
+- Unresolved native merge conflicts in iOS/Android entry files.
+
+## Reliability Rules
+
+- Keep operations version-pair scoped (`current_version -> target_version`).
+- Prefer official sources over ad-hoc guides.
+- Record every manual deviation from Upgrade Helper.
+- Do not run Expo-specific commands here.
+
+## Common Pitfalls
+
+- Upgrading an Expo project with only RN CLI steps: apply the Expo layer ([expo-sdk-upgrade.md](expo-sdk-upgrade.md)).
+- Skipping the Upgrade Helper: leads to missed native config changes.
+- Treating `RnDiffApp` paths as literal project paths.
+- Copying the entire template wholesale: use the diff as a guide and merge only needed changes.
+- Using the wrong changelog: `0.7x` changes live in `CHANGELOG-0.7x.md`.
+- Running the wrong package manager: always match the repo lockfile.
+- Forgetting CocoaPods: iOS builds will fail without `pod install`.
+- Not updating Android Gradle wrapper binary: update `android/gradle/wrapper/gradle-wrapper.jar` for the target RN version. Source template:
+ - `https://raw.githubusercontent.com/react-native-community/rn-diff-purge/release//RnDiffApp/android/gradle/wrapper/gradle-wrapper.jar`
+- Flipper artifacts lingering after removal in v0.74: remove `ReactNativeFlipper.kt` and `FLIPPER_VERSION` when the target RN version drops Flipper.
+- Skipping platform rebuilds after Pod/Gradle changes.
+
+## Related Skills
+
+- [upgrading-react-native.md](upgrading-react-native.md) - Routing and mode selection
+- [upgrading-dependencies.md](upgrading-dependencies.md) - Dependency compatibility and migration plan
+- [expo-sdk-upgrade.md](expo-sdk-upgrade.md) - Expo-only layer on top of core workflow
+- [react.md](react.md) - React and React 19 alignment
+- [upgrade-verification.md](upgrade-verification.md) - Manual post-upgrade validation
+- [monorepo-singlerepo-targeting.md](monorepo-singlerepo-targeting.md) - Repo/app selection and command scoping
diff --git a/.opencode/skills/upgrading-react-native/references/upgrade-verification.md b/.opencode/skills/upgrading-react-native/references/upgrade-verification.md
new file mode 100644
index 0000000..58909ff
--- /dev/null
+++ b/.opencode/skills/upgrading-react-native/references/upgrade-verification.md
@@ -0,0 +1,43 @@
+---
+title: Upgrade Verification
+impact: HIGH
+tags: verification, regression, android, ios, navigation
+---
+
+# Skill: Upgrade Verification
+
+Manual validation checklist for human developers after React Native and/or Expo upgrades.
+
+## Scope
+
+- Focus on behavior and UX regressions that static diffs cannot prove.
+- Keep checks small, repeatable, and tied to critical user flows.
+
+## Manual Checks (Required)
+
+1. App launch and core journeys work on both iOS and Android.
+2. Navigation behavior is correct (forward/back stack, params, deep links, modal flows).
+3. Android edge-to-edge is visually correct (status bar, nav bar, safe area insets, keyboard overlays).
+4. Permissions and device APIs work (camera, location, notifications, file/media access).
+5. Background/restore paths work (app resume, push open, interrupted flows).
+
+## Build and Test Gates
+
+1. Run unit/integration tests and fix all upgrade-related failures.
+2. If `target_version >= 0.81` and tests fail due to missing modules, add proper mocks.
+ - Example (`BackHandler` mock removal): https://github.com/facebook/react-native/issues/52667#issuecomment-3094788618
+3. Build installable artifacts for both platforms.
+4. For Expo apps, run `npx expo-doctor` from [expo-sdk-upgrade.md](expo-sdk-upgrade.md).
+
+## Evidence to Capture
+
+- Screen recordings/screenshots for changed flows.
+- List of verified scenarios and pass/fail status.
+- Follow-up fixes for any observed regressions.
+
+## Related Skills
+
+- [upgrading-react-native.md](upgrading-react-native.md) - Upgrade workflow router
+- [upgrade-helper-core.md](upgrade-helper-core.md) - Core RN diff/merge workflow
+- [expo-sdk-upgrade.md](expo-sdk-upgrade.md) - Expo-specific checks and commands
+- [react.md](react.md) - React-specific upgrade rules
diff --git a/.opencode/skills/upgrading-react-native/references/upgrading-dependencies.md b/.opencode/skills/upgrading-react-native/references/upgrading-dependencies.md
new file mode 100644
index 0000000..e62e834
--- /dev/null
+++ b/.opencode/skills/upgrading-react-native/references/upgrading-dependencies.md
@@ -0,0 +1,41 @@
+---
+title: Upgrading Dependencies
+impact: HIGH
+tags: react-native, dependencies, compatibility, migration
+---
+
+# Skill: Upgrading Dependencies
+
+Common dependency issues and mitigations when upgrading React Native.
+
+## Quick Checks
+
+```bash
+npm ls --depth=0
+```
+
+## Dependency Risk and Migration Plan
+
+1. Review compatibility signals:
+ - [RN nightly tests](https://react-native-community.github.io/nightly-tests/)
+ - [React Native Directory](https://reactnative.directory/packages?newArchitecture=false)
+2. If `react` is upgraded, run [react.md](react.md) for companion package alignment and React 19 rules.
+3. Handle known risky packages:
+ - `react-native-fast-image` -> prefer `@d11/react-native-fast-image` or `expo-image` (confirm with user)
+ - `@react-native-cookies/cookies` -> prefer `@preeternal/react-native-cookie-manager` (confirm with user)
+ - `react-native-code-push` -> treat as incompatible; disable for upgrade and consider `@appzung/react-native-code-push`, `@bravemobile/react-native-code-push`, or `expo-updates`
+ - `react-native-image-crop-picker` -> upgrade to `>=0.51.1`; if unstable, plan migration to `expo-image-picker` (confirm with user)
+ - `react-native-network-logger` - lists `react` and `react-native` in peer deps as `*` which can be misleading. Upgrade to v2 if `target_version >= 0.79`.
+ - `react-native-permissions` - upgrade to v5 if possible (requires RN 0.74+)
+4. Apply additional cleanup rules:
+ - If `@rnx-kit/metro-resolver-symlinks` is present, remove it from deps and `metro.config.js` (Metro supports symlinks since 0.72)
+ - If app uses `react-native-localize` timezone APIs and `@callstack/timezone-hermes-fix` is missing, ask whether to add it
+5. If no safe alternative is found for a critical dependency, ask for explicit user confirmation before continuing.
+6. Read only breaking/manual steps from RN blog posts between `current_version` and `target_version`.
+
+## Related Skills
+
+- [upgrade-helper-core.md](upgrade-helper-core.md) - Core upgrade workflow
+- [react.md](react.md) - React and React 19 alignment
+- [expo-sdk-upgrade.md](expo-sdk-upgrade.md) - Expo-specific dependency alignment
+- [upgrading-react-native.md](upgrading-react-native.md) - Routing and mode selection
diff --git a/.opencode/skills/upgrading-react-native/references/upgrading-react-native.md b/.opencode/skills/upgrading-react-native/references/upgrading-react-native.md
new file mode 100644
index 0000000..c2940a1
--- /dev/null
+++ b/.opencode/skills/upgrading-react-native/references/upgrading-react-native.md
@@ -0,0 +1,52 @@
+---
+title: Upgrading React Native
+impact: HIGH
+tags: react-native, upgrade, routing
+---
+
+# Skill: Upgrading React Native
+
+Router for React Native upgrade workflows. Start with core Upgrade Helper instructions, then apply focused add-ons by project shape.
+
+## Prerequisites (All Upgrade Paths)
+
+- Ensure the repo is clean or on a dedicated upgrade branch.
+- Know which package manager the repo uses (`npm`, `yarn`, `pnpm`, `bun`).
+- Use Node.js `20.19.4+`, Java `17`, and Xcode `16.4+` (with Command Line Tools), following https://reactnative.dev/docs/set-up-your-environment.
+ - Optional: use [Xcodes](https://github.com/XcodesOrg/XcodesApp) to manage Xcode versions.
+- Verify active versions before upgrading: `node -v`, `java -version`.
+- Verify Android Studio is installed.
+- For iOS, verify Xcode CLI toolchain is in sync (common pitfall after Xcode upgrades):
+ - Check:
+ - `xcode-select --print-path`
+ - `xcodebuild -version`
+ - `xcrun --sdk iphoneos --show-sdk-version`
+ - If mismatch is suspected, re-point and initialize:
+ - `sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`
+ - `sudo xcodebuild -runFirstLaunch`
+
+## Quick Start
+
+0. Run the [Prerequisites (All Upgrade Paths)](#prerequisites-all-upgrade-paths) checklist.
+1. Set `APP_DIR` to the app folder (`.` for single-app repos).
+2. Use [monorepo-singlerepo-targeting.md](monorepo-singlerepo-targeting.md) if you need help choosing `APP_DIR`.
+3. Run [upgrade-helper-core.md](upgrade-helper-core.md) first to anchor changes to rn-diff-purge.
+4. Publish a short plan (ordered phases) before making versioned edits.
+5. Run [upgrading-dependencies.md](upgrading-dependencies.md) to assess risky packages and migrations.
+6. Apply dependency updates in one pass and run a single install with the repo package manager.
+7. Run [react.md](react.md) when `react` is upgraded.
+8. Add [expo-sdk-upgrade.md](expo-sdk-upgrade.md) only if `expo` is present in `APP_DIR/package.json`.
+9. Finish with [upgrade-verification.md](upgrade-verification.md).
+
+## Decision Map
+
+- Need canonical RN diff/merge workflow: [upgrade-helper-core.md](upgrade-helper-core.md)
+- Need to ensure dependencies are compatible: [upgrading-dependencies.md](upgrading-dependencies.md)
+- Need React and React 19 alignment: [react.md](react.md)
+- Project contains Expo SDK deps: [expo-sdk-upgrade.md](expo-sdk-upgrade.md)
+- Need manual post-upgrade validation: [upgrade-verification.md](upgrade-verification.md)
+
+## Related Skills
+
+- [native-platform-setup.md](../../react-native-best-practices/references/native-platform-setup.md) - Tooling and native dependency basics
+- [native-android-16kb-alignment.md](../../react-native-best-practices/references/native-android-16kb-alignment.md) - Third-party library alignment for Google Play
diff --git a/package.json b/package.json
index 4660dd3..12b91aa 100644
--- a/package.json
+++ b/package.json
@@ -54,184 +54,184 @@
"electron:build:linux": "yarn web:build && electron-builder --config electron-builder.config.js --linux"
},
"dependencies": {
- "@config-plugins/react-native-callkeep": "^11.0.0",
- "@config-plugins/react-native-webrtc": "~12.0.0",
- "@dev-plugins/react-query": "~0.2.0",
- "@expo/html-elements": "~0.10.1",
- "@expo/metro-runtime": "~6.1.2",
- "@gluestack-ui/accordion": "~1.0.6",
- "@gluestack-ui/actionsheet": "~0.2.44",
- "@gluestack-ui/alert": "~0.1.15",
- "@gluestack-ui/alert-dialog": "~0.1.30",
- "@gluestack-ui/avatar": "~0.1.17",
- "@gluestack-ui/button": "~1.0.14",
- "@gluestack-ui/checkbox": "~0.1.31",
- "@gluestack-ui/divider": "~0.1.9",
- "@gluestack-ui/fab": "~0.1.21",
- "@gluestack-ui/form-control": "~0.1.18",
- "@gluestack-ui/icon": "~0.1.27",
- "@gluestack-ui/image": "~0.1.10",
- "@gluestack-ui/input": "~0.1.38",
- "@gluestack-ui/link": "~0.1.22",
- "@gluestack-ui/menu": "~0.2.43",
- "@gluestack-ui/modal": "~0.1.35",
- "@gluestack-ui/nativewind-utils": "~1.0.26",
- "@gluestack-ui/overlay": "~0.1.16",
- "@gluestack-ui/popover": "~0.1.49",
- "@gluestack-ui/pressable": "~0.1.16",
- "@gluestack-ui/progress": "~0.1.16",
- "@gluestack-ui/radio": "~0.1.40",
- "@gluestack-ui/select": "~0.1.31",
- "@gluestack-ui/slider": "~0.1.32",
- "@gluestack-ui/spinner": "~0.1.14",
- "@gluestack-ui/switch": "~0.1.22",
- "@gluestack-ui/textarea": "~0.1.23",
- "@gluestack-ui/toast": "~1.0.8",
- "@gluestack-ui/tooltip": "~0.1.32",
- "@gorhom/bottom-sheet": "~5.2.13",
- "@hookform/resolvers": "~3.9.0",
- "@legendapp/motion": "~2.4.0",
- "@livekit/react-native": "^2.9.1",
- "@livekit/react-native-expo-plugin": "^1.0.1",
- "@livekit/react-native-webrtc": "^137.0.2",
- "@microsoft/signalr": "~8.0.7",
- "@notifee/react-native": "^9.1.8",
- "@novu/react-native": "^3.11.0",
- "@react-native-community/netinfo": "^11.4.1",
- "@react-native-firebase/app": "^23.5.0",
- "@react-native-firebase/messaging": "^23.5.0",
+ "@config-plugins/react-native-callkeep": "11.0.0",
+ "@config-plugins/react-native-webrtc": "12.0.0",
+ "@dev-plugins/react-query": "0.2.0",
+ "@expo/html-elements": "0.10.1",
+ "@expo/metro-runtime": "6.1.2",
+ "@gluestack-ui/accordion": "1.0.14",
+ "@gluestack-ui/actionsheet": "0.2.53",
+ "@gluestack-ui/alert": "0.1.16",
+ "@gluestack-ui/alert-dialog": "0.1.38",
+ "@gluestack-ui/avatar": "0.1.18",
+ "@gluestack-ui/button": "1.0.14",
+ "@gluestack-ui/checkbox": "0.1.39",
+ "@gluestack-ui/divider": "0.1.10",
+ "@gluestack-ui/fab": "0.1.28",
+ "@gluestack-ui/form-control": "0.1.19",
+ "@gluestack-ui/icon": "0.1.27",
+ "@gluestack-ui/image": "0.1.17",
+ "@gluestack-ui/input": "0.1.38",
+ "@gluestack-ui/link": "0.1.29",
+ "@gluestack-ui/menu": "0.2.43",
+ "@gluestack-ui/modal": "0.1.41",
+ "@gluestack-ui/nativewind-utils": "1.0.28",
+ "@gluestack-ui/overlay": "0.1.22",
+ "@gluestack-ui/popover": "0.1.49",
+ "@gluestack-ui/pressable": "0.1.23",
+ "@gluestack-ui/progress": "0.1.18",
+ "@gluestack-ui/radio": "0.1.40",
+ "@gluestack-ui/select": "0.1.31",
+ "@gluestack-ui/slider": "0.1.32",
+ "@gluestack-ui/spinner": "0.1.15",
+ "@gluestack-ui/switch": "0.1.29",
+ "@gluestack-ui/textarea": "0.1.25",
+ "@gluestack-ui/toast": "1.0.9",
+ "@gluestack-ui/tooltip": "0.1.44",
+ "@gorhom/bottom-sheet": "5.2.13",
+ "@hookform/resolvers": "3.9.1",
+ "@legendapp/motion": "2.4.0",
+ "@livekit/react-native": "2.9.1",
+ "@livekit/react-native-expo-plugin": "1.0.1",
+ "@livekit/react-native-webrtc": "137.0.2",
+ "@microsoft/signalr": "8.0.17",
+ "@notifee/react-native": "9.1.8",
+ "@novu/react-native": "3.11.0",
+ "@react-native-community/netinfo": "11.4.1",
+ "@react-native-firebase/app": "23.5.0",
+ "@react-native-firebase/messaging": "23.5.0",
"@rnmapbox/maps": "10.2.10",
- "@semantic-release/git": "^10.0.1",
- "@sentry/react-native": "^8.10.0",
+ "@semantic-release/git": "10.0.1",
+ "@sentry/react-native": "8.10.0",
"@shopify/flash-list": "2.0.2",
- "@tanstack/react-query": "~5.52.1",
- "app-icon-badge": "^0.1.2",
- "axios": "~1.12.0",
- "babel-plugin-module-resolver": "^5.0.2",
- "babel-plugin-transform-import-meta": "^2.3.3",
- "buffer": "^6.0.3",
+ "@tanstack/react-query": "5.52.3",
+ "app-icon-badge": "0.1.2",
+ "axios": "1.12.2",
+ "babel-plugin-module-resolver": "5.0.2",
+ "babel-plugin-transform-import-meta": "2.3.3",
+ "buffer": "6.0.3",
"countly-sdk-react-native-bridge": "26.1.0",
- "date-fns": "^4.1.0",
- "expo": "^54.0.33",
- "expo-application": "~7.0.8",
- "expo-asset": "~12.0.12",
- "expo-audio": "~1.1.1",
- "expo-auth-session": "~7.0.10",
- "expo-av": "~16.0.8",
- "expo-build-properties": "~1.0.10",
- "expo-clipboard": "~8.0.8",
- "expo-constants": "~18.0.13",
- "expo-crypto": "~15.0.8",
- "expo-dev-client": "~6.0.20",
- "expo-device": "~8.0.10",
- "expo-document-picker": "~14.0.8",
- "expo-file-system": "~19.0.21",
- "expo-font": "~14.0.11",
- "expo-image": "~3.0.11",
- "expo-image-manipulator": "~14.0.8",
- "expo-image-picker": "~17.0.10",
- "expo-keep-awake": "~15.0.8",
- "expo-linking": "~8.0.11",
- "expo-localization": "~17.0.8",
- "expo-location": "~19.0.8",
- "expo-navigation-bar": "~5.0.10",
- "expo-router": "~6.0.23",
- "expo-screen-orientation": "~9.0.8",
- "expo-secure-store": "~15.0.8",
- "expo-sharing": "~14.0.8",
- "expo-splash-screen": "~31.0.13",
- "expo-status-bar": "~3.0.9",
- "expo-system-ui": "~6.0.9",
- "expo-task-manager": "~14.0.9",
- "expo-web-browser": "~15.0.10",
- "geojson": "~0.5.0",
- "i18next": "~23.14.0",
- "livekit-client": "^2.15.7",
- "lodash": "^4.17.21",
- "lodash.memoize": "~4.1.2",
- "lucide-react-native": "~0.475.0",
+ "date-fns": "4.1.0",
+ "expo": "54.0.33",
+ "expo-application": "7.0.8",
+ "expo-asset": "12.0.12",
+ "expo-audio": "1.1.1",
+ "expo-auth-session": "7.0.10",
+ "expo-av": "16.0.8",
+ "expo-build-properties": "1.0.10",
+ "expo-clipboard": "8.0.8",
+ "expo-constants": "18.0.13",
+ "expo-crypto": "15.0.8",
+ "expo-dev-client": "6.0.20",
+ "expo-device": "8.0.10",
+ "expo-document-picker": "14.0.8",
+ "expo-file-system": "19.0.21",
+ "expo-font": "14.0.11",
+ "expo-image": "3.0.11",
+ "expo-image-manipulator": "14.0.8",
+ "expo-image-picker": "17.0.10",
+ "expo-keep-awake": "15.0.8",
+ "expo-linking": "8.0.11",
+ "expo-localization": "17.0.8",
+ "expo-location": "19.0.8",
+ "expo-navigation-bar": "5.0.10",
+ "expo-router": "6.0.23",
+ "expo-screen-orientation": "9.0.8",
+ "expo-secure-store": "15.0.8",
+ "expo-sharing": "14.0.8",
+ "expo-splash-screen": "31.0.13",
+ "expo-status-bar": "3.0.9",
+ "expo-system-ui": "6.0.9",
+ "expo-task-manager": "14.0.9",
+ "expo-web-browser": "15.0.10",
+ "geojson": "0.5.0",
+ "i18next": "23.14.0",
+ "livekit-client": "2.15.7",
+ "lodash": "4.17.23",
+ "lodash.memoize": "4.1.2",
+ "lucide-react-native": "0.475.0",
"mapbox-gl": "3.18.1",
- "moti": "~0.29.0",
- "nativewind": "~4.1.21",
+ "moti": "0.29.0",
+ "nativewind": "4.1.23",
"promise": "8.3.0",
"react": "19.1.0",
"react-dom": "19.1.0",
- "react-error-boundary": "~4.0.13",
- "react-hook-form": "~7.53.0",
- "react-i18next": "~15.0.1",
+ "react-error-boundary": "4.0.13",
+ "react-hook-form": "7.53.2",
+ "react-i18next": "15.0.3",
"react-native": "0.81.5",
- "react-native-base64": "~0.2.1",
- "react-native-ble-manager": "^12.1.5",
+ "react-native-base64": "0.2.2",
+ "react-native-ble-manager": "12.2.1",
"react-native-callkeep": "github:Irfanwani/react-native-callkeep#957193d0716f1c2dfdc18e627cbff0f8a0800971",
"react-native-edge-to-edge": "1.6.0",
- "react-native-flash-message": "~0.4.2",
- "react-native-gesture-handler": "~2.28.0",
+ "react-native-flash-message": "0.4.2",
+ "react-native-gesture-handler": "2.28.0",
"react-native-keyboard-controller": "1.18.5",
- "react-native-logs": "~5.3.0",
- "react-native-mmkv": "~3.1.0",
- "react-native-reanimated": "~4.1.1",
+ "react-native-logs": "5.3.0",
+ "react-native-mmkv": "3.1.0",
+ "react-native-reanimated": "4.1.7",
"react-native-restart": "0.0.27",
- "react-native-safe-area-context": "~5.6.0",
- "react-native-screens": "~4.16.0",
+ "react-native-safe-area-context": "5.6.2",
+ "react-native-screens": "4.16.0",
"react-native-svg": "15.12.1",
- "react-native-web": "^0.21.0",
+ "react-native-web": "0.21.2",
"react-native-webview": "13.15.0",
"react-native-worklets": "0.5.1",
- "react-query-kit": "~3.3.0",
- "tailwind-variants": "~0.2.1",
- "zod": "~3.23.8",
- "zustand": "~4.5.5"
+ "react-query-kit": "3.3.2",
+ "tailwind-variants": "0.2.1",
+ "zod": "3.23.8",
+ "zustand": "4.5.7"
},
"devDependencies": {
- "@babel/core": "~7.26.0",
- "@commitlint/cli": "~19.2.2",
- "@commitlint/config-conventional": "~19.2.2",
- "@expo/config": "~12.0.12",
- "@testing-library/jest-dom": "~6.5.0",
- "@testing-library/react-native": "~12.9.0",
- "@types/geojson": "~7946.0.16",
- "@types/i18n-js": "~3.8.9",
- "@types/jest": "~29.5.14",
- "@types/lodash.memoize": "~4.1.9",
+ "@babel/core": "7.26.10",
+ "@commitlint/cli": "19.2.2",
+ "@commitlint/config-conventional": "19.2.2",
+ "@expo/config": "12.0.13",
+ "@testing-library/jest-dom": "6.5.0",
+ "@testing-library/react-native": "12.9.0",
+ "@types/geojson": "7946.0.16",
+ "@types/i18n-js": "3.8.9",
+ "@types/jest": "29.5.14",
+ "@types/lodash.memoize": "4.1.9",
"@types/mapbox-gl": "3.4.1",
- "@types/react": "~19.1.10",
- "@types/react-native-base64": "~0.2.2",
- "@typescript-eslint/eslint-plugin": "^8.0.0",
- "@typescript-eslint/parser": "^8.0.0",
- "babel-jest": "~30.0.0",
+ "@types/react": "19.1.17",
+ "@types/react-native-base64": "0.2.2",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "babel-jest": "30.0.5",
"concurrently": "9.2.1",
- "cross-env": "~7.0.3",
- "dotenv": "~16.4.5",
+ "cross-env": "7.0.3",
+ "dotenv": "16.4.7",
"electron": "40.0.0",
"electron-builder": "26.4.0",
- "electron-squirrel-startup": "^1.0.1",
- "eslint": "~8.57.0",
- "eslint-config-expo": "~9.2.0",
- "eslint-config-prettier": "~9.1.0",
- "eslint-import-resolver-typescript": "~3.6.3",
- "eslint-plugin-i18n-json": "~4.0.0",
- "eslint-plugin-import": "~2.31.0",
- "eslint-plugin-prettier": "~5.2.1",
+ "electron-squirrel-startup": "1.0.1",
+ "eslint": "8.57.1",
+ "eslint-config-expo": "9.2.0",
+ "eslint-config-prettier": "9.1.2",
+ "eslint-import-resolver-typescript": "3.6.3",
+ "eslint-plugin-i18n-json": "4.0.1",
+ "eslint-plugin-import": "2.31.0",
+ "eslint-plugin-prettier": "5.2.6",
"eslint-plugin-react-compiler": "19.0.0-beta-a7bf2bd-20241110",
- "eslint-plugin-simple-import-sort": "~10.0.0",
- "eslint-plugin-tailwindcss": "~3.15.2",
- "eslint-plugin-testing-library": "~6.2.2",
- "eslint-plugin-unicorn": "~46.0.1",
- "eslint-plugin-unused-imports": "~2.0.0",
- "jest": "~29.7.0",
- "jest-environment-jsdom": "~29.7.0",
- "jest-expo": "~54.0.17",
- "jest-junit": "~16.0.0",
- "lint-staged": "~15.2.9",
- "np": "~10.0.7",
- "patch-package": "^8.0.0",
- "postinstall-postinstall": "^2.1.0",
- "prettier": "~3.3.3",
- "react-native-svg-transformer": "~1.5.1",
+ "eslint-plugin-simple-import-sort": "10.0.0",
+ "eslint-plugin-tailwindcss": "3.15.2",
+ "eslint-plugin-testing-library": "6.2.2",
+ "eslint-plugin-unicorn": "46.0.1",
+ "eslint-plugin-unused-imports": "2.0.0",
+ "jest": "29.7.0",
+ "jest-environment-jsdom": "29.7.0",
+ "jest-expo": "54.0.17",
+ "jest-junit": "16.0.0",
+ "lint-staged": "15.2.11",
+ "np": "10.0.7",
+ "patch-package": "8.0.1",
+ "postinstall-postinstall": "2.1.0",
+ "prettier": "3.3.3",
+ "react-native-svg-transformer": "1.5.1",
"tailwindcss": "3.4.4",
- "ts-jest": "~29.1.2",
- "ts-node": "~10.9.2",
- "typescript": "~5.9.2",
+ "ts-jest": "29.1.5",
+ "ts-node": "10.9.2",
+ "typescript": "5.9.3",
"wait-on": "9.0.3"
},
"repository": {
@@ -267,7 +267,7 @@
"initVersion": "7.0.4"
},
"resolutions": {
- "@expo/metro-config": "~54.0.14",
+ "@expo/metro-config": "54.0.15",
"form-data": "4.0.4",
"promise": "8.3.0"
}
diff --git a/plugins/withCheckInLiveActivity.js b/plugins/withCheckInLiveActivity.js
index 387f625..482743a 100644
--- a/plugins/withCheckInLiveActivity.js
+++ b/plugins/withCheckInLiveActivity.js
@@ -500,6 +500,12 @@ const withCheckInLiveActivity = (config, props = {}) => {
CODE_SIGN_STYLE: 'Automatic',
MARKETING_VERSION: resolvedMarketingVersion,
CURRENT_PROJECT_VERSION: resolvedCurrentProjectVersion,
+ // Disable independent code signing for the widget extension.
+ // The widget is embedded in the main app and signed during the
+ // main target's archive/export phase. Without this, EAS (which
+ // uses manual signing) fails because it has no provisioning
+ // profile for the widget's bundle identifier.
+ CODE_SIGNING_ALLOWED: 'NO',
// Propagate the development team from the host target so the
// widget extension can be signed (required since Xcode 14+).
...(hostDevelopmentTeam ? { DEVELOPMENT_TEAM: hostDevelopmentTeam } : {}),
diff --git a/src/api/common/client.tsx b/src/api/common/client.tsx
index f56c327..92d2005 100644
--- a/src/api/common/client.tsx
+++ b/src/api/common/client.tsx
@@ -108,7 +108,7 @@ axiosInstance.interceptors.response.use(
if (!isNetworkError) {
// Only logout for non-network errors (e.g., invalid refresh token, 400/401 from token endpoint)
- logger.error({
+ logger.warn({
message: 'Token refresh failed with non-recoverable error, logging out user',
context: { error: refreshError },
});
diff --git a/src/app/(app)/_layout.tsx b/src/app/(app)/_layout.tsx
index 519e722..587ddd1 100644
--- a/src/app/(app)/_layout.tsx
+++ b/src/app/(app)/_layout.tsx
@@ -44,6 +44,7 @@ export default function TabLayout() {
const [isOpen, setIsOpen] = React.useState(false);
const [isNotificationsOpen, setIsNotificationsOpen] = React.useState(false);
const [isInitComplete, setIsInitComplete] = useState(false);
+ const [initRetryCount, setInitRetryCount] = useState(0);
const { width, height } = useWindowDimensions();
const insets = useSafeAreaInsets();
const isLandscape = width > height;
@@ -80,7 +81,6 @@ export default function TabLayout() {
const hasInitialized = useRef(false);
const isInitializing = useRef(false);
const hasHiddenSplash = useRef(false);
- const lastSignedInStatus = useRef(null);
const parentRef = useRef(null);
// Render counting for diagnostics (web only)
@@ -184,6 +184,7 @@ export default function TabLayout() {
});
// Reset initialization state on error so it can be retried
hasInitialized.current = false;
+ setInitRetryCount((c) => c + 1);
} finally {
isInitializing.current = false;
setIsInitComplete(true);
@@ -201,7 +202,7 @@ export default function TabLayout() {
// Refresh data
await Promise.all([useCoreStore.getState().fetchConfig(), useCallsStore.getState().fetchCalls(), useRolesStore.getState().fetchRoles(), useWeatherAlertsStore.getState().fetchActiveAlerts()]);
} catch (error) {
- logger.error({
+ logger.warn({
message: 'Failed to refresh data on app resume',
context: { error },
});
@@ -226,38 +227,48 @@ export default function TabLayout() {
}, [status, hideSplash]);
// Handle app initialization - simplified logic
+ const MAX_INIT_RETRIES = 3;
+ useEffect(() => {
+ if (status !== 'signedIn' && initRetryCount > 0) {
+ setInitRetryCount(0);
+ }
+ }, [status, initRetryCount]);
useEffect(() => {
- const shouldInitialize = status === 'signedIn' && !hasInitialized.current && !isInitializing.current && lastSignedInStatus.current !== 'signedIn';
+ const shouldInitialize = status === 'signedIn' && !hasInitialized.current && !isInitializing.current && initRetryCount < MAX_INIT_RETRIES;
if (shouldInitialize) {
logger.info({
message: 'Triggering app initialization',
context: {
- statusChanged: lastSignedInStatus.current !== status,
+ hasInitialized: hasInitialized.current,
+ initRetryCount,
},
});
initializeApp();
}
-
- // Update last known status
- lastSignedInStatus.current = status;
- }, [status, initializeApp]);
+ }, [status, initializeApp, initRetryCount]);
// Handle app resuming from background - separate from initialization
useEffect(() => {
// On web, isActive/appState are always active — skip the initial fire
- // and only refresh when they genuinely change (i.e., on native background→foreground)
+ // and only trigger on genuine state changes (i.e., on native background→foreground)
if (Platform.OS === 'web') return;
- // Only trigger on state change, not on initial render
- if (isActive && appState === 'active' && hasInitialized.current) {
+ if (isActive && appState === 'active') {
const timer = setTimeout(() => {
- refreshDataFromBackground();
+ if (hasInitialized.current) {
+ // Normal refresh after successful init
+ refreshDataFromBackground();
+ } else if (!isInitializing.current) {
+ // Retry initialization if it previously failed
+ logger.info({ message: 'Retrying app initialization after returning to foreground' });
+ initializeApp();
+ }
}, 500); // Small delay to prevent multiple rapid calls
return () => clearTimeout(timer);
}
- }, [isActive, appState, refreshDataFromBackground]);
+ }, [isActive, appState, refreshDataFromBackground, initializeApp]);
// Force drawer open in landscape (guard with functional update to avoid unnecessary re-render)
useEffect(() => {
diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx
index 4d76d02..3e3bde7 100644
--- a/src/app/_layout.tsx
+++ b/src/app/_layout.tsx
@@ -7,6 +7,8 @@ import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { registerGlobals } from '@livekit/react-native';
import notifee from '@notifee/react-native';
import { createNavigationContainerRef, DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
+import type { EventHint } from '@sentry/core';
+import type { ErrorEvent, StackFrame } from '@sentry/react-native';
import * as Sentry from '@sentry/react-native';
import { isRunningInExpoGo } from 'expo';
import { Stack, useNavigationContainerRef } from 'expo-router';
@@ -56,6 +58,22 @@ Sentry.init({
navigationIntegration,
],
enableNativeFramesTracking: Platform.OS !== 'web', //!isRunningInExpoGo(), // Tracks slow and frozen frames in the application
+ beforeSend(event: ErrorEvent, _hint: EventHint) {
+ // Filter known Mapbox GL JS bug: GeolocateControl._onSuccess calls
+ // fitBounds/_cameraForBoundsOnGlobe which throws when the globe
+ // projection matrix is null (TypeError: Cannot read properties of null (reading '3'))
+ const values = event.exception?.values;
+ if (values?.length) {
+ const top = values[values.length - 1];
+ if (top.type === 'TypeError' && top.value?.includes("Cannot read properties of null (reading '3')")) {
+ const frames = top.stacktrace?.frames;
+ if (frames?.some((f: StackFrame) => f.function?.includes('_cameraForBounds') || f.function?.includes('GeolocateControl') || f.function?.includes('fromInvProjectionMatrix'))) {
+ return null;
+ }
+ }
+ }
+ return event;
+ },
// Add additional options to prevent timing issues
beforeSendTransaction(event: any) {
// Filter out problematic navigation transactions that might cause timestamp errors
diff --git a/src/components/calls/call-files-modal.tsx b/src/components/calls/call-files-modal.tsx
index 67d981a..35c150f 100644
--- a/src/components/calls/call-files-modal.tsx
+++ b/src/components/calls/call-files-modal.tsx
@@ -110,14 +110,6 @@ export const CallFilesModal: React.FC = ({ isOpen, onClose,
try {
setDownloadingFiles((prev) => ({ ...prev, [file.Id]: 0 }));
- const fileData = await getCallAttachmentFile(file.Url, {
- onEvent: (event) => {
- if (event.type === 'progress' && event.progress !== undefined) {
- setDownloadingFiles((prev) => ({ ...prev, [file.Id]: event.progress! }));
- }
- },
- });
-
// Create a temporary file
const fileName = file.FileName || file.Name || `file_${file.Id}`;
if (!documentDirectory) {
@@ -125,18 +117,29 @@ export const CallFilesModal: React.FC = ({ isOpen, onClose,
}
const fileUri = `${documentDirectory}${fileName}`;
- // Convert blob to base64
- const base64Data = await new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onload = () => {
- const result = reader.result as string;
- // Remove data URL prefix if present
- const base64 = result.split(',')[1] || result;
- resolve(base64);
- };
- reader.onerror = reject;
- reader.readAsDataURL(fileData);
- });
+ // Convert blob to base64 in a scoped block so the blob reference
+ // is released as soon as conversion completes
+ const base64Data = await (async () => {
+ const blob = await getCallAttachmentFile(file.Url!, {
+ onEvent: (event) => {
+ if (event.type === 'progress' && event.progress !== undefined) {
+ setDownloadingFiles((prev) => ({ ...prev, [file.Id]: event.progress! }));
+ }
+ },
+ });
+
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onload = () => {
+ const result = reader.result as string;
+ // Remove data URL prefix if present
+ const base64 = result.split(',')[1] || result;
+ resolve(base64);
+ };
+ reader.onerror = reject;
+ reader.readAsDataURL(blob);
+ });
+ })();
// Write file to device
await writeAsStringAsync(fileUri, base64Data, {
diff --git a/src/services/__tests__/location.test.ts b/src/services/__tests__/location.test.ts
index 1c171a7..1adbf4c 100644
--- a/src/services/__tests__/location.test.ts
+++ b/src/services/__tests__/location.test.ts
@@ -561,7 +561,7 @@ describe('LocationService', () => {
const locationCallback = mockLocation.watchPositionAsync.mock.calls[0][1] as Function;
await locationCallback(mockLocationObject);
- expect(mockLogger.error).toHaveBeenCalledWith({
+ expect(mockLogger.warn).toHaveBeenCalledWith({
message: 'Failed to send location to API',
context: {
error: 'API Error',
diff --git a/src/services/location.ts b/src/services/location.ts
index 45a096d..b6997a6 100644
--- a/src/services/location.ts
+++ b/src/services/location.ts
@@ -48,7 +48,7 @@ const sendLocationToAPI = async (location: Location.LocationObject): Promise {
const { result } = renderHook(() => useCoreStore());
await act(async () => {
- await result.current.init();
+ await expect(result.current.init()).rejects.toThrow('Failed to fetch config');
});
expect(result.current.isInitialized).toBe(false);
diff --git a/src/stores/app/core-store.ts b/src/stores/app/core-store.ts
index 1517dfa..059ec84 100644
--- a/src/stores/app/core-store.ts
+++ b/src/stores/app/core-store.ts
@@ -122,6 +122,7 @@ export const useCoreStore = create()(
message: `Failed to init core app data: ${JSON.stringify(error)}`,
context: { error },
});
+ throw error;
}
},
setActiveUnit: async (unitId: string) => {
diff --git a/src/stores/calls/store.ts b/src/stores/calls/store.ts
index b2f747d..7f3b077 100644
--- a/src/stores/calls/store.ts
+++ b/src/stores/calls/store.ts
@@ -64,7 +64,19 @@ export const useCallsStore = create((set, get) => ({
set({ isLoading: true, error: null });
try {
const response = await getCalls();
- set({ calls: Array.isArray(response.Data) ? response.Data : [], isLoading: false, lastFetchedAt: Date.now() });
+ const newCalls = Array.isArray(response.Data) ? response.Data : [];
+
+ // Evict dispatches for calls no longer in the active list to prevent unbounded memory growth
+ const activeIds = new Set(newCalls.map((c) => c.CallId));
+ const existing = get().callDispatches;
+ const pruned: Record = {};
+ for (const id in existing) {
+ if (activeIds.has(id)) {
+ pruned[id] = existing[id];
+ }
+ }
+
+ set({ calls: newCalls, callDispatches: pruned, isLoading: false, lastFetchedAt: Date.now() });
} catch (error) {
set({ error: 'Failed to fetch calls', isLoading: false });
}
diff --git a/src/stores/signalr/__tests__/signalr-store.test.ts b/src/stores/signalr/__tests__/signalr-store.test.ts
index dc7a60f..f8c2dec 100644
--- a/src/stores/signalr/__tests__/signalr-store.test.ts
+++ b/src/stores/signalr/__tests__/signalr-store.test.ts
@@ -192,7 +192,7 @@ describe('useSignalRStore', () => {
});
expect(result.current.error).toEqual(connectionError);
- expect(logger.error).toHaveBeenCalledWith({
+ expect(logger.warn).toHaveBeenCalledWith({
message: 'Failed to connect to SignalR hubs',
context: { error: connectionError },
});
@@ -223,7 +223,7 @@ describe('useSignalRStore', () => {
});
expect(result.current.error).toEqual(disconnectError);
- expect(logger.error).toHaveBeenCalledWith({
+ expect(logger.warn).toHaveBeenCalledWith({
message: 'Failed to disconnect from SignalR hubs',
context: { error: disconnectError },
});
@@ -276,7 +276,7 @@ describe('useSignalRStore', () => {
});
expect(result.current.error).toEqual(disconnectError);
- expect(logger.error).toHaveBeenCalledWith({
+ expect(logger.warn).toHaveBeenCalledWith({
message: 'Failed to disconnect from SignalR hubs',
context: { error: disconnectError },
});
diff --git a/src/stores/signalr/signalr-store.ts b/src/stores/signalr/signalr-store.ts
index f82ecc8..8dae579 100644
--- a/src/stores/signalr/signalr-store.ts
+++ b/src/stores/signalr/signalr-store.ts
@@ -168,7 +168,7 @@ export const useSignalRStore = create((set, get) => ({
});
} catch (error) {
const err = error instanceof Error ? error : new Error('Unknown error occurred');
- logger.error({
+ logger.warn({
message: 'Failed to connect to SignalR hubs',
context: { error: err },
});
@@ -181,7 +181,7 @@ export const useSignalRStore = create((set, get) => ({
set({ isUpdateHubConnected: false, lastUpdateMessage: null });
} catch (error) {
const err = error instanceof Error ? error : new Error('Unknown error occurred');
- logger.error({
+ logger.warn({
message: 'Failed to disconnect from SignalR hubs',
context: { error: err },
});
@@ -238,7 +238,7 @@ export const useSignalRStore = create((set, get) => ({
});
} catch (error) {
const err = error instanceof Error ? error : new Error('Unknown error occurred');
- logger.error({
+ logger.warn({
message: 'Failed to connect to SignalR hubs',
context: { error: err },
});
@@ -251,7 +251,7 @@ export const useSignalRStore = create((set, get) => ({
set({ isGeolocationHubConnected: false, lastGeolocationMessage: null });
} catch (error) {
const err = error instanceof Error ? error : new Error('Unknown error occurred');
- logger.error({
+ logger.warn({
message: 'Failed to disconnect from SignalR hubs',
context: { error: err },
});
diff --git a/yarn.lock b/yarn.lock
index 2ed976e..d0c1556 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -65,6 +65,27 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d"
integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==
+"@babel/core@7.26.10":
+ version "7.26.10"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9"
+ integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==
+ dependencies:
+ "@ampproject/remapping" "^2.2.0"
+ "@babel/code-frame" "^7.26.2"
+ "@babel/generator" "^7.26.10"
+ "@babel/helper-compilation-targets" "^7.26.5"
+ "@babel/helper-module-transforms" "^7.26.0"
+ "@babel/helpers" "^7.26.10"
+ "@babel/parser" "^7.26.10"
+ "@babel/template" "^7.26.9"
+ "@babel/traverse" "^7.26.10"
+ "@babel/types" "^7.26.10"
+ convert-source-map "^2.0.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.3"
+ semver "^6.3.1"
+
"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.0", "@babel/core@^7.21.3", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.25.2", "@babel/core@^7.27.4":
version "7.28.4"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.4.tgz#12a550b8794452df4c8b084f95003bce1742d496"
@@ -86,27 +107,6 @@
json5 "^2.2.3"
semver "^6.3.1"
-"@babel/core@~7.26.0":
- version "7.26.10"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9"
- integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==
- dependencies:
- "@ampproject/remapping" "^2.2.0"
- "@babel/code-frame" "^7.26.2"
- "@babel/generator" "^7.26.10"
- "@babel/helper-compilation-targets" "^7.26.5"
- "@babel/helper-module-transforms" "^7.26.0"
- "@babel/helpers" "^7.26.10"
- "@babel/parser" "^7.26.10"
- "@babel/template" "^7.26.9"
- "@babel/traverse" "^7.26.10"
- "@babel/types" "^7.26.10"
- convert-source-map "^2.0.0"
- debug "^4.1.0"
- gensync "^1.0.0-beta.2"
- json5 "^2.2.3"
- semver "^6.3.1"
-
"@babel/generator@^7.20.5", "@babel/generator@^7.25.0", "@babel/generator@^7.26.10", "@babel/generator@^7.28.3", "@babel/generator@^7.7.2":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e"
@@ -1024,7 +1024,7 @@
resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-1.10.1.tgz#1d76d15290c0212076c15ede94d15157ba0c6344"
integrity sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ==
-"@commitlint/cli@~19.2.2":
+"@commitlint/cli@19.2.2":
version "19.2.2"
resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-19.2.2.tgz#7b6d78596dcf6d716942b147aa07c04c4ee126df"
integrity sha512-P8cbOHfg2PQRzfICLSrzUVOCVMqjEZ8Hlth6mtJ4yOEjT47Q5PbIGymgX3rLVylNw+3IAT2Djn9IJ2wHbXFzBg==
@@ -1037,7 +1037,7 @@
execa "^8.0.1"
yargs "^17.0.0"
-"@commitlint/config-conventional@~19.2.2":
+"@commitlint/config-conventional@19.2.2":
version "19.2.2"
resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-19.2.2.tgz#1f4e6975d428985deacf2b3ff6547e02c9302054"
integrity sha512-mLXjsxUVLYEGgzbxbxicGPggDuyWNkf25Ht23owXIH+zV2pv1eJuzLK3t1gDY5Gp6pxdE60jZnWUY5cvgL3ufw==
@@ -1179,12 +1179,12 @@
"@types/conventional-commits-parser" "^5.0.0"
chalk "^5.3.0"
-"@config-plugins/react-native-callkeep@^11.0.0":
+"@config-plugins/react-native-callkeep@11.0.0":
version "11.0.0"
resolved "https://registry.yarnpkg.com/@config-plugins/react-native-callkeep/-/react-native-callkeep-11.0.0.tgz#d97526bb7cebc3082701fd6b3c285a56479a0a66"
integrity sha512-DsfpNU+VCgF9/fNU/BHxlJa1+RiMz0UpFDp4Yj86hk0uahA5sPHmMIThhMg4DJjdYL70U6wELSzsGYaAcsI4vQ==
-"@config-plugins/react-native-webrtc@~12.0.0":
+"@config-plugins/react-native-webrtc@12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@config-plugins/react-native-webrtc/-/react-native-webrtc-12.0.0.tgz#2bad8d59fa8aeb1b311c82c8704259154b00805f"
integrity sha512-EIYR+ArIOFBz8cEHSdPjxqFPhaN+nNeoPnI6iVStctYKZdl6AEgmXq6Qfpds6qyin1W4JP7wq2jJh32OsRfwxg==
@@ -1203,7 +1203,7 @@
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
-"@dev-plugins/react-query@~0.2.0":
+"@dev-plugins/react-query@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@dev-plugins/react-query/-/react-query-0.2.0.tgz#623f53f082c550dca0601879dbe1423136f25bb2"
integrity sha512-tbGfXiR4/Pd9V6oJGDqx/YFtNpHJ0jrfLhDnY6k9yZu2e5niuIStDVKDimZ3m+HYXQDw62Ydk5NFa6fcRq4soA==
@@ -1547,7 +1547,7 @@
resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-54.0.10.tgz#688f4338255d2fdea970f44e2dfd8e8d37dec292"
integrity sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA==
-"@expo/config@~12.0.11", "@expo/config@~12.0.12", "@expo/config@~12.0.13":
+"@expo/config@12.0.13", "@expo/config@~12.0.11", "@expo/config@~12.0.13":
version "12.0.13"
resolved "https://registry.yarnpkg.com/@expo/config/-/config-12.0.13.tgz#8e696e6121c3c364e1dd527f595cf0a1d9386828"
integrity sha512-Cu52arBa4vSaupIWsF0h7F/Cg//N374nYb7HAxV0I4KceKA7x2UXpYaHOL7EEYYvp7tZdThBjvGpVmr8ScIvaQ==
@@ -1609,7 +1609,7 @@
resolve-from "^5.0.0"
semver "^7.6.0"
-"@expo/html-elements@~0.10.1":
+"@expo/html-elements@0.10.1":
version "0.10.1"
resolved "https://registry.yarnpkg.com/@expo/html-elements/-/html-elements-0.10.1.tgz#ec2625370cf1d4cb78efa954df45d422532d5ab6"
integrity sha512-3PTmtkV15D7+lykXVtvkH1jQ5Y6JE+e3zCaoMMux7z2cSLGQUNwDEUwG37gew3OEB1/E4/SEWgjvg8m7E6/e2Q==
@@ -1635,7 +1635,7 @@
"@babel/code-frame" "^7.20.0"
json5 "^2.2.3"
-"@expo/metro-config@54.0.14", "@expo/metro-config@~54.0.14":
+"@expo/metro-config@54.0.14", "@expo/metro-config@54.0.15", "@expo/metro-config@~54.0.14":
version "54.0.15"
resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-54.0.15.tgz#aafdd2c2627fa60927e2d307f4d8cd303b6c5169"
integrity sha512-SqIya4VZ9KHM1S9g+xR0A+QKw1Tfs7Gacx6bQNJ98vs4+O7I5+QP5mHZIB0QSZLUV8opiXebHYTiTu+0OAsIUw==
@@ -1662,7 +1662,7 @@
postcss "~8.4.32"
resolve-from "^5.0.0"
-"@expo/metro-runtime@^6.1.2", "@expo/metro-runtime@~6.1.2":
+"@expo/metro-runtime@6.1.2", "@expo/metro-runtime@^6.1.2":
version "6.1.2"
resolved "https://registry.yarnpkg.com/@expo/metro-runtime/-/metro-runtime-6.1.2.tgz#5a4ff117df6115f9c9d4dcc561065e16d69c078b"
integrity sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g==
@@ -2247,7 +2247,7 @@
dependencies:
tslib "^2.8.0"
-"@gluestack-ui/accordion@~1.0.6":
+"@gluestack-ui/accordion@1.0.14":
version "1.0.14"
resolved "https://registry.yarnpkg.com/@gluestack-ui/accordion/-/accordion-1.0.14.tgz#22c1192557c9fcf958daf7a63fa17d35bbc6cbfe"
integrity sha512-dpqlSnIZk1grZOPtLoMYh08OGmRgl/Sjpv1KltPdpjNaG2Gt7DPAmT/8l9wVuJYMOfdsLOsBOfzGo5eECZ9WFg==
@@ -2257,7 +2257,7 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "0.2.16"
-"@gluestack-ui/actionsheet@~0.2.44":
+"@gluestack-ui/actionsheet@0.2.53":
version "0.2.53"
resolved "https://registry.yarnpkg.com/@gluestack-ui/actionsheet/-/actionsheet-0.2.53.tgz#09608d32f373f99c0dff9fb62856895a843f1a05"
integrity sha512-93qHvq6BHezJ7wt2lce4OQ38wXCGsDtglj5nlmwo2T41vj4ubOtDVoSUhXT+hfH0EmRr0TxFNeFqIgesO46qVw==
@@ -2270,7 +2270,7 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "0.2.16"
-"@gluestack-ui/alert-dialog@~0.1.30":
+"@gluestack-ui/alert-dialog@0.1.38":
version "0.1.38"
resolved "https://registry.yarnpkg.com/@gluestack-ui/alert-dialog/-/alert-dialog-0.1.38.tgz#fd63bdbdf2ddd627aa3e55e4758a2c0be61b411c"
integrity sha512-qarZlXlmGHwjfOFaEJgLpNstDd38b7TBMdYtpb6xXIvOA6W6IyNnnKYPNasOiUlNrW8qvqrZrZc+xJfK5USr0w==
@@ -2282,19 +2282,19 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "0.2.16"
-"@gluestack-ui/alert@~0.1.15":
+"@gluestack-ui/alert@0.1.16":
version "0.1.16"
resolved "https://registry.yarnpkg.com/@gluestack-ui/alert/-/alert-0.1.16.tgz#cede162e04c3dcb07a3da797221852bb7c127311"
integrity sha512-vE0litmicuKFJFp9TY3mRuZmyH/E24IdHlC0f3FZjfa+AzeOS+bZdKMRix2EYkvBGrA6uNffWUuHeXWQGMRVJQ==
-"@gluestack-ui/avatar@~0.1.17":
+"@gluestack-ui/avatar@0.1.18":
version "0.1.18"
resolved "https://registry.yarnpkg.com/@gluestack-ui/avatar/-/avatar-0.1.18.tgz#bb890a7b889060e01faf549f59a47d11c04ada1c"
integrity sha512-VA9XwtavYLYCWrjxHc2u9gRpV97cPRcr/6KJ4tLiMiQbiRL1b4zckiL+/F39fB6xjUOUQHl3Fjo/Yd8swa0MBg==
dependencies:
"@gluestack-ui/utils" "^0.1.14"
-"@gluestack-ui/button@~1.0.14":
+"@gluestack-ui/button@1.0.14":
version "1.0.14"
resolved "https://registry.yarnpkg.com/@gluestack-ui/button/-/button-1.0.14.tgz#c8c2d60888df7aa472485792a6c848a194a933e0"
integrity sha512-Rqv5PY18jzeVACBwsVxXlyDbHjBiSXGVqY29VO6KHZGMMokCPW6MXYXT8ZpriLxMc1DRkgTtpTVYZCJBFaYTbg==
@@ -2303,7 +2303,7 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "0.2.16"
-"@gluestack-ui/checkbox@~0.1.31":
+"@gluestack-ui/checkbox@0.1.39":
version "0.1.39"
resolved "https://registry.yarnpkg.com/@gluestack-ui/checkbox/-/checkbox-0.1.39.tgz#1c84a3a19120b9ab9ea1c9c6ac7e8771902bd194"
integrity sha512-CPL9+g9qIFuGGQAQuFySATTxrqGMhIx/ASpBdl/YC50Ec/d7gMZlnKww3N1og3a6JqMG8yeWwbH+GH3z07XsfA==
@@ -2317,12 +2317,12 @@
"@react-native-aria/utils" "0.2.12"
"@react-stately/checkbox" "^3.4.2"
-"@gluestack-ui/divider@~0.1.9":
+"@gluestack-ui/divider@0.1.10":
version "0.1.10"
resolved "https://registry.yarnpkg.com/@gluestack-ui/divider/-/divider-0.1.10.tgz#506ec45f9b5183a0e3b293dbff7b7da19edcfda8"
integrity sha512-/hZx1Rmy4Pgln9AwAprAEQcxYPEHjHSNF4xCUWlK/q0peyiMT5Nagt54VnxykVn5A0b2zg5QKP0pOqOF9xeE6w==
-"@gluestack-ui/fab@~0.1.21":
+"@gluestack-ui/fab@0.1.28":
version "0.1.28"
resolved "https://registry.yarnpkg.com/@gluestack-ui/fab/-/fab-0.1.28.tgz#603da60deabea09e71baddb82a4abe61262b9d7d"
integrity sha512-NTzTN08KyX3iqcC5LiRqymrO+TlCzfcjDS5GeO0Y+1AiH7loKwWwDm0PFn3TbflfycRTG0jw8RkUqs2Ckov/mg==
@@ -2331,7 +2331,7 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "0.2.16"
-"@gluestack-ui/form-control@^0.1.19", "@gluestack-ui/form-control@~0.1.18":
+"@gluestack-ui/form-control@0.1.19", "@gluestack-ui/form-control@^0.1.19":
version "0.1.19"
resolved "https://registry.yarnpkg.com/@gluestack-ui/form-control/-/form-control-0.1.19.tgz#7a75b65f5ec89da95c30442750d6f7af71db6868"
integrity sha512-6YbPbi/RZrXc5DyVPbxPV17FYaBoEl1yAdSwut8iE6n+yQekjluINrh2q5ZPWF2SGmyo7VSNcL85yeU5I97xHg==
@@ -2344,7 +2344,7 @@
resolved "https://registry.yarnpkg.com/@gluestack-ui/hooks/-/hooks-0.1.13.tgz#4f1494850bb6e95a1eaeef10c724c83adfe6954f"
integrity sha512-2x5EGcAuvdWFoOaqBRJsilR9Nx07tDvuAr3MI5SOmDdn7Bjzi+3CVAZwwDE2nGwAL3ldJvbAlzVkiNCw4TsSeA==
-"@gluestack-ui/icon@~0.1.27":
+"@gluestack-ui/icon@0.1.27":
version "0.1.27"
resolved "https://registry.yarnpkg.com/@gluestack-ui/icon/-/icon-0.1.27.tgz#6a2241bcdf3d15e53e7d191922792b634e290b52"
integrity sha512-mIsfgdVUihtRORoeDxn3kMFph5XOXK+Scnj1JXLufbt7h9kbfaZLGhYDc9cp2zpPc1Xki9SC1yXYfw8y4FAAdA==
@@ -2353,7 +2353,7 @@
"@gluestack-ui/utils" "^0.1.14"
"@react-native-aria/focus" "^0.2.9"
-"@gluestack-ui/image@~0.1.10":
+"@gluestack-ui/image@0.1.17":
version "0.1.17"
resolved "https://registry.yarnpkg.com/@gluestack-ui/image/-/image-0.1.17.tgz#b92c788bea557c5a4e34231c5430398cc1220011"
integrity sha512-hcyTVBUw55bSjEpyynIZJVm3Ff6lcjKXFT55f890oy2PNGyFdsKr6V+t/jy5n/2fwbv1eR2KU0uDDiI2vmBh+A==
@@ -2362,7 +2362,7 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "0.2.16"
-"@gluestack-ui/input@~0.1.38":
+"@gluestack-ui/input@0.1.38":
version "0.1.38"
resolved "https://registry.yarnpkg.com/@gluestack-ui/input/-/input-0.1.38.tgz#8430b5f4901e59475a99d5e3f473bd5b0d248a19"
integrity sha512-NzwDOXkkMYzBQ0h7UnhKA2h54/qlxDxMFGXykkmYOl7mc7QJc1aJaveo4yMHtpYvcQG17xLyD+Z+5CQYA76nvw==
@@ -2372,7 +2372,7 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "0.2.16"
-"@gluestack-ui/link@~0.1.22":
+"@gluestack-ui/link@0.1.29":
version "0.1.29"
resolved "https://registry.yarnpkg.com/@gluestack-ui/link/-/link-0.1.29.tgz#5dcba644772570dc9165e8b36862c70bd49ea8fb"
integrity sha512-7rNx0puyHr+LG4moy/OfMybwVIxQ/ES4H/xyskvcvylCtbxPqN+0oSQ9WA4CISeipwK4x+2aQQRvjasYt9rXgA==
@@ -2381,7 +2381,7 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "0.2.16"
-"@gluestack-ui/menu@~0.2.43":
+"@gluestack-ui/menu@0.2.43":
version "0.2.43"
resolved "https://registry.yarnpkg.com/@gluestack-ui/menu/-/menu-0.2.43.tgz#54cbaef5d9f4e1c1a9d5b1e809023d1e8b20cebd"
integrity sha512-RjThV7nqGOFnsdCMG/JHs2qrVvtjw3MwKJJPD0bAJa+rhBEk2n9tbEFHPR6KWWNUmSnP3aWSZldT5Y87JiVKNA==
@@ -2398,7 +2398,7 @@
"@react-stately/utils" "^3.6.0"
react-stately "^3.21.0"
-"@gluestack-ui/modal@~0.1.35":
+"@gluestack-ui/modal@0.1.41":
version "0.1.41"
resolved "https://registry.yarnpkg.com/@gluestack-ui/modal/-/modal-0.1.41.tgz#e8c04fd9ac7598dd0f2a2ff15f8526b9cceab2d2"
integrity sha512-pEO5h6TxEpYyU/CEhRD/pAkuBsDfqnFUe51AxK7w6FLjBuTKA5P0wsQ1pfcovJeykkpreG+MiUvNPIo3MW/Lfw==
@@ -2411,7 +2411,7 @@
"@react-native-aria/interactions" "0.2.16"
"@react-native-aria/overlays" "^0.3.15"
-"@gluestack-ui/nativewind-utils@~1.0.26":
+"@gluestack-ui/nativewind-utils@1.0.28":
version "1.0.28"
resolved "https://registry.yarnpkg.com/@gluestack-ui/nativewind-utils/-/nativewind-utils-1.0.28.tgz#54ec885c5683ce7a18aeafbd31ef00e17913ef11"
integrity sha512-POWYUK99Y9zRbDbTv0/FF6YtPmymaAmqgWKU3QYFjraK3HPAgF9HjovJVEN8SycEDylJAnACw53AoHFQ3Bxh+A==
@@ -2420,7 +2420,7 @@
patch-package "8.0.0"
tailwind-variants "0.1.20"
-"@gluestack-ui/overlay@0.1.22", "@gluestack-ui/overlay@^0.1.16", "@gluestack-ui/overlay@^0.1.20", "@gluestack-ui/overlay@^0.1.22", "@gluestack-ui/overlay@~0.1.16":
+"@gluestack-ui/overlay@0.1.22", "@gluestack-ui/overlay@^0.1.16", "@gluestack-ui/overlay@^0.1.20", "@gluestack-ui/overlay@^0.1.22":
version "0.1.22"
resolved "https://registry.yarnpkg.com/@gluestack-ui/overlay/-/overlay-0.1.22.tgz#b0c03eb88f37e1564b6522f3d021052dcce86f4a"
integrity sha512-ttlSjO/ysN9NuTq5JPoVwOAU8aKMaj738xTCxOVgBRr1+kbBUVpmBnygVlC6PvHvJF1A00a0ZTzw48CIVL5aNw==
@@ -2429,7 +2429,7 @@
"@react-native-aria/interactions" "0.2.16"
"@react-native-aria/overlays" "^0.3.15"
-"@gluestack-ui/popover@~0.1.49":
+"@gluestack-ui/popover@0.1.49":
version "0.1.49"
resolved "https://registry.yarnpkg.com/@gluestack-ui/popover/-/popover-0.1.49.tgz#38fa4971938d5a84c74fab9e305c0293dc7c36c5"
integrity sha512-AI73AXEPoIPGpwhQICn9BiQIJzCDGSb4RT6OQI2V3TKlBYM+RodZvK0RKMipEsROBQwNWbcJoPJRkH9jwDVrBA==
@@ -2442,7 +2442,7 @@
"@react-native-aria/interactions" "0.2.16"
"@react-native-aria/overlays" "0.3.15"
-"@gluestack-ui/pressable@~0.1.16":
+"@gluestack-ui/pressable@0.1.23":
version "0.1.23"
resolved "https://registry.yarnpkg.com/@gluestack-ui/pressable/-/pressable-0.1.23.tgz#4324aa4bdbb6e5844416d7cdcc7f62f9f6956c89"
integrity sha512-y7Sqwwe4+nIM5pECr3UT9qx7MMyuJHt1od6dfB/K+S2X91uZgTJEw7PUQgvOW6Jr8dBrStxFiTWfHmDqX/FVOQ==
@@ -2451,7 +2451,7 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "0.2.16"
-"@gluestack-ui/progress@~0.1.16":
+"@gluestack-ui/progress@0.1.18":
version "0.1.18"
resolved "https://registry.yarnpkg.com/@gluestack-ui/progress/-/progress-0.1.18.tgz#9a659115b4579fee057c2d13bbc52e9e080a6958"
integrity sha512-WQCLdvqoiIIQnLBXG7+HDTREMbUfEy9rV7FuBMcBBqEFILuVT1W4hyuXLz/CPrUoCWZJ8OUKvLZvaWWzW8pA9Q==
@@ -2467,7 +2467,7 @@
tsconfig "7"
typescript "^5.6.3"
-"@gluestack-ui/radio@~0.1.40":
+"@gluestack-ui/radio@0.1.40":
version "0.1.40"
resolved "https://registry.yarnpkg.com/@gluestack-ui/radio/-/radio-0.1.40.tgz#cfbb709db8170d9a7dc70decdb7a3522fb0e9e67"
integrity sha512-UJ6i3qpbBVsSWrxQi889yXczPjmfhfHCplMK2n4xkEfsYcjDVKpronVfqbJ41gf27fBzyLJFnCec3uPL4+obSA==
@@ -2487,7 +2487,7 @@
dependencies:
"@react-native-aria/focus" "^0.2.9"
-"@gluestack-ui/select@~0.1.31":
+"@gluestack-ui/select@0.1.31":
version "0.1.31"
resolved "https://registry.yarnpkg.com/@gluestack-ui/select/-/select-0.1.31.tgz#b0f91fdaf0124db5fa3cdd0e3a7460db9406eff0"
integrity sha512-d8vfdCK4VFBYEDYMCTRYcZCWvYaf7UWMJGg2uMnutHA8Y4sDXjKR5P821xKdh75QVkmZRZm1MdzSWFvXUzAlgg==
@@ -2497,7 +2497,7 @@
"@gluestack-ui/utils" "^0.1.14"
"@react-native-aria/focus" "^0.2.9"
-"@gluestack-ui/slider@~0.1.32":
+"@gluestack-ui/slider@0.1.32":
version "0.1.32"
resolved "https://registry.yarnpkg.com/@gluestack-ui/slider/-/slider-0.1.32.tgz#65d166ff51a5a94c27a68fc1ccf2d4d373bd1824"
integrity sha512-g0e7dAGOYYARlL3cdHe3mhN71j85TnqUgK/xOYWjVDE0U+atIXxxTVEXeO0ZPGJ3YUOUUAInIVGaa0xvnjEkYg==
@@ -2510,12 +2510,12 @@
"@react-native-aria/slider" "^0.2.12"
"@react-stately/slider" "^3.2.4"
-"@gluestack-ui/spinner@~0.1.14":
+"@gluestack-ui/spinner@0.1.15":
version "0.1.15"
resolved "https://registry.yarnpkg.com/@gluestack-ui/spinner/-/spinner-0.1.15.tgz#712276227568476f55672b7bbe0082478c0c0a65"
integrity sha512-jkXOGhna05HM2tcPKbA+t+vqcVRgRowwC6ES0A9kY5fcuURH1V41UFx/YWyHdSsHwmWlIoCCdWycF78UbNVdPg==
-"@gluestack-ui/switch@~0.1.22":
+"@gluestack-ui/switch@0.1.29":
version "0.1.29"
resolved "https://registry.yarnpkg.com/@gluestack-ui/switch/-/switch-0.1.29.tgz#d2a26f66cfc3065f27ea4af0206f4cb0ef34f5ca"
integrity sha512-swx0J20ULpN+pkHg8KioWibZETWE/6XvCBckmApNZf9RO2MiHHkNMw4Txne4u1z0OyLKGzBW1dbmkhLIOObaXg==
@@ -2526,7 +2526,7 @@
"@react-native-aria/interactions" "0.2.16"
"@react-stately/toggle" "^3.4.4"
-"@gluestack-ui/textarea@~0.1.23":
+"@gluestack-ui/textarea@0.1.25":
version "0.1.25"
resolved "https://registry.yarnpkg.com/@gluestack-ui/textarea/-/textarea-0.1.25.tgz#203f3e60c0177ad3b722f0d2745db5ae7fbcd3d8"
integrity sha512-hXJx9LYSfrx7/Lrh2D2cTjr7PqvdkxJXBv2VydcR01X4WEN34DZkik+at/qk5FBw+p4t0vE+BTAK3IoMJwWvGg==
@@ -2536,7 +2536,7 @@
"@react-native-aria/focus" "^0.2.9"
"@react-native-aria/interactions" "^0.2.16"
-"@gluestack-ui/toast@~1.0.8":
+"@gluestack-ui/toast@1.0.9":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@gluestack-ui/toast/-/toast-1.0.9.tgz#6ee18051e7af0459fbb3ede4cc4bf1a9ec1719a0"
integrity sha512-aMlPczeoH/PZTMnhV29fqqW1Xc/9QmYEsR0bU9BfLyAGM9UMjW3vGe4yZSgxX7xjQ9C7+KO5WnTH0FmPoAbVtg==
@@ -2547,7 +2547,7 @@
"@gluestack-ui/utils" "^0.1.14"
"@react-native-aria/focus" "^0.2.9"
-"@gluestack-ui/tooltip@~0.1.32":
+"@gluestack-ui/tooltip@0.1.44":
version "0.1.44"
resolved "https://registry.yarnpkg.com/@gluestack-ui/tooltip/-/tooltip-0.1.44.tgz#bc682f046573001a8c611f04991ab6ed3200e76f"
integrity sha512-gHVOYwjQK41ofM+D7131miNobbBAr0fuxr/52x3EJJpntqItF5u35ikj8XKy1T7nrOb0iZaYh7C0W08if1Skgg==
@@ -2576,7 +2576,7 @@
dependencies:
"@react-native-aria/focus" "^0.2.9"
-"@gorhom/bottom-sheet@~5.2.13":
+"@gorhom/bottom-sheet@5.2.13":
version "5.2.13"
resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-5.2.13.tgz#67764e2300b224aff332a818b6deba2c899badd6"
integrity sha512-cMxyd9kIowMME9kw2wwXAuWrXUQnPkJQz7rDbOSBBomZ+PpV/C/tlO1UozBrAe2zs3tp9th3JMW21FI/y0VeuQ==
@@ -2643,7 +2643,7 @@
dependencies:
"@hapi/hoek" "^11.0.2"
-"@hookform/resolvers@~3.9.0":
+"@hookform/resolvers@3.9.1":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.9.1.tgz#a23883c40bfd449cb6c6ab5a0fa0729184c950ff"
integrity sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==
@@ -3368,7 +3368,7 @@
"@solid-primitives/refs" "^1.0.5"
"@solid-primitives/utils" "^6.2.1"
-"@legendapp/motion@~2.4.0":
+"@legendapp/motion@2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@legendapp/motion/-/motion-2.4.0.tgz#eef6f934b784e07fe94631d5648d412f34fa0c6b"
integrity sha512-AAYpRLGvxGD5hIGl9sVHyoUufr66zoH82PuxYcKiPSMdCBI3jwZFWh6CuHjV1leRKVIRk2py1rSvIVabG8eqcw==
@@ -3410,12 +3410,12 @@
dependencies:
"@bufbuild/protobuf" "^1.10.0"
-"@livekit/react-native-expo-plugin@^1.0.1":
+"@livekit/react-native-expo-plugin@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@livekit/react-native-expo-plugin/-/react-native-expo-plugin-1.0.1.tgz#8472299b1b399209463347ed27de7b0f743d700d"
integrity sha512-CSPjjzgDDlBH1ZyFyaw7/FW2Ql1S51eUkIxv/vjGwVshn+lUD6eQ9VgfUh7ha84itvjXi9X87FvP0XWKn9CiFQ==
-"@livekit/react-native-webrtc@^137.0.2":
+"@livekit/react-native-webrtc@137.0.2":
version "137.0.2"
resolved "https://registry.yarnpkg.com/@livekit/react-native-webrtc/-/react-native-webrtc-137.0.2.tgz#5fbd7876e6768a2247d04ef5653c373d92d74783"
integrity sha512-0aXYATcBraOMDTteKzmfH5ICNHw8xFyMPHmhKg14+94fAGZ2hGjdHZUSkzL14+e508W486aIAmbXipuSQCCJgA==
@@ -3424,7 +3424,7 @@
debug "4.3.4"
event-target-shim "6.0.2"
-"@livekit/react-native@^2.9.1":
+"@livekit/react-native@2.9.1":
version "2.9.1"
resolved "https://registry.yarnpkg.com/@livekit/react-native/-/react-native-2.9.1.tgz#1d0d9a40ebecd49e6c088cbe0d62b6380c1ff7ca"
integrity sha512-oN1aWQeAuo3NhhrQRCI/V9upi8PhRQRFyZHBqnU/AlxYFyTKWKGmtVJtJWkGW+zNUF3lEF+nZMnpIHft9aJdqQ==
@@ -3497,7 +3497,7 @@
resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe"
integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
-"@microsoft/signalr@~8.0.7":
+"@microsoft/signalr@8.0.17":
version "8.0.17"
resolved "https://registry.yarnpkg.com/@microsoft/signalr/-/signalr-8.0.17.tgz#6eac218beac38e4e4ba4a3577ed39ac33711b2ed"
integrity sha512-5pM6xPtKZNJLO0Tq5nQasVyPFwi/WBY3QB5uc/v3dIPTpS1JXQbaXAQAPxFoQ5rTBFE094w8bbqkp17F9ReQvA==
@@ -3608,7 +3608,7 @@
resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e"
integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==
-"@notifee/react-native@^9.1.8":
+"@notifee/react-native@9.1.8":
version "9.1.8"
resolved "https://registry.yarnpkg.com/@notifee/react-native/-/react-native-9.1.8.tgz#3d55cb3fbcc21f9e35931e366afdf64b294da891"
integrity sha512-Az/dueoPerJsbbjRxu8a558wKY+gONUrfoy3Hs++5OqbeMsR0dYe6P+4oN6twrLFyzAhEA1tEoZRvQTFDRmvQg==
@@ -3631,7 +3631,7 @@
solid-motionone "^1.0.3"
tailwind-merge "^2.4.0"
-"@novu/react-native@^3.11.0":
+"@novu/react-native@3.11.0":
version "3.11.0"
resolved "https://registry.yarnpkg.com/@novu/react-native/-/react-native-3.11.0.tgz#6fc1dda2f35bb5dff0a546822d1c0c9c307412f7"
integrity sha512-lBpdjV9y9gy2qEPeh9lxx3BBDdSDXcFGOyxjMELF1QE6+m5/ooGgq2xOUOO7Z7OeWyPkAQBr0WpiIQaP3qEy0A==
@@ -4245,19 +4245,19 @@
"@react-aria/ssr" "^3.0.1"
"@react-aria/utils" "^3.3.0"
-"@react-native-community/netinfo@^11.4.1":
+"@react-native-community/netinfo@11.4.1":
version "11.4.1"
resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-11.4.1.tgz#a3c247aceab35f75dd0aa4bfa85d2be5a4508688"
integrity sha512-B0BYAkghz3Q2V09BF88RA601XursIEA111tnc2JOaN7axJWmNefmfjZqw/KdSxKZp7CZUuPpjBmz/WCR9uaHYg==
-"@react-native-firebase/app@^23.5.0":
+"@react-native-firebase/app@23.5.0":
version "23.5.0"
resolved "https://registry.yarnpkg.com/@react-native-firebase/app/-/app-23.5.0.tgz#b84540b1822e510dfd3c3890e8ca10b2655759fe"
integrity sha512-TOlm6V6fbILwgFP37QZM9Y0nfAW6zqNGVIWlMlepQB6b/BzzFMrCl1FiyknqD5l7i1jgdFQrqX1WH6ZO4ePa/g==
dependencies:
firebase "12.4.0"
-"@react-native-firebase/messaging@^23.5.0":
+"@react-native-firebase/messaging@23.5.0":
version "23.5.0"
resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-23.5.0.tgz#8e39a44a90f7bf95a9a167649efa7f1e8251f9b7"
integrity sha512-2EM28isDWgqCauar/kOnhpFQZ8ARnq9iE0N093TrS/sr+Mu6PHkPEDJElV9LFfp6nfxxjlm75h+x+nJrEDRkhQ==
@@ -4929,7 +4929,7 @@
resolved "https://registry.yarnpkg.com/@semantic-release/error/-/error-3.0.0.tgz#30a3b97bbb5844d695eb22f9d3aa40f6a92770c2"
integrity sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==
-"@semantic-release/git@^10.0.1":
+"@semantic-release/git@10.0.1":
version "10.0.1"
resolved "https://registry.yarnpkg.com/@semantic-release/git/-/git-10.0.1.tgz#c646e55d67fae623875bf3a06a634dd434904498"
integrity sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==
@@ -5060,7 +5060,7 @@
dependencies:
"@sentry/cli" "3.4.1"
-"@sentry/react-native@^8.10.0":
+"@sentry/react-native@8.10.0":
version "8.10.0"
resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-8.10.0.tgz#eeaf79b53758aeee5e993276c616372cfe19a206"
integrity sha512-Pfr7h1unqMsE87UMwaUIZ26VjX7SSsitBLpK4gHeIwYmuXn+qfdYUmme6RnoLlL5IPzu8pCLoRNCvdAJy6eTgw==
@@ -5329,14 +5329,14 @@
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.52.3.tgz#4d66b3e7a6d77513cb4d3cb661b2c03009103f5a"
integrity sha512-+Gh7lXn+eoAsarvvnndgqBeJ5lOjup8qgQnrTsFuhNTEAo0H934DxEPro4s3TlmvITfDTJ3UDCy7kY8Azm0qsA==
-"@tanstack/react-query@~5.52.1":
+"@tanstack/react-query@5.52.3":
version "5.52.3"
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.52.3.tgz#eb36592e6ff0869248ccfd674e626d458e1225c1"
integrity sha512-1K7l2hkqlWuh5SdaTYPSwMmHJF5dDk5INK+EtiEwUZW4+usWTXZx7QeHuk078oSzTzaVkEFyT3VquK7F0hYkUw==
dependencies:
"@tanstack/query-core" "5.52.3"
-"@testing-library/jest-dom@~6.5.0":
+"@testing-library/jest-dom@6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz#50484da3f80fb222a853479f618a9ce5c47bfe54"
integrity sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==
@@ -5349,7 +5349,7 @@
lodash "^4.17.21"
redent "^3.0.0"
-"@testing-library/react-native@~12.9.0":
+"@testing-library/react-native@12.9.0":
version "12.9.0"
resolved "https://registry.yarnpkg.com/@testing-library/react-native/-/react-native-12.9.0.tgz#9c727d9ffec91024be3288ed9376df3673154784"
integrity sha512-wIn/lB1FjV2N4Q7i9PWVRck3Ehwq5pkhAef5X5/bmQ78J/NoOsGbVY2/DG5Y9Lxw+RfE+GvSEh/fe5Tz6sKSvw==
@@ -5598,7 +5598,7 @@
dependencies:
"@types/geojson" "*"
-"@types/geojson@*", "@types/geojson@^7946.0.10", "@types/geojson@^7946.0.16", "@types/geojson@^7946.0.7", "@types/geojson@~7946.0.16":
+"@types/geojson@*", "@types/geojson@7946.0.16", "@types/geojson@^7946.0.10", "@types/geojson@^7946.0.16", "@types/geojson@^7946.0.7":
version "7946.0.16"
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a"
integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==
@@ -5625,7 +5625,7 @@
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4"
integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==
-"@types/i18n-js@~3.8.9":
+"@types/i18n-js@3.8.9":
version "3.8.9"
resolved "https://registry.yarnpkg.com/@types/i18n-js/-/i18n-js-3.8.9.tgz#074d1389539d2db992e6afd7eb379aa02929ef93"
integrity sha512-bSxgya4x5O+x+QhfCGckiDDE+17XGPp1TNBgBA/vfF5EwdiZC70F4cKG5QK2v44+v62oY7/t/InreRhxskulcA==
@@ -5649,7 +5649,7 @@
dependencies:
"@types/istanbul-lib-report" "*"
-"@types/jest@~29.5.14":
+"@types/jest@29.5.14":
version "29.5.14"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5"
integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==
@@ -5683,7 +5683,7 @@
dependencies:
"@types/node" "*"
-"@types/lodash.memoize@~4.1.9":
+"@types/lodash.memoize@4.1.9":
version "4.1.9"
resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.9.tgz#9f8912d39b6e450c0d342a2b74c99d331bf2016b"
integrity sha512-glY1nQuoqX4Ft8Uk+KfJudOD7DQbbEDF6k9XpGncaohW3RW4eSWBlx6AA0fZCrh40tZcQNH4jS/Oc59J6Eq+aw==
@@ -5756,12 +5756,12 @@
"@types/node" "*"
xmlbuilder ">=11.0.1"
-"@types/react-native-base64@~0.2.2":
+"@types/react-native-base64@0.2.2":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@types/react-native-base64/-/react-native-base64-0.2.2.tgz#d4e1d537e6d547d23d96a1e64627acc13587ae6b"
integrity sha512-obr+/L9Jaxdr+xCVS/IQcYgreg5xtnui4Wqw/G1acBUtW2CnqVJj6lK6F/5F3+5d2oZEo5xDDLqy8GVn2HbEmw==
-"@types/react@~19.1.10":
+"@types/react@19.1.17":
version "19.1.17"
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.17.tgz#8be0b9c546cede389b930a98eb3fad1897f209c3"
integrity sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==
@@ -5831,7 +5831,7 @@
dependencies:
"@types/node" "*"
-"@typescript-eslint/eslint-plugin@^8.0.0", "@typescript-eslint/eslint-plugin@^8.18.2":
+"@typescript-eslint/eslint-plugin@8.56.0", "@typescript-eslint/eslint-plugin@^8.18.2":
version "8.56.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz#5aec3db807a6b8437ea5d5ebf7bd16b4119aba8d"
integrity sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==
@@ -5845,7 +5845,7 @@
natural-compare "^1.4.0"
ts-api-utils "^2.4.0"
-"@typescript-eslint/parser@^8.0.0", "@typescript-eslint/parser@^8.18.2":
+"@typescript-eslint/parser@8.56.0", "@typescript-eslint/parser@^8.18.2":
version "8.56.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.56.0.tgz#8ecff1678b8b1a742d29c446ccf5eeea7f971d72"
integrity sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==
@@ -6387,7 +6387,7 @@ app-builder-lib@26.4.0:
tiny-async-pool "1.3.0"
which "^5.0.0"
-app-icon-badge@^0.1.2:
+app-icon-badge@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/app-icon-badge/-/app-icon-badge-0.1.2.tgz#4df187989429fc282de4ee3fe0c3ee683a9a3f22"
integrity sha512-k9IEyaRFWEBjuazjgJIf6DnypoXppdxn0olkCQvts83VBehg1vm3IIp6HJpGpfDsUgymNJkYd7r8Q4pDfO6cpw==
@@ -6621,6 +6621,15 @@ available-typed-arrays@^1.0.7:
dependencies:
possible-typed-array-names "^1.0.0"
+axios@1.12.2:
+ version "1.12.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.2.tgz#6c307390136cf7a2278d09cec63b136dfc6e6da7"
+ integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==
+ dependencies:
+ follow-redirects "^1.15.6"
+ form-data "^4.0.4"
+ proxy-from-env "^1.1.0"
+
axios@^1.13.2:
version "1.13.3"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.13.3.tgz#f123e77356630a22b0163920662556944f2be1a1"
@@ -6630,14 +6639,18 @@ axios@^1.13.2:
form-data "^4.0.4"
proxy-from-env "^1.1.0"
-axios@~1.12.0:
- version "1.12.2"
- resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.2.tgz#6c307390136cf7a2278d09cec63b136dfc6e6da7"
- integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==
+babel-jest@30.0.5:
+ version "30.0.5"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.0.5.tgz#7cc7dd03d0d613125d458521f635b8c2361e89cc"
+ integrity sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg==
dependencies:
- follow-redirects "^1.15.6"
- form-data "^4.0.4"
- proxy-from-env "^1.1.0"
+ "@jest/transform" "30.0.5"
+ "@types/babel__core" "^7.20.5"
+ babel-plugin-istanbul "^7.0.0"
+ babel-preset-jest "30.0.1"
+ chalk "^4.1.2"
+ graceful-fs "^4.2.11"
+ slash "^3.0.0"
babel-jest@^29.2.1, babel-jest@^29.7.0:
version "29.7.0"
@@ -6652,19 +6665,6 @@ babel-jest@^29.2.1, babel-jest@^29.7.0:
graceful-fs "^4.2.9"
slash "^3.0.0"
-babel-jest@~30.0.0:
- version "30.0.5"
- resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.0.5.tgz#7cc7dd03d0d613125d458521f635b8c2361e89cc"
- integrity sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg==
- dependencies:
- "@jest/transform" "30.0.5"
- "@types/babel__core" "^7.20.5"
- babel-plugin-istanbul "^7.0.0"
- babel-preset-jest "30.0.1"
- chalk "^4.1.2"
- graceful-fs "^4.2.11"
- slash "^3.0.0"
-
babel-plugin-istanbul@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73"
@@ -6706,7 +6706,7 @@ babel-plugin-jest-hoist@^29.6.3:
"@types/babel__core" "^7.1.14"
"@types/babel__traverse" "^7.0.6"
-babel-plugin-module-resolver@^5.0.2:
+babel-plugin-module-resolver@5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz#cdeac5d4aaa3b08dd1ac23ddbf516660ed2d293e"
integrity sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg==
@@ -6767,7 +6767,7 @@ babel-plugin-transform-flow-enums@^0.0.2:
dependencies:
"@babel/plugin-syntax-flow" "^7.12.1"
-babel-plugin-transform-import-meta@^2.3.3:
+babel-plugin-transform-import-meta@2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.3.3.tgz#863de841f7df37e2bf39a057572a24e4f65f3c51"
integrity sha512-bbh30qz1m6ZU1ybJoNOhA2zaDvmeXMnGNBMVMDOJ1Fni4+wMBoy/j7MTRVmqAUCIcy54/rEnr9VEBsfcgbpm3Q==
@@ -7021,6 +7021,14 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+buffer@6.0.3, buffer@^6.0.3:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
+ integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.2.1"
+
buffer@^5.1.0, buffer@^5.2.0, buffer@^5.4.3, buffer@^5.5.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@@ -7029,14 +7037,6 @@ buffer@^5.1.0, buffer@^5.2.0, buffer@^5.4.3, buffer@^5.5.0:
base64-js "^1.3.1"
ieee754 "^1.1.13"
-buffer@^6.0.3:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
- integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
- dependencies:
- base64-js "^1.3.1"
- ieee754 "^1.2.1"
-
builder-util-runtime@9.5.1:
version "9.5.1"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz#74125fb374d1ecbf472ae1787485485ff7619702"
@@ -7757,7 +7757,7 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
-cross-env@~7.0.3:
+cross-env@7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
@@ -7912,16 +7912,16 @@ data-view-byte-offset@^1.0.1:
es-errors "^1.3.0"
is-data-view "^1.0.1"
+date-fns@4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14"
+ integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==
+
date-fns@^1.27.2:
version "1.30.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
-date-fns@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14"
- integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==
-
debounce@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/debounce/-/debounce-2.2.0.tgz#f895fa2fbdb579a0f0d3dcf5dde19657e50eaad5"
@@ -8276,16 +8276,16 @@ dotenv-expand@^11.0.6, dotenv-expand@~11.0.6:
dependencies:
dotenv "^16.4.5"
+dotenv@16.4.7, dotenv@~16.4.5:
+ version "16.4.7"
+ resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26"
+ integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==
+
dotenv@^16.4.5:
version "16.6.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020"
integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==
-dotenv@~16.4.5:
- version "16.4.7"
- resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26"
- integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==
-
dunder-proto@^1.0.0, dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
@@ -8347,7 +8347,7 @@ electron-publish@26.3.4:
lazy-val "^1.0.5"
mime "^2.5.2"
-electron-squirrel-startup@^1.0.1:
+electron-squirrel-startup@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz#c9171568d724884c7a2b03760bfeedcf921c63ab"
integrity sha512-sTfFIHGku+7PsHLJ7v0dRcZNkALrV+YEozINTW8X1nM//e5O3L+rfYuvSW00lmGHnYmUjARZulD8F2V8ISI9RA==
@@ -8686,7 +8686,7 @@ escodegen@^2.0.0:
optionalDependencies:
source-map "~0.6.1"
-eslint-config-expo@~9.2.0:
+eslint-config-expo@9.2.0:
version "9.2.0"
resolved "https://registry.yarnpkg.com/eslint-config-expo/-/eslint-config-expo-9.2.0.tgz#5b7d9371950cc8378ccdbf1040176dd5758eb2e4"
integrity sha512-TQgmSx+2mRM7qUS0hB5kTDrHcSC35rA1UzOSgK5YRLmSkSMlKLmXkUrhwOpnyo9D/nHdf4ERRAySRYxgA6dlrw==
@@ -8700,7 +8700,7 @@ eslint-config-expo@~9.2.0:
eslint-plugin-react-hooks "^5.1.0"
globals "^16.0.0"
-eslint-config-prettier@~9.1.0:
+eslint-config-prettier@9.1.2:
version "9.1.2"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz#90deb4fa0259592df774b600dbd1d2249a78ce91"
integrity sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==
@@ -8714,20 +8714,7 @@ eslint-import-resolver-node@^0.3.9:
is-core-module "^2.13.0"
resolve "^1.22.4"
-eslint-import-resolver-typescript@^3.6.3:
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz#23dac32efa86a88e2b8232eb244ac499ad636db2"
- integrity sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==
- dependencies:
- "@nolyfill/is-core-module" "1.0.39"
- debug "^4.4.0"
- get-tsconfig "^4.10.0"
- is-bun-module "^2.0.0"
- stable-hash "^0.0.5"
- tinyglobby "^0.2.13"
- unrs-resolver "^1.6.2"
-
-eslint-import-resolver-typescript@~3.6.3:
+eslint-import-resolver-typescript@3.6.3:
version "3.6.3"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz#bb8e388f6afc0f940ce5d2c5fd4a3d147f038d9e"
integrity sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==
@@ -8741,6 +8728,19 @@ eslint-import-resolver-typescript@~3.6.3:
is-bun-module "^1.0.2"
is-glob "^4.0.3"
+eslint-import-resolver-typescript@^3.6.3:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz#23dac32efa86a88e2b8232eb244ac499ad636db2"
+ integrity sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==
+ dependencies:
+ "@nolyfill/is-core-module" "1.0.39"
+ debug "^4.4.0"
+ get-tsconfig "^4.10.0"
+ is-bun-module "^2.0.0"
+ stable-hash "^0.0.5"
+ tinyglobby "^0.2.13"
+ unrs-resolver "^1.6.2"
+
eslint-module-utils@^2.12.0, eslint-module-utils@^2.12.1, eslint-module-utils@^2.8.1:
version "2.12.1"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz#f76d3220bfb83c057651359295ab5854eaad75ff"
@@ -8757,7 +8757,7 @@ eslint-plugin-expo@^0.1.4:
"@typescript-eslint/utils" "^8.29.1"
eslint "^9.24.0"
-eslint-plugin-i18n-json@~4.0.0:
+eslint-plugin-i18n-json@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-i18n-json/-/eslint-plugin-i18n-json-4.0.1.tgz#52f88613f05859fd79a1c88b215c78798fd8db07"
integrity sha512-LNQS2XeEy1fdCOn/n3Aeh7RWPVbwKL0tH4Q1c2Y/M1TN6Jo3uN6C3cTvtjzJEms7ul0rmCAPoGqM4IsVCOIxnw==
@@ -8772,57 +8772,57 @@ eslint-plugin-i18n-json@~4.0.0:
plur "^2.1.2"
pretty-format "^22.0.3"
-eslint-plugin-import@^2.30.0:
- version "2.32.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz#602b55faa6e4caeaa5e970c198b5c00a37708980"
- integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==
+eslint-plugin-import@2.31.0:
+ version "2.31.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7"
+ integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==
dependencies:
"@rtsao/scc" "^1.1.0"
- array-includes "^3.1.9"
- array.prototype.findlastindex "^1.2.6"
- array.prototype.flat "^1.3.3"
- array.prototype.flatmap "^1.3.3"
+ array-includes "^3.1.8"
+ array.prototype.findlastindex "^1.2.5"
+ array.prototype.flat "^1.3.2"
+ array.prototype.flatmap "^1.3.2"
debug "^3.2.7"
doctrine "^2.1.0"
eslint-import-resolver-node "^0.3.9"
- eslint-module-utils "^2.12.1"
+ eslint-module-utils "^2.12.0"
hasown "^2.0.2"
- is-core-module "^2.16.1"
+ is-core-module "^2.15.1"
is-glob "^4.0.3"
minimatch "^3.1.2"
object.fromentries "^2.0.8"
object.groupby "^1.0.3"
- object.values "^1.2.1"
+ object.values "^1.2.0"
semver "^6.3.1"
- string.prototype.trimend "^1.0.9"
+ string.prototype.trimend "^1.0.8"
tsconfig-paths "^3.15.0"
-eslint-plugin-import@~2.31.0:
- version "2.31.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7"
- integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==
+eslint-plugin-import@^2.30.0:
+ version "2.32.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz#602b55faa6e4caeaa5e970c198b5c00a37708980"
+ integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==
dependencies:
"@rtsao/scc" "^1.1.0"
- array-includes "^3.1.8"
- array.prototype.findlastindex "^1.2.5"
- array.prototype.flat "^1.3.2"
- array.prototype.flatmap "^1.3.2"
+ array-includes "^3.1.9"
+ array.prototype.findlastindex "^1.2.6"
+ array.prototype.flat "^1.3.3"
+ array.prototype.flatmap "^1.3.3"
debug "^3.2.7"
doctrine "^2.1.0"
eslint-import-resolver-node "^0.3.9"
- eslint-module-utils "^2.12.0"
+ eslint-module-utils "^2.12.1"
hasown "^2.0.2"
- is-core-module "^2.15.1"
+ is-core-module "^2.16.1"
is-glob "^4.0.3"
minimatch "^3.1.2"
object.fromentries "^2.0.8"
object.groupby "^1.0.3"
- object.values "^1.2.0"
+ object.values "^1.2.1"
semver "^6.3.1"
- string.prototype.trimend "^1.0.8"
+ string.prototype.trimend "^1.0.9"
tsconfig-paths "^3.15.0"
-eslint-plugin-prettier@~5.2.1:
+eslint-plugin-prettier@5.2.6:
version "5.2.6"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz#be39e3bb23bb3eeb7e7df0927cdb46e4d7945096"
integrity sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==
@@ -8871,12 +8871,12 @@ eslint-plugin-react@^7.37.3:
string.prototype.matchall "^4.0.12"
string.prototype.repeat "^1.0.0"
-eslint-plugin-simple-import-sort@~10.0.0:
+eslint-plugin-simple-import-sort@10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz#cc4ceaa81ba73252427062705b64321946f61351"
integrity sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==
-eslint-plugin-tailwindcss@~3.15.2:
+eslint-plugin-tailwindcss@3.15.2:
version "3.15.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.15.2.tgz#92a9af3bb3f44a87087073109aa5a51bd32cb161"
integrity sha512-+HJfWcyP5B/e8r8qVSaTbf2i4+HsESJJsue66qFHRstV11CNTfdaDD9zkCVA1pm2EplBZ/BSJ3Htfzvb4YTVKw==
@@ -8884,14 +8884,14 @@ eslint-plugin-tailwindcss@~3.15.2:
fast-glob "^3.2.5"
postcss "^8.4.4"
-eslint-plugin-testing-library@~6.2.2:
+eslint-plugin-testing-library@6.2.2:
version "6.2.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-testing-library/-/eslint-plugin-testing-library-6.2.2.tgz#67e84ff891a2b3a8078ced0afa95ee6f343c00c1"
integrity sha512-1E94YOTUDnOjSLyvOwmbVDzQi/WkKm3WVrMXu6SmBr6DN95xTGZmI6HJ/eOkSXh/DlheRsxaPsJvZByDBhWLVQ==
dependencies:
"@typescript-eslint/utils" "^5.58.0"
-eslint-plugin-unicorn@~46.0.1:
+eslint-plugin-unicorn@46.0.1:
version "46.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-46.0.1.tgz#222ff65b30b2d9ed6f90de908ceb6a05dd0514d9"
integrity sha512-setGhMTiLAddg1asdwjZ3hekIN5zLznNa5zll7pBPwFOka6greCKDQydfqy4fqyUhndi74wpDzClSQMEcmOaew==
@@ -8913,7 +8913,7 @@ eslint-plugin-unicorn@~46.0.1:
semver "^7.3.8"
strip-indent "^3.0.0"
-eslint-plugin-unused-imports@~2.0.0:
+eslint-plugin-unused-imports@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz#d8db8c4d0cfa0637a8b51ce3fd7d1b6bc3f08520"
integrity sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==
@@ -8964,47 +8964,7 @@ eslint-visitor-keys@^5.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz#b9aa1a74aa48c44b3ae46c1597ce7171246a94a9"
integrity sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==
-eslint@^9.24.0:
- version "9.39.2"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.2.tgz#cb60e6d16ab234c0f8369a3fe7cc87967faf4b6c"
- integrity sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==
- dependencies:
- "@eslint-community/eslint-utils" "^4.8.0"
- "@eslint-community/regexpp" "^4.12.1"
- "@eslint/config-array" "^0.21.1"
- "@eslint/config-helpers" "^0.4.2"
- "@eslint/core" "^0.17.0"
- "@eslint/eslintrc" "^3.3.1"
- "@eslint/js" "9.39.2"
- "@eslint/plugin-kit" "^0.4.1"
- "@humanfs/node" "^0.16.6"
- "@humanwhocodes/module-importer" "^1.0.1"
- "@humanwhocodes/retry" "^0.4.2"
- "@types/estree" "^1.0.6"
- ajv "^6.12.4"
- chalk "^4.0.0"
- cross-spawn "^7.0.6"
- debug "^4.3.2"
- escape-string-regexp "^4.0.0"
- eslint-scope "^8.4.0"
- eslint-visitor-keys "^4.2.1"
- espree "^10.4.0"
- esquery "^1.5.0"
- esutils "^2.0.2"
- fast-deep-equal "^3.1.3"
- file-entry-cache "^8.0.0"
- find-up "^5.0.0"
- glob-parent "^6.0.2"
- ignore "^5.2.0"
- imurmurhash "^0.1.4"
- is-glob "^4.0.0"
- json-stable-stringify-without-jsonify "^1.0.1"
- lodash.merge "^4.6.2"
- minimatch "^3.1.2"
- natural-compare "^1.4.0"
- optionator "^0.9.3"
-
-eslint@~8.57.0:
+eslint@8.57.1:
version "8.57.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9"
integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
@@ -9048,6 +9008,46 @@ eslint@~8.57.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
+eslint@^9.24.0:
+ version "9.39.2"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.2.tgz#cb60e6d16ab234c0f8369a3fe7cc87967faf4b6c"
+ integrity sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.8.0"
+ "@eslint-community/regexpp" "^4.12.1"
+ "@eslint/config-array" "^0.21.1"
+ "@eslint/config-helpers" "^0.4.2"
+ "@eslint/core" "^0.17.0"
+ "@eslint/eslintrc" "^3.3.1"
+ "@eslint/js" "9.39.2"
+ "@eslint/plugin-kit" "^0.4.1"
+ "@humanfs/node" "^0.16.6"
+ "@humanwhocodes/module-importer" "^1.0.1"
+ "@humanwhocodes/retry" "^0.4.2"
+ "@types/estree" "^1.0.6"
+ ajv "^6.12.4"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.6"
+ debug "^4.3.2"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^8.4.0"
+ eslint-visitor-keys "^4.2.1"
+ espree "^10.4.0"
+ esquery "^1.5.0"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^8.0.0"
+ find-up "^5.0.0"
+ glob-parent "^6.0.2"
+ ignore "^5.2.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.1.2"
+ natural-compare "^1.4.0"
+ optionator "^0.9.3"
+
espree@^10.0.1, espree@^10.4.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837"
@@ -9198,12 +9198,12 @@ expect@^29.0.0, expect@^29.7.0:
jest-message-util "^29.7.0"
jest-util "^29.7.0"
-expo-application@~7.0.8:
+expo-application@7.0.8, expo-application@~7.0.8:
version "7.0.8"
resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-7.0.8.tgz#320af0d6c39b331456d3bc833b25763c702d23db"
integrity sha512-qFGyxk7VJbrNOQWBbE09XUuGuvkOgFS9QfToaK2FdagM2aQ+x3CvGV2DuVgl/l4ZxPgIf3b/MNh9xHpwSwn74Q==
-expo-asset@~12.0.12:
+expo-asset@12.0.12, expo-asset@~12.0.12:
version "12.0.12"
resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-12.0.12.tgz#15eb7d92cd43cc81c37149e5bbcdc3091875a85b"
integrity sha512-CsXFCQbx2fElSMn0lyTdRIyKlSXOal6ilLJd+yeZ6xaC7I9AICQgscY5nj0QcwgA+KYYCCEQEBndMsmj7drOWQ==
@@ -9211,12 +9211,12 @@ expo-asset@~12.0.12:
"@expo/image-utils" "^0.8.8"
expo-constants "~18.0.12"
-expo-audio@~1.1.1:
+expo-audio@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/expo-audio/-/expo-audio-1.1.1.tgz#7b9763118e321c5dfbf2771cd4a5b6790ce4fc8d"
integrity sha512-CPCpJ+0AEHdzWROc0f00Zh6e+irLSl2ALos/LPvxEeIcJw1APfBa4DuHPkL4CQCWsVe7EnUjFpdwpqsEUWcP0g==
-expo-auth-session@~7.0.10:
+expo-auth-session@7.0.10:
version "7.0.10"
resolved "https://registry.yarnpkg.com/expo-auth-session/-/expo-auth-session-7.0.10.tgz#37250576baf5d56f16b861fb7c387a990f8eaf45"
integrity sha512-XDnKkudvhHSKkZfJ+KkodM+anQcrxB71i+h0kKabdLa5YDXTQ81aC38KRc3TMqmnBDHAu0NpfbzEVd9WDFY3Qg==
@@ -9228,12 +9228,12 @@ expo-auth-session@~7.0.10:
expo-web-browser "~15.0.10"
invariant "^2.2.4"
-expo-av@~16.0.8:
+expo-av@16.0.8:
version "16.0.8"
resolved "https://registry.yarnpkg.com/expo-av/-/expo-av-16.0.8.tgz#b1671127f3b2ecaeb9c69fc2301cf791d4504dd6"
integrity sha512-cmVPftGR/ca7XBgs7R6ky36lF3OC0/MM/lpgX/yXqfv0jASTsh7AYX9JxHCwFmF+Z6JEB1vne9FDx4GiLcGreQ==
-expo-build-properties@~1.0.10:
+expo-build-properties@1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/expo-build-properties/-/expo-build-properties-1.0.10.tgz#2c3fb4248f78828e952defa636635a653e3ad546"
integrity sha512-mFCZbrbrv0AP5RB151tAoRzwRJelqM7bCJzCkxpu+owOyH+p/rFC/q7H5q8B9EpVWj8etaIuszR+gKwohpmu1Q==
@@ -9241,12 +9241,12 @@ expo-build-properties@~1.0.10:
ajv "^8.11.0"
semver "^7.6.0"
-expo-clipboard@~8.0.8:
+expo-clipboard@8.0.8:
version "8.0.8"
resolved "https://registry.yarnpkg.com/expo-clipboard/-/expo-clipboard-8.0.8.tgz#5e52054a4bbaebef090ec6fe5eaa200072ff94f7"
integrity sha512-VKoBkHIpZZDJTB0jRO4/PZskHdMNOEz3P/41tmM6fDuODMpqhvyWK053X0ebspkxiawJX9lX33JXHBCvVsTTOA==
-expo-constants@~18.0.11, expo-constants@~18.0.12, expo-constants@~18.0.13:
+expo-constants@18.0.13, expo-constants@~18.0.11, expo-constants@~18.0.12, expo-constants@~18.0.13:
version "18.0.13"
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-18.0.13.tgz#0117f1f3d43be7b645192c0f4f431fb4efc4803d"
integrity sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ==
@@ -9254,14 +9254,14 @@ expo-constants@~18.0.11, expo-constants@~18.0.12, expo-constants@~18.0.13:
"@expo/config" "~12.0.13"
"@expo/env" "~2.0.8"
-expo-crypto@~15.0.8:
+expo-crypto@15.0.8, expo-crypto@~15.0.8:
version "15.0.8"
resolved "https://registry.yarnpkg.com/expo-crypto/-/expo-crypto-15.0.8.tgz#339198aae149b3ccc0b44f7150d7261a3a1f5287"
integrity sha512-aF7A914TB66WIlTJvl5J6/itejfY78O7dq3ibvFltL9vnTALJ/7LYHvLT4fwmx9yUNS6ekLBtDGWivFWnj2Fcw==
dependencies:
base64-js "^1.3.0"
-expo-dev-client@~6.0.20:
+expo-dev-client@6.0.20:
version "6.0.20"
resolved "https://registry.yarnpkg.com/expo-dev-client/-/expo-dev-client-6.0.20.tgz#d5b65974785100ae7f2538e16701fa1ef55f5fad"
integrity sha512-5XjoVlj1OxakNxy55j/AUaGPrDOlQlB6XdHLLWAw61w5ffSpUDHDnuZzKzs9xY1eIaogOqTOQaAzZ2ddBkdXLA==
@@ -9293,24 +9293,24 @@ expo-dev-menu@7.0.18:
dependencies:
expo-dev-menu-interface "2.0.0"
-expo-device@~8.0.10:
+expo-device@8.0.10:
version "8.0.10"
resolved "https://registry.yarnpkg.com/expo-device/-/expo-device-8.0.10.tgz#88be854d6de5568392ed814b44dad0e19d1d50f8"
integrity sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA==
dependencies:
ua-parser-js "^0.7.33"
-expo-document-picker@~14.0.8:
+expo-document-picker@14.0.8:
version "14.0.8"
resolved "https://registry.yarnpkg.com/expo-document-picker/-/expo-document-picker-14.0.8.tgz#ca1d99cc432c48e69a6390eb035f3301557e3699"
integrity sha512-3tyQKpPqWWFlI8p9RiMX1+T1Zge5mEKeBuXWp1h8PEItFMUDSiOJbQ112sfdC6Hxt8wSxreV9bCRl/NgBdt+fA==
-expo-file-system@~19.0.21:
+expo-file-system@19.0.21, expo-file-system@~19.0.21:
version "19.0.21"
resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-19.0.21.tgz#e96a68107fb629cf0dd1906fe7b46b566ff13e10"
integrity sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg==
-expo-font@~14.0.11:
+expo-font@14.0.11, expo-font@~14.0.11:
version "14.0.11"
resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-14.0.11.tgz#198743d17332520545107df026d8a261e6b2732f"
integrity sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==
@@ -9322,21 +9322,21 @@ expo-image-loader@~6.0.0:
resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-6.0.0.tgz#15230442cbb90e101c080a4c81e37d974e43e072"
integrity sha512-nKs/xnOGw6ACb4g26xceBD57FKLFkSwEUTDXEDF3Gtcu3MqF3ZIYd3YM+sSb1/z9AKV1dYT7rMSGVNgsveXLIQ==
-expo-image-manipulator@~14.0.8:
+expo-image-manipulator@14.0.8:
version "14.0.8"
resolved "https://registry.yarnpkg.com/expo-image-manipulator/-/expo-image-manipulator-14.0.8.tgz#1c457acbd2bcabe987fbd650c0f29120c3366ba6"
integrity sha512-sXsXjm7rIxLWZe0j2A41J/Ph53PpFJRdyzJ3EQ/qetxLUvS2m3K1sP5xy37px43qCf0l79N/i6XgFgenFV36/Q==
dependencies:
expo-image-loader "~6.0.0"
-expo-image-picker@~17.0.10:
+expo-image-picker@17.0.10:
version "17.0.10"
resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-17.0.10.tgz#b4a714971378b2813e53d97d8ca81ab2c32cdf30"
integrity sha512-a2xrowp2trmvXyUWgX3O6Q2rZaa2C59AqivKI7+bm+wLvMfTEbZgldLX4rEJJhM8xtmEDTNU+lzjtObwzBRGaw==
dependencies:
expo-image-loader "~6.0.0"
-expo-image@~3.0.11:
+expo-image@3.0.11:
version "3.0.11"
resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-3.0.11.tgz#54195565dc710e632c10414c3609deebb7149ac5"
integrity sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA==
@@ -9346,12 +9346,12 @@ expo-json-utils@~0.15.0:
resolved "https://registry.yarnpkg.com/expo-json-utils/-/expo-json-utils-0.15.0.tgz#6723574814b9e6b0a90e4e23662be76123ab6ae9"
integrity sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==
-expo-keep-awake@~15.0.8:
+expo-keep-awake@15.0.8, expo-keep-awake@~15.0.8:
version "15.0.8"
resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-15.0.8.tgz#911c5effeba9baff2ccde79ef0ff5bf856215f8d"
integrity sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ==
-expo-linking@~8.0.10, expo-linking@~8.0.11:
+expo-linking@8.0.11, expo-linking@~8.0.10:
version "8.0.11"
resolved "https://registry.yarnpkg.com/expo-linking/-/expo-linking-8.0.11.tgz#b13ca9fc409ef0536352443221eb50e5e2bee366"
integrity sha512-+VSaNL5om3kOp/SSKO5qe6cFgfSIWnnQDSbA7XLs3ECkYzXRquk5unxNS3pg7eK5kNUmQ4kgLI7MhTggAEUBLA==
@@ -9359,14 +9359,14 @@ expo-linking@~8.0.10, expo-linking@~8.0.11:
expo-constants "~18.0.12"
invariant "^2.2.4"
-expo-localization@~17.0.8:
+expo-localization@17.0.8:
version "17.0.8"
resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-17.0.8.tgz#eb74ae0f9b5b49596322d68d2005662346211100"
integrity sha512-UrdwklZBDJ+t+ZszMMiE0SXZ2eJxcquCuQcl6EvGHM9K+e6YqKVRQ+w8qE+iIB3H75v2RJy6MHAaLK+Mqeo04g==
dependencies:
rtl-detect "^1.0.2"
-expo-location@~19.0.8:
+expo-location@19.0.8:
version "19.0.8"
resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-19.0.8.tgz#1805393151b1286021c1ad36246b6fd095d09b55"
integrity sha512-H/FI75VuJ1coodJbbMu82pf+Zjess8X8Xkiv9Bv58ZgPKS/2ztjC1YO1/XMcGz7+s9DrbLuMIw22dFuP4HqneA==
@@ -9397,7 +9397,7 @@ expo-modules-core@3.0.29:
dependencies:
invariant "^2.2.4"
-expo-navigation-bar@~5.0.10:
+expo-navigation-bar@5.0.10:
version "5.0.10"
resolved "https://registry.yarnpkg.com/expo-navigation-bar/-/expo-navigation-bar-5.0.10.tgz#64e4fdb91ff3872110373b56c8e65d196b40979c"
integrity sha512-r9rdLw8mY6GPMQmVVOY/r1NBBw74DZefXHF60HxhRsdNI2kjc1wLdfWfR2rk4JVdOvdMDujnGrc9HQmqM3n8Jg==
@@ -9406,7 +9406,7 @@ expo-navigation-bar@~5.0.10:
debug "^4.3.2"
react-native-is-edge-to-edge "^1.2.1"
-expo-router@~6.0.23:
+expo-router@6.0.23:
version "6.0.23"
resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-6.0.23.tgz#480fbcb4901fd692f9d11762f33894280dcbd75a"
integrity sha512-qCxVAiCrCyu0npky6azEZ6dJDMt77OmCzEbpF6RbUTlfkaCA417LvY14SBkk0xyGruSxy/7pvJOI6tuThaUVCA==
@@ -9435,12 +9435,12 @@ expo-router@~6.0.23:
use-latest-callback "^0.2.1"
vaul "^1.1.2"
-expo-screen-orientation@~9.0.8:
+expo-screen-orientation@9.0.8:
version "9.0.8"
resolved "https://registry.yarnpkg.com/expo-screen-orientation/-/expo-screen-orientation-9.0.8.tgz#15b8f85bd4d183831943fc5a21e3020e17432867"
integrity sha512-qRoPi3E893o3vQHT4h1NKo51+7g2hjRSbDeg1fsSo/u2pOW5s6FCeoacLvD+xofOP33cH2MkE4ua54aWWO7Icw==
-expo-secure-store@~15.0.8:
+expo-secure-store@15.0.8:
version "15.0.8"
resolved "https://registry.yarnpkg.com/expo-secure-store/-/expo-secure-store-15.0.8.tgz#678065599bb76061b5a85b15b9426bf7a11089ae"
integrity sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw==
@@ -9450,26 +9450,26 @@ expo-server@^1.0.5:
resolved "https://registry.yarnpkg.com/expo-server/-/expo-server-1.0.5.tgz#2d52002199a2af99c2c8771d0657916004345ca9"
integrity sha512-IGR++flYH70rhLyeXF0Phle56/k4cee87WeQ4mamS+MkVAVP+dDlOHf2nN06Z9Y2KhU0Gp1k+y61KkghF7HdhA==
-expo-sharing@~14.0.8:
+expo-sharing@14.0.8:
version "14.0.8"
resolved "https://registry.yarnpkg.com/expo-sharing/-/expo-sharing-14.0.8.tgz#cfd5fcf77ab5f64cf3d192a40a925abb316d3545"
integrity sha512-A1pPr2iBrxypFDCWVAESk532HK+db7MFXbvO2sCV9ienaFXAk7lIBm6bkqgE6vzRd9O3RGdEGzYx80cYlc089Q==
-expo-splash-screen@~31.0.13:
+expo-splash-screen@31.0.13:
version "31.0.13"
resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-31.0.13.tgz#f41f1a4c8bb1ae7fcc52b760e7dd485d7ddec642"
integrity sha512-1epJLC1cDlwwj089R2h8cxaU5uk4ONVAC+vzGiTZH4YARQhL4Stlz1MbR6yAS173GMosvkE6CAeihR7oIbCkDA==
dependencies:
"@expo/prebuild-config" "^54.0.8"
-expo-status-bar@~3.0.9:
+expo-status-bar@3.0.9:
version "3.0.9"
resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-3.0.9.tgz#87cfc803fa614f09a985b8e75e3dd7abd51ce2cb"
integrity sha512-xyYyVg6V1/SSOZWh4Ni3U129XHCnFHBTcUo0dhWtFDrZbNp/duw5AGsQfb2sVeU0gxWHXSY1+5F0jnKYC7WuOw==
dependencies:
react-native-is-edge-to-edge "^1.2.1"
-expo-system-ui@~6.0.9:
+expo-system-ui@6.0.9:
version "6.0.9"
resolved "https://registry.yarnpkg.com/expo-system-ui/-/expo-system-ui-6.0.9.tgz#09b4a4301ab25ec594ae39beb7d24197c231a30c"
integrity sha512-eQTYGzw1V4RYiYHL9xDLYID3Wsec2aZS+ypEssmF64D38aDrqbDgz1a2MSlHLQp2jHXSs3FvojhZ9FVela1Zcg==
@@ -9477,7 +9477,7 @@ expo-system-ui@~6.0.9:
"@react-native/normalize-colors" "0.81.5"
debug "^4.3.2"
-expo-task-manager@~14.0.9:
+expo-task-manager@14.0.9:
version "14.0.9"
resolved "https://registry.yarnpkg.com/expo-task-manager/-/expo-task-manager-14.0.9.tgz#7e410711cf3fd0c465a191916d699c6560c93192"
integrity sha512-GKWtXrkedr4XChHfTm5IyTcSfMtCPxzx89y4CMVqKfyfROATibrE/8UI5j7UC/pUOfFoYlQvulQEvECMreYuUA==
@@ -9489,12 +9489,12 @@ expo-updates-interface@~2.0.0:
resolved "https://registry.yarnpkg.com/expo-updates-interface/-/expo-updates-interface-2.0.0.tgz#7721cb64c37bcb46b23827b2717ef451a9378749"
integrity sha512-pTzAIufEZdVPKql6iMi5ylVSPqV1qbEopz9G6TSECQmnNde2nwq42PxdFBaUEd8IZJ/fdJLQnOT3m6+XJ5s7jg==
-expo-web-browser@~15.0.10:
+expo-web-browser@15.0.10, expo-web-browser@~15.0.10:
version "15.0.10"
resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-15.0.10.tgz#ee7fb59b4f143f262c13c020433a83444181f1a3"
integrity sha512-fvDhW4bhmXAeWFNFiInmsGCK83PAqAcQaFyp/3pE/jbdKmFKoRCWr46uZGIfN4msLK/OODhaQ/+US7GSJNDHJg==
-expo@^54.0.33:
+expo@54.0.33:
version "54.0.33"
resolved "https://registry.yarnpkg.com/expo/-/expo-54.0.33.tgz#f7d572857323f5a8250a9afe245a487d2ee2735f"
integrity sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==
@@ -10011,7 +10011,7 @@ geojson-vt@^4.0.2:
resolved "https://registry.yarnpkg.com/geojson-vt/-/geojson-vt-4.0.2.tgz#1162f6c7d61a0ba305b1030621e6e111f847828a"
integrity sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==
-geojson@~0.5.0:
+geojson@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/geojson/-/geojson-0.5.0.tgz#3cd6c96399be65b56ee55596116fe9191ce701c0"
integrity sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==
@@ -10541,7 +10541,7 @@ hyphenate-style-name@^1.0.3:
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz#1797bf50369588b47b72ca6d5e65374607cf4436"
integrity sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==
-i18next@~23.14.0:
+i18next@23.14.0:
version "23.14.0"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.14.0.tgz#d415a858390cc849f3db0df539cb2bbfe24a3cdb"
integrity sha512-Y5GL4OdA8IU2geRrt2+Uc1iIhsjICdHZzT9tNwQ3TVqdNzgxHToGCKf/TPRP80vTCAP6svg2WbbJL+Gx5MFQVA==
@@ -11455,7 +11455,7 @@ jest-each@^29.7.0:
jest-util "^29.7.0"
pretty-format "^29.7.0"
-jest-environment-jsdom@^29.2.1, jest-environment-jsdom@~29.7.0:
+jest-environment-jsdom@29.7.0, jest-environment-jsdom@^29.2.1:
version "29.7.0"
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f"
integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==
@@ -11481,7 +11481,7 @@ jest-environment-node@^29.7.0:
jest-mock "^29.7.0"
jest-util "^29.7.0"
-jest-expo@~54.0.17:
+jest-expo@54.0.17:
version "54.0.17"
resolved "https://registry.yarnpkg.com/jest-expo/-/jest-expo-54.0.17.tgz#c4b905097889340fe44f868d601c165c113ddc55"
integrity sha512-LyIhrsP4xvHEEcR1R024u/LBj3uPpAgB+UljgV+YXWkEHjprnr0KpE4tROsMNYCVTM1pPlAnPuoBmn5gnAN9KA==
@@ -11548,7 +11548,7 @@ jest-haste-map@^29.7.0:
optionalDependencies:
fsevents "^2.3.2"
-jest-junit@~16.0.0:
+jest-junit@16.0.0:
version "16.0.0"
resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-16.0.0.tgz#d838e8c561cf9fdd7eb54f63020777eee4136785"
integrity sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==
@@ -11812,7 +11812,7 @@ jest-worker@^29.7.0:
merge-stream "^2.0.0"
supports-color "^8.0.0"
-jest@~29.7.0:
+jest@29.7.0:
version "29.7.0"
resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613"
integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==
@@ -12263,7 +12263,7 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
-lint-staged@~15.2.9:
+lint-staged@15.2.11:
version "15.2.11"
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.11.tgz#e88440982f4a4c1d55a9a7a839259ec3806bd81b"
integrity sha512-Ev6ivCTYRTGs9ychvpVw35m/bcNDuBN+mnTeObCL5h+boS5WzBEC6LHI4I9F/++sZm1m+J2LEiy0gxL/R9TBqQ==
@@ -12345,7 +12345,7 @@ listr@^0.14.3:
p-map "^2.0.0"
rxjs "^6.3.3"
-livekit-client@^2.15.7:
+livekit-client@2.15.7:
version "2.15.7"
resolved "https://registry.yarnpkg.com/livekit-client/-/livekit-client-2.15.7.tgz#98a63cb259eaf6b54a15fe0376d212c6b37fe06b"
integrity sha512-19m8Q1cvRl5PslRawDUgWXeP8vL8584tX8kiZEJaPZo83U/L6VPS/O7pP06phfJaBWeeV8sAOVtEPlQiZEHtpg==
@@ -12423,7 +12423,7 @@ lodash.kebabcase@^4.1.1:
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==
-lodash.memoize@4.x, lodash.memoize@~4.1.2:
+lodash.memoize@4.1.2, lodash.memoize@4.x:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
@@ -12468,16 +12468,16 @@ lodash.zip@^4.2.0:
resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020"
integrity sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==
+lodash@4.17.23, lodash@^4.17.15:
+ version "4.17.23"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a"
+ integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==
+
lodash@^4.17.12, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
-lodash@^4.17.15:
- version "4.17.23"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a"
- integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==
-
log-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@@ -12586,7 +12586,7 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
-lucide-react-native@~0.475.0:
+lucide-react-native@0.475.0:
version "0.475.0"
resolved "https://registry.yarnpkg.com/lucide-react-native/-/lucide-react-native-0.475.0.tgz#a96bbcaa8d953c845cf3038d136aba408318c484"
integrity sha512-V5tho5qQ89GD4qdzL07ZyXdrnpXZFLirGfaG6BB2vKhO6X1iA7UYYqntgBQ//ZuTUEdevskl+dVT5O4A9oOJUg==
@@ -13337,7 +13337,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
-moti@~0.29.0:
+moti@0.29.0:
version "0.29.0"
resolved "https://registry.yarnpkg.com/moti/-/moti-0.29.0.tgz#bd8820749bbaae61ef28298b53fabb84fe4ad171"
integrity sha512-o/blVE3lm0i/6E5X0RLK59SVWEGxo7pQh8dTm+JykVCYY9bcz0lWyZFCO1s+MMNq+nMsSZBX8lkp4im/AZmhyw==
@@ -13398,7 +13398,7 @@ napi-postinstall@^0.3.0:
resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.3.tgz#93d045c6b576803ead126711d3093995198c6eb9"
integrity sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==
-nativewind@~4.1.21:
+nativewind@4.1.23:
version "4.1.23"
resolved "https://registry.yarnpkg.com/nativewind/-/nativewind-4.1.23.tgz#badfa94a5cd2e12ff8971dd274d234cd843ca74e"
integrity sha512-oLX3suGI6ojQqWxdQezOSM5GmJ4KvMnMtmaSMN9Ggb5j7ysFt4nHxb1xs8RDjZR7BWc+bsetNJU8IQdQMHqRpg==
@@ -13545,7 +13545,7 @@ normalize-url@^6.0.1:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
-np@~10.0.7:
+np@10.0.7:
version "10.0.7"
resolved "https://registry.yarnpkg.com/np/-/np-10.0.7.tgz#6f0dc3c7440c8ac95d55a2fa5d488c3d0848b7a5"
integrity sha512-vIPKQwOYKpQU40PU5x/vLfN2haj8ObxMvR1QGt7EZnBPWdm4WEbHdumYAnMV7AeR9kACsMqcqAP37sAo5cW5jA==
@@ -14092,7 +14092,7 @@ patch-package@8.0.0:
tmp "^0.0.33"
yaml "^2.2.2"
-patch-package@^8.0.0:
+patch-package@8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.1.tgz#79d02f953f711e06d1f8949c8a13e5d3d7ba1a60"
integrity sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==
@@ -14378,7 +14378,7 @@ postcss@~8.4.32:
picocolors "^1.1.1"
source-map-js "^1.2.1"
-postinstall-postinstall@^2.1.0:
+postinstall-postinstall@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3"
integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==
@@ -14400,7 +14400,7 @@ prettier-linter-helpers@^1.0.0:
dependencies:
fast-diff "^1.1.2"
-prettier@~3.3.3:
+prettier@3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
@@ -14640,7 +14640,7 @@ react-dom@19.1.0:
dependencies:
scheduler "^0.26.0"
-react-error-boundary@~4.0.13:
+react-error-boundary@4.0.13:
version "4.0.13"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.13.tgz#80386b7b27b1131c5fbb7368b8c0d983354c7947"
integrity sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==
@@ -14657,12 +14657,12 @@ react-freeze@^1.0.0:
resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.4.tgz#cbbea2762b0368b05cbe407ddc9d518c57c6f3ad"
integrity sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==
-react-hook-form@~7.53.0:
+react-hook-form@7.53.2:
version "7.53.2"
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.2.tgz#6fa37ae27330af81089baadd7f322cc987b8e2ac"
integrity sha512-YVel6fW5sOeedd1524pltpHX+jgU2u3DSDtXEaBORNdqiNrsX/nUI/iGXONegttg0mJVnfrIkiV0cmTU6Oo2xw==
-react-i18next@~15.0.1:
+react-i18next@15.0.3:
version "15.0.3"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.0.3.tgz#01606062f7f81f9b3a88f388e0a8097f8230f5d4"
integrity sha512-BlO1P+oLKjjIxDBQ0GkAIMacgjfMbnvops+3Y5nZXF7UJ99v4KCWr0Na1azJXC8AMiNWp4kgUcFCJM7U9ZsUDg==
@@ -14685,12 +14685,12 @@ react-is@^19.1.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.1.1.tgz#038ebe313cf18e1fd1235d51c87360eb87f7c36a"
integrity sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==
-react-native-base64@~0.2.1:
+react-native-base64@0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/react-native-base64/-/react-native-base64-0.2.2.tgz#9cadd1bbb9e458f31c7307a23f0ab5cd8577f92e"
integrity sha512-9iDzlDQrJqRlgoi7GnO4dqK/7/6lpA3DFrArhp85tDB7ZI6wLr7luHihb/pX6jhm4zlHqOz2OYSGJ6PSgyUO1g==
-react-native-ble-manager@^12.1.5:
+react-native-ble-manager@12.2.1:
version "12.2.1"
resolved "https://registry.yarnpkg.com/react-native-ble-manager/-/react-native-ble-manager-12.2.1.tgz#b6f4f4dedd9ccab2a862962692eef8ad4e70d390"
integrity sha512-5NPBcr418cLw29RyGjSDuSV58G0UrHBjGdxdq/piE/qndauUNZMZeNuRbcy6TzQVGwSaRemTqJlyau/P6DcJeA==
@@ -14716,7 +14716,7 @@ react-native-edge-to-edge@1.6.0:
resolved "https://registry.yarnpkg.com/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz#2ba63b941704a7f713e298185c26cde4d9e4b973"
integrity sha512-2WCNdE3Qd6Fwg9+4BpbATUxCLcouF6YRY7K+J36KJ4l3y+tWN6XCqAC4DuoGblAAbb2sLkhEDp4FOlbOIot2Og==
-react-native-flash-message@~0.4.2:
+react-native-flash-message@0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/react-native-flash-message/-/react-native-flash-message-0.4.2.tgz#ed8ffe60c532bc1274c3b49a60dd0bd2fca59c50"
integrity sha512-YvdXRW9AGMTI99S3DJZhLO0mbk/ehKv/UQf4/Df+3dtGi8DlkidRbyqCQZk1WMtZ7rN85PMTGr/xEI9CF9z0YA==
@@ -14724,7 +14724,7 @@ react-native-flash-message@~0.4.2:
prop-types "^15.8.1"
react-native-iphone-screen-helper "^2.0.2"
-react-native-gesture-handler@~2.28.0:
+react-native-gesture-handler@2.28.0:
version "2.28.0"
resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz#07fb4f5eae72f810aac3019b060d26c1835bfd0c"
integrity sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==
@@ -14750,12 +14750,12 @@ react-native-keyboard-controller@1.18.5:
dependencies:
react-native-is-edge-to-edge "^1.2.1"
-react-native-logs@~5.3.0:
+react-native-logs@5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/react-native-logs/-/react-native-logs-5.3.0.tgz#49ad1ec1fd37a5ef90280cc3e27e7233727fc921"
integrity sha512-tq4S0JFy06ggu1D/udYeV80qPy5koURNNcKrVJmv0Hf3x44akysctaE4ARybD5Pv7MnFD8fP1VFhycSLjqXHQw==
-react-native-mmkv@~3.1.0:
+react-native-mmkv@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/react-native-mmkv/-/react-native-mmkv-3.1.0.tgz#4b2c321cf11bde2f9da32acf76e0178ecd332ccc"
integrity sha512-HDh89nYVSufHMweZ3TVNUHQp2lsEh1ApaoV08bUOU1nrlmGgC3I7tGUn1Uy40Hs7yRMPKx5NWKE5Dh86jTVrwg==
@@ -14767,7 +14767,7 @@ react-native-quick-base64@2.1.1:
dependencies:
base64-js "^1.5.1"
-react-native-reanimated@~4.1.1:
+react-native-reanimated@4.1.7:
version "4.1.7"
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-4.1.7.tgz#b4e8524503a1b6ec1b5a40c460ee807a6a9fd2cf"
integrity sha512-Q4H6xA3Tn7QL0/E/KjI86I1KK4tcf+ErRE04LH34Etka2oVQhW6oXQ+Q8ZcDCVxiWp5vgbBH6XcH8BOo4w/Rhg==
@@ -14780,12 +14780,12 @@ react-native-restart@0.0.27:
resolved "https://registry.yarnpkg.com/react-native-restart/-/react-native-restart-0.0.27.tgz#43aa8210312c9dfa5ec7bd4b2f35238ad7972b19"
integrity sha512-8KScVICrXwcTSJ1rjWkqVTHyEKQIttm5AIMGSK1QG1+RS5owYlE4z/1DykOTdWfVl9l16FIk0w9Xzk9ZO6jxlA==
-react-native-safe-area-context@~5.6.0:
+react-native-safe-area-context@5.6.2:
version "5.6.2"
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz#283e006f5b434fb247fcb4be0971ad7473d5c560"
integrity sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==
-react-native-screens@~4.16.0:
+react-native-screens@4.16.0:
version "4.16.0"
resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.16.0.tgz#efa42e77a092aa0b5277c9ae41391ea0240e0870"
integrity sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==
@@ -14794,7 +14794,7 @@ react-native-screens@~4.16.0:
react-native-is-edge-to-edge "^1.2.1"
warn-once "^0.1.0"
-react-native-svg-transformer@~1.5.1:
+react-native-svg-transformer@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/react-native-svg-transformer/-/react-native-svg-transformer-1.5.1.tgz#04467563becbea65b8e576df7e33c9a9a86961e8"
integrity sha512-dFvBNR8A9VPum9KCfh+LE49YiJEF8zUSnEFciKQroR/bEOhlPoZA0SuQ0qNk7m2iZl2w59FYjdRe0pMHWMDl0Q==
@@ -14820,7 +14820,7 @@ react-native-url-polyfill@^1.3.0:
dependencies:
whatwg-url-without-unicode "8.0.0-3"
-react-native-web@^0.21.0:
+react-native-web@0.21.2:
version "0.21.2"
resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.21.2.tgz#0f6983dfea600d9cc1c66fda87ff9ca585eaa647"
integrity sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==
@@ -14899,7 +14899,7 @@ react-native@0.81.5:
ws "^6.2.3"
yargs "^17.6.2"
-react-query-kit@~3.3.0:
+react-query-kit@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/react-query-kit/-/react-query-kit-3.3.2.tgz#d918d99d68b31221ec38a2c671d0888325e717d2"
integrity sha512-2dKF2kKNshfyOcEs7JG3VXCoh8SHmg8VtDb7LMgAKDrV3fFdcOh3efuEOkmD8IZ6PD+DjX9As2pL3uXKsMv2VQ==
@@ -16446,7 +16446,7 @@ tailwind-variants@0.1.20:
dependencies:
tailwind-merge "^1.14.0"
-tailwind-variants@~0.2.1:
+tailwind-variants@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/tailwind-variants/-/tailwind-variants-0.2.1.tgz#132f2537b0150819036f6c4f47d5c50b929b758d"
integrity sha512-2xmhAf4UIc3PijOUcJPA1LP4AbxhpcHuHM2C26xM0k81r0maAO6uoUSHl3APmvHZcY5cZCY/bYuJdfFa4eGoaw==
@@ -16733,7 +16733,7 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
-ts-jest@~29.1.2:
+ts-jest@29.1.5:
version "29.1.5"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.5.tgz#d6c0471cc78bffa2cb4664a0a6741ef36cfe8f69"
integrity sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==
@@ -16747,7 +16747,7 @@ ts-jest@~29.1.2:
semver "^7.5.3"
yargs-parser "^21.0.1"
-ts-node@~10.9.2:
+ts-node@10.9.2:
version "10.9.2"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f"
integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
@@ -16917,16 +16917,16 @@ typed-emitter@^2.1.0:
optionalDependencies:
rxjs "^7.5.2"
+typescript@5.9.3:
+ version "5.9.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
+ integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
+
typescript@^5.6.3:
version "5.9.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6"
integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==
-typescript@~5.9.2:
- version "5.9.3"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
- integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
-
ua-parser-js@^0.7.33:
version "0.7.41"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.41.tgz#9f6dee58c389e8afababa62a4a2dc22edb69a452"
@@ -17725,17 +17725,17 @@ zod-validation-error@^3.0.3:
resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.5.3.tgz#85ba33290200d8db9f043621e284f40dddefb7e5"
integrity sha512-OT5Y8lbUadqVZCsnyFaTQ4/O2mys4tj7PqhdbBCp7McPwvIEKfPtdA6QfPeFQK2/Rz5LgwmAXRJTugBNBi0btw==
+zod@3.23.8:
+ version "3.23.8"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
+ integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
+
zod@^3.22.4:
version "3.25.76"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34"
integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==
-zod@~3.23.8:
- version "3.23.8"
- resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
- integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
-
-zustand@~4.5.5:
+zustand@4.5.7:
version "4.5.7"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.7.tgz#7d6bb2026a142415dd8be8891d7870e6dbe65f55"
integrity sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==