diff --git a/.env b/.env deleted file mode 100644 index ac0c8a2..0000000 --- a/.env +++ /dev/null @@ -1,4 +0,0 @@ -# TabataFit Environment Variables -# Supabase Configuration -EXPO_PUBLIC_SUPABASE_URL=https://supabase.1000co.fr -EXPO_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzcyMjMzMjAwLCJleHAiOjE5Mjk5OTk2MDB9.SlYN046eGvUSObW0tFQHcMRUqFvtMqBLfFRlZliSx_w diff --git a/.env.example b/.env.example deleted file mode 100644 index 459ffac..0000000 --- a/.env.example +++ /dev/null @@ -1,13 +0,0 @@ -# TabataFit Environment Variables -# Copy this file to .env and fill in your credentials - -# Supabase Configuration -EXPO_PUBLIC_SUPABASE_URL=your_supabase_project_url -EXPO_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key - -# RevenueCat (Apple subscriptions) -# Defaults to test_ sandbox key if not set -EXPO_PUBLIC_REVENUECAT_API_KEY=your_revenuecat_api_key - -# Admin Dashboard (optional - for admin authentication) -EXPO_PUBLIC_ADMIN_EMAIL=admin@tabatafit.app diff --git a/README.md b/README.md deleted file mode 100644 index 3c13cc2..0000000 --- a/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# TabataFit - -> **Apple Fitness+ for Tabata** — The Premium HIIT Experience - -![Expo](https://img.shields.io/badge/Expo-52-black) -![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue) -![License](https://img.shields.io/badge/License-Proprietary-red) -![Tests](https://img.shields.io/badge/Tests-546%20passing-brightgreen) -![Coverage](https://img.shields.io/badge/Coverage-Statements%20%7C%20Branches%20%7C%20Functions%20%7C%20Lines-blue) - -## Vision - -TabataFit est l'Apple Fitness+ du Tabata. Une expérience premium, video-first, guidée par des coachs, qui transforme 4 minutes d'exercice en une expérience de fitness immersive. - -## Features - -- 🎬 **Video-led workouts** — HD video demonstrations by professional trainers -- ⏱️ **Smart timer** — Tabata timer with work/rest phases -- 🔥 **Burn Bar** — Compare your calories with the community -- 📊 **Activity tracking** — Streaks, stats, and trends -- 🎵 **Music sync** — Curated playlists for each workout -- ⌚ **Apple Watch** — Heart rate and activity rings - -## Tech Stack - -- **Framework**: Expo SDK 52 -- **Navigation**: Expo Router v3 -- **State**: Zustand -- **Video**: expo-av (HLS streaming) -- **Payments**: RevenueCat -- **Analytics**: PostHog - -## Getting Started - -```bash -# Install dependencies -npm install - -# Start development server -npx expo start - -# Run on device (scan QR with Expo Go) -``` - -## Documentation - -| Document | Description | -|----------|-------------| -| [PRD v2.0](./TabataFit_PRD_v2.0.md) | Product Requirements | -| [PDD v2.0](./TabataFit_PDD_v2.0.md) | Product Design | -| [BDSD v2.0](./TabataFit_BDSD_v2.0.md) | Brand Design | - -## Project Structure - -``` -src/ - features/ - home/ # Home tab - workouts/ # Workouts browser - player/ # Video player + timer - activity/ # Stats & history - browse/ # Filters & trainers - profile/ # User settings - shared/ - components/ # Reusable UI - hooks/ # Custom hooks - constants/ # Design tokens -app/ # Expo Router routes -``` - -## Testing - -```bash -# Unit tests with coverage -npm run test:coverage - -# Component render tests -npm run test:render - -# All unit + render tests -npm test && npm run test:render - -# Maestro E2E (requires Expo dev server + simulator) -npm run test:maestro - -# Admin-web tests -cd admin-web && npm test # Unit tests -cd admin-web && npm run test:e2e # Playwright E2E -``` - -### Test Coverage - -| Layer | Target | Tests | -|-------|--------|-------| -| Stores | 80%+ | playerStore, activityStore, userStore, programStore | -| Services | 80%+ | analytics, music, purchases, sync | -| Hooks | 70%+ | useTimer, useHaptics, useAudio, usePurchases, useMusicPlayer, useNotifications, useSupabaseData | -| Components | 50%+ | StyledText, VideoPlayer, WorkoutCard, GlassCard, CollectionCard, modals, Skeleton | -| Data | 80%+ | achievements, collections, programs, trainers, workouts | - -### E2E Tests - -- **Mobile (Maestro)**: Onboarding, tab navigation, program browse, workout player, activity, profile/settings -- **Admin Web (Playwright)**: Auth, navigation, workouts CRUD, trainers, collections - -## License - -Proprietary — All rights reserved. - ---- - -Built with ❤️ for HIIT lovers diff --git a/SUPABASE_MUSIC_SETUP.md b/SUPABASE_MUSIC_SETUP.md deleted file mode 100644 index 0046754..0000000 --- a/SUPABASE_MUSIC_SETUP.md +++ /dev/null @@ -1,182 +0,0 @@ -# Supabase Music Storage Setup - -This guide walks you through setting up the Supabase Storage bucket for music tracks in TabataFit. - -## Overview - -TabataFit loads background music from Supabase Storage based on workout `musicVibe` values. The music service organizes tracks by vibe folders. - -## Step 1: Create the Storage Bucket - -1. Go to your Supabase Dashboard → Storage -2. Click **New bucket** -3. Name: `music` -4. Enable **Public access** (tracks are streamed to authenticated users) -5. Click **Create bucket** - -## Step 2: Set Up Folder Structure - -Create folders for each music vibe: - -``` -music/ -├── electronic/ -├── hip-hop/ -├── pop/ -├── rock/ -└── chill/ -``` - -### Via Dashboard: -1. Open the `music` bucket -2. Click **New folder** for each vibe -3. Name folders exactly as the `MusicVibe` type values - -### Via SQL (optional): -```sql --- Storage folders are virtual in Supabase --- Just upload files with path prefixes like "electronic/track.mp3" -``` - -## Step 3: Upload Music Tracks - -### Supported Formats -- MP3 (recommended) -- M4A (AAC) -- OGG (if needed) - -### File Naming Convention -Name files with artist and title for best results: -``` -Artist Name - Track Title.mp3 -``` - -Examples: -``` -Neon Dreams - Energy Pulse.mp3 -Urban Flow - Street Heat.mp3 -The Popstars - Summer Energy.mp3 -``` - -### Upload via Dashboard: -1. Open a vibe folder (e.g., `electronic/`) -2. Click **Upload files** -3. Select your audio files -4. Ensure the path shows `music/electronic/` - -### Upload via CLI: -```bash -# Install Supabase CLI if not already installed -npm install -g supabase - -# Login -supabase login - -# Link your project -supabase link --project-ref your-project-ref - -# Upload tracks -supabase storage upload music/electronic/ "path/to/your/tracks/*.mp3" -``` - -## Step 4: Configure Storage Policies - -### RLS Policy for Authenticated Users - -Go to Supabase Dashboard → Storage → Policies → `music` bucket - -Add these policies: - -#### 1. Select Policy (Read Access) -```sql -CREATE POLICY "Allow authenticated users to read music" -ON storage.objects FOR SELECT -TO authenticated -USING (bucket_id = 'music'); -``` - -#### 2. Insert Policy (Admin Upload Only) -```sql -CREATE POLICY "Allow admin uploads" -ON storage.objects FOR INSERT -TO authenticated -WITH CHECK ( - bucket_id = 'music' - AND (auth.jwt() ->> 'role') = 'admin' -); -``` - -### Via Dashboard UI: -1. Go to **Storage** → **Policies** -2. Under `music` bucket, click **New policy** -3. Select **For SELECT** (get) -4. Allowed operation: **Authenticated** -5. Policy definition: `true` (or custom check) - -## Step 5: Test the Setup - -1. Start your Expo app: `npx expo start` -2. Start a workout with music enabled -3. Check console logs for: - ``` - [Music] Loaded X tracks for vibe: electronic - ``` - -### Troubleshooting - -**No tracks loading:** -- Check Supabase credentials in `.env` -- Verify folder names match `MusicVibe` type exactly -- Check Storage RLS policies allow read access - -**Tracks not playing:** -- Ensure files are accessible (try signed URL in browser) -- Check audio format is supported by expo-av -- Verify CORS settings in Supabase - -**CORS errors:** -Add to Supabase Dashboard → Settings → API → CORS: -``` -app://* -http://localhost:8081 -``` - -## Step 6: Environment Variables - -Ensure your `.env` file has: - -```env -EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co -EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key -``` - -## Music Vibes Reference - -The app supports these vibe categories: - -| Vibe | Description | Typical BPM | -|------|-------------|-------------| -| `electronic` | EDM, House, Techno | 128-140 | -| `hip-hop` | Rap, Trap, Beats | 85-110 | -| `pop` | Pop hits, Dance-pop | 100-130 | -| `rock` | Rock, Alternative | 120-160 | -| `chill` | Lo-fi, Ambient, Downtempo | 60-90 | - -## Best Practices - -1. **Track Duration**: 3-5 minutes ideal for Tabata workouts -2. **File Size**: Keep under 10MB for faster loading -3. **Bitrate**: 128-192kbps MP3 for good quality/size balance -4. **Loudness**: Normalize tracks to similar levels (-14 LUFS) -5. **Metadata**: Include ID3 tags for artist/title info - -## Alternative: Local Development - -If Supabase is not configured, the app uses mock tracks automatically. To force mock data, temporarily set invalid Supabase credentials. - -## Next Steps - -- [ ] Upload initial track library (5-10 tracks per vibe) -- [ ] Test on physical device -- [ ] Consider CDN for production scale -- [ ] Implement track favoriting/personal playlists diff --git a/SUPABASE_SETUP.md b/SUPABASE_SETUP.md deleted file mode 100644 index 3687fc8..0000000 --- a/SUPABASE_SETUP.md +++ /dev/null @@ -1,228 +0,0 @@ -# TabataFit Supabase Integration - -This document explains how to set up and use the Supabase backend for TabataFit. - -## Overview - -TabataFit now uses Supabase as its backend for: -- **Database**: Storing workouts, trainers, collections, programs, and achievements -- **Storage**: Managing video files, thumbnails, and trainer avatars -- **Authentication**: Admin dashboard access control -- **Real-time**: Future support for live features - -## Setup Instructions - -### 1. Create Supabase Project - -1. Go to [Supabase Dashboard](https://app.supabase.com) -2. Create a new project -3. Note your project URL and anon key - -### 2. Configure Environment Variables - -Create a `.env` file in the project root: - -```bash -EXPO_PUBLIC_SUPABASE_URL=your_supabase_project_url -EXPO_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key -``` - -### 3. Run Database Migrations - -1. Go to your Supabase project's SQL Editor -2. Open `supabase/migrations/001_initial_schema.sql` -3. Run the entire script - -This creates: -- All necessary tables (workouts, trainers, collections, programs, achievements) -- Storage buckets (videos, thumbnails, avatars) -- Row Level Security policies -- Triggers for auto-updating timestamps - -### 4. Seed the Database - -Run the seed script to populate your database with initial data: - -```bash -npx ts-node supabase/seed.ts -``` - -This will import all 50 workouts, 5 trainers, 6 collections, 3 programs, and 8 achievements. - -### 5. Set Up Admin User - -1. Go to Supabase Dashboard → Authentication → Users -2. Create a new user with email/password -3. Go to SQL Editor and run: - -```sql -INSERT INTO admin_users (id, email, role) -VALUES ('USER_UUID_HERE', 'admin@example.com', 'admin'); -``` - -Replace `USER_UUID_HERE` with the actual user UUID from step 2. - -### 6. Configure Storage - -In Supabase Dashboard → Storage: - -1. Verify buckets exist: `videos`, `thumbnails`, `avatars` -2. Set bucket privacy to public for all three -3. Configure CORS if needed for your domain - -## Architecture - -### Data Flow - -``` -┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐ -│ React Native │────▶│ Supabase │────▶│ PostgreSQL │ -│ Client │ │ Client │ │ Database │ -└─────────────────┘ └──────────────┘ └─────────────────┘ - │ - │ ┌──────────────┐ - └──────────────▶│ Admin │ - │ Dashboard │ - └──────────────┘ -``` - -### Key Components - -1. **Supabase Client** (`src/shared/supabase/`) - - `client.ts`: Configured Supabase client - - `database.types.ts`: TypeScript definitions for tables - -2. **Data Service** (`src/shared/data/dataService.ts`) - - `SupabaseDataService`: Handles all database operations - - Falls back to mock data if Supabase is not configured - -3. **React Hooks** (`src/shared/hooks/useSupabaseData.ts`) - - `useWorkouts`, `useTrainers`, `useCollections`, etc. - - Automatic loading states and error handling - -4. **Admin Service** (`src/admin/services/adminService.ts`) - - CRUD operations for content management - - File upload/delete for storage - - Admin authentication - -5. **Admin Dashboard** (`app/admin/`) - - `/admin/login`: Authentication screen - - `/admin`: Main dashboard with stats - - `/admin/workouts`: Manage workouts - - `/admin/trainers`: Manage trainers - - `/admin/collections`: Manage collections - - `/admin/media`: Storage management - -## Database Schema - -### Tables - -| Table | Description | -|-------|-------------| -| `trainers` | Trainer profiles with colors and avatars | -| `workouts` | Workout definitions with exercises and metadata | -| `collections` | Curated workout collections | -| `collection_workouts` | Many-to-many link between collections and workouts | -| `programs` | Multi-week workout programs | -| `program_workouts` | Link between programs and workouts with week/day | -| `achievements` | User achievement definitions | -| `admin_users` | Admin dashboard access control | - -### Storage Buckets - -| Bucket | Purpose | Access | -|--------|---------|--------| -| `videos` | Workout videos | Public read, Admin write | -| `thumbnails` | Workout thumbnails | Public read, Admin write | -| `avatars` | Trainer avatars | Public read, Admin write | - -## Using the App - -### As a User - -The app works seamlessly: -- If Supabase is configured: Loads data from the cloud -- If not configured: Falls back to local mock data - -### As an Admin - -1. Navigate to `/admin` in the app -2. Sign in with your admin credentials -3. Manage content through the dashboard: - - View all workouts, trainers, collections - - Delete items (create/edit coming soon) - - Upload media files - -## Development - -### Adding New Workouts - -```typescript -import { adminService } from '@/src/admin/services/adminService' - -await adminService.createWorkout({ - title: 'New Workout', - trainer_id: 'emma', - category: 'full-body', - level: 'Beginner', - duration: 4, - calories: 45, - rounds: 8, - prep_time: 10, - work_time: 20, - rest_time: 10, - equipment: ['No equipment'], - music_vibe: 'electronic', - exercises: [ - { name: 'Jumping Jacks', duration: 20 }, - // ... - ], -}) -``` - -### Uploading Media - -```typescript -const videoUrl = await adminService.uploadVideo(file, 'workout-1.mp4') -const thumbnailUrl = await adminService.uploadThumbnail(file, 'workout-1.jpg') -``` - -## Troubleshooting - -### "Supabase is not configured" Warning - -This is expected if environment variables are not set. The app will use mock data. - -### Authentication Errors - -1. Verify admin user exists in `admin_users` table -2. Check that email/password match -3. Ensure user is confirmed in Supabase Auth - -### Storage Upload Failures - -1. Verify storage buckets exist -2. Check RLS policies allow admin uploads -3. Ensure file size is within limits - -### Data Not Syncing - -1. Check network connection -2. Verify Supabase URL and key are correct -3. Check browser console for errors - -## Security Considerations - -- Row Level Security (RLS) is enabled on all tables -- Public can only read content -- Only admin users can write content -- Storage buckets have public read access -- Storage uploads restricted to admin users - -## Future Enhancements - -- [ ] Real-time workout tracking -- [ ] User progress sync across devices -- [ ] Offline support with local caching -- [ ] Push notifications -- [ ] Analytics and user insights diff --git a/TabataFit_BDSD_v2.0.md b/TabataFit_BDSD_v2.0.md deleted file mode 100644 index 704f1a9..0000000 --- a/TabataFit_BDSD_v2.0.md +++ /dev/null @@ -1,594 +0,0 @@ -# TabataFit — Brand Design Specification v2.0 -> Apple Liquid Glass Design for Tabata - ---- - -## Brand Positioning - -**TabataFit = Apple Fitness+ avec Liquid Glass (iOS 18.4)** - -| Attribute | Description | -|-----------|-------------| -| **Analogy** | "If Apple made a Tabata app in 2026" | -| **Vibe** | Premium, glassy, fluid, immersive | -| **Emotion** | Confident, empowering, sleek | -| **Differentiator** | Video-led + Liquid Glass UI | - ---- - -## 🪟 Liquid Glass System (iOS 18.4 Style) - -### Concept -Le **Liquid Glass** est le nouveau design language d'Apple qui combine : -- **Glassmorphism avancé** — Blur dynamique, transparence multicouche -- **Fluidité organique** — Formes arrondies, animations liquides -- **Lumière réactive** — Reflets, glows qui répondent au contenu -- **Profondeur atmosphérique** — Layers de verre empilés - -### Glass Layers - -```typescript -const GLASS = { - // Base glass (surfaces) - BASE: { - backgroundColor: 'rgba(255, 255, 255, 0.05)', - backdropFilter: 'blur(40px)', - borderWidth: 1, - borderColor: 'rgba(255, 255, 255, 0.1)', - shadowColor: '#000', - shadowOffset: { width: 0, height: 8 }, - shadowOpacity: 0.3, - shadowRadius: 32, - }, - - // Elevated glass (cards, modals) - ELEVATED: { - backgroundColor: 'rgba(255, 255, 255, 0.08)', - backdropFilter: 'blur(60px)', - borderWidth: 1, - borderColor: 'rgba(255, 255, 255, 0.15)', - shadowColor: '#000', - shadowOffset: { width: 0, height: 12 }, - shadowOpacity: 0.4, - shadowRadius: 48, - }, - - // Inset glass (input fields, controls) - INSET: { - backgroundColor: 'rgba(0, 0, 0, 0.2)', - backdropFilter: 'blur(20px)', - borderWidth: 1, - borderColor: 'rgba(255, 255, 255, 0.05)', - }, - - // Tinted glass (accent overlays) - TINTED: { - backgroundColor: 'rgba(255, 107, 53, 0.15)', // Brand tint - backdropFilter: 'blur(40px)', - borderWidth: 1, - borderColor: 'rgba(255, 107, 53, 0.3)', - }, -} -``` - -### Liquid Animations - -```typescript -const LIQUID = { - // Morphing shapes - MORPH: { - duration: 600, - easing: 'cubic-bezier(0.4, 0, 0.2, 1)', - }, - - // Ripple effect on tap - RIPPLE: { - scale: { from: 0.8, to: 1 }, - opacity: { from: 0.5, to: 0 }, - duration: 400, - }, - - // Breathing glow - BREATHE: { - scale: { from: 1, to: 1.02 }, - shadowRadius: { from: 20, to: 30 }, - duration: 2000, - loop: true, - }, - - // Slide with liquid easing - SLIDE: { - damping: 20, - stiffness: 300, - mass: 1, - }, -} -``` - ---- - -## Visual Identity - -### Logo Concept - -``` -┌─────────────────────────────┐ -│ │ -│ TABATA │ ← Bold, black -│ FIT │ ← Accent orange -│ │ -│ [Flame icon] │ ← Optional mark -│ │ -└─────────────────────────────┘ -``` - -### Brand Voice - -| DO | DON'T | -|----|-------| -| "Let's burn." | "Get ripped fast!" | -| "4 minutes to stronger." | "Lose weight now!" | -| "Your daily dose of HIIT." | "The #1 fitness app!" | -| Minimal, confident copy | Excessive exclamation marks!! | - ---- - -## Color Palette - -### Primary Colors - -``` -┌─────────────────────────────────────────────────────────┐ -│ │ -│ BLACK ████████████ #000000 Background │ -│ CHARCOAL ████████████ #1C1C1E Surfaces │ -│ SLATE ████████████ #2C2C2E Elevated │ -│ │ -│ FLAME ████████████ #FF6B35 Brand accent │ -│ FLAME LIGHT ████████████ #FF8C5A Highlights │ -│ │ -│ ICE ████████████ #5AC8FA Rest phases │ -│ GLOW ████████████ #FFD60A Achievement │ -│ ENERGY ████████████ #30D158 Success/Complete│ -│ │ -└─────────────────────────────────────────────────────────┘ -``` - -### Semantic Usage - -| Color | Hex | Usage | -|-------|-----|-------| -| **Black** | #000000 | Main background, video frames | -| **Charcoal** | #1C1C1E | Cards, raised surfaces | -| **Slate** | #2C2C2E | Modals, elevated elements | -| **Flame** | #FF6B35 | Work phase, CTAs, brand | -| **Ice** | #5AC8FA | Rest phase, calm states | -| **Glow** | #FFD60A | Achievement badges, streaks | -| **Energy** | #30D158 | Complete states, success | - -### Phase Colors (Critical) - -```typescript -const PHASE_COLORS = { - PREP: '#FF9500', // Orange-yellow - get ready - WORK: '#FF6B35', // Flame orange - WORK! - REST: '#5AC8FA', // Ice blue - recover - COMPLETE: '#30D158', // Energy green - done! -} -``` - ---- - -## Typography - -### Font Stack - -**Primary**: Inter (Google Fonts) -- Clean, modern, excellent readability -- Variable weight support -- Apple SF Pro alternative - -### Type Scale - -| Style | Size | Weight | Use Case | -|-------|------|--------|----------| -| **HERO** | 48px | 900 | Marketing, celebration | -| **TITLE_1** | 34px | 700 | Screen titles | -| **TITLE_2** | 28px | 700 | Section headers | -| **TITLE_3** | 22px | 600 | Card titles | -| **BODY** | 17px | 400 | Default text | -| **BODY_BOLD** | 17px | 600 | Emphasis | -| **CAPTION** | 15px | 400 | Metadata | -| **MICRO** | 13px | 400 | Small labels | -| **TIMER** | 96px | 900 | Countdown display | - -### Timer Typography (Special) - -```typescript -const TIMER_STYLES = { - // Main countdown number - NUMBER: { - fontFamily: 'Inter_900Black', - fontSize: 96, - fontVariant: ['tabular-nums'], // Monospace digits - letterSpacing: -2, - }, - - // Phase label (WORK, REST) - PHASE: { - fontFamily: 'Inter_700Bold', - fontSize: 24, - letterSpacing: 2, - textTransform: 'uppercase', - }, - - // Round indicator - ROUND: { - fontFamily: 'Inter_500Medium', - fontSize: 17, - fontVariant: ['tabular-nums'], - }, -} -``` - ---- - -## Spacing System - -```typescript -const SPACING = { - // Base unit: 4px - 0: 0, - 1: 4, - 2: 8, - 3: 12, - 4: 16, - 5: 20, - 6: 24, - 8: 32, - 10: 40, - 12: 48, - 16: 64, - - // Semantic - XS: 4, - SM: 8, - MD: 16, - LG: 24, - XL: 32, - XXL: 48, -} - -const LAYOUT = { - SCREEN_PADDING: 24, // Horizontal screen padding - CARD_RADIUS: 16, // Standard card radius - BUTTON_RADIUS: 12, // Button radius - TAB_BAR_HEIGHT: 80, // Bottom tab bar - STATUS_BAR: 44, // iOS status bar -} -``` - ---- - -## Component Styles - -### Cards - -```typescript -const CARD = { - CONTAINER: { - backgroundColor: '#1C1C1E', - borderRadius: 16, - overflow: 'hidden', - }, - - THUMBNAIL: { - aspectRatio: 16/9, - backgroundColor: '#2C2C2E', - }, - - CONTENT: { - padding: 16, - }, - - // Variants - FEATURED: { - borderRadius: 20, - }, - - COMPACT: { - flexDirection: 'row', - borderRadius: 12, - }, -} -``` - -### Buttons - -```typescript -const BUTTON = { - PRIMARY: { - backgroundColor: '#FF6B35', - paddingVertical: 16, - paddingHorizontal: 24, - borderRadius: 12, - alignItems: 'center', - justifyContent: 'center', - }, - - PRIMARY_TEXT: { - color: '#FFFFFF', - fontFamily: 'Inter_600SemiBold', - fontSize: 17, - letterSpacing: 0.5, - }, - - SECONDARY: { - backgroundColor: 'transparent', - borderWidth: 1, - borderColor: '#3A3A3C', - }, - - GHOST: { - backgroundColor: 'transparent', - }, -} -``` - -### Timer Display - -```typescript -const TIMER = { - CONTAINER: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, - padding: 24, - background: 'linear-gradient(transparent, rgba(0,0,0,0.8))', - }, - - NUMBER: { - fontFamily: 'Inter_900Black', - fontSize: 96, - color: '#FFFFFF', - textAlign: 'center', - }, - - PROGRESS_BAR: { - height: 4, - backgroundColor: 'rgba(255,255,255,0.2)', - borderRadius: 2, - marginTop: 16, - }, - - PROGRESS_FILL: { - height: '100%', - borderRadius: 2, - // Color based on phase - }, -} -``` - ---- - -## Animation Specifications - -### Timing Constants - -```typescript -const DURATION = { - INSTANT: 100, - FAST: 200, - NORMAL: 300, - SLOW: 500, - XSLow: 800, -} - -const EASING = { - // Standard iOS ease - DEFAULT: 'ease-out', - - // Spring animations - BOUNCY: { - damping: 15, - stiffness: 180, - }, - - GENTLE: { - damping: 20, - stiffness: 100, - }, -} -``` - -### Key Animations - -**Timer Countdown:** -```typescript -// Number pulses slightly each second -Animated.sequence([ - Animated.timing(scale, { toValue: 1.05, duration: 100 }), - Animated.timing(scale, { toValue: 1, duration: 100 }), -]) -``` - -**Progress Bar:** -```typescript -// Smooth linear fill during phase -Animated.timing(width, { - toValue: 100, - duration: phaseDuration, - easing: Easing.linear, -}) -``` - -**Phase Transition:** -```typescript -// Color crossfade -Animated.timing(colorProgress, { - toValue: 1, - duration: 300, -}) -``` - -**Workout Complete:** -```typescript -// Celebration with scale + fade -Animated.parallel([ - Animated.spring(scale, { toValue: 1, ...BOUNCY }), - Animated.timing(opacity, { toValue: 1, duration: 500 }), -]) -``` - ---- - -## Icon System - -Using SF Symbols / Ionicons equivalent: - -| Context | Icon | Size | -|---------|------|------| -| Home Tab | house.fill | 24 | -| Workouts Tab | flame.fill | 24 | -| Activity Tab | chart.bar.fill | 24 | -| Browse Tab | square.grid.2x2.fill | 24 | -| Profile Tab | person.fill | 24 | -| Play | play.fill | 20 | -| Pause | pause.fill | 20 | -| Close | xmark | 20 | -| Back | chevron.left | 20 | -| Forward | chevron.right | 20 | -| Heart | heart.fill | 20 | -| Search | magnifyingglass | 20 | - ---- - -## Video Player UI - -### Overlay Layout - -``` -┌─────────────────────────────────────────────┐ -│ [Close X] [♥︎] [···] │ ← Top bar -│ │ -│ │ -│ [VIDEO CONTENT] │ -│ │ -│ │ -│ │ -│ ┌─────────────────────────────────────────┐ │ -│ │ 🔥 WORK │ │ -│ │ │ │ -│ │ 00:14 │ │ ← Timer overlay -│ │ │ │ -│ │ Round 3 of 8 │ │ -│ │ ████████████░░░░ │ │ -│ └─────────────────────────────────────────┘ │ -└─────────────────────────────────────────────┘ -``` - -### Video Requirements - -- **Resolution**: 1080p minimum -- **Aspect**: 16:9 (landscape) or 9:16 (portrait mode) -- **Format**: HLS (m3u8) for streaming -- **Audio**: AAC, 128kbps minimum -- **Thumbnail**: JPG, same aspect ratio - ---- - -## Sound Design - -### Sound Effects - -| Event | Sound | Description | -|-------|-------|-------------| -| Phase Start | "ding" | Quick, bright chime | -| Count 3-2-1 | "tick" | Subtle tick sound | -| Workout Start | "whoosh" | Energy whoosh | -| Workout Complete | "success" | Celebratory chime | -| Button Tap | "tap" | Soft tap feedback | -| Streak Achieved | "fire" | Crackling fire | - -### Haptics - -| Event | Haptic | -|-------|--------| -| Phase change | Medium | -| Button tap | Light | -| Countdown tick | Selection | -| Workout complete | Success | -| Error | Error | - ---- - -## Dark Mode Only - -TabataFit is **dark mode only** — no light mode. This is a deliberate design choice matching Apple Fitness+ and creating an immersive, cinematic experience. - -Reasons: -1. Better video contrast -2. Less eye strain during workouts -3. Premium feel -4. Consistent with fitness studio lighting - ---- - -## Image Guidelines - -### Trainer Photos - -- Professional, high-quality headshots -- Warm, approachable expressions -- Fitness attire visible -- Consistent lighting style -- Background: Dark or studio setting - -### Workout Thumbnails - -- Action shot from the workout -- Clear exercise demonstration -- Trainer visible -- Dark/gradient background -- Text overlay: Title, duration, level - -### Collection Banners - -- 16:9 aspect ratio -- Composite of trainer + exercises -- Gradient overlay for text -- Brand accent color elements - ---- - -## Copy Guidelines - -### Tone - -- **Confident** but not arrogant -- **Encouraging** but not cheesy -- **Clear** and direct -- **Minimal** — let the content speak - -### Examples - -| Context | Good | Bad | -|---------|------|-----| -| CTA | "Start Workout" | "Start Your Workout Now!" | -| Empty state | "No workouts yet" | "You haven't done any workouts :(" | -| Error | "Connection lost" | "Oh no! Something went wrong!" | -| Success | "Workout complete" | "AMAZING! You crushed it!!!" | - -### Coach Dialogue - -- Motivational but authentic -- Form cues during exercises -- Breathing reminders during rest -- Encouragement without clichés - ---- - -*Document created: February 18, 2026* -*Version: 2.0* -*Brand: Apple Fitness+ inspired* diff --git a/TabataFit_PDD_v2.0.md b/TabataFit_PDD_v2.0.md deleted file mode 100644 index e368f86..0000000 --- a/TabataFit_PDD_v2.0.md +++ /dev/null @@ -1,765 +0,0 @@ -# TabataFit — Product Design Document v2.0 -> Apple Fitness+ Design Language for Tabata - ---- - -## Design Philosophy - -**"Make it feel like a premium fitness studio in your pocket."** - -TabataFit adopts le design language d'Apple Fitness+ : -- **Dark mode premium** — Fond noir profond, couleurs vibrantes -- **Video-first** — Le contenu est le héros -- **Typography bold** — Gros titres, textes épurés -- **Subtle animations** — Transitions fluides, feedback délicat -- **Inclusive imagery** — Diversité des coachs et body types - ---- - -## Color System — Dark Premium - -### Background Colors - -```typescript -const COLORS = { - // Backgrounds - BACKGROUND: '#000000', // Pure black — comme Apple TV - SURFACE: '#1C1C1E', // Raised surfaces - ELEVATED: '#2C2C2E', // Cards, modals - OVERLAY: 'rgba(0,0,0,0.6)', // Video overlays - - // Brand Accent (Vibrant Orange-Red) - BRAND: '#FF6B35', // Energy, action - BRAND_LIGHT: '#FF8C5A', // Highlights - BRAND_DARK: '#E55A25', // Pressed states - - // Secondary Accents - SUCCESS: '#34C759', // Completed, streaks - WARNING: '#FF9500', // Rest phases - INFO: '#5AC8FA', // Tips, info - - // Text - TEXT_PRIMARY: '#FFFFFF', // Main text - TEXT_SECONDARY: '#EBEBF5', // Secondary (87% opacity) - TEXT_TERTIARY: '#EBEBF599', // Tertiary (60% opacity) - TEXT_DISABLED: '#3A3A3C', // Disabled text - - // Semantic - WORK: '#FF6B35', // Active work phase - REST: '#5AC8FA', // Rest phase (calm blue) - PREP: '#FF9500', // Countdown prep -} -``` - -### Gradient Presets - -```typescript -const GRADIENTS = { - // Hero banners - HERO_WORK: ['#FF6B35', '#E55A25'], - HERO_REST: ['#5AC8FA', '#007AFF'], - HERO_FEAT: ['#1C1C1E', '#000000'], - - // Video overlays - VIDEO_OVERLAY: ['transparent', 'rgba(0,0,0,0.8)'], - VIDEO_TOP: ['rgba(0,0,0,0.4)', 'transparent'], - - // Buttons - CTA: ['#FF6B35', '#FF8C5A'], -} -``` - ---- - -## Typography System — Apple SF Pro Style - -```typescript -const TYPOGRAPHY = { - // Hero/Display - HERO: { - fontFamily: 'Inter_900Black', - fontSize: 48, - lineHeight: 56, - letterSpacing: -1, - }, - - // Section Headers (like Apple Fitness+) - TITLE_1: { - fontFamily: 'Inter_700Bold', - fontSize: 34, - lineHeight: 41, - letterSpacing: 0.37, - }, - - TITLE_2: { - fontFamily: 'Inter_700Bold', - fontSize: 28, - lineHeight: 34, - letterSpacing: 0.36, - }, - - TITLE_3: { - fontFamily: 'Inter_600SemiBold', - fontSize: 22, - lineHeight: 28, - letterSpacing: 0.35, - }, - - // Body - BODY: { - fontFamily: 'Inter_400Regular', - fontSize: 17, - lineHeight: 22, - letterSpacing: -0.41, - }, - - BODY_BOLD: { - fontFamily: 'Inter_600SemiBold', - fontSize: 17, - lineHeight: 22, - letterSpacing: -0.41, - }, - - // Metadata - CAPTION_1: { - fontFamily: 'Inter_400Regular', - fontSize: 15, - lineHeight: 20, - letterSpacing: -0.24, - }, - - CAPTION_2: { - fontFamily: 'Inter_400Regular', - fontSize: 14, - lineHeight: 18, - letterSpacing: -0.15, - }, - - // Timer (special) - TIMER: { - fontFamily: 'Inter_900Black', - fontSize: 96, - lineHeight: 96, - letterSpacing: -2, - }, - - TIMER_PHASE: { - fontFamily: 'Inter_700Bold', - fontSize: 24, - lineHeight: 28, - letterSpacing: 2, // Uppercase tracking - textTransform: 'uppercase', - }, -} -``` - ---- - -## Screen Designs - -### 1. Home Tab — "For You" - -#### Layout Structure - -``` -┌─────────────────────────────────────────────────────┐ -│ status bar │ -├─────────────────────────────────────────────────────┤ -│ │ -│ Bonjour, Alex [Profile] │ ← 24px padding -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ [VIDEO PREVIEW - LOOPING] │ │ ← Hero Card -│ │ │ │ 16:9 aspect -│ │ ┌─────────────────────────────────┐ │ │ -│ │ │ 🔥 FEATURED │ │ │ -│ │ │ │ │ │ -│ │ │ FULL BODY BURN │ │ │ ← Text overlay -│ │ │ 4 min • Beginner • Emma │ │ │ on video -│ │ │ │ │ │ -│ │ │ [▶️ START] [♡ Save] │ │ │ -│ │ └─────────────────────────────────┘ │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ Continue See All → │ ← Section header -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ -│ │ [Thumb] │ │ [Thumb] │ │ [Thumb] │ │ ← Horizontal scroll -│ │ ━━━━━ │ │ ━━━━━ │ │ ━━━━━ │ │ 140x200px cards -│ │ 65% │ │ 30% │ │ 10% │ │ -│ │ Core │ │ HIIT │ │ Full │ │ -│ │ Burn │ │ Extreme │ │ Body │ │ -│ └─────────┘ └─────────┘ └─────────┘ │ -│ │ -│ Popular This Week │ -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ -│ │ [Thumb] │ │ [Thumb] │ │ [Thumb] │ │ [Thumb] │ │ ← Smaller cards -│ │ │ │ │ │ │ │ │ │ │ 120x120px -│ │ Quick │ │ Strength│ │ Cardio │ │ Core │ │ -│ │ Burn │ │ Tabata │ │ Blast │ │ Crush │ │ -│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ -│ │ -│ Collections │ -│ ┌─────────────────────────────────────────────┐ │ -│ │ 🌅 Morning Energizer │ │ ← Full-width cards -│ │ 5 workouts • 20 min total │ │ -│ └─────────────────────────────────────────────┘ │ -│ ┌─────────────────────────────────────────────┐ │ -│ │ 🔥 7-Day Challenge │ │ -│ │ 7 workouts • Progressive intensity │ │ -│ └─────────────────────────────────────────────┘ │ -│ │ -├─────────────────────────────────────────────────────┤ -│ [Home] [Workouts] [Activity] [Browse] [Profile] │ -└─────────────────────────────────────────────────────┘ -``` - -#### Component Specs - -**Hero Card:** -- Full width - 48px padding -- 16:9 aspect ratio -- Video preview looping (muted) -- Gradient overlay: transparent → rgba(0,0,0,0.8) -- Featured badge top-left -- Title, metadata, CTA bottom - -**Continue Watching Card:** -- 140px width × 200px height -- Thumbnail with progress bar overlay -- Progress percentage badge -- Workout name + duration - -**Popular Card:** -- 120px × 120px square -- Thumbnail only -- Category name below - -**Collection Card:** -- Full width - 48px padding -- 80px height -- Icon + title + description -- Chevron right - ---- - -### 2. Workouts Tab - -#### Layout Structure - -``` -┌─────────────────────────────────────────────────────┐ -│ status bar │ -├─────────────────────────────────────────────────────┤ -│ │ -│ Workouts [🔍 Search] │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ [LARGE CATEGORY THUMBNAIL] │ │ -│ │ │ │ -│ │ 🔥 QUICK BURN │ │ -│ │ 4 min • All levels │ │ -│ │ 12 workouts │ │ -│ │ [→] │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ [LARGE CATEGORY THUMBNAIL] │ │ -│ │ │ │ -│ │ 💪 STRENGTH TABATA │ │ -│ │ 8 min • Intermediate │ │ -│ │ 8 workouts │ │ -│ │ [→] │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ 🏃 CARDIO BLAST │ │ -│ │ 4-12 min • All levels • 15 workouts [→] │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ 🧘 CORE & FLEXIBILITY │ │ -│ │ 4 min • Beginner • 6 workouts [→] │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ ⚡ HIIT EXTREME │ │ -│ │ 12-20 min • Advanced • 10 workouts [→] │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -├─────────────────────────────────────────────────────┤ -│ [Home] [Workouts] [Activity] [Browse] [Profile] │ -└─────────────────────────────────────────────────────┘ -``` - -#### Category Detail View - -``` -┌─────────────────────────────────────────────────────┐ -│ ← Quick Burn │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ 4 min • All levels • 12 workouts │ │ -│ │ │ │ -│ │ Filter: [All] [Beginner] [Intermediate] │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ ┌──────┐ │ │ -│ │ │[Vid] │ Full Body Ignite │ │ -│ │ │ │ 4 min • Beginner • Emma │ │ -│ │ └──────┘ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ ┌──────┐ │ │ -│ │ │[Vid] │ Cardio Crusher │ │ -│ │ │ │ 4 min • Intermediate • Alex │ │ -│ │ └──────┘ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ ┌──────┐ │ │ -│ │ │[Vid] │ Lower Body Blast │ │ -│ │ │ │ 4 min • Intermediate • Jake │ │ -│ │ └──────┘ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────┘ -``` - ---- - -### 3. Pre-Workout Detail Screen - -``` -┌─────────────────────────────────────────────────────┐ -│ ← [♡] [···] │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ │ │ -│ │ [VIDEO PREVIEW - LOOPING] │ │ -│ │ │ │ -│ │ Coach Emma in action │ │ -│ │ │ │ -│ │ │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ FULL BODY IGNITE │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ 👩 Emma • 💪 Beginner • ⏱️ 4 min • 🔥 45cal │ -│ │ -│ ───────────────────────────────────────────────── │ -│ │ -│ What You'll Need │ -│ ○ No equipment required │ -│ ○ Yoga mat optional │ -│ │ -│ ───────────────────────────────────────────────── │ -│ │ -│ Exercises (8 rounds) │ -│ ┌─────────────────────────────────────────────┐ │ -│ │ 1. Jump Squats 20s work │ │ -│ │ 2. Mountain Climbers 20s work │ │ -│ │ 3. Burpees 20s work │ │ -│ │ 4. High Knees 20s work │ │ -│ │ ─────────────────────── │ │ -│ │ Repeat × 2 rounds │ │ -│ └─────────────────────────────────────────────┘ │ -│ │ -│ ───────────────────────────────────────────────── │ -│ │ -│ Music │ -│ 🎵 Electronic Energy │ -│ Upbeat, high-energy electronic tracks │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ ▶️ START WORKOUT │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────┘ -``` - ---- - -### 4. Active Workout Screen — The Core Experience - -#### Work Phase - -``` -┌─────────────────────────────────────────────────────┐ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ │ │ -│ │ │ │ -│ │ [FULL SCREEN VIDEO] │ │ -│ │ │ │ -│ │ Coach doing Jump Squats │ │ -│ │ in perfect form │ │ -│ │ │ │ -│ │ │ │ -│ │ ┌───────────────────────────────────────┐ │ │ -│ │ │ ┌─────────────────────────────────┐ │ │ │ -│ │ │ │ 🔥 WORK │ │ │ │ ← Timer overlay -│ │ │ │ │ │ │ │ bottom gradient -│ │ │ │ 00:14 │ │ │ │ -│ │ │ │ │ │ │ │ -│ │ │ │ Round 3 of 8 │ │ │ │ -│ │ │ │ ████████████░░░░ 65% │ │ │ │ -│ │ │ └─────────────────────────────────┘ │ │ │ -│ │ └───────────────────────────────────────┘ │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ JUMP SQUATS │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ -│ │ 52 │ │ 142 │ │ 85% │ │ -│ │ CALORIES │ │ BPM │ │ EFFORT │ │ -│ └───────────┘ └───────────┘ └───────────┘ │ -│ │ -│ Burn Bar │ -│ ░░░░░░░░████████████████░░░░ 72nd percentile │ -│ │ -│ [⏸️ Pause] [⛶ Fullscreen]│ -│ │ -└─────────────────────────────────────────────────────┘ -``` - -#### Rest Phase - -``` -┌─────────────────────────────────────────────────────┐ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ [COACH IN REST POSITION] │ │ -│ │ │ │ -│ │ "Shake it out, take a breath" │ │ -│ │ │ │ -│ │ ┌───────────────────────────────────────┐ │ │ -│ │ │ ┌─────────────────────────────────┐ │ │ │ -│ │ │ │ 💙 REST │ │ │ │ ← Blue rest theme -│ │ │ │ │ │ │ │ -│ │ │ │ 00:08 │ │ │ │ -│ │ │ │ │ │ │ │ -│ │ │ │ Next: Mountain Climbers │ │ │ │ -│ │ │ │ ░░░░░░░░░░░░░░░░░░░ 40% │ │ │ │ -│ │ │ └─────────────────────────────────┘ │ │ │ -│ │ └───────────────────────────────────────┘ │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ UP NEXT │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ ┌──────┐ │ │ -│ │ │ GIF │ Mountain Climbers │ │ -│ │ │preview│ "Core engaged, drive knees forward" │ │ -│ │ └──────┘ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ [⏸️ Pause] [⛶ Fullscreen]│ -│ │ -└─────────────────────────────────────────────────────┘ -``` - -#### 3-2-1 Countdown (Pre-Work) - -``` -┌─────────────────────────────────────────────────────┐ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ │ │ -│ │ │ │ -│ │ │ │ -│ │ │ │ -│ │ GET READY! │ │ -│ │ │ │ -│ │ 3 │ │ ← Giant number -│ │ │ │ centered -│ │ │ │ -│ │ JUMP SQUATS │ │ -│ │ │ │ -│ │ │ │ -│ │ │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────┘ -``` - ---- - -### 5. Workout Complete Screen - -``` -┌─────────────────────────────────────────────────────┐ -│ │ -│ │ -│ 🎉 │ -│ │ -│ WORKOUT COMPLETE │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ [ANIMATED CELEBRATION RINGS] │ │ -│ │ │ │ -│ │ 🔥 💪 ⚡ │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ -│ │ 52 │ │ 4 │ │ 100% │ │ -│ │ CALORIES │ │ MINUTES │ │ COMPLETE │ │ -│ └───────────┘ └───────────┘ └───────────┘ │ -│ │ -│ Burn Bar │ -│ You beat 73% of users! │ -│ ░░░░░░░░████████████████░░░░ │ -│ │ -│ ───────────────────────────────────────────────── │ -│ │ -│ 🔥 7 Day Streak! │ -│ Keep the momentum going! │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ 📤 SHARE YOUR WORKOUT │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ ← BACK TO HOME │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ Recommended Next │ -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ -│ │ [Thumb] │ │ [Thumb] │ │ [Thumb] │ │ -│ │ Core │ │ Upper │ │ Cardio │ │ -│ │ Crush │ │ Body │ │ Blast │ │ -│ └─────────┘ └─────────┘ └─────────┘ │ -│ │ -└─────────────────────────────────────────────────────┘ -``` - ---- - -### 6. Activity Tab - -``` -┌─────────────────────────────────────────────────────┐ -│ │ -│ Activity │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ 🔥 STREAK │ │ -│ │ │ │ -│ │ 7 │ │ -│ │ DAYS │ │ -│ │ │ │ -│ │ ● ● ● ● ● ● ● ○ ○ ○ │ │ -│ │ M T W T F S S M T W │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ This Week │ -│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ -│ │ 5 │ │ 156 │ │ 20 │ │ -│ │ WORKOUTS │ │ CALORIES │ │ MINUTES │ │ -│ │ 5 goal │ │ 150 goal │ │ 20 goal │ │ -│ └───────────┘ └───────────┘ └───────────┘ │ -│ │ -│ Monthly Summary │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ [CALENDAR HEAT MAP] │ │ -│ │ │ │ -│ │ Jan 2026 │ │ -│ │ S M T W T F S │ │ -│ │ 1 2 3 4 5 6 │ │ -│ │ 7 8 9 10 11 12 13 │ │ -│ │ 14 15 16 17 18 19 20 │ │ -│ │ ░ ░ █ █ ░ █ █ │ │ -│ │ █ █ ░ █ █ ░ ░ │ │ -│ │ ░ █ █ █ █ █ █ │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ Trends │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ 📈 Workouts trending up! │ │ -│ │ +23% vs last month │ │ -│ │ │ │ -│ │ [WEEKLY CHART] │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ Burn Bar Position │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ Your average: 45 cal/workout │ │ -│ │ ████████████░░░░ 68th percentile │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────┘ -``` - ---- - -### 7. Browse Tab - -``` -┌─────────────────────────────────────────────────────┐ -│ │ -│ Browse │ -│ │ -│ Filters [Edit] │ -│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ -│ │ All ▼ │ │ 4 min │ │ 8 min │ │ Begin │ │ -│ └────────┘ └────────┘ └────────┘ └────────┘ │ -│ │ -│ Trainers │ -│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ -│ │ 👩 │ │ 👨 │ │ 👩 │ │ 👨 │ │ -│ │ Emma │ │ Jake │ │ Mia │ │ Alex │ │ -│ │ 12 wk │ │ 8 wk │ │ 10 wk │ │ 6 wk │ │ -│ └────────┘ └────────┘ └────────┘ └────────┘ │ -│ │ -│ Duration │ -│ ┌─────────────────────────────────────────────┐ │ -│ │ ○ 4 min (Classic Tabata) 20 │ │ -│ │ ○ 8 min (Double Tabata) 15 │ │ -│ │ ○ 12 min (Triple Tabata) 10 │ │ -│ │ ○ 20 min (Tabata Marathon) 5 │ │ -│ └─────────────────────────────────────────────┘ │ -│ │ -│ Focus Area │ -│ ┌─────────────────────────────────────────────┐ │ -│ │ ○ Full Body 20 │ │ -│ │ ○ Upper Body 8 │ │ -│ │ ○ Lower Body 8 │ │ -│ │ ○ Core 8 │ │ -│ │ ○ Cardio 6 │ │ -│ └─────────────────────────────────────────────┘ │ -│ │ -│ Music Vibe │ -│ ┌─────────────────────────────────────────────┐ │ -│ │ ○ Electronic Energy 18 │ │ -│ │ ○ Hip-Hop Beats 12 │ │ -│ │ ○ Rock Power 10 │ │ -│ │ ○ Chill Focus 10 │ │ -│ └─────────────────────────────────────────────┘ │ -│ │ -│ Collections │ -│ ┌─────────────────────────────────────────────┐ │ -│ │ 🌅 Morning Energizer • 5 workouts │ │ -│ └─────────────────────────────────────────────┘ │ -│ ┌─────────────────────────────────────────────┐ │ -│ │ 💪 No Equipment • 15 workouts │ │ -│ └─────────────────────────────────────────────┘ │ -│ ┌─────────────────────────────────────────────┐ │ -│ │ 🔥 7-Day Challenge • 7 workouts │ │ -│ └─────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────┘ -``` - ---- - -### 8. Profile Tab - -``` -┌─────────────────────────────────────────────────────┐ -│ │ -│ Profile │ -│ │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ 👤 Alex Martin │ │ -│ │ Member since Jan 2026 │ │ -│ │ ✨ Premium │ │ -│ │ │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ Weekly Goal │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ 5 workouts per week │ │ -│ │ ████████████████░░░░ 4/5 this week │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ Achievements │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ 🏆 7-Day Streak 🥵 First Sweat │ │ -│ │ 💯 100 Workouts 🌅 Early Bird │ │ -│ │ 🔥 500 Calories ⚡ Speed Demon │ │ -│ │ │ │ -│ │ [See All Achievements →] │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ Settings │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ Notifications [→] │ │ -│ │ Apple Watch [→] │ │ -│ │ Music Preferences [→] │ │ -│ │ Workout Preferences [→] │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ Account │ -│ ┌───────────────────────────────────────────────┐ │ -│ │ Subscription [→] │ │ -│ │ Privacy & Security [→] │ │ -│ │ Help & Support [→] │ │ -│ │ Sign Out │ │ -│ └───────────────────────────────────────────────┘ │ -│ │ -│ TabataFit v1.0.0 │ -│ Made with ❤️ for HIIT lovers │ -│ │ -└─────────────────────────────────────────────────────┘ -``` - ---- - -## Animation Specifications - -### Screen Transitions -- **Push/Pop**: 300ms ease-out -- **Modal**: Slide up from bottom, 350ms - -### Micro-interactions -- **Button press**: Scale to 0.96, 100ms -- **Card tap**: Scale to 0.98, 150ms -- **Toggle**: 200ms spring animation - -### Timer Animations -- **Countdown**: Number scales up/down each second -- **Progress bar**: Smooth width animation -- **Phase change**: Color crossfade 300ms - -### Celebration -- **Confetti**: Lottie animation on workout complete -- **Rings**: Animated fill when stats update -- **Streak badge**: Pulse animation - ---- - -## Accessibility - -- **Dynamic Type**: Support up to 200% text scaling -- **VoiceOver**: Full screen reader support -- **Reduce Motion**: Disable animations when requested -- **High Contrast**: Alternative color scheme option - ---- - -*Document created: February 18, 2026* -*Version: 2.0* -*Design System: Apple Fitness+ Inspired* diff --git a/TabataFit_PRD_v2.0.md b/TabataFit_PRD_v2.0.md deleted file mode 100644 index dcd4c66..0000000 --- a/TabataFit_PRD_v2.0.md +++ /dev/null @@ -1,545 +0,0 @@ -# TabataFit — Product Requirements Document v2.0 -> Apple Fitness+ for Tabata — The Premium HIIT Experience - ---- - -## Vision Statement - -**TabataFit est l'Apple Fitness+ du Tabata.** Une expérience premium, visuellement stunante, guidée par des coachs, qui transforme 4 minutes d'exercice en une expérience de fitness immersive. - -*"Workouts that work. Beautifully."* - ---- - -## Positionnement - -| Aspect | Apple Fitness+ | TabataFit | -|--------|---------------|-----------| -| **Focus** | Multi-activité (Yoga, HIIT, Strength, etc.) | Spécialiste Tabata/HIIT | -| **Durée** | 5-45 min | 4-20 min (format Tabata) | -| **Différenciateur** | Intégration Apple Watch | Timer intelligent + Coaching audio | -| **Cible** | Grand public fitness | Athlètes HIIT, busy professionals | -| **Vibe** | Studio californien | Énergie explosive, motivational | - ---- - -## Core Philosophy — Apple Fitness+ Principles - -1. **Content is King** — Vidéos HD, coachs charismatiques, production Netflix-quality -2. **Inclusive** — Tous niveaux, modifications montrées -3. **Personalized** — Recommandations basées sur l'historique -4. **Immersive** — Music sync, Burn Bar, stats temps réel -5. **Beautiful** — Design épuré, animations fluides, dark theme élégant - ---- - -## Architecture Produit - -### Tab Bar (5 onglets — comme Apple Fitness+) - -``` -┌─────────────────────────────────────────────────────────────┐ -│ │ -│ 🏠 Home 🔥 Workouts 📊 Activity 🔍 Browse 👤 Profile │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - -### 1. Home Tab — "For You" - -**Inspiration**: Apple Fitness+ Home — grande bannière, collections, recommandations - -``` -┌─────────────────────────────────────────┐ -│ ☀️ Bonjour Alex │ -│ │ -│ ┌───────────────────────────────────┐ │ -│ │ 🎬 FEATURED WORKOUT │ │ -│ │ ─────────────────────── │ │ -│ │ Full Body Burn │ │ -│ │ 4 min • Beginner • Emma │ │ -│ │ │ │ -│ │ [▶️ START NOW] │ │ -│ └───────────────────────────────────┘ │ -│ │ -│ Continue Watching │ -│ ┌─────┐ ┌─────┐ ┌─────┐ │ -│ │ 65%│ │ 30%│ │ 10%│ │ -│ └─────┘ └─────┘ └─────┘ │ -│ │ -│ Popular This Week │ -│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ -│ └─────┘ └─────┘ └─────┘ └─────┘ │ -│ │ -│ Collections │ -│ 🌅 Morning Energizer │ -│ 💪 No Equipment Needed │ -│ 🔥 7-Day Challenge │ -│ │ -└─────────────────────────────────────────┘ -``` - -**Elements clés:** -- Hero banner avec vidéo preview en boucle -- "Continue Watching" — workouts non terminés -- "Popular This Week" — trending workouts -- Collections thématiques -- Coach du moment - -### 2. Workouts Tab — Parcourir par type - -**Inspiration**: Apple Fitness+ workout browser — categories visuelles - -``` -┌─────────────────────────────────────────┐ -│ WORKOUTS 🔍 │ -│ │ -│ ┌─────────────────────────────────┐ │ -│ │ 🔥 QUICK BURN │ │ -│ │ 4 min • All levels │ │ -│ │ 12 workouts │ │ -│ └─────────────────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────┐ │ -│ │ 💪 STRENGTH TABATA │ │ -│ │ 8 min • Intermediate │ │ -│ │ 8 workouts │ │ -│ └─────────────────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────┐ │ -│ │ 🏃 CARDIO BLAST │ │ -│ │ 4-12 min • All levels │ │ -│ │ 15 workouts │ │ -│ └─────────────────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────┐ │ -│ │ 🧘 CORE & FLEXIBILITY │ │ -│ │ 4 min • Beginner friendly │ │ -│ │ 6 workouts │ │ -│ └─────────────────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────┐ │ -│ │ ⚡ HIIT EXTREME │ │ -│ │ 12-20 min • Advanced │ │ -│ │ 10 workouts │ │ -│ └─────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────┘ -``` - -**Categories:** -1. **Quick Burn** — 4 min, perfect for beginners -2. **Strength Tabata** — Resistance exercises -3. **Cardio Blast** — Pure cardio, no equipment -4. **Core & Flexibility** — Abs, stretching -5. **HIIT Extreme** — Advanced, longer sessions - -### 3. Activity Tab — Stats & Progress - -**Inspiration**: Apple Fitness+ Activity rings + trends - -``` -┌─────────────────────────────────────────┐ -│ ACTIVITY │ -│ │ -│ ┌─────────────────────────────────┐ │ -│ │ 🔥 STREAK │ │ -│ │ ───────── │ │ -│ │ 7 │ │ -│ │ DAYS │ │ -│ │ │ │ -│ │ ○ ○ ○ ○ ○ ○ ○ ● ○ ○ │ │ -│ │ M T W T F S S M T │ │ -│ └─────────────────────────────────┘ │ -│ │ -│ This Week │ -│ ┌───────┐ ┌───────┐ ┌───────┐ │ -│ │ 5 │ │ 156 │ │ 32 │ │ -│ │Workout│ │ Calories│ │ Minutes│ │ -│ └───────┘ └───────┘ └───────┘ │ -│ │ -│ Trends │ -│ ┌─────────────────────────────────┐ │ -│ │ 📈 Workouts are trending up! │ │ -│ │ +23% vs last month │ │ -│ └─────────────────────────────────┘ │ -│ │ -│ Burn Bar Position │ -│ ┌─────────────────────────────────┐ │ -│ │ Your avg: 45 cal/workout │ │ -│ │ ████████░░░░ 68th percentile │ │ -│ └─────────────────────────────────┘ │ -│ │ -│ Monthly Summary │ -│ [ Calendar view with heat map ] │ -│ │ -└─────────────────────────────────────────┘ -``` - -**Features:** -- Streak counter avec calendrier visuel -- Stats hebdomadaires (workouts, calories, minutes) -- Trends ("You're on fire! 🔥") -- Burn Bar — comparaison avec autres utilisateurs -- Calendar heat map - -### 4. Browse Tab — Tout le contenu - -**Inspiration**: Apple Fitness+ Browse — filtres, trainers, music - -``` -┌─────────────────────────────────────────┐ -│ BROWSE │ -│ │ -│ Filters [Edit]│ -│ [All ▼] [4 min] [8 min] [Beginner] │ -│ │ -│ By Trainer │ -│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ -│ │ 👩 │ │ 👨 │ │ 👩 │ │ 👨 │ │ -│ │Emma │ │Jake │ │Mia │ │Alex │ │ -│ └─────┘ └─────┘ └─────┘ └─────┘ │ -│ │ -│ By Duration │ -│ ○ 4 min (Classic Tabata) │ -│ ○ 8 min (Double Tabata) │ -│ ○ 12 min (Triple Tabata) │ -│ ○ 20 min (Tabata Marathon) │ -│ │ -│ By Focus Area │ -│ ○ Full Body │ -│ ○ Upper Body │ -│ ○ Lower Body │ -│ ○ Core │ -│ ○ Cardio │ -│ │ -│ Music Vibe │ -│ ○ Electronic Energy │ -│ ○ Hip-Hop Beats │ -│ ○ Rock Power │ -│ ○ Chill Focus │ -│ │ -└─────────────────────────────────────────┘ -``` - -### 5. Profile Tab — Settings & Account - -**Inspiration**: Apple Fitness+ Profile — minimal, clean - -``` -┌─────────────────────────────────────────┐ -│ PROFILE │ -│ │ -│ ┌─────────────────────────────────┐ │ -│ │ 👤 Alex Martin │ │ -│ │ Member since Jan 2026 │ │ -│ │ Premium ✨ │ │ -│ └─────────────────────────────────┘ │ -│ │ -│ Goals │ -│ ┌─────────────────────────────────┐ │ -│ │ Weekly Goal: 5 workouts │ │ -│ │ ████████░░ 4/5 this week │ │ -│ └─────────────────────────────────┘ │ -│ │ -│ Achievements │ -│ 🏆 7-Day Streak │ -│ 🥵 First Sweat │ -│ 💯 100 Workouts │ -│ [See all →] │ -│ │ -│ Settings │ -│ • Notifications │ -│ • Apple Watch │ -│ • Music Preferences │ -│ • Account │ -│ • Subscription │ -│ │ -└─────────────────────────────────────────┘ -``` - ---- - -## The Workout Experience — Cœur du Produit - -### Pre-Workout Screen - -**Inspiration**: Apple Fitness+ workout preview — trailer, details, start - -``` -┌─────────────────────────────────────────┐ -│ ← │ -│ │ -│ ┌───────────────────────────────────┐ │ -│ │ │ │ -│ │ [VIDEO PREVIEW LOOP] │ │ -│ │ Coach Emma demonstrating │ │ -│ │ Jump Squats │ │ -│ │ │ │ -│ └───────────────────────────────────┘ │ -│ │ -│ FULL BODY BURN │ -│ ───────────────────────────────────── │ -│ │ -│ 👩 Emma • 💪 Intermediate • ⏱️ 4 min │ -│ │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ What You'll Need │ -│ ○ No equipment │ -│ ○ Mat recommended │ -│ │ -│ Exercises Preview │ -│ 1. Jump Squats (20s work) │ -│ 2. Mountain Climbers (20s work) │ -│ 3. Burpees (20s work) │ -│ 4. High Knees (20s work) │ -│ × 2 rounds │ -│ │ -│ Music │ -│ 🎵 Electronic Energy playlist │ -│ │ -│ ┌───────────────────────────────────┐ │ -│ │ ▶️ START WORKOUT │ │ -│ └───────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────┘ -``` - -### Active Workout Screen — Apple Fitness+ Style - -**Inspiration**: Apple Fitness+ player — video dominant, stats overlay - -``` -┌─────────────────────────────────────────┐ -│ │ -│ ┌───────────────────────────────────┐ │ -│ │ │ │ -│ │ │ │ -│ │ [FULL SCREEN VIDEO] │ │ -│ │ │ │ -│ │ Coach doing exercise │ │ -│ │ in perfect form │ │ -│ │ │ │ -│ │ │ │ -│ │ │ │ -│ │ ┌─────────────────────────────┐ │ │ -│ │ │ 🔥 WORK • 00:14 │ │ │ -│ │ │ Round 3 of 8 │ │ │ -│ │ │ ████████████░░░░ 65% │ │ │ -│ │ └─────────────────────────────┘ │ │ -│ │ │ │ -│ │ │ │ -│ └───────────────────────────────────┘ │ -│ │ -│ JUMP SQUATS │ -│ ───────────────────────────────────── │ -│ │ -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ -│ │ 14 │ │ 52 │ │ 85% │ │ -│ │ cal │ │ bpm │ │ effort │ │ -│ └─────────┘ └─────────┘ └─────────┘ │ -│ │ -│ Burn Bar: ████████░░░░ 72% │ -│ │ -└─────────────────────────────────────────┘ -``` - -**Key Features:** -- Video full screen avec coach -- Timer overlay (phase + countdown) -- Round indicator -- Progress bar -- Stats temps réel (calories, bpm si Apple Watch) -- Burn Bar - -### During Rest Phase - -``` -┌─────────────────────────────────────────┐ -│ │ -│ ┌───────────────────────────────────┐ │ -│ │ │ │ -│ │ [COACH IN REST POSE] │ │ -│ │ Stretching / breathing │ │ -│ │ │ │ -│ │ ┌─────────────────────────────┐ │ │ -│ │ │ 💙 REST • 00:08 │ │ │ -│ │ │ Next: Mountain Climbers │ │ │ -│ │ │ ░░░░░░░░░░░░░░░░░░ 40% │ │ │ -│ │ └─────────────────────────────┘ │ │ -│ │ │ │ -│ │ "Shake it out, you're │ │ -│ │ doing great!" │ │ -│ │ │ │ -│ └───────────────────────────────────┘ │ -│ │ -│ Up Next: Mountain Climbers │ -│ [GIF preview of next exercise] │ -│ │ -└─────────────────────────────────────────┘ -``` - -### Workout Complete — Celebration - -**Inspiration**: Apple Fitness+ celebration screen - -``` -┌─────────────────────────────────────────┐ -│ │ -│ 🎉 WORKOUT COMPLETE! │ -│ │ -│ ┌───────────────────────────────────┐ │ -│ │ │ │ -│ │ [ANIMATED RINGS] │ │ -│ │ 🔥 🔥 🔥 │ │ -│ │ │ │ -│ └───────────────────────────────────┘ │ -│ │ -│ YOUR STATS │ -│ ───────────────────────────────────── │ -│ │ -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ -│ │ 52 │ │ 4 │ │ 100% │ │ -│ │ CALORIES│ │ MINUTES │ │ COMPLETE│ │ -│ └─────────┘ └─────────┘ └─────────┘ │ -│ │ -│ Burn Bar │ -│ ████████████░░░ You beat 73%! │ -│ │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ │ -│ 🔥 7 Day Streak! Keep it going! │ -│ │ -│ ┌───────────────────────────────────┐ │ -│ │ SHARE YOUR WORKOUT │ │ -│ └───────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────┐ │ -│ │ ← BACK TO HOME │ │ -│ └───────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────┘ -``` - ---- - -## Content Strategy — 50+ Workouts au Launch - -### Par Durée - -| Duration | Format | Rounds | Count | -|----------|--------|--------|-------| -| 4 min | Classic Tabata | 8 rounds | 20 workouts | -| 8 min | Double Tabata | 16 rounds | 15 workouts | -| 12 min | Triple Tabata | 24 rounds | 10 workouts | -| 20 min | Tabata Marathon | 40 rounds | 5 workouts | - -### Par Focus - -- **Full Body** — 20 workouts -- **Upper Body** — 8 workouts -- **Lower Body** — 8 workouts -- **Core** — 8 workouts -- **Cardio Only** — 6 workouts - -### Par Niveau - -- **Beginner** — 15 workouts -- **Intermediate** — 20 workouts -- **Advanced** — 15 workouts - -### Trainers (5 au launch) - -1. **Emma** — Energy queen, beginner-friendly -2. **Jake** — Strength focus, motivating -3. **Mia** — Form perfectionist, technical -4. **Alex** — Cardio beast, intense -5. **Sofia** — Chill but effective, recovery - ---- - -## Technical Requirements - -### Video Pipeline -- HLS streaming (adaptive bitrate) -- 1080p minimum, 4K for featured -- Offline download for Premium -- Preload next exercise during rest - -### Audio -- Multiple music tracks (by vibe) -- Coach voice-over (can be muted) -- Sound effects (beeps, transitions) -- Haptic feedback sync - -### Apple Watch Integration -- Heart rate display -- Calories calculation -- Activity rings update -- Now Playing controls - -### Offline Support -- Download workouts for offline -- Sync when back online -- Local stats caching - ---- - -## Monetization - -### Free Tier -- 3 workouts free forever -- Basic stats -- Ads between workouts - -### Premium ($6.99/mo or $49.99/yr) -- Unlimited workouts -- All trainers -- Offline downloads -- Advanced stats & trends -- Apple Watch integration -- No ads -- Family Sharing (up to 5) - ---- - -## Success Metrics - -| Metric | Target (Month 3) | -|--------|------------------| -| DAU | 10,000 | -| Workout completion rate | 75% | -| 7-day retention | 40% | -| Premium conversion | 8% | -| Average workouts/user/week | 3.5 | - ---- - -## Roadmap - -### Phase 1 — MVP (Weeks 1-4) -- [ ] Home + Workouts tabs -- [ ] 20 workouts (4 min only) -- [ ] 2 trainers -- [ ] Basic timer + video player - -### Phase 2 — Core (Weeks 5-8) -- [ ] Activity tab with stats -- [ ] 30 workouts total -- [ ] 4 trainers -- [ ] Apple Watch integration - -### Phase 3 — Premium (Weeks 9-12) -- [ ] Browse + Profile tabs -- [ ] 50+ workouts -- [ ] 5 trainers -- [ ] Offline downloads -- [ ] Burn Bar -- [ ] Subscription system - ---- - -*Document created: February 18, 2026* -*Version: 2.0* -*Status: Ready for Design Phase* diff --git a/TabataKine_Guide_Complet.md b/TabataKine_Guide_Complet.md deleted file mode 100644 index 750cb9f..0000000 --- a/TabataKine_Guide_Complet.md +++ /dev/null @@ -1,1179 +0,0 @@ -# TABATA KINÉ — Guide Complet des Programmes - -> **Programmes conçus par une Kinésithérapeute Diplômée d'État** -> Sécurité · Progression médicale · Accessibilité -> 6 programmes · 26 semaines · 100+ exercices détaillés - ---- - -## Sommaire - -1. [Vue d'ensemble](#1-vue-densemble) -2. [Programme Débutant — 4 semaines](#2-programme-débutant--4-semaines) -3. [Programme Intermédiaire — 4 semaines](#3-programme-intermédiaire--4-semaines) -4. [Programme Avancé — 4 semaines](#4-programme-avancé--4-semaines) -5. [Programme Post-Partum — 6 semaines](#5-programme-post-partum--6-semaines) -6. [Programme Seniors — 4 semaines](#6-programme-seniors--4-semaines) -7. [Programme Bureau — 4 semaines](#7-programme-bureau--4-semaines) -8. [Récapitulatif global](#8-récapitulatif-global) - ---- - -## 1. Vue d'ensemble - -| Programme | Durée | Séances/sem | Blocs max | Impacts | Prérequis | Tier | -|---|---|---|---|---|---|---| -| 🟢 Débutant | 4 semaines | 3 | 3 blocs | ❌ Aucun | Aucun | Gratuit | -| 🔵 Intermédiaire | 4 semaines | 4 | 4 blocs | ⚡ Léger | Débutant | Premium | -| 🔴 Avancé | 4 semaines | 5 | 5 blocs | 🔥 Fort | Intermédiaire | Premium | -| 🩷 Post-partum | 6 semaines | 4 | 3 blocs | ❌ Aucun | Accord médical | Kiné+ | -| 🟤 Seniors | 4 semaines | 3 | 3 blocs | ❌ Aucun | Accord médecin | Kiné+ | -| 🟡 Bureau | 4 semaines | 3–5 | 5 blocs | ❌ Aucun | Aucun | Premium | - -### La signature kiné — ce qui rend ces programmes uniques - -- Chaque exercice inclut un conseil kinésithérapeutique sur la technique, les erreurs à éviter et les contre-indications -- Les progressions sont médicalement raisonnées : aucun impact avant maîtrise du mouvement, semaine de décharge systématique -- Les programmes de niche (post-partum, seniors, bureau) adressent des besoins non couverts par les apps concurrentes -- Les tests de fin de programme sont issus de protocoles cliniques validés - ---- - -## 2. Programme Débutant — 4 semaines - -**Objectif :** apprendre le protocole tabata, construire les bases techniques de chaque mouvement fondamental, et terminer 12 séances sans douleur articulaire. Le succès n'est pas mesuré en calories brûlées mais en confiance acquise. - -**Prérequis :** aucun — accessible à tous, pas de matériel requis. -**Organisation :** 3 séances/semaine — Lundi · Mercredi · Vendredi recommandé. - -### Principes fondateurs - -**Règle 1 — Zéro impact les 2 premières semaines.** Pas de sauts, pas de chocs. Les articulations doivent s'adapter progressivement. Cause numéro 1 des abandons par douleur. - -**Règle 2 — La technique avant l'intensité.** 20 secondes c'est court mais suffisant pour faire une mauvaise répétition. Mieux vaut 8 squats propres que 15 squats avec le dos arrondi. - -**Règle 3 — Semaine 4 = décharge.** Volume réduit de 40%. C'est là que le corps consolide les adaptations. La progression se fait pendant le repos, pas pendant l'effort. - ---- - -### SEMAINE 1 — Découverte du rythme - -**Format :** 1 bloc tabata par séance (4 min) + échauffement + retour au calme -**Durée totale :** ~20 minutes - ---- - -#### Séance 1A — Membres inférieurs - -**Échauffement — 4 min** -- 1 min — Marche sur place avec genoux hauts -- 1 min — Cercles de chevilles (30 sec chaque pied) -- 1 min — Flexions de genoux lentes (3 sec descente / 1 sec montée) -- 1 min — Fentes statiques alternées lentes - -**⏱ Bloc 1 — 4 min | 8 rounds | 20 sec effort / 10 sec repos** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Squat classique** — pieds largeur d'épaules, descente jusqu'aux cuisses parallèles au sol, regard droit, talons à plat | **Pont fessier** — allongé sur le dos, pieds à plat, monter et descendre le bassin lentement, serrer les fessiers en haut | -| 📋 *Si les talons se soulèvent : écarter davantage les pieds ou placer un support sous les talons. Genoux dans l'axe des pieds, jamais vers l'intérieur.* | 📋 *Ne pas creuser le bas du dos en position haute. Le bassin monte grâce aux fessiers, pas grâce aux lombaires.* | - -**Retour au calme — 3 min** -- 45 sec — Étirement quadriceps debout (chaque jambe) -- 45 sec — Étirement ischio-jambiers assis (chaque jambe) -- 30 sec — Respiration diaphragmatique - ---- - -#### Séance 1B — Membres supérieurs & gainage - -**Échauffement — 4 min** -- 1 min — Rotations d'épaules avant et arrière -- 1 min — Ouvertures de poitrine (mains croisées dans le dos) -- 1 min — Cercles de poignets dans les deux sens -- 1 min — Cat-cow : mobilité lombaire à quatre pattes - -**⏱ Bloc 1 — 4 min | 8 rounds | 20 sec effort / 10 sec repos** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Pompes genoux** — corps aligné des genoux aux épaules, descendre la poitrine à 2–3 cm du sol, coudes à 45° | **Planche basse sur avant-bras** — corps aligné, coudes sous les épaules, ne pas laisser tomber les hanches ni les remonter | -| 📋 *Si douleur aux poignets : faire sur les poings fermés ou les avant-bras. Vérifier l'alignement poignet / coude / épaule.* | 📋 *Respirer normalement. La planche doit être une contraction active — réduire la durée si tremblements excessifs.* | - -**Retour au calme — 3 min** -- 45 sec — Étirement pectoraux contre un mur (chaque côté) -- 30 sec — Étirement triceps derrière la tête (chaque bras) -- 30 sec — Étirement cervical latéral doux (chaque côté) - ---- - -#### Séance 1C — Corps entier - -**Échauffement — 4 min** -- 1 min — Jumping jacks lents sans sauter (décaler les pieds) -- 1 min — Rotations de hanches debout -- 1 min — Marche avec bras croisés devant -- 1 min — Squats ¼ de descente (activation légère) - -**⏱ Bloc 1 — 4 min | 8 rounds | 20 sec effort / 10 sec repos** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Step touch latéral** — pas latéraux rapides droite/gauche avec bras actifs, rythme soutenu | **Superman** — allongé ventre au sol, lever simultanément bras et jambes 2 secondes, relâcher complètement | -| 📋 *Garder le regard droit. Les bras actifs contribuent à 20% de l'effort cardiovasculaire.* | 📋 *Ne pas forcer sur le cou — il reste dans l'axe. Exercice roi pour les lombaires et les paravertébraux.* | - -**Retour au calme — 3 min** -- 1 min — Posture de l'enfant (balasana) -- 45 sec — Étirement des hanches en pigeon (chaque côté) - ---- - -### SEMAINE 2 — Consolidation - -**Format :** 2 blocs tabata + 1 min récupération active entre les blocs -**Durée totale :** ~25 minutes - -#### Nouveaux exercices introduits en semaine 2 - -**Fente avant alternée** — un pas en avant, genou arrière descend vers le sol sans toucher. -📋 *Le genou avant ne dépasse pas les orteils. Si douleur rotulienne : réduire l'amplitude.* - -**Dead bug** — allongé sur le dos, bras vers le plafond, jambes à 90°. Descendre un bras et la jambe opposée sans que le bas du dos se décolle. Expirer en descendant. -📋 *Gainage profond transverse — excellent pour les lombaires. La version un seul membre reste disponible si besoin.* - -**Bird dog** — à quatre pattes, tendre simultanément le bras droit et la jambe gauche. Maintenir 2 secondes, bassin horizontal. Alterner. -📋 *Placer une bouteille sur le dos pour vérifier que le bassin reste horizontal.* - ---- - -#### Séance 2A — Membres inférieurs renforcés - -**⏱ Bloc 1** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Squat classique (consolidation)** — augmenter la profondeur, viser cuisses parallèles au sol, tempo 2-1-2 | **Pont fessier (consolidation)** — ajouter 1 sec de maintien en haut, soulever les orteils pour intensifier | - -⏸ *1 min récupération active — marche lente, respiration nasale* - -**⏱ Bloc 2** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Fente avant alternée** — vérifier que le genou avant ne dépasse pas les orteils, tronc droit, regard devant | **Dead bug** — dos bien à plat au sol, expirer en descendant le membre, réduire l'amplitude si le dos se décolle | - -**Retour au calme — 4 min** -- 1 min — Étirement du psoas en fente basse (chaque côté) -- 45 sec — Étirement des ischio-jambiers allongé (chaque jambe) -- 30 sec — Respiration guidée - ---- - -#### Séance 2B — Haut du corps renforcé - -**⏱ Bloc 1** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Pompes genoux (consolidation)** — essayer 2–3 pompes complètes sur orteils si possible | **Planche avant-bras (consolidation)** — tenter la planche sur orteils 10 sec puis repasser sur genoux si besoin | - -⏸ *1 min récupération active* - -**⏱ Bloc 2** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Bird dog** — maintenir le bassin parfaitement horizontal | **Superman dynamique** — version dynamique : lever et descendre en rythme avec la respiration | - ---- - -#### Séance 2C — Corps entier mixte - -**⏱ Bloc 1** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Step touch + bras (consolidation)** — augmenter la vitesse, bras au-dessus des épaules | **Superman (consolidation)** — maintenir la position haute 3 secondes au lieu de 2 | - -⏸ *1 min récupération active* - -**⏱ Bloc 2** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Fente avant alternée + rotation de buste** — en fente basse, rotation du tronc vers le genou avant | **Dead bug lent** — 5 secondes par membre, qualité absolue | - ---- - -### SEMAINE 3 — Montée en intensité - -**Format :** 3 blocs tabata + 1 min récupération entre chaque -**Durée totale :** ~30 minutes - -#### Nouveaux exercices introduits en semaine 3 - -**Squat jump low** — même mouvement que le squat mais à la montée on monte sur la pointe des pieds sans quitter le sol. Préparation au squat sauté. -📋 *Réception silencieuse sur avant-pied. Si bruit à l'atterrissage : réduire la hauteur.* - -**Step-up sur marche basse** — monter/descendre sur une marche basse, alterner les jambes. Équivalent cardio des fentes sautées sans l'impact. -📋 *Genou de la jambe de montée au-dessus du pied de la marche.* - -**Mountain climber lent** — en position de pompes, ramener un genou vers la poitrine en 1 sec aller / 1 sec retour. -📋 *Gainage parfait pendant tout le mouvement. Ne pas laisser les hanches monter.* - ---- - -#### Séances 3A/B/C — Structure type (3 blocs) - -**⏱ Bloc 1** — exercice maîtrisé des semaines précédentes - -| Rounds impairs | Rounds pairs | -|---|---| -| **Squat classique** — focus sur la respiration : inspirer en descendant, expirer en remontant | **Pont fessier unilatéral** — jambe non-travaillante tendue en l'air | -| | 📋 *Le bassin ne doit pas s'incliner du côté de la jambe levée — signe de faiblesse du moyen fessier.* | - -⏸ *1 min récupération active* - -**⏱ Bloc 2** — nouveau mouvement - -| Rounds impairs | Rounds pairs | -|---|---| -| **Squat jump low** — réception silencieuse sur avant-pied | **Fente avant alternée (maîtrisée)** — ¼ de descente supplémentaire | - -⏸ *1 min récupération active* - -**⏱ Bloc 3** — mixte - -| Rounds impairs | Rounds pairs | -|---|---| -| **Step-up sur marche basse** — alterner les jambes à mi-bloc | **Mountain climber lent** — gainage parfait, hanches basses | - ---- - -### SEMAINE 4 — Décharge & consolidation - -**Format :** retour à 2 blocs tabata. Volume réduit de 40%. -**Durée totale :** ~25 minutes - -> 💡 **Pourquoi la décharge ?** C'est pendant le repos que le corps consolide les adaptations musculaires et articulaires. Les sportifs qui sautent la décharge progressent moins vite à long terme. - -Pas de nouveaux exercices. Focus sur : -- Technique parfaite à chaque répétition -- Respiration consciente et coordonnée avec le mouvement -- Ressenti musculaire : identifier les muscles sollicités -- Bilan personnel : quels exercices sont devenus faciles ? Lesquels restent difficiles ? - ---- - -### Récapitulatif Programme Débutant - -| Semaine | Blocs/séance | Min effectives | Séances/sem | Impacts | Durée totale | -|---|---|---|---|---|---| -| Semaine 1 | 1 bloc | 4 min | 3 | ❌ Aucun | ~20 min | -| Semaine 2 | 2 blocs | 8 min | 3 | ❌ Aucun | ~25 min | -| Semaine 3 | 3 blocs | 12 min | 3 | ⚡ Très léger | ~30 min | -| Semaine 4 | 2 blocs | 8 min | 3 | ❌ Aucun | ~25 min | - -**Critères de passage au programme Intermédiaire :** -- Planche sur avant-bras tenue 30 secondes sans compensation -- 10 squats propres consécutifs sans douleur aux genoux -- 5 pompes complètes (sur orteils) avec corps parfaitement aligné -- Aucune douleur articulaire résiduelle après les séances - ---- - -## 3. Programme Intermédiaire — 4 semaines - -**Objectif :** introduire la plyométrie contrôlée, intensifier le travail unilatéral, passer à 4 blocs tabata. L'utilisateur construit puissance et conscience corporelle, apprend à distinguer brûlure musculaire (normale) et douleur articulaire (signal d'arrêt). - -**Organisation :** 4 séances/semaine — Lundi · Mardi · Jeudi · Samedi. 1 séance mobilité/récupération active. Ne jamais faire 2 séances intenses consécutives. - -**Règle de réception des impacts :** silencieuse, sur avant-pied, genou fléchi. Un bruit à l'atterrissage = muscles ne font pas leur travail d'amortisseur. - ---- - -### SEMAINE 1 — Transition avec impacts - -**Format :** 3 blocs tabata + 1 min récupération active entre chaque -**Durée totale :** ~35 minutes - ---- - -#### Séance 2A — Membres inférieurs plyométriques - -**Échauffement — 5 min** -- 1 min — Marche rapide avec bras actifs -- 1 min — Squats lents x10 (activation) -- 1 min — Fentes alternées lentes x8 -- 30 sec — Sauts très légers sur place (test des chevilles) -- 30 sec — Montées de genoux en trottinant -- 1 min — Hip circles : cercles de hanches debout - -**⏱ Bloc 1** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Squat jump** — descente contrôlée, explosion vers le haut, réception silencieuse avant-pied, absorption immédiate en flexion | **Pont fessier unilatéral** — une jambe tendue levée, montée du bassin, maintien 2 sec en haut | -| 📋 *Si la réception fait du bruit : les muscles ne font pas leur travail. Réduire la hauteur. Genoux dans l'axe des pieds.* | 📋 *Le bassin ne s'incline pas du côté de la jambe levée. Si c'est le cas : faiblesse du moyen fessier.* | - -⏸ *1 min récupération active — marche lente, respiration nasale* - -**⏱ Bloc 2** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Fente sautée alternée** — depuis la fente basse, saut pour changer de jambe, réception directement en fente | **Isométrie squat (chaise)** — dos au mur, cuisses parallèles au sol, tenir 20 secondes, respirer calmement | -| 📋 *Si douleur antérieure du genou : revenir à la fente marchée. Réception absorbée sur 2–3 secondes.* | 📋 *L'isométrie renforce l'endurance musculaire sans impact. Excellent pour skieurs et cyclistes.* | - -⏸ *1 min récupération active* - -**⏱ Bloc 3** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Step-up explosif** — monter rapidement en poussant sur le pied avant, descendre contrôlé, alterner à mi-bloc | **Glute kickback à quatre pattes** — genou fléchi à 90°, pousser le talon vers le plafond, lent et contrôlé | -| 📋 *La descente est aussi importante que la montée. Ne pas sauter en bas.* | 📋 *Ne pas cambrer le bas du dos pour aller plus haut — c'est la hanche qui travaille, pas les lombaires.* | - -**Retour au calme — 5 min** -- 1 min — Étirement psoas en fente basse (chaque côté) -- 45 sec — Étirement mollets contre un mur (chaque jambe) -- 1 min — Automassage quadriceps -- 30 sec — Respiration guidée - ---- - -#### Séance 2B — Haut du corps & gainage dynamique - -**Échauffement — 5 min** -- 1 min — Rotations complètes d'épaules avec serviette -- 1 min — Face pulls avec résistance manuelle (rétraction scapulaire) -- 1 min — Pompes lentes x8 (échauffement scapulaires) -- 1 min — Planche dynamique haute ↔ basse lentement -- 1 min — Dead bug lent (consolidation) - -**⏱ Bloc 1** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Pompes classiques complètes** — corps aligné, coudes à 45°, descendre jusqu'au contact de la poitrine | **Renegade row sans poids** — en position de pompes, lever alternativement un bras vers la hanche en contractant l'omoplate | -| 📋 *Douleur aux poignets = vérifier l'alignement poignet/coude/épaule. Si persistant : pompes sur les poings.* | 📋 *Gainage total pendant le mouvement. Le bassin ne se balance pas.* | - -⏸ *1 min récupération active* - -**⏱ Bloc 2** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Pompes avec rotation en T** — en haut de la pompe, rotation latérale avec bras vers le plafond, alterner | **Planche avec tap épaule** — en planche haute, toucher alternativement l'épaule opposée, minimiser la rotation du bassin | -| 📋 *Exercice combiné : obliques + dentelés antérieurs + rotateurs d'épaule. Ouvrir complètement la hanche.* | 📋 *La résistance à la rotation du bassin est l'objectif. Pieds plus écartés pour stabiliser.* | - -⏸ *1 min récupération active* - -**⏱ Bloc 3** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Dips sur chaise** — mains sur le bord, corps décollé, descendre les coudes à 90° | **Superman en Y·W·T** — allongé ventre, lever bras et jambes puis former successivement Y, W, T avec les bras | -| 📋 *Si inconfort à l'avant de l'épaule : réduire l'amplitude. Contre-indiqué si tendinopathie du biceps.* | 📋 *Travail intense des rhomboïdes et trapèzes inférieurs. Muscles posturaux clés contre la position assise.* | - -**Retour au calme — 5 min** -- 1 min — Étirement pectoraux en porte (chaque côté) -- 45 sec — Étirement grand dorsal avec inclinaison latérale (chaque côté) -- 1 min — Mobilisation cervicale douce - ---- - -#### Séance 2C — Corps entier cardio dominant - -**⏱ Bloc 1** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Burpee modifié** — marcher les pieds en arrière jusqu'à planche, marcher retour, se relever (pas de saut) | **Mountain climber rapide** — alterner les jambes le plus vite possible en maintenant le gainage | -| 📋 *Le burpee modifié permet de maîtriser le schéma moteur sans impact lombaire brutal. Le burpee complet vient en semaine 3.* | 📋 *Les hanches ne remontent pas au-dessus des épaules. Regard vers le sol.* | - -⏸ *1 min récupération active* - -**⏱ Bloc 2** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Skaters** — saut latéral d'un pied sur l'autre en simulant le patineur, bras opposé en avant | **Planche to downward dog** — depuis planche avant-bras, pousser en planche haute puis lever les hanches en V inversé | -| 📋 *Atterrissage sur un pied — demande une bonne proprioception de cheville. En cas d'entorse récente : éviter.* | 📋 *Tirer les talons vers le sol en V inversé pour étirer les mollets.* | - -⏸ *1 min récupération active* - -**⏱ Bloc 3** - -| Rounds impairs | Rounds pairs | -|---|---| -| **High knees** — course sur place avec genoux hauts, bras actifs, regard droit, maintenir le rythme | **Bear crawl sur place** — à quatre pattes, genoux à 3 cm du sol, reculer/avancer sur 2 pas rapidement | -| 📋 *Le but est le rythme, pas la hauteur maximale. Les bras pompants contribuent à 15% de la dépense calorique.* | 📋 *Très intense pour le gainage et les épaules malgré l'apparente simplicité. Genoux à 3 cm du sol.* | - -**Retour au calme — 5 min** -- 2 min — Foam roller ou automassage mollets et IT band -- 1 min — Pigeon yoga (chaque côté) -- 1 min — Respiration 4-7-8 (inspirer 4 sec, bloquer 7, expirer 8) × 4 cycles - ---- - -#### Séance 2D — Mobilité & récupération active - -> ℹ️ **Format spécial — Pas de blocs 20/10.** Cette séance ne suit pas le protocole tabata. 25–30 min de travail doux mais structuré. Elle est aussi importante que les trois autres — c'est pendant la récupération que le corps reconstruit les fibres musculaires. - -- 10 min — Mobilité articulaire complète : chevilles, hanches, colonne, épaules -- 10 min — Étirements ciblés sur les zones sollicitées en semaine -- 5 min — Respiration et relaxation guidée - ---- - -### SEMAINE 2 — Montée en densité - -**Format :** 4 blocs tabata + 1 min récup entre chaque -**Durée totale :** ~40 minutes - -#### Nouveaux exercices introduits en semaine 2 - -**Thruster** — squat puis poussée des bras vers le haut. Expirer lors de la poussée. Exercice total-body par excellence. -📋 *La montée en pression intra-abdominale est importante — expirer lors de la poussée vers le haut.* - -**Burpee complet** — depuis debout : squat, mains au sol, saut des pieds en arrière, pompe optionnelle, saut retour, saut vertical avec clap. Point critique : fléchir les genoux en posant les mains. -📋 *Ne jamais arrondir violemment le dos sous fatigue.* - -**Hollow body** — allongé, bras tendus derrière la tête, jambes tendues à 30° du sol, bas du dos collé au sol. Maintenir 20 secondes. -📋 *Gainage profond issu de la gymnastique artistique — le plus efficace pour les abdominaux profonds.* - -**V-sit hold** — assis, jambes levées à 45° et bras parallèles au sol. Tenir. -📋 *Si douleur lombaire : fléchir légèrement les genoux. Ne jamais forcer si le bas du dos compense.* - ---- - -### SEMAINE 3 — Intensité maximale - -**Format :** 4 blocs tabata denses -**Durée totale :** ~40 minutes - -#### Exercices signature de la semaine 3 - -**Burpee avec saut latéral** — ajouter un saut latéral à la fin du burpee classique. Augmente l'impact cardiovasculaire et la coordination. - -**Pistol squat assisté** — squat sur une jambe en tenant un support léger. -📋 *Révélateur des asymétries de force et de mobilité. Si un côté est significativement plus difficile : information précieuse sur les déséquilibres à corriger.* - -**Archer push-up** — pompes larges en ramenant tout le poids sur un seul bras, l'autre restant tendu à plat. Quasi-pompe à un bras. - -**Turkish get-up simplifié** — depuis allongé, se lever en séquence contrôlée : coude → main → genou → debout, puis redescendre. -📋 *Exercice de rééducation fonctionnelle utilisé en cabinet kiné. Exceptionnel pour la coordination et la proprioception.* - ---- - -### SEMAINE 4 — Décharge & bilan - -**Format :** retour à 3 blocs tabata, exercices maîtrisés, focus sur la technique parfaite. - -#### Tests de fin de programme Intermédiaire - -| Test | Protocole | Objectif | -|---|---|---| -| Planche avant-bras | Tenir en planche basse sans compensation | 60 secondes | -| Burpee endurance | Compter les burpees complets en 1 minute | 12 à 15 répétitions | -| Squat jump | 8 rounds de 20 sec de squat jump | Maintenir le rythme au round 7-8 | -| Asymétrie | Pistol squat assisté G/D | Différence < 20% entre les côtés | - ---- - -### Récapitulatif Programme Intermédiaire - -| Semaine | Blocs/séance | Min effectives | Séances/sem | Impacts | Durée totale | -|---|---|---|---|---|---| -| Semaine 1 | 3 blocs | 12 min | 4 | ⚡ Moyens | ~35 min | -| Semaine 2 | 4 blocs | 16 min | 4 | 🔥 Intenses | ~40 min | -| Semaine 3 | 4 blocs | 16 min | 4 | 🔥🔥 Max | ~40 min | -| Semaine 4 | 3 blocs | 12 min | 4 | ⚡ Moyens | ~35 min | - ---- - -## 4. Programme Avancé — 4 semaines - -**Objectif :** mouvements complexes sous fatigue, travail unilatéral systématique, préparation physique fonctionnelle de haut niveau. L'utilisateur développe une intelligence physique : savoir doser l'intensité sur 20 minutes de travail effectif. - -**Prérequis :** -- 15 burpees/min sans s'effondrer -- Planche avant-bras 60 secondes -- Squat jump ×8 rounds propres -- Pistol squat assisté G/D symétrique -- Aucune douleur articulaire chronique - -**Organisation :** 5 séances/semaine — 4 tabata + 1 MetCon. Repos : mercredi + dimanche. - -> ⚠️ La fatigue neurologique se manifeste par une perte de coordination, des réactions ralenties, une irritabilité. Si ces signes apparaissent : réduire d'une séance par semaine avant de forcer. - ---- - -### SEMAINE 1 — Bascule vers la complexité - -**Format :** 4 blocs tabata + 1 min 30 récupération active entre chaque -**Durée totale :** ~45 minutes - ---- - -#### Séance 3A — Membres inférieurs explosifs - -**Échauffement — 6 min** -- 1 min — Jogging sur place progressif -- 1 min — Leg swings dynamiques avant/arrière et latéraux -- 1 min — Fentes dynamiques avec rotation du tronc -- 1 min — Squat profond maintenu 3 sec en bas (ouverture des hanches) -- 1 min — Sauts bas et rapides à deux pieds (activation système nerveux) -- 30 sec — Clamshells rapides debout (activation fessiers) - -**⏱ Bloc 1** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Squat jump avec rotation 180°** — squat, explosion, rotation 180° dans les airs, réception en squat absorbée | **Pistol squat complet** — squat sur une jambe sans support, jambe libre tendue devant, descente complète | -| 📋 *La réception en rotation sollicite fortement le LCA. Antécédent de genou = rester au squat jump classique. Réception sur 2–3 secondes, jamais jambe tendue.* | 📋 *Talon qui se soulève = manque de mobilité de cheville. Tronc qui bascule = faiblesse du moyen fessier. Corriger avant de forcer.* | - -⏸ *1 min 30 récupération active — marche avec respiration nasale* - -**⏱ Bloc 2** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Nordic curl assisté** — à genoux, pieds bloqués, descendre le buste vers le sol lentement en résistant avec les ischio-jambiers, poser les mains avant d'atterrir | **Isométrie fente bulgare** — pied arrière posé sur une chaise, descendre en fente, maintenir 20 secondes | -| 📋 *Cliniquement prouvé pour réduire les blessures ischio-jambiers de 50%. Même 3–4 reps contrôlées par round sont excellentes.* | 📋 *Sollicitation intense du psoas-iliaque et du quadriceps. Excellent pour les coureurs et personnes sédentaires.* | - -⏸ *1 min 30 récupération active* - -**⏱ Bloc 3** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Box jump** — saut à deux pieds sur surface surélevée stable, réception en squat, descente en marchant | **Lateral bound** — saut explosif latéral d'un pied sur l'autre le plus loin possible, réception sur un pied avec absorption | -| 📋 *Descendre en marchant — ne pas sauter en arrière. Sauter en arrière pour descendre multiplie les contraintes articulaires.* | 📋 *Exercice de prévention des entorses par renforcement proprioceptif. Regard fixe droit devant.* | - -⏸ *1 min 30 récupération active* - -**⏱ Bloc 4** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Broad jump + recul** — saut vers l'avant le plus loin possible à deux pieds, reculer en marche contrôlée, relancer immédiatement | **Single leg deadlift sans poids** — debout sur une jambe, basculer le buste en levant la jambe libre derrière, dos plat, descendre vers le sol | -| 📋 *Le recul contrôlé est aussi important que le saut. Il renforce les stabilisateurs de cheville sous fatigue.* | 📋 *Exercice fondamental de proprioception. Si le dos s'arrondit avant de toucher le sol : réduire l'amplitude. Qualité > profondeur.* | - -**Retour au calme — 6 min** -- 2 min — Douche froide sur les jambes si possible -- 1 min 30 — Étirement ischio-jambiers dynamique puis statique (chaque jambe) -- 1 min — Massage des mollets et du pied -- 30 sec — Respiration guidée récupération - ---- - -#### Séance 3B — Haut du corps athlétique - -**Échauffement — 6 min** -- 1 min — Cercles d'épaules complets avec résistance -- 1 min — Face pulls avec élastique (rétraction scapulaire) -- 1 min — Pompes lentes x6 (descente 4 secondes) -- 1 min — Dips lents x6 sur chaise -- 1 min — Planche dynamique haute ↔ basse x6 -- 1 min — Rotation thoracique en position de fente basse - -**⏱ Bloc 1** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Pompes à un bras assistées** — une main au sol, l'autre sur support surélevé, alterner les côtés à mi-bloc | **Pike push-up** — en V inversé (hanches hautes), fléchir les coudes pour descendre la tête vers le sol | -| 📋 *L'asymétrie révèle les faiblesses de stabilisation scapulaire. L'épaule du bras porteur ne s'affaisse pas.* | 📋 *Simule le développé épaules. Contre-indiqué si syndrome d'accrochage sous-acromial. Amplitude réduite au départ.* | - -⏸ *1 min 30 récupération active* - -**⏱ Bloc 2** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Pompe plyométrique** — pompe explosive avec les mains qui décollent du sol, réception souple, enchaîner immédiatement | **Around the world planche** — en planche haute, déplacer les mains pour faire un tour complet imaginaire | -| 📋 *Poignets en parfait alignement — échauffement spécifique des poignets non négociable. En cas de douleur : version sur genoux.* | 📋 *Ultra-exigeant pour les rotateurs de l'épaule et les dentelés antérieurs.* | - -⏸ *1 min 30 récupération active* - -**⏱ Bloc 3** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Archer push-up complet** — pompes larges en ramenant tout le poids sur un bras, l'autre tendu, alterner | **Planche latérale avec rotation** — en planche latérale, amener le bras libre sous le corps en rotation, revenir en ouverture vers le plafond | -| 📋 *Maintenir l'alignement corps-bras porteur parfait.* | 📋 *Sollicite simultanément obliques, carré des lombes et rotateurs de l'épaule.* | - -⏸ *1 min 30 récupération active* - -**⏱ Bloc 4** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Pseudo planche push-up** — mains plus basses vers les hanches, corps incliné avant, pompe avec bras le long du corps | **Superman dynamique battement** — allongé ventre, lever et descendre rapidement bras et jambes en battements contrôlés | -| 📋 *Charge extrême sur les triceps et pectoraux inférieurs. Commencer par 3–4 répétitions propres.* | 📋 *Travail des extenseurs dorsaux sous fatigue. Maintenir le cou dans l'axe.* | - -**Retour au calme — 6 min** -- 1 min — Étirement rotateurs externes d'épaule contre mur (chaque côté) -- 2 min — Mobilisation thoracique sur rouleau -- 1 min — Étirement grand pectoral profond (chaque côté) - ---- - -#### Séance 3C — Corps entier haute intensité - -**Échauffement — 6 min** -Activation neurologique progressive : jumping jacks → high knees → skaters → burpee modifié → 2 burpees complets → mobilisation chevilles/poignets → 10 hollow body holds 5 secondes - -**⏱ Bloc 1** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Burpee avec saut groupé** — burpee complet, à la montée ramener les genoux vers la poitrine | **V-up** — allongé, lever simultanément jambes tendues et buste, toucher les orteils au sommet | -| 📋 *Le saut groupé augmente la charge lombaire à la réception. Atterrir genoux fléchis, jamais en extension.* | 📋 *Si douleur au bas du dos : revenir au hollow body ou dead bug. Contre-indiqué si psoas irrité.* | - -⏸ *1 min 30 récupération active* - -**⏱ Bloc 2** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Tuck jump** — sauts avec genoux ramenés au maximum vers la poitrine, rythme rapide, atterrissage silencieux | **Mountain climber croisé** — en planche haute, ramener le genou droit vers le coude gauche et vice versa | -| 📋 *Si syndrome de l'essuie-glace (IT band) : éviter et substituer par jumping squats.* | 📋 *Plus difficile que le MC classique car la rotation engage les obliques. Hanches basses.* | - -⏸ *1 min 30 récupération active* - -**⏱ Bloc 3** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Devil's press poids de corps** — burpee, en planche ramener les deux genoux simultanément (grenouille), se relever, sauter | **Bear crawl en déplacement** — à quatre pattes genoux décollés, avancer 4 pas et reculer 4 pas rapidement | -| 📋 *Enchaînement fluide entre les phases. Pas de pause entre le grenouille et le relever.* | 📋 *Gainage parfait malgré la vitesse. Genoux à 3 cm du sol.* | - -⏸ *1 min 30 récupération active* - -**⏱ Bloc 4** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Sprawl** — depuis debout, jeter les mains au sol, projeter les hanches vers le bas (ventre au sol), remonter en explosif | **Hollow body rocks** — depuis hollow body, se balancer d'avant en arrière comme un berceau sans perdre la position | -| 📋 *Exercice issu du MMA. La projection vers le bas doit être contrôlée — ne jamais s'écraser.* | 📋 *La continuité du gainage pendant le balancement est l'objectif. Si la position se casse : reprendre le hollow body statique.* | - -**Retour au calme — 6 min** -- 2 min — Foam roller colonne vertébrale -- 1 min 30 — Supine twist (torsion au sol) chaque côté -- 2 min — Savasana avec respiration guidée - ---- - -#### Séance 3D — Gainage profond & mobilité avancée - -> ℹ️ **Format spécial — Pas de blocs 20/10.** 35 minutes de travail structuré de qualité. La séance qui sépare les sportifs qui durent de ceux qui se blessent. - -**Gainage profond — 15 min** -- Hollow body progressif : 5×10 sec → 5×15 sec → 3×20 sec -- Dead bug avec résistance imaginaire : 3×10 répétitions très lentes -- Pallof press anti-rotation debout : 3×10 chaque côté -- Copenhagen plank (pied sur chaise) : 3×20 sec chaque côté -📋 *Le Copenhagen plank est l'exercice de prévention des adducteurs le plus efficace existant. Très peu connu du grand public.* - -**Mobilité active — 15 min** -- 90/90 stretching (mobilité profonde de hanche) : 2 min chaque côté -- Squat profond asiatique maintenu : 2 min progressifs -- Brettzel (rotation thoracique + fléchisseur de hanche) : 2 min chaque côté -- Mobilisation du thorax en extension sur rouleau : 2 min - -**Récupération — 5 min** -- Nidra yoga : scan corporel allongé, relâchement segment par segment - ---- - -#### Séance 3E — MetCon 20 minutes (AMRAP) - -> 📚 **MetCon — Metabolic Conditioning** : réaliser autant de tours que possible en 20 minutes. Teste l'endurance de force sur la durée. Le nombre de tours complets est le KPI de progression. - -Circuit à répéter en continu pendant 20 minutes : -- 5 — Burpees complets -- 10 — Squats jump -- 10 — Mountain climbers croisés (5 chaque côté) -- 5 — Pompes explosives -- 10 — V-ups - -> ⚠️ Ne jamais sacrifier la technique sur les burpees et les pompes sous fatigue — c'est là que les blessures d'épaule surviennent. Si la forme s'effondre : marcher 15 secondes et reprendre. - ---- - -### SEMAINE 2 — Densification - -**Format :** 5 blocs tabata + 1 min récup -**Durée totale :** ~50 minutes - -#### Exercices introduits en semaine 2 - -**Handstand push-up contre le mur** — en équilibre dos au mur, fléchir les coudes pour descendre la tête. Commencer par amplitude réduite de 5 cm. -📋 *Vérifier l'absence de douleur cervicale. Ne jamais toucher le sol avec force.* - -**Single leg burpee** — burpee complet sur une seule jambe, l'autre reste décollée du sol pendant toute la séquence. -📋 *Test ultime de force, coordination et proprioception. L'asymétrie G/D est visible immédiatement.* - -**Dragon flag partiel** — allongé, mains sous les épaules, lever le corps en planche depuis les épaules. Version partielle = fléchir les genoux. -📋 *Ne jamais forcer si douleur lombaire. L'exercice de Bruce Lee.* - ---- - -### SEMAINE 3 — Pic d'intensité & Complexes - -**Format :** 5 blocs tabata + MetCon étendu à 25 minutes - -> 📚 **Qu'est-ce qu'un complexe ?** Deux exercices différents enchaînés sans pause, comptés comme une seule répétition. Outil favori des préparateurs physiques de haut niveau : combine force, puissance et endurance. La fatigue s'accumule très vite. - -#### Complexes de la semaine 3 - -**🔥 Complexe 1 — Lower body power** -Squat jump + fente sautée : squat jump, atterrissage en fente droite, saut retour en squat, recommencer. - -**🔥 Complexe 2 — Push + core** -Pompe explosive + mountain climber ×2 : pompe avec mains décollées, atterrissage, 2 MC croisés rapides, recommencer. - -**🔥 Complexe 3 — Total body** -Burpee + broad jump : burpee complet, dans l'élan du saut se propulser vers l'avant le plus loin possible, reculer en marchant, recommencer. - -> ⚠️ Si la technique s'effondre au round 5 : réduire le complexe à un seul exercice pour finir le bloc proprement. La qualité prime toujours. - ---- - -### SEMAINE 4 — Décharge & Tests finaux - -**Format :** 3 blocs tabata. Exercices connus, focus sur la technique parfaite. Tests de performance complets. - -#### Tests finaux du Programme Avancé - -| Test | Protocole | Objectif avancé | Signal d'alerte | -|---|---|---|---| -| Endurance de force | Burpees en 3 minutes | 35+ répétitions | < 25 = revoir sem 3 | -| Force relative | Pistol squat complet G et D | 5 reps propres chaque côté | Écart > 20% = déséquilibre | -| Gainage dynamique | Planche + MC croisé 2 minutes | Maintien sans perte de position | Bascule hanche = fatigue | -| Puissance | Broad jump mesuré | Progression vs test intermédiaire | +10 cm minimum | -| Coordination | Single leg DL × 8 chaque côté | Sans poser le pied ni tomber | Chronométrer les yeux fermés | -| Asymétrie | Tous tests G vs D | Écart < 20% partout | Si > 20% : programme correctif | - ---- - -### Récapitulatif Programme Avancé - -| Semaine | Blocs/séance | Min effectives | Séances/sem | Type exercices | Durée totale | -|---|---|---|---|---|---| -| Semaine 1 | 4 blocs | 16 min | 5 | Unilatéral + plyos | ~45 min | -| Semaine 2 | 5 blocs | 20 min | 5 | Handstand + complexes légers | ~50 min | -| Semaine 3 | 5 blocs + complexes | 20 min+ | 5 | Complexes complets | ~50 min | -| Semaine 4 | 3 blocs | 12 min | 5 | Bilan + tests | ~35 min | - ---- - -## 5. Programme Post-Partum — 6 semaines - -> ⚠️ **AVERTISSEMENT MÉDICAL OBLIGATOIRE** -> Ce programme est conçu pour les femmes ayant accouché il y a minimum **8 semaines (voie basse)** ou **12 semaines (césarienne)**, avec accord de leur médecin ou sage-femme lors de la visite post-natale. En cas de douleur pelvienne, fuites urinaires, sensation de pesanteur ou cicatrice douloureuse : consulter un kinésithérapeute périnéal avant de commencer. - -**Objectif :** reconnecter le cerveau au périnée et aux abdominaux profonds, rééduquer la sangle abdominale, corriger les déséquilibres posturaux de la grossesse, retrouver progressivement le mouvement en toute sécurité. - -### Contexte kiné — Ce que le corps a vécu - -**Le périnée** a subi une distension importante, parfois une déchirure ou épisiotomie. Doit être réhabilité avant tout travail de gainage ou d'impact. La reprise trop précoce des abdominaux classiques est l'erreur n°1. - -**La sangle abdominale** présente souvent un diastasis des droits — écartement de la ligne blanche. Faire des crunchs avec un diastasis non résolu aggrave la situation. Test requis avant phase 2. - -**Les ligaments** restent laxes plusieurs mois après l'accouchement (relaxine encore présente). Les articulations sont plus vulnérables. Aucun impact avant récupération ligamentaire. - -**La posture** a été modifiée par 9 mois de grossesse : hyperlordose lombaire, épaules enroulées, bassin antéversé. Le programme corrige ces déséquilibres, ne les aggrave pas. - ---- - -### PHASE 1 — Semaines 1-2 : Réveil du corps - -**Objectif :** reconnecter le cerveau au périnée et aux abdominaux profonds. - -> **Format spécial — Protocole INVERSÉ : 10 sec effort / 20 sec repos.** -> Pourquoi ? Les muscles profonds (transverse, plancher pelvien) ont besoin de contractions courtes et qualitatives, pas de résistance à la fatigue. L'objectif est neuromusculaire, pas cardiovasculaire. - -**4 séances/semaine — Durée totale : 20 minutes** - -#### Échauffement — 5 min -- Respiration diaphragmatique en décubitus : inspirer en laissant le ventre se gonfler, expirer en rentrant doucement le nombril. 10 respirations conscientes. -- Bascules de bassin allongée : aplatir puis creuser doucement le bas du dos. Réapprendre la proprioception lombaire. -- Rotations de chevilles et poignets. -- Mobilisation de la nuque et des épaules (tensions fréquentes du portage et de l'allaitement). - -#### ⏱ Bloc principal — 10 min | Protocole INVERSÉ 10 sec effort / 20 sec repos - -**Exercice 1 — Hypopressif de base** -Debout, légère flexion des genoux, dos neutre. Inspirer profondément, expirer complètement puis rentrer le ventre au maximum sans bloquer la respiration. Maintenir 8 secondes. -📋 *Les exercices hypopressifs créent une dépression abdominale qui remonte le plancher pelvien sans pression vers le bas. Base de la rééducation périnéale. Absent de toutes les apps tabata concurrentes.* - -**Exercice 2 — Pont fessier doux coordonné** -Allongée, pieds à plat. Montée lente du bassin avec contraction simultanée du périnée. 3 sec montée / 3 sec maintien / 3 sec descente. -📋 *Associer la contraction périnéale à la montée du bassin est le premier réflexe postural à reconstruire. Fondamental pour prévenir les fuites urinaires à long terme.* - -**Exercice 3 — Clamshell** -Allongée sur le côté, genoux fléchis, ouvrir et fermer le genou supérieur. Renforcement du moyen fessier sans contrainte pelvienne. - -**Exercice 4 — Dead bug modifié (un seul membre)** -Dos au sol, un seul membre à la fois (bras OU jambe, pas les deux simultanément). Colonne lombaire en contact avec le sol. -📋 *La version complète (bras et jambe opposés) viendra en phase 2 uniquement si le diastasis est inférieur à 2 cm.* - -#### Retour au calme — 5 min -- Étirement du psoas allongée (genoux vers la poitrine alternativement) -- Respiration guidée 2 minutes -- Automassage nuque et épaules - ---- - -### PHASE 2 — Semaines 3-4 : Reconstruction - -**Objectif :** réintroduire le gainage global, augmenter l'intensité cardiovasculaire. Retour au protocole tabata classique 20/10 sur des exercices sans impact. - -> 📋 **Test diastasis obligatoire avant phase 2.** Allongée en crunch partiel, placer les doigts sur la ligne blanche. Si l'écartement dépasse 2 doigts : rester en phase 1 deux semaines supplémentaires et consulter un kiné périnéal. Ce n'est pas un échec — c'est de la prudence médicale. - -#### Nouveaux exercices introduits en Phase 2 - -**Bird dog progressif (deux membres)** — bras droit + jambe gauche simultanément si le diastasis le permet. Bassin parfaitement horizontal. -📋 *La version un membre de la phase 1 reste disponible en cas de doute.* - -**Squat avec respiration coordonnée** — inspirer en descendant, expirer EN REMONTANT avec contraction périnéale. -📋 *La majorité des fuites urinaires à l'effort viennent d'une dissociation entre effort et contraction périnéale. Ce squat "respiré" est thérapeutique.* - -**Fente statique basse tenue** — sans saut, sans impact. 20 secondes de maintien. Renforcement sans pression pelvienne verticale. - -**Planche sur genoux et avant-bras** — 20 secondes de maintien en respirant normalement. Pas de planche complète sur orteils avant la semaine 5. - ---- - -### PHASE 3 — Semaines 5-6 : Retour au Tabata - -**Objectif :** réintégrer un vrai protocole tabata avec exercices à intensité modérée. Format identique au programme Débutant semaines 1-2. - -**Ce qui reste exclu jusqu'à la fin du programme :** -- Tous les sauts et impacts — même légers -- Les crunchs, sit-ups, V-ups, relevés de buste classiques -- La planche complète sur orteils avant semaine 5 -- Les exercices avec pression intra-abdominale forte - -**Exercices de la phase 3 :** -- Squat avec respiration coordonnée (périnée) -- Pont fessier bilatéral avec contraction périnéale -- Bird dog complet -- Planche sur orteils (introduction semaine 5 uniquement) -- Fente avant statique -- Dead bug complet (si diastasis < 2 doigts confirmé) - -> ⚠️ *La sensation subjective de récupération est toujours en avance sur la réalité tissulaire. Même si l'utilisatrice se sent prête pour les sauts : attendre 3 mois de rééducation périnéale.* - ---- - -### Récapitulatif Programme Post-Partum - -| Phase | Semaines | Protocole | Séances/sem | Impacts | Focus principal | -|---|---|---|---|---|---| -| Phase 1 | Sem. 1-2 | 10 sec / 20 sec (inversé) | 4 | ❌ Aucun | Périnée + transverse | -| Phase 2 | Sem. 3-4 | 20 sec / 10 sec | 4 | ❌ Aucun | Gainage global | -| Phase 3 | Sem. 5-6 | 20 sec / 10 sec | 4 | ❌ Aucun | Retour tabata doux | - -**Après le programme :** accès au programme Débutant standard. Parcours logique : Post-partum → Débutant → Intermédiaire → Avancé. - ---- - -## 6. Programme Seniors — 4 semaines - -> ⚠️ **Avertissement médical.** Programme destiné aux personnes de 60 ans et plus, actives ou en reprise d'activité, sans contre-indication médicale majeure. Si vous n'avez pas pratiqué de sport depuis plus de 2 ans ou si vous avez des antécédents cardiovasculaires : consulter votre médecin avant de commencer. - -**Objectif :** lutter contre la sarcopénie, améliorer l'équilibre et la proprioception pour prévenir les chutes, renforcer les muscles fonctionnels du quotidien, maintenir ou améliorer la mobilité articulaire. - -### Les 4 réalités physiologiques qui guident ce programme - -**Sarcopénie** — perte de ~1% de masse musculaire par an après 50 ans. Le tabata adapté est l'un des outils les plus efficaces pour la contrer. - -**Proprioception** — l'équilibre décline avec l'âge. Les chutes sont la 1ère cause de perte d'autonomie. Chaque séance contient un exercice de prévention des chutes. - -**Récupération** — plus lente : 48–72h contre 24–48h chez le jeune adulte. Maximum 3 séances/semaine, jamais 2 jours consécutifs. - -**Mobilité articulaire** — amplitudes réduites, cartilages plus fragiles. Chaque exercice est réalisable dans les amplitudes disponibles — pas les amplitudes théoriques d'un adulte jeune. - -### Les 3 règles non-négociables - -- **ZÉRO impact** tout le programme — pas de sauts, pas de burpees, pas de réceptions -- **Protocole modifié** : 20 sec / 15 sec (semaines 1-2) puis 20/10 (semaines 3-4) -- **Travail d'équilibre** dans chaque séance — non négociable - ---- - -### SEMAINE 1 — Éveil et repères - -**Format :** 2 blocs tabata (20/15) par séance. 3 séances/semaine. -**Durée totale :** ~25 minutes - ---- - -#### Séance Senior A — Membres inférieurs & équilibre - -**Échauffement — 6 min** -- 2 min — Marche sur place avec bras actifs -- 1 min — Montées de genoux lentes en tenant le dossier d'une chaise -- 1 min — Flexions-extensions de chevilles assis -- 1 min — Cercles de hanches debout -- 1 min — Transferts de poids latéraux (balancement doux d'un pied sur l'autre) - -**⏱ Bloc 1 — 20 sec effort / 15 sec repos** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Squat assisté avec chaise** — se lever et s'asseoir lentement d'une chaise sans se laisser tomber, contrôler la descente jusqu'au bout | **Équilibre unipodal** — se tenir sur un pied en tenant légèrement un support, 10 secondes chaque pied | -| 📋 *C'est le mouvement fonctionnel le plus important de la vie quotidienne. RÈGLE : ne jamais s'écraser — contrôler la descente jusqu'au bout.* | 📋 *Fermer les yeux progressivement augmente considérablement la difficulté proprioceptive — progression naturelle pour les semaines suivantes.* | - -⏸ *1 min récupération active — marche lente* - -**⏱ Bloc 2 — 20 sec effort / 15 sec repos** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Fente statique tenue avec appui** — un pied devant, l'autre derrière, légère flexion, tenir 20 secondes, alterner | **Marche talon-orteil** — marcher en ligne en posant le talon immédiatement devant l'orteil de l'autre pied | -| 📋 *Tenir un support de la main non-travaillante. L'appui sécurise et permet de se concentrer sur l'amplitude.* | 📋 *Exercice de coordination vestibulaire utilisé en rééducation neurologique. Prévoir un mur à portée de main.* | - -**Retour au calme — 5 min** -- Étirements en position assise (jambes, dos) -- Respiration guidée -- Automassage des pieds (essentiels pour la proprioception) - ---- - -#### Séance Senior B — Haut du corps & gainage doux - -**Échauffement — 6 min** -- Mobilisation complète des épaules, poignets, nuque -- Activation des rhomboïdes : pincer les omoplates 10 fois -- Chat-chameau en position assise (mobilité lombaire) - -**⏱ Bloc 1 — 20 sec / 15 sec** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Pompes contre le mur** — debout à 50 cm d'un mur, mains à hauteur d'épaules | **Rowing avec serviette** — simuler le geste de rameur en tirant les coudes vers l'arrière, omoplates rapprochées | -| 📋 *À l'angle correct (corps incliné à 30°), la charge sur pectoraux et triceps est significative sans contrainte sur les poignets.* | 📋 *Renforcement des trapèzes moyens et rhomboïdes — muscles clés de la posture. Contre les épaules enroulées.* | - -⏸ *1 min récupération active* - -**⏱ Bloc 2 — 20 sec / 15 sec** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Planche contre le mur** — corps incliné, avant-bras contre le mur, maintenir la position | **Rotation thoracique assise** — assis, bras croisés sur la poitrine, rotation lente droite-gauche, bassin fixe | -| 📋 *Alternative idéale à la planche au sol pour les personnes avec difficultés à descendre ou douleurs aux poignets.* | 📋 *Mobilité thoracique essentielle pour la conduite, les gestes du quotidien et la prévention des douleurs cervicales.* | - ---- - -#### Séance Senior C — Corps entier fonctionnel - -> 💡 Cette séance travaille les **mouvements de la vie quotidienne**. C'est ce qu'aucune autre app tabata ne propose — et c'est précisément ce dont les seniors ont besoin. - -**⏱ Bloc 1 — 20 sec / 15 sec** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Ramasser au sol** — descendre pour simuler le ramassage d'un objet EN FLÉCHISSANT LES GENOUX, pas en arrondissant le dos | **Monter-descendre une marche** — monter et descendre une marche basse alternativement | -| 📋 *C'est le mouvement le plus souvent réalisé incorrectement — l'une des principales causes de lumbago. Ce programme enseigne le bon geste.* | 📋 *Tenir un mur ou une rambarde si nécessaire. La sécurité prime sur la difficulté.* | - -⏸ *1 min récupération active* - -**⏱ Bloc 2 — 20 sec / 15 sec** - -| Rounds impairs | Rounds pairs | -|---|---| -| **Porter et poser simulé** — tenir un livre ou bouteille, le poser à hauteur d'épaule (étagère simulée), le reprendre | **Rotation avec objet léger** — tenir un objet, rotation droite-gauche en portant l'objet d'un côté à l'autre | -| 📋 *Travaille la coordination œil-main, stabilité d'épaule et contraction abdominale réflexe. Directement transférable à la vie quotidienne.* | 📋 *Maintenir le bassin immobile — seul le tronc tourne.* | - ---- - -### SEMAINE 2 — Renforcement - -Passage au protocole 20/10 classique. Nouveaux exercices : -- **Squat sans chaise** — même mouvement mais sans support. Chaise derrière soi comme filet de sécurité. -- **Équilibre unipodal yeux fermés** — 5 secondes chaque pied. -- **Pompes contre plan incliné (table)** — angle plus difficile, charge plus importante. -- **Semi-squat unilatéral avec appui** — une jambe légèrement fléchie, l'autre levée légèrement. - -### SEMAINE 3 — Progression & confiance - -3 blocs tabata par séance. Exercices debout plus dynamiques — toujours sans impact. Nouveaux exercices : -- **Marche rapide avec bras pompants** — simuler une marche athlétique intense sur place. Excellent pour la FC sans impact. -- **Squat avec rotation** — au sommet du squat, rotation du buste à 45°. Travail combiné jambes + tronc. -- **Deadlift fonctionnel avec objet léger** — charnière de hanche en avant. Prévient les douleurs lombaires chroniques. -📋 *Le deadlift fonctionnel est le geste de se pencher en avant. Le renforcer prévient directement les douleurs lombaires chroniques, très fréquentes après 60 ans.* -- **Élévation latérale des bras** — bras tendus, lever latéralement à hauteur d'épaule. Maintien de la capacité à porter. - -### SEMAINE 4 — Décharge & autonomie - -Retour à 2 blocs. Exercices connus. Tests de fin de programme. - -#### Tests cliniques de fin de programme - -| Test clinique | Protocole | Objectif | -|---|---|---| -| Five Times Sit to Stand | Se lever/s'asseoir d'une chaise, compter en 30 secondes | 12+ répétitions | -| Équilibre unipodal | Tenir sur chaque pied yeux ouverts | 20 secondes sans appui | -| Force haut du corps | Pompes contre le mur en 20 secondes | 12+ répétitions | - -> 📋 Ces trois tests sont des indicateurs cliniques réels utilisés en bilan kinésithérapeutique. Les intégrer dans l'app donne une valeur médicale concrète aux résultats. - ---- - -### Récapitulatif Programme Seniors - -| Semaine | Blocs/séance | Protocole | Séances/sem | Nouveautés | Durée totale | -|---|---|---|---|---|---| -| Semaine 1 | 2 blocs | 20/15 | 3 | Bases fonctionnelles | ~25 min | -| Semaine 2 | 2 blocs | 20/10 | 3 | Sans chaise, incliné | ~25 min | -| Semaine 3 | 3 blocs | 20/10 | 3 | Mouvements dynamiques | ~30 min | -| Semaine 4 | 2 blocs | 20/10 | 3 | Tests cliniques | ~25 min | - ---- - -## 7. Programme Bureau — 4 semaines - -**Objectif :** intégrer le mouvement comme réflexe dans la journée de travail. 10 minutes de mouvement actif réduisent les douleurs lombaires de 30%, améliorent la concentration de 20% et contrebalancent partiellement les effets d'une journée sédentaire. - -### Les 5 contraintes de conception - -- **🔇 Silence** — pas de sauts, pas d'impacts, pas de chutes au sol, niveau sonore d'un bureau normal -- **🚫 Pas de sol** — tous les exercices debout ou assis sur une chaise, zéro exercice au sol -- **💧 Pas de sueur visible** — intensité calibrée pour élever la FC sans transpiration abondante -- **⏱ 10 ou 20 min max** — deux formats selon la disponibilité -- **👔 Tenue de bureau** — aucun exercice qui déforme une veste ou froisse un pantalon - ---- - -### FORMAT A — 10 minutes au bureau - -**Protocole :** 20 sec effort / 10 sec repos. 4 blocs de 2 minutes. Pendant une pause, sans bouger de sa zone de travail. - -#### Échauffement — 1 minute (assis, discret) -- Cercles de poignets dans les deux sens -- Mobilisation cervicale : inclinaisons et rotations douces -- Ouverture de poitrine : bras en arrière, omoplates rapprochées - -#### ⏱ Bloc 1 — Activation profonde (assis) - -| Rounds impairs | Rounds pairs | -|---|---| -| **Contraction abdominale isométrique** — rentrer le nombril, maintenir en respirant normalement, invisible de l'extérieur | **Serrage fessier** — serrer les fessiers et maintenir 20 secondes, discret, invisible | -| 📋 *C'est le transverse. Le contracter régulièrement en position assise combat directement les douleurs lombaires liées à la sédentarité.* | 📋 *Contre la rétroversion du bassin liée à la position assise prolongée.* | - -#### ⏱ Bloc 2 — Membres inférieurs (assis) - -| Rounds impairs | Rounds pairs | -|---|---| -| **Extension de jambe** — assis, lever une jambe tendue à hauteur de hanche, maintenir, alterner | **Élévation de talons assis** — pieds à plat, lever les talons en contractant les mollets | -| 📋 *Renforcement du quadriceps. Ajouter une contraction du pied (orteils vers soi) pour activer les tibias antérieurs.* | 📋 *Active la pompe veineuse des jambes. Prévention des jambes lourdes et des varices.* | - -#### ⏱ Bloc 3 — Membres inférieurs (debout, silencieux) - -| Rounds impairs | Rounds pairs | -|---|---| -| **Wall sit (chaise invisible)** — dos au mur, cuisses parallèles au sol, maintenir | **Élévation de talons debout** — montées sur la pointe des pieds, descente lente en 3 secondes | -| 📋 *Aucun bruit, aucun mouvement visible de l'extérieur, intensité maximale pour les quadriceps. Respirer calmement.* | 📋 *Renforcement des soléaires et gastrocnémiens. Bras tendus devant soi pour l'équilibre si nécessaire.* | - -#### ⏱ Bloc 4 — Haut du corps (debout) - -| Rounds impairs | Rounds pairs | -|---|---| -| **Pompes contre le bureau** — mains sur le bureau, corps incliné à 30°, silencieux, propre | **Pull apart imaginaire** — bras tendus devant, simuler l'écartement d'une résistance élastique en rétractant les omoplates | -| 📋 *S'assurer que le bureau est stable avant de commencer. Plus incliné = plus facile.* | 📋 *Contre le syndrome de l'épaule roulée du bureau. Ouvrir la poitrine au maximum.* | - -#### Retour au calme — 1 minute -- Étirement cervical latéral doux (chaque côté) -- Étirement des poignets (extenseurs et fléchisseurs) -- 3 grandes respirations diaphragmatiques - ---- - -### FORMAT B — 20 minutes en espace calme - -**Protocole :** tabata classique 20/10. 5 blocs de 4 minutes. Plus de liberté de mouvement, toujours zéro impact. - -#### Exercices spécifiques du Format B - -**Squat silencieux avec tempo ralenti** — tempo 3-3 pour rester sous le seuil de transpiration tout en maintenant l'efficacité musculaire. - -**Fente statique avec rotation de buste** — fente basse tenue + rotation du tronc vers le genou avant. Mobilité thoracique et renforcement simultanés. - -**Bureau dips** — mains sur le bureau derrière soi, corps décollé, fléchir les coudes. Triceps et stabilisateurs d'épaule. -📋 *Vérifier la stabilité du bureau avant de commencer.* - -**Calf raises avec déséquilibre** — montées sur la pointe des pieds avec les yeux fermés. Double effet : renforcement + proprioception. - -**Isométrie fessiers debout** — contracter les fessiers alternativement sans bouger les jambes. Activation du moyen fessier inhibé par la position assise. - -**Rotation de buste debout rapide** — bras croisés sur la poitrine, rotation droite-gauche rapide. Active les obliques et échauffe les disques intervertébraux. - ---- - -### FORMAT C — Walking Meeting (20 minutes) - -> Format conçu pour être utilisé pendant une marche. L'app guide uniquement en **audio** — aucun besoin de regarder l'écran. - -**Structure :** alterner des phases de marche rapide tabata (20 sec intensité maximale) et des phases de marche normale (10 sec récup), sur 5 blocs de 4 minutes. - -| Type de marche | Utilisation | Intensité | -|---|---|---| -| Marche normale | Phase de repos (10 sec) | ⬜ Faible | -| Marche rapide | Phase d'effort standard | 🟡 Modérée | -| Marche genoux hauts | Phase d'effort intensifié | 🟠 Élevée | -| Marche en fente | Pas le plus long possible | 🔴 Haute | -| Montée d'escaliers | Si disponibles | 🔴 Haute | - -📋 *La marche active à 6–7 km/h suffit à atteindre 70–80% de la fréquence cardiaque maximale chez un adulte sédentaire. Amplement suffisant pour les bénéfices cardiovasculaires du HIIT sans impact traumatisant.* - ---- - -### Progression sur 4 semaines - -| Semaine | Format A | Format B | Format C | Objectif | -|---|---|---|---|---| -| Semaine 1 | 3×/semaine | — | — | Créer l'habitude de la pause active | -| Semaine 2 | 2×/semaine | 1×/semaine | — | Introduction du format long | -| Semaine 3 | 1×/semaine | 2×/semaine | 1×/semaine | Walking meeting intégré | -| Semaine 4 | Libre | Libre | Libre | Autonomie — choisir selon la semaine | - -> 💡 **L'objectif final** n'est pas de suivre un programme — c'est d'intégrer le mouvement comme réflexe dans la journée de travail. La semaine 4 sans structure fixe est intentionnelle : c'est la semaine de la prise d'autonomie. - ---- - -## 8. Récapitulatif global - -| Programme | Durée | Séances/sem | Blocs max | Impacts | Protocole | Tier | Exercices signature | -|---|---|---|---|---|---|---|---| -| 🟢 Débutant | 4 sem | 3 | 3 | ❌ | 20/10 | Gratuit | Squat, pompes genoux, pont fessier, planche AV, bird dog, dead bug | -| 🔵 Intermédiaire | 4 sem | 4 | 4 | ⚡ | 20/10 | Premium | Squat jump, fente sautée, burpee, hollow body, pistol squat assisté | -| 🔴 Avancé | 4 sem | 5 | 5 | 🔥 | 20/10 | Premium | Nordic curl, pistol squat complet, pompe plyométrique, complexes, MetCon | -| 🩷 Post-partum | 6 sem | 4 | 3 | ❌ | 10/20 → 20/10 | Kiné+ | Hypopressifs, pont périnéal, dead bug 1 membre, bird dog progressif | -| 🟤 Seniors | 4 sem | 3 | 3 | ❌ | 20/15 → 20/10 | Kiné+ | Squat chaise, équilibre unipodal, pompes mur, marche talon-orteil | -| 🟡 Bureau | 4 sem | 3–5 | 5 | ❌ | 20/10 | Premium | Wall sit, pompes bureau, pull apart, extension de jambe assis | - -### Parcours utilisateurs recommandés - -**Parcours Standard** -Débutant → Intermédiaire → Avancé - -**Parcours Maman** -Post-partum → Débutant → Intermédiaire → Avancé - -**Parcours Senior** -Seniors → Débutant (si capacité) → Maintenance Seniors cyclique - -**Parcours Actif Sédentaire** -Bureau → Débutant → Intermédiaire → Bureau en maintenance - ---- - -> *Ce guide représente 26 semaines de contenu kiné structuré. Chaque exercice est médicalement raisonné. Chaque progression est physiologiquement justifiée. C'est ce qu'aucune app tabata concurrente ne propose aujourd'hui.* - ---- -*Document élaboré en avril 2026 — App Tabata Kiné — Tous droits réservés* diff --git a/app.json b/app.json deleted file mode 100644 index 37c7d9e..0000000 --- a/app.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "expo": { - "name": "TabataFit", - "slug": "tabatafit", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/images/icon.png", - "scheme": "tabatafit", - "userInterfaceStyle": "automatic", - "newArchEnabled": true, - "ios": { - "supportsTablet": true, - "bundleIdentifier": "com.millianlmx.tabatafit", - "buildNumber": "1", - "infoPlist": { - "NSHealthShareUsageDescription": "TabataFit uses HealthKit to read and write workout data including heart rate, calories burned, and exercise minutes.", - "NSHealthUpdateUsageDescription": "TabataFit saves your workout sessions to Apple Health so you can track your fitness progress.", - "NSCameraUsageDescription": "TabataFit uses the camera for profile photos and workout form checks.", - "NSUserTrackingUsageDescription": "TabataFit uses this to provide personalized workout recommendations.", - "ITSAppUsesNonExemptEncryption": false - }, - "config": { - "usesNonExemptEncryption": false - }, - "associatedDomains": [ - "applinks:tabatafit.app" - ] - }, - "android": { - "adaptiveIcon": { - "backgroundColor": "#E6F4FE", - "foregroundImage": "./assets/images/android-icon-foreground.png", - "backgroundImage": "./assets/images/android-icon-background.png", - "monochromeImage": "./assets/images/android-icon-monochrome.png" - }, - "edgeToEdgeEnabled": true, - "predictiveBackGestureEnabled": false, - "package": "com.millianlmx.tabatafit", - "intentFilters": [ - { - "action": "VIEW", - "autoVerify": true, - "data": [ - { - "scheme": "https", - "host": "tabatafit.app", - "pathPrefix": "/workout" - }, - { - "scheme": "https", - "host": "tabatafit.app", - "pathPrefix": "/player" - }, - { - "scheme": "https", - "host": "tabatafit.app", - "pathPrefix": "/program" - } - ], - "category": ["BROWSABLE", "DEFAULT"] - } - ] - }, - "web": { - "output": "static", - "favicon": "./assets/images/favicon.png" - }, - "plugins": [ - "expo-router", - [ - "expo-splash-screen", - { - "image": "./assets/images/splash-icon.png", - "imageWidth": 200, - "resizeMode": "contain", - "backgroundColor": "#ffffff", - "dark": { - "backgroundColor": "#000000" - } - } - ], - "expo-video", - "expo-localization", - "./plugins/withStoreKitConfig" - ], - "experiments": { - "typedRoutes": true, - "reactCompiler": true - } - } -} diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx deleted file mode 100644 index a5c65a7..0000000 --- a/app/(tabs)/_layout.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/** - * TabataGo Tab Layout - * Native liquid glass tab bar (iOS 26+) via expo-router/unstable-native-tabs - * 3 tabs: Home, Activity, Profile - */ - -import { Redirect } from 'expo-router' -import { NativeTabs, Icon, Label } from 'expo-router/unstable-native-tabs' -import { useTranslation } from 'react-i18next' - -import { BRAND, TEXT, NAVY } from '@/src/shared/constants/colors' -import { useUserStore } from '@/src/shared/stores' - -export default function TabLayout() { - const { t } = useTranslation() - const onboardingCompleted = useUserStore((s) => s.profile.onboardingCompleted) - - if (!onboardingCompleted) { - return - } - - return ( - - - - - - - - - - - - - - - - - ) -} diff --git a/app/(tabs)/activity.tsx b/app/(tabs)/activity.tsx deleted file mode 100644 index 2cee486..0000000 --- a/app/(tabs)/activity.tsx +++ /dev/null @@ -1,179 +0,0 @@ -/** - * TabataGo Activity Tab - * Streak, weekly sessions, program history — driven by progressStore. - */ - -import { useMemo } from 'react' -import { View, Text, StyleSheet, ScrollView } from 'react-native' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useTranslation } from 'react-i18next' -import { Icon } from '@/src/shared/components/Icon' -import { useProgressStore } from '@/src/shared/stores/progressStore' -import { useThemeColors } from '@/src/shared/theme' -import type { ThemeColors } from '@/src/shared/theme/types' -import { TYPOGRAPHY } from '@/src/shared/constants/typography' -import { SPACING, LAYOUT } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { GREEN, NAVY, TEXT, BORDER_COLORS } from '@/src/shared/constants/colors' - -export default function ActivityScreen() { - const { t } = useTranslation() - const insets = useSafeAreaInsets() - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - - const history = useProgressStore(s => s.history) - const streak = useProgressStore(s => s.streak) - const weeklyCount = useProgressStore(s => s.getWeeklyCount()) - const completedCount = useProgressStore(s => s.getCompletedCount()) - - const totalMinutes = useMemo( - () => history.reduce((sum, s) => sum + Math.round(s.durationSeconds / 60), 0), - [history], - ) - - return ( - - {t('screens:tabs.progression')} - - {/* Streak hero */} - - - {streak.current} - {t('screens:activity.dayStreak')} - - {t('screens:activity.longest')}: {streak.longest} - - - - {/* Stats grid */} - - - - - - - {/* Recent history */} - {history.length > 0 && ( - - {t('screens:activity.recent')} - {history.slice(0, 10).map((session, i) => ( - - - - {session.programId} - - {Math.round(session.durationSeconds / 60)} min - {' · '} - {new Date(session.completedAt).toLocaleDateString()} - - - - ))} - - )} - - {history.length === 0 && ( - - {t('screens:activity.emptyTitle')} - {t('screens:activity.emptySubtitle')} - - )} - - ) -} - -function StatCard({ icon, value, label, color }: { icon: any; value: number; label: string; color: string }) { - return ( - - - {value} - {label} - - ) -} - -const cardStyles = StyleSheet.create({ - card: { - flex: 1, - alignItems: 'center', - padding: SPACING[3], - borderRadius: RADIUS.LG, - backgroundColor: NAVY[800], - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - gap: SPACING[1], - borderCurve: 'continuous', - }, - value: { ...TYPOGRAPHY.TITLE_2, color: TEXT.PRIMARY, fontVariant: ['tabular-nums'] }, - label: { ...TYPOGRAPHY.CAPTION_2, color: TEXT.TERTIARY, textAlign: 'center' }, -}) - -function createStyles(colors: ThemeColors) { - return StyleSheet.create({ - container: { flex: 1, backgroundColor: colors.bg.base }, - content: { paddingHorizontal: LAYOUT.SCREEN_PADDING }, - - title: { ...TYPOGRAPHY.LARGE_TITLE, color: TEXT.PRIMARY, marginBottom: SPACING[5] }, - - streakHero: { - alignItems: 'center', - paddingVertical: SPACING[6], - marginBottom: SPACING[4], - backgroundColor: NAVY[800], - borderRadius: RADIUS.XL, - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - gap: SPACING[1], - }, - streakCount: { ...TYPOGRAPHY.LARGE_TITLE, color: TEXT.PRIMARY, fontSize: 56, fontVariant: ['tabular-nums'] }, - streakLabel: { ...TYPOGRAPHY.HEADLINE, color: TEXT.SECONDARY }, - streakRecord: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.TERTIARY, marginTop: SPACING[1] }, - - grid: { flexDirection: 'row', gap: SPACING[3], marginBottom: SPACING[6] }, - - historySection: { gap: SPACING[2] }, - sectionTitle: { - ...TYPOGRAPHY.CAPTION_1, - color: TEXT.TERTIARY, - textTransform: 'uppercase', - letterSpacing: 0.5, - marginBottom: SPACING[1], - }, - historyRow: { - flexDirection: 'row', - alignItems: 'center', - gap: SPACING[3], - padding: SPACING[3], - backgroundColor: colors.surface.default.backgroundColor, - borderRadius: RADIUS.MD, - borderWidth: 1, - borderColor: colors.surface.default.borderColor, - }, - historyTitle: { ...TYPOGRAPHY.SUBHEADLINE, color: TEXT.PRIMARY }, - historyMeta: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.TERTIARY, marginTop: 2 }, - - emptyState: { alignItems: 'center', marginTop: SPACING[12], gap: SPACING[2] }, - emptyTitle: { ...TYPOGRAPHY.HEADLINE, color: TEXT.PRIMARY }, - emptySubtitle: { ...TYPOGRAPHY.BODY, color: TEXT.TERTIARY, textAlign: 'center' }, - }) -} diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx deleted file mode 100644 index fff039b..0000000 --- a/app/(tabs)/index.tsx +++ /dev/null @@ -1,212 +0,0 @@ -/** - * TabataGo Home Screen - * Mascot + 3 stat pills + 3 body zone cards + settings button. - */ - -import { View, Text, StyleSheet, ScrollView, Pressable } from 'react-native' -import { useRouter } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useTranslation } from 'react-i18next' - -import { Icon } from '@/src/shared/components/Icon' -import { Mascot } from '@/src/shared/components/Mascot' -import { useUserStore } from '@/src/shared/stores/userStore' -import { useProgressStore } from '@/src/shared/stores/progressStore' -import { BODY_ZONE_META, type BodyZone } from '@/src/shared/types/workoutProgram' -import { TYPOGRAPHY } from '@/src/shared/constants/typography' -import { SPACING } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { TEXT, NAVY, GREEN, BORDER_COLORS } from '@/src/shared/constants/colors' -import { withOpacity } from '@/src/shared/utils/color' - -const BODY_ZONES: BodyZone[] = ['upper-body', 'lower-body', 'full-body'] - -export default function HomeScreen() { - const router = useRouter() - const insets = useSafeAreaInsets() - const { t } = useTranslation() - - const firstName = useUserStore(s => s.profile.name) - const streak = useProgressStore(s => s.streak.current) - const weeklyCount = useProgressStore(s => s.getWeeklyCount()) - const completedCount = useProgressStore(s => s.getCompletedCount()) - - const nameSuffix = firstName ? `, ${firstName}` : '' - const mascotMessage = streak > 0 - ? t('screens:home.mascotStreak', { count: streak, name: nameSuffix }) - : t('screens:home.mascotReady', { name: nameSuffix }) - - return ( - - {/* Header with settings */} - - TabataGo - router.push('/settings')} style={styles.iconBtn} hitSlop={8}> - - - - - {/* Mascot */} - - - - - {/* Stats pills */} - - - - - - - {/* Body zone cards */} - {t('screens:zone.chooseYourFocus')} - - {BODY_ZONES.map(zone => ( - router.push(`/zone/${zone}`)} /> - ))} - - - ) -} - -function StatPill({ - value, - label, - icon, - color, -}: { - value: number - label: string - icon: any - color: string -}) { - return ( - - - {value} - {label} - - ) -} - -function ZoneCard({ zone, onPress }: { zone: BodyZone; onPress: () => void }) { - const meta = BODY_ZONE_META[zone] - const { t } = useTranslation() - return ( - [ - styles.zoneCard, - { borderColor: withOpacity(meta.color, 0.3) }, - pressed && { opacity: 0.85, transform: [{ scale: 0.98 }] }, - ]} - > - {/* Colored top strip with large icon */} - - - - - - - {/* Content area */} - - {meta.label} - - {t(meta.descKey)} - - - {/* Bottom row: level badge + chevron */} - - - - {t('screens:home.zoneLevels')} - - - - - - - ) -} - -const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: NAVY[900] }, - content: { paddingHorizontal: SPACING[5] }, - - header: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - marginBottom: SPACING[4], - }, - brand: { ...TYPOGRAPHY.TITLE_1, color: TEXT.PRIMARY, letterSpacing: -0.5 }, - iconBtn: { - width: 40, - height: 40, - borderRadius: 20, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: NAVY[800], - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - }, - - mascotWrap: { alignItems: 'center', marginVertical: SPACING[4] }, - - statsRow: { flexDirection: 'row', gap: SPACING[2], marginBottom: SPACING[6] }, - pill: { - flex: 1, - alignItems: 'center', - paddingVertical: SPACING[3], - paddingHorizontal: SPACING[2], - borderRadius: RADIUS.MD, - borderWidth: 1, - backgroundColor: NAVY[800], - gap: 4, - }, - pillValue: { ...TYPOGRAPHY.TITLE_2, color: TEXT.PRIMARY, fontVariant: ['tabular-nums'] }, - pillLabel: { ...TYPOGRAPHY.CAPTION_2, color: TEXT.TERTIARY, textAlign: 'center' }, - - sectionTitle: { ...TYPOGRAPHY.HEADLINE, color: TEXT.PRIMARY, marginBottom: SPACING[3] }, - zoneList: { gap: SPACING[4] }, - zoneCard: { - borderRadius: RADIUS.XL, - borderWidth: 1, - backgroundColor: NAVY[800], - overflow: 'hidden' as const, - boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)', - }, - zoneTopStrip: { - alignItems: 'center' as const, - justifyContent: 'center' as const, - paddingVertical: SPACING[5], - }, - zoneIconCircle: { - width: 72, - height: 72, - borderRadius: 36, - alignItems: 'center' as const, - justifyContent: 'center' as const, - }, - zoneContent: { - padding: SPACING[4], - gap: SPACING[2], - }, - zoneTitle: { ...TYPOGRAPHY.TITLE_2, color: TEXT.PRIMARY }, - zoneDesc: { ...TYPOGRAPHY.SUBHEADLINE, color: TEXT.SECONDARY, lineHeight: 20 }, - zoneFooter: { - flexDirection: 'row' as const, - alignItems: 'center' as const, - justifyContent: 'space-between' as const, - marginTop: SPACING[1], - }, - zoneBadge: { - paddingHorizontal: SPACING[3], - paddingVertical: SPACING[1], - borderRadius: RADIUS.SM, - }, - zoneBadgeText: { ...TYPOGRAPHY.CAPTION_1, fontWeight: '600' as const }, -}) diff --git a/app/(tabs)/profile.tsx b/app/(tabs)/profile.tsx deleted file mode 100644 index aecbf56..0000000 --- a/app/(tabs)/profile.tsx +++ /dev/null @@ -1,181 +0,0 @@ -/** - * TabataGo Profile Tab - * User info, subscription status, quick stats. Settings via form sheet. - */ - -import { useMemo } from 'react' -import { View, Text, StyleSheet, ScrollView, Pressable } from 'react-native' -import { useRouter } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useTranslation } from 'react-i18next' -import { Icon } from '@/src/shared/components/Icon' -import { useUserStore } from '@/src/shared/stores/userStore' -import { useProgressStore } from '@/src/shared/stores/progressStore' -import { usePurchases } from '@/src/shared/hooks' -import { useThemeColors } from '@/src/shared/theme' -import type { ThemeColors } from '@/src/shared/theme/types' -import { TYPOGRAPHY } from '@/src/shared/constants/typography' -import { SPACING, LAYOUT } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { GREEN, NAVY, TEXT, BORDER_COLORS } from '@/src/shared/constants/colors' - -export default function ProfileScreen() { - const { t } = useTranslation() - const router = useRouter() - const insets = useSafeAreaInsets() - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - - const profile = useUserStore(s => s.profile) - const { isPremium } = usePurchases() - - const completedCount = useProgressStore(s => s.getCompletedCount()) - const streak = useProgressStore(s => s.streak) - const weeklyCount = useProgressStore(s => s.getWeeklyCount()) - - const avatarLetter = profile.name?.[0]?.toUpperCase() || '?' - - return ( - - {/* Avatar + name */} - - - {avatarLetter} - - - {profile.name || t('screens:profile.guest')} - - - {isPremium ? t('screens:settings.premium') : t('screens:settings.free')} - - - - router.push('/settings')} hitSlop={8}> - - - - - {/* Stats row */} - - - - - - - {/* Upgrade banner (free users) */} - {!isPremium && ( - router.push('/paywall')} - > - - - {t('screens:profile.upgradeTitle')} - {t('screens:profile.upgradeDescription')} - - - - )} - - {/* Settings link */} - router.push('/settings')}> - - {t('screens:settings.title')} - - - - ) -} - -function StatPill({ value, label, icon, color }: { value: number; label: string; icon: any; color: string }) { - return ( - - - {value} - {label} - - ) -} - -const pillStyles = StyleSheet.create({ - pill: { - flex: 1, - alignItems: 'center', - paddingVertical: SPACING[3], - borderRadius: RADIUS.MD, - borderWidth: 1, - backgroundColor: NAVY[800], - borderColor: BORDER_COLORS.DIM, - gap: 4, - }, - value: { ...TYPOGRAPHY.TITLE_2, color: TEXT.PRIMARY, fontVariant: ['tabular-nums'] }, - label: { ...TYPOGRAPHY.CAPTION_2, color: TEXT.TERTIARY, textAlign: 'center' }, -}) - -function createStyles(colors: ThemeColors) { - return StyleSheet.create({ - container: { flex: 1, backgroundColor: colors.bg.base }, - content: { paddingHorizontal: LAYOUT.SCREEN_PADDING }, - - profileHeader: { - flexDirection: 'row', - alignItems: 'center', - gap: SPACING[3], - marginBottom: SPACING[5], - }, - avatar: { - width: 60, - height: 60, - borderRadius: 30, - backgroundColor: NAVY[700] ?? NAVY[800], - alignItems: 'center', - justifyContent: 'center', - borderWidth: 2, - borderColor: BORDER_COLORS.DIM, - }, - avatarLetter: { ...TYPOGRAPHY.TITLE_1, color: TEXT.PRIMARY }, - name: { ...TYPOGRAPHY.TITLE_2, color: TEXT.PRIMARY }, - planBadge: { - alignSelf: 'flex-start', - marginTop: 4, - paddingHorizontal: SPACING[2], - paddingVertical: 2, - borderRadius: RADIUS.SM, - borderWidth: 1, - }, - planText: { ...TYPOGRAPHY.CAPTION_2, fontWeight: '600' }, - - statsRow: { flexDirection: 'row', gap: SPACING[2], marginBottom: SPACING[5] }, - - upgradeBanner: { - flexDirection: 'row', - alignItems: 'center', - gap: SPACING[3], - padding: SPACING[4], - borderRadius: RADIUS.LG, - borderWidth: 1, - backgroundColor: colors.surface.default.backgroundColor, - marginBottom: SPACING[3], - borderCurve: 'continuous', - }, - upgradeTitle: { ...TYPOGRAPHY.HEADLINE, color: TEXT.PRIMARY }, - upgradeDesc: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.SECONDARY, marginTop: 2 }, - - settingsRow: { - flexDirection: 'row', - alignItems: 'center', - gap: SPACING[3], - padding: SPACING[4], - borderRadius: RADIUS.LG, - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - backgroundColor: colors.surface.default.backgroundColor, - borderCurve: 'continuous', - }, - settingsLabel: { ...TYPOGRAPHY.BODY, color: TEXT.PRIMARY, flex: 1 }, - }) -} diff --git a/app/CLAUDE.md b/app/CLAUDE.md deleted file mode 100644 index 0c8e6a6..0000000 --- a/app/CLAUDE.md +++ /dev/null @@ -1,70 +0,0 @@ - -# Recent Activity - - - -### Feb 19, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #5001 | 9:35 AM | 🔵 | Host Wrapper Located at Root Layout Level | ~153 | - -### Feb 20, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #5115 | 8:57 AM | 🔵 | Root Layout Stack Configuration with Screen Animations | ~256 | -| #5061 | 8:47 AM | 🔵 | Expo Router Tab Navigation Structure Found | ~196 | -| #5053 | 8:23 AM | ✅ | Completed removal of all Host wrappers from application | ~255 | -| #5052 | " | ✅ | Removed Host wrapper from root layout entirely | ~224 | -| #5019 | 8:13 AM | 🔵 | Root layout properly wraps Stack with Host component | ~198 | - -### Feb 28, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #5598 | 9:22 PM | 🟣 | Enabled PostHog analytics in development mode | ~253 | -| #5597 | " | 🔄 | PostHogProvider initialization updated with client check and autocapture config | ~303 | -| #5589 | 7:51 PM | 🟣 | PostHog screen tracking added to onboarding flow | ~246 | -| #5588 | 7:50 PM | ✅ | Added trackScreen function to onboarding analytics imports | ~203 | -| #5585 | " | ✅ | Enhanced PostHogProvider initialization with null safety | ~239 | -| #5584 | 7:49 PM | ✅ | Imported trackScreen function in root layout | ~202 | -| #5583 | " | 🟣 | PostHog user identification added to onboarding completion | ~291 | -| #5582 | " | ✅ | Enhanced onboarding analytics with user identification | ~187 | -| #5579 | 7:47 PM | 🔵 | Comprehensive analytics tracking in onboarding flow | ~345 | -| #5575 | 7:44 PM | 🔵 | PostHog integration architecture in root layout | ~279 | -| #5572 | 7:43 PM | 🔵 | PostHog integration points identified | ~228 | - -### Apr 10, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #6017 | 10:06 AM | 🔵 | Explore Filter Sheet for Level and Equipment | ~307 | -| #6000 | 10:01 AM | 🔵 | Root App Architecture Examined | ~316 | - -### Apr 11, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #6129 | 7:42 PM | 🔄 | Onboarding Wow icon circle opacity refactored | ~295 | -| #6126 | 7:41 PM | 🔵 | Assessment screen imports reviewed | ~293 | -| #6122 | " | 🔵 | Onboarding screen uses dynamic color with hex transparency | ~277 | - -### Apr 13, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #6175 | 10:04 PM | 🟣 | Completed Explore Tab Removal | ~196 | -| #6170 | 10:03 PM | 🟣 | Removed Explore Filters Modal Route | ~143 | -| #6165 | 10:02 PM | 🔵 | Located Explore Filters Screen Configuration | ~141 | -| #6160 | " | 🔵 | Identified Explore Filters Screen Configuration | ~141 | -| #6156 | " | 🔵 | Found Explore Tab References | ~155 | - -### Apr 17, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #6373 | 10:26 AM | 🟣 | Registered body zone detail screen route in app navigation | ~296 | -| #6372 | 10:25 AM | 🔵 | Reviewed app layout navigation configuration for workout and program screens | ~318 | -| #6371 | " | 🔵 | Examined app routing layout structure for workout routes | ~247 | - \ No newline at end of file diff --git a/app/_layout.tsx b/app/_layout.tsx deleted file mode 100644 index 7b8473e..0000000 --- a/app/_layout.tsx +++ /dev/null @@ -1,260 +0,0 @@ -/** - * TabataFit Root Layout - * Expo Router v3 — SF Pro system font (no custom font loading) - * Waits for store hydration before rendering - */ - -import '@/src/shared/i18n' -import '@/src/shared/i18n/types' - -import { useState, useEffect, useCallback, Component } from 'react' -import { Stack } from 'expo-router' -import { StatusBar } from 'expo-status-bar' -import { View, Text, Pressable, StyleSheet } from 'react-native' -import * as SplashScreen from 'expo-splash-screen' -import * as Notifications from 'expo-notifications' - -import { PostHogProvider } from 'posthog-react-native' - -import { ThemeProvider, useThemeColors } from '@/src/shared/theme' -import { TEXT, NAVY, GREEN } from '@/src/shared/constants/colors' -import { useUserStore } from '@/src/shared/stores' -import { useNotifications } from '@/src/shared/hooks' -import { OfflineBanner } from '@/src/shared/components/OfflineBanner' -import { initializePurchases } from '@/src/shared/services/purchases' -import { initializeAnalytics, getPostHogClient, trackScreen } from '@/src/shared/services/analytics' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' - -Notifications.setNotificationHandler({ - handleNotification: async () => ({ - shouldShowAlert: false, - shouldPlaySound: false, - shouldSetBadge: false, - shouldShowBanner: false, - shouldShowList: false, - }), -}) - -SplashScreen.preventAutoHideAsync() - -// ─── Error Boundary (F-108) ──────────────────────────────────────────────── -interface ErrorBoundaryState { - hasError: boolean - error: Error | null -} - -class ErrorBoundary extends Component<{ children: React.ReactNode }, ErrorBoundaryState> { - state: ErrorBoundaryState = { hasError: false, error: null } - - static getDerivedStateFromError(error: Error): ErrorBoundaryState { - return { hasError: true, error } - } - - componentDidCatch(error: Error, info: React.ErrorInfo) { - console.error('ErrorBoundary caught:', error, info.componentStack) - } - - private handleRetry = () => { - this.setState({ hasError: false, error: null }) - } - - render() { - if (this.state.hasError) { - return ( - - ⚠️ - Something went wrong - - {this.state.error?.message ?? 'An unexpected error occurred.'} - - - Try again - - - ) - } - return this.props.children - } -} - -const errorStyles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: NAVY[900], - alignItems: 'center', - justifyContent: 'center', - padding: 32, - }, - emoji: { fontSize: 48, marginBottom: 16 }, - title: { - fontSize: 22, - fontWeight: '700', - color: TEXT.PRIMARY, - marginBottom: 8, - }, - message: { - fontSize: 15, - fontWeight: '400', - color: TEXT.SECONDARY, - textAlign: 'center', - marginBottom: 24, - lineHeight: 20, - }, - button: { - backgroundColor: '#00C896', - paddingHorizontal: 32, - paddingVertical: 14, - borderRadius: 12, - borderCurve: 'continuous', - minHeight: 44, - }, - buttonText: { - fontSize: 17, - fontWeight: '600', - color: NAVY[900], - }, -}) - -// Create React Query Client -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - staleTime: 1000 * 60 * 5, // 5 minutes - gcTime: 1000 * 60 * 60 * 24, // 24 hours - retry: 2, - refetchOnWindowFocus: false, - }, - }, -}) - -function RootLayoutInner() { - const colors = useThemeColors() - - useNotifications() - - // Wait for persisted store to hydrate from AsyncStorage - const [hydrated, setHydrated] = useState(useUserStore.persist.hasHydrated()) - - useEffect(() => { - const unsub = useUserStore.persist.onFinishHydration(() => setHydrated(true)) - return unsub - }, []) - - // Initialize RevenueCat + PostHog after hydration - useEffect(() => { - if (hydrated) { - initializePurchases().catch((err) => { - console.error('Failed to initialize RevenueCat:', err) - }) - initializeAnalytics().catch((err) => { - console.error('Failed to initialize PostHog:', err) - }) - } - }, [hydrated]) - - const onLayoutRootView = useCallback(async () => { - if (hydrated) { - await SplashScreen.hideAsync() - } - }, [hydrated]) - - if (!hydrated) { - return null - } - - const content = ( - - - - - - - - - - - - - - - - - - - ) - - const posthogClient = getPostHogClient() - - // Only wrap with PostHogProvider if client is initialized - if (!posthogClient) { - return content - } - - return ( - - {content} - - ) -} - -export default function RootLayout() { - return ( - - - - - - ) -} diff --git a/app/complete/CLAUDE.md b/app/complete/CLAUDE.md deleted file mode 100644 index bee9fb7..0000000 --- a/app/complete/CLAUDE.md +++ /dev/null @@ -1,14 +0,0 @@ - -# Recent Activity - - - -### Feb 20, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #5047 | 8:22 AM | ✅ | Completed Host wrapper removal from all screens | ~241 | -| #5046 | " | ✅ | Removed opening Host tag from workout complete screen | ~165 | -| #5033 | 8:20 AM | ✅ | Removed Host import from workout complete screen | ~212 | -| #5026 | 8:18 AM | 🔵 | Workout complete screen properly wraps content with Host component | ~252 | - \ No newline at end of file diff --git a/app/complete/[id].tsx b/app/complete/[id].tsx deleted file mode 100644 index d490af5..0000000 --- a/app/complete/[id].tsx +++ /dev/null @@ -1,211 +0,0 @@ -/** - * TabataFit Workout Complete Screen - * Celebration + stats driven by progressStore. - */ - -import { useEffect, useMemo, useRef } from 'react' -import { - View, - Text as RNText, - StyleSheet, - ScrollView, - Animated, -} from 'react-native' -import { useRouter, useLocalSearchParams } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { Icon, type IconName } from '@/src/shared/components/Icon' -import { useTranslation } from 'react-i18next' -import * as Sharing from 'expo-sharing' - -import { useHaptics } from '@/src/shared/hooks' -import { useProgressStore } from '@/src/shared/stores' -import { NativeButton } from '@/src/shared/components/native' - -import { useThemeColors } from '@/src/shared/theme' -import type { ThemeColors } from '@/src/shared/theme/types' -import { TYPOGRAPHY } from '@/src/shared/constants/typography' -import { SPACING, LAYOUT } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { SPRING } from '@/src/shared/constants/animations' -import { GREEN, NAVY, TEXT, BORDER_COLORS } from '@/src/shared/constants/colors' - -function StatCard({ - value, - label, - icon, - delay = 0, -}: { - value: string | number - label: string - icon: IconName - delay?: number -}) { - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - const scaleAnim = useRef(new Animated.Value(0)).current - - useEffect(() => { - Animated.sequence([ - Animated.delay(delay), - Animated.spring(scaleAnim, { toValue: 1, ...SPRING.BOUNCY, useNativeDriver: true }), - ]).start() - }, [delay]) - - return ( - - - {value} - {label} - - ) -} - -export default function WorkoutCompleteScreen() { - const insets = useSafeAreaInsets() - const router = useRouter() - const haptics = useHaptics() - const { t } = useTranslation() - const { id } = useLocalSearchParams<{ id: string }>() - - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - - const history = useProgressStore((s) => s.history) - const streak = useProgressStore((s) => s.streak) - const weeklyCount = useProgressStore((s) => s.getWeeklyCount()) - - // Latest session (the one we just completed) - const latest = history[0] - const resultMinutes = latest ? Math.round(latest.durationSeconds / 60) : 0 - - const handleGoHome = () => { - haptics.buttonTap() - router.replace('/') - } - - const handleShare = async () => { - haptics.selection() - const isAvailable = await Sharing.isAvailableAsync() - if (isAvailable) { - await Sharing.shareAsync('https://tabatafit.app', { - dialogTitle: t('screens:complete.shareTitle', { minutes: resultMinutes }), - }) - } - } - - useEffect(() => { - haptics.workoutComplete() - }, []) - - return ( - - - {/* Celebration */} - - 🎉 - {t('screens:complete.title')} - - - {/* Stats Grid */} - - - - - - - - - {/* Streak */} - - - - - - - {t('screens:complete.streakDays', { count: streak.current })} - - - {t('screens:complete.streakRecord', { count: streak.longest })} - - - - - - - {/* Share */} - - - - - - {/* Fixed Bottom Button */} - - - - - - - ) -} - -function createStyles(colors: ThemeColors) { - return StyleSheet.create({ - container: { flex: 1, backgroundColor: colors.bg.base }, - scrollContent: { paddingHorizontal: LAYOUT.SCREEN_PADDING }, - - celebrationSection: { alignItems: 'center', paddingVertical: SPACING[8] }, - celebrationEmoji: { fontSize: 64, marginBottom: SPACING[4] }, - celebrationTitle: { ...TYPOGRAPHY.TITLE_1, color: TEXT.PRIMARY, letterSpacing: 1 }, - - statsGrid: { flexDirection: 'row', gap: SPACING[3], marginBottom: SPACING[6] }, - statCard: { - flex: 1, - padding: SPACING[3], - borderRadius: RADIUS.LG, - backgroundColor: colors.surface.default.backgroundColor, - alignItems: 'center', - borderWidth: 1, - borderColor: colors.surface.default.borderColor, - borderCurve: 'continuous', - }, - statValue: { ...TYPOGRAPHY.TITLE_1, color: TEXT.PRIMARY, marginTop: SPACING[2], fontVariant: ['tabular-nums'] }, - statLabel: { ...TYPOGRAPHY.CAPTION_2, color: TEXT.TERTIARY, marginTop: SPACING[1] }, - - divider: { height: 1, backgroundColor: BORDER_COLORS.DIM, marginVertical: SPACING[2] }, - - streakSection: { flexDirection: 'row', alignItems: 'center', paddingVertical: SPACING[4], gap: SPACING[4] }, - streakBadge: { width: 64, height: 64, borderRadius: RADIUS.FULL, alignItems: 'center', justifyContent: 'center' }, - streakInfo: { flex: 1 }, - streakTitle: { ...TYPOGRAPHY.TITLE_2, color: TEXT.PRIMARY }, - streakSubtitle: { ...TYPOGRAPHY.BODY, color: TEXT.TERTIARY, marginTop: SPACING[1] }, - - shareSection: { paddingVertical: SPACING[4], alignItems: 'center' }, - - bottomBar: { - position: 'absolute', - bottom: 0, left: 0, right: 0, - paddingHorizontal: LAYOUT.SCREEN_PADDING, - paddingTop: SPACING[4], - backgroundColor: colors.bg.base, - borderTopWidth: 1, - borderTopColor: BORDER_COLORS.DIM, - }, - homeButtonContainer: { height: 56, justifyContent: 'center' }, - }) -} diff --git a/app/onboarding.tsx b/app/onboarding.tsx deleted file mode 100644 index 2ed84ec..0000000 --- a/app/onboarding.tsx +++ /dev/null @@ -1,1341 +0,0 @@ -/** - * TabataFit Onboarding — 6-Screen Conversion Funnel - * Problem → Empathy → Solution → Wow Moment → Personalization → Paywall - */ - -import { useState, useRef, useEffect, useCallback, useMemo } from 'react' -import { - View, - StyleSheet, - Pressable, - Animated, - Dimensions, - ScrollView, - TextInput, -} from 'react-native' -import { useRouter } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { Icon } from '@/src/shared/components/Icon' - -import { Alert } from 'react-native' -import { useTranslation } from 'react-i18next' -import { useHaptics, usePurchases } from '@/src/shared/hooks' -import { useUserStore } from '@/src/shared/stores' -import { OnboardingStep } from '@/src/shared/components/OnboardingStep' -import { StyledText } from '@/src/shared/components/StyledText' - -import { useThemeColors } from '@/src/shared/theme' -import type { ThemeColors } from '@/src/shared/theme/types' -import { SPACING, LAYOUT } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { DURATION, EASE, SPRING } from '@/src/shared/constants/animations' -import { track, identifyUser, setUserProperties, trackScreen } from '@/src/shared/services/analytics' -import { GREEN, NAVY, BORDER_COLORS } from '@/src/shared/constants/colors' -import { withOpacity } from '@/src/shared/utils/color' -import { PHASE } from '@/src/shared/constants/colors' -import { NativeButton } from '@/src/shared/components/native' - -import type { FitnessLevel, FitnessGoal, WeeklyFrequency } from '@/src/shared/types' - -const { width: SCREEN_WIDTH } = Dimensions.get('window') -const TOTAL_STEPS = 6 - -// ═══════════════════════════════════════════════════════════════════════════ -// SCREEN 1 — THE PROBLEM -// ═══════════════════════════════════════════════════════════════════════════ - -function ProblemScreen({ onNext }: { onNext: () => void }) { - const { t } = useTranslation('screens') - const haptics = useHaptics() - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - const clockScale = useRef(new Animated.Value(0.8)).current - const clockOpacity = useRef(new Animated.Value(0)).current - const textOpacity = useRef(new Animated.Value(0)).current - - useEffect(() => { - // Clock animation - Animated.parallel([ - Animated.spring(clockScale, { - toValue: 1, - ...SPRING.BOUNCY, - useNativeDriver: true, - }), - Animated.timing(clockOpacity, { - toValue: 1, - duration: DURATION.SLOW, - easing: EASE.EASE_OUT, - useNativeDriver: true, - }), - ]).start() - - // Text fade in after clock - setTimeout(() => { - Animated.timing(textOpacity, { - toValue: 1, - duration: DURATION.SLOW, - easing: EASE.EASE_OUT, - useNativeDriver: true, - }).start() - }, 400) - }, []) - - return ( - - - - - - - - {t('onboarding.problem.title')} - - - {t('onboarding.problem.subtitle1')} - - - {t('onboarding.problem.subtitle2')} - - - - - { - haptics.buttonTap() - onNext() - }} - > - - {t('onboarding.problem.cta')} - - - - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// SCREEN 2 — EMPATHY -// ═══════════════════════════════════════════════════════════════════════════ - -const BARRIERS = [ - { id: 'no-time', labelKey: 'onboarding.empathy.noTime' as const, icon: 'clock' as const }, - { id: 'low-motivation', labelKey: 'onboarding.empathy.lowMotivation' as const, icon: 'battery.0percent' as const }, - { id: 'no-knowledge', labelKey: 'onboarding.empathy.noKnowledge' as const, icon: 'questionmark.circle' as const }, - { id: 'no-gym', labelKey: 'onboarding.empathy.noGym' as const, icon: 'house' as const }, -] - -function EmpathyScreen({ - onNext, - barriers, - setBarriers, -}: { - onNext: () => void - barriers: string[] - setBarriers: (b: string[]) => void -}) { - const { t } = useTranslation('screens') - const haptics = useHaptics() - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - - const toggleBarrier = (id: string) => { - haptics.selection() - if (barriers.includes(id)) { - setBarriers(barriers.filter((b) => b !== id)) - } else if (barriers.length < 2) { - setBarriers([...barriers, id]) - } - } - - return ( - - - {t('onboarding.empathy.title')} - - - {t('onboarding.empathy.chooseUpTo')} - - - - {BARRIERS.map((item) => { - const selected = barriers.includes(item.id) - return ( - toggleBarrier(item.id)} - > - - - {t(item.labelKey)} - - - ) - })} - - - - { - if (barriers.length > 0) { - haptics.buttonTap() - onNext() - } - }} - > - 0 ? NAVY[900] : colors.text.disabled} - > - {t('common:continue')} - - - - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// SCREEN 3 — THE SOLUTION (Scientific Proof) -// ═══════════════════════════════════════════════════════════════════════════ - -function SolutionScreen({ onNext }: { onNext: () => void }) { - const { t } = useTranslation('screens') - const haptics = useHaptics() - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - const tabataHeight = useRef(new Animated.Value(0)).current - const cardioHeight = useRef(new Animated.Value(0)).current - const citationOpacity = useRef(new Animated.Value(0)).current - - useEffect(() => { - // Animate bars - Animated.sequence([ - Animated.delay(300), - Animated.parallel([ - Animated.spring(tabataHeight, { - toValue: 1, - ...SPRING.GENTLE, - useNativeDriver: false, - }), - Animated.spring(cardioHeight, { - toValue: 1, - ...SPRING.GENTLE, - useNativeDriver: false, - }), - ]), - Animated.delay(200), - Animated.timing(citationOpacity, { - toValue: 1, - duration: DURATION.SLOW, - easing: EASE.EASE_OUT, - useNativeDriver: true, - }), - ]).start() - }, []) - - const MAX_BAR_HEIGHT = 160 - - return ( - - - {t('onboarding.solution.title')} - - - {/* Comparison bars */} - - {/* Tabata bar */} - - - {t('onboarding.solution.tabataCalories')} - - - - - - {t('onboarding.solution.tabata')} - - - {t('onboarding.solution.tabataDuration')} - - - - {/* VS */} - - - {t('onboarding.solution.vs')} - - - - {/* Cardio bar */} - - - {t('onboarding.solution.cardioCalories')} - - - - - - {t('onboarding.solution.cardio')} - - - {t('onboarding.solution.cardioDuration')} - - - - - {/* Citation */} - - - {t('onboarding.solution.citation')} - - - {t('onboarding.solution.citationAuthor')} - - - - - { - haptics.buttonTap() - onNext() - }} - > - - {t('onboarding.solution.cta')} - - - - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// SCREEN 4 — WOW MOMENT (Staggered Feature Reveal) -// ═══════════════════════════════════════════════════════════════════════════ - -const WOW_FEATURES = [ - { icon: 'timer' as const, iconColor: GREEN[500], titleKey: 'onboarding.wow.card1Title', subtitleKey: 'onboarding.wow.card1Subtitle' }, - { icon: 'dumbbell' as const, iconColor: PHASE.REST, titleKey: 'onboarding.wow.card2Title', subtitleKey: 'onboarding.wow.card2Subtitle' }, - { icon: 'mic' as const, iconColor: PHASE.PREP, titleKey: 'onboarding.wow.card3Title', subtitleKey: 'onboarding.wow.card3Subtitle' }, - { icon: 'arrow.up.right' as const, iconColor: PHASE.COMPLETE, titleKey: 'onboarding.wow.card4Title', subtitleKey: 'onboarding.wow.card4Subtitle' }, -] as const - -function WowScreen({ onNext }: { onNext: () => void }) { - const { t } = useTranslation('screens') - const haptics = useHaptics() - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - const wowStyles = useMemo(() => createWowStyles(colors), [colors]) - const rowAnims = useRef(WOW_FEATURES.map(() => ({ - opacity: new Animated.Value(0), - translateY: new Animated.Value(20), - }))).current - const ctaOpacity = useRef(new Animated.Value(0)).current - const [ctaReady, setCtaReady] = useState(false) - - useEffect(() => { - // Staggered reveal: each row fades in + slides up, 150ms apart, starting at 300ms - const STAGGER_DELAY = 150 - const ROW_DURATION = DURATION.NORMAL // 300ms - const START_DELAY = 300 - - WOW_FEATURES.forEach((_, i) => { - setTimeout(() => { - Animated.parallel([ - Animated.timing(rowAnims[i].opacity, { - toValue: 1, - duration: ROW_DURATION, - easing: EASE.EASE_OUT, - useNativeDriver: true, - }), - Animated.timing(rowAnims[i].translateY, { - toValue: 0, - duration: ROW_DURATION, - easing: EASE.EASE_OUT, - useNativeDriver: true, - }), - ]).start() - }, START_DELAY + i * STAGGER_DELAY) - }) - - // CTA fades in 200ms after last row finishes - const ctaDelay = START_DELAY + (WOW_FEATURES.length - 1) * STAGGER_DELAY + ROW_DURATION + 200 - setTimeout(() => { - setCtaReady(true) - Animated.timing(ctaOpacity, { - toValue: 1, - duration: ROW_DURATION, - easing: EASE.EASE_OUT, - useNativeDriver: true, - }).start() - }, ctaDelay) - }, []) - - return ( - - - {t('onboarding.wow.title')} - - - {t('onboarding.wow.subtitle')} - - - {/* Feature list */} - - {WOW_FEATURES.map((feature, i) => ( - - - - - - - {t(feature.titleKey)} - - - {t(feature.subtitleKey)} - - - - ))} - - - {/* CTA fades in after all rows */} - - { - if (ctaReady) { - haptics.buttonTap() - onNext() - } - }} - > - - {t('common:next')} - - - - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// SCREEN 5 — PERSONALIZATION -// ═══════════════════════════════════════════════════════════════════════════ - -const LEVELS: { value: FitnessLevel; labelKey: string }[] = [ - { value: 'beginner', labelKey: 'common:levels.beginner' }, - { value: 'intermediate', labelKey: 'common:levels.intermediate' }, - { value: 'advanced', labelKey: 'common:levels.advanced' }, -] - -const GOALS: { value: FitnessGoal; labelKey: string }[] = [ - { value: 'weight-loss', labelKey: 'onboarding.personalization.goals.weightLoss' }, - { value: 'cardio', labelKey: 'onboarding.personalization.goals.cardio' }, - { value: 'strength', labelKey: 'onboarding.personalization.goals.strength' }, - { value: 'wellness', labelKey: 'onboarding.personalization.goals.wellness' }, -] - -const FREQUENCIES: { value: WeeklyFrequency; labelKey: string }[] = [ - { value: 2, labelKey: 'onboarding.personalization.frequencies.2x' }, - { value: 3, labelKey: 'onboarding.personalization.frequencies.3x' }, - { value: 5, labelKey: 'onboarding.personalization.frequencies.5x' }, -] - -function PersonalizationScreen({ - onNext, - name, - setName, - level, - setLevel, - goal, - setGoal, - frequency, - setFrequency, -}: { - onNext: () => void - name: string - setName: (n: string) => void - level: FitnessLevel - setLevel: (l: FitnessLevel) => void - goal: FitnessGoal - setGoal: (g: FitnessGoal) => void - frequency: WeeklyFrequency - setFrequency: (f: WeeklyFrequency) => void -}) { - const { t } = useTranslation('screens') - const haptics = useHaptics() - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - - return ( - - - {t('onboarding.personalization.title')} - - - {/* Name input */} - - - {t('onboarding.personalization.yourName')} - - - - - {/* Fitness Level */} - - - {t('onboarding.personalization.fitnessLevel')} - - - {LEVELS.map((item) => ( - { - haptics.selection() - setLevel(item.value) - }} - > - - {t(item.labelKey)} - - - ))} - - - - {/* Goal */} - - - {t('onboarding.personalization.yourGoal')} - - - {GOALS.map((item) => ( - { - haptics.selection() - setGoal(item.value) - }} - > - - {t(item.labelKey)} - - - ))} - - - - {/* Frequency */} - - - {t('onboarding.personalization.weeklyFrequency')} - - - {FREQUENCIES.map((item) => ( - { - haptics.selection() - setFrequency(item.value) - }} - > - - {t(item.labelKey)} - - - ))} - - - - {name.trim().length > 0 && ( - - {t('onboarding.personalization.readyMessage')} - - )} - - - { - if (name.trim()) { - haptics.buttonTap() - onNext() - } - }} - > - - {t('common:continue')} - - - - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// SCREEN 6 — PAYWALL -// ═══════════════════════════════════════════════════════════════════════════ - -const PREMIUM_FEATURE_KEYS = [ - 'onboarding.paywall.features.unlimited', - 'onboarding.paywall.features.offline', - 'onboarding.paywall.features.stats', - 'onboarding.paywall.features.noAds', -] as const - -function PaywallScreen({ - onSubscribe, - onSkip, -}: { - onSubscribe: (plan: 'premium-monthly' | 'premium-yearly') => void - onSkip: () => void -}) { - const { t } = useTranslation('screens') - const haptics = useHaptics() - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - const { - isLoading, - monthlyPackage, - annualPackage, - purchasePackage, - restorePurchases, - } = usePurchases() - - const [selectedPlan, setSelectedPlan] = useState<'premium-monthly' | 'premium-yearly'>('premium-yearly') - const [isPurchasing, setIsPurchasing] = useState(false) - const featureAnims = useRef(PREMIUM_FEATURE_KEYS.map(() => new Animated.Value(0))).current - - const handlePlanSelect = (plan: 'premium-monthly' | 'premium-yearly') => { - haptics.selection() - setSelectedPlan(plan) - track('onboarding_paywall_plan_selected', { plan }) - } - - useEffect(() => { - // Staggered feature fade-in - PREMIUM_FEATURE_KEYS.forEach((_, i) => { - setTimeout(() => { - Animated.timing(featureAnims[i], { - toValue: 1, - duration: DURATION.NORMAL, - easing: EASE.EASE_OUT, - useNativeDriver: true, - }).start() - }, i * 100) - }) - }, []) - - // Get localized prices from RevenueCat packages - const yearlyPrice = annualPackage?.product.priceString ?? t('onboarding.paywall.yearlyPrice') - const monthlyPrice = monthlyPackage?.product.priceString ?? t('onboarding.paywall.monthlyPrice') - - const handlePurchase = async () => { - if (isPurchasing) return - - const pkg = selectedPlan === 'premium-yearly' ? annualPackage : monthlyPackage - const price = selectedPlan === 'premium-yearly' - ? (annualPackage?.product.priceString ?? t('onboarding.paywall.yearlyPrice')) - : (monthlyPackage?.product.priceString ?? t('onboarding.paywall.monthlyPrice')) - - track('onboarding_paywall_purchase_tapped', { plan: selectedPlan, price }) - - // DEV mode: if RevenueCat hasn't loaded or has no packages, show simulated purchase dialog - if (__DEV__ && (isLoading || !pkg)) { - haptics.buttonTap() - const planLabel = selectedPlan === 'premium-yearly' - ? `Annual (${t('onboarding.paywall.yearlyPrice')})` - : `Monthly (${t('onboarding.paywall.monthlyPrice')})` - Alert.alert( - 'Confirm Subscription', - `Subscribe to TabataFit+ ${planLabel}?\n\nThis is a sandbox purchase — no real charge.`, - [ - { text: 'Cancel', style: 'cancel' }, - { - text: 'Subscribe', - onPress: () => { - track('onboarding_paywall_purchase_success', { plan: selectedPlan }) - onSubscribe(selectedPlan) - }, - }, - ] - ) - return - } - - if (isLoading || !pkg) return - - setIsPurchasing(true) - haptics.buttonTap() - - try { - const result = await purchasePackage(pkg) - if (result.success) { - track('onboarding_paywall_purchase_success', { plan: selectedPlan }) - onSubscribe(selectedPlan) - } - } finally { - setIsPurchasing(false) - } - } - - const handleRestore = async () => { - haptics.buttonTap() - const restored = await restorePurchases() - track('onboarding_paywall_restored', { success: !!restored }) - if (restored) { - // User has premium now, complete onboarding - onSubscribe('premium-yearly') - } - } - - return ( - - - {t('onboarding.paywall.title')} - - - {/* Features */} - - {PREMIUM_FEATURE_KEYS.map((featureKey, i) => ( - - - - {t(featureKey)} - - - ))} - - - {/* Pricing cards */} - - {/* Annual */} - handlePlanSelect('premium-yearly')} - > - - - {t('onboarding.paywall.bestValue')} - - - - {yearlyPrice} - - - {t('common:units.perYear')} - - - {t('onboarding.paywall.savePercent')} - - - - {/* Monthly */} - handlePlanSelect('premium-monthly')} - > - - {monthlyPrice} - - - {t('common:units.perMonth')} - - - - - {/* CTA */} - - - {isPurchasing ? '...' : t('onboarding.paywall.trialCta')} - - - - {/* Guarantees */} - - - {t('onboarding.paywall.guarantees')} - - - - {/* Restore Purchases */} - - - {t('onboarding.paywall.restorePurchases')} - - - - {/* Skip */} - { - track('onboarding_paywall_skipped') - onSkip() - }} - > - - {t('onboarding.paywall.skipButton')} - - - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// MAIN ONBOARDING CONTROLLER -// ═══════════════════════════════════════════════════════════════════════════ - -const STEP_NAMES: Record = { - 1: 'problem', - 2: 'empathy', - 3: 'solution', - 4: 'wow', - 5: 'personalization', - 6: 'paywall', -} - -export default function OnboardingScreen() { - const router = useRouter() - const [step, setStep] = useState(1) - - // Personalization state - const [barriers, setBarriers] = useState([]) - const [name, setName] = useState('') - const [level, setLevel] = useState('beginner') - const [goal, setGoal] = useState('cardio') - const [frequency, setFrequency] = useState(3) - - const completeOnboarding = useUserStore((s) => s.completeOnboarding) - const setSubscription = useUserStore((s) => s.setSubscription) - - // Analytics: track time per step and total onboarding time - const onboardingStartTime = useRef(Date.now()) - const stepStartTime = useRef(Date.now()) - - // Track onboarding_started + first step viewed on mount - useEffect(() => { - trackScreen('onboarding') - track('onboarding_started') - track('onboarding_step_viewed', { step: 1, step_name: STEP_NAMES[1] }) - }, []) - - const finishOnboarding = useCallback( - (plan: 'free' | 'premium-monthly' | 'premium-yearly') => { - const totalTime = Date.now() - onboardingStartTime.current - - track('onboarding_completed', { - plan, - total_time_ms: totalTime, - steps_completed: step, - }) - - const userData = { - name: name.trim() || 'Athlete', - fitnessLevel: level, - goal, - weeklyFrequency: frequency, - barriers, - } - - completeOnboarding(userData) - - // Identify user in PostHog for session replay linking - const userId = `user_${Date.now()}` // In production, use actual user ID from backend - identifyUser(userId, { - name: userData.name, - fitness_level: level, - fitness_goal: goal, - weekly_frequency: frequency, - subscription_plan: plan, - onboarding_completed_at: new Date().toISOString(), - barriers: barriers.join(','), - }) - - if (plan !== 'free') { - setSubscription(plan) - } - router.replace('/') - }, - [name, level, goal, frequency, barriers, step] - ) - - const nextStep = useCallback(() => { - const now = Date.now() - const timeOnStep = now - stepStartTime.current - - // Track step completed - track('onboarding_step_completed', { - step, - step_name: STEP_NAMES[step], - time_on_step_ms: timeOnStep, - }) - - // Track specific step data - if (step === 2) { - track('onboarding_barriers_selected', { - barriers, - barrier_count: barriers.length, - }) - } - if (step === 5) { - track('onboarding_personalization_completed', { - name_provided: name.trim().length > 0, - level, - goal, - frequency, - }) - } - - const next = Math.min(step + 1, TOTAL_STEPS) - stepStartTime.current = now - - // Track next step viewed - track('onboarding_step_viewed', { step: next, step_name: STEP_NAMES[next] }) - - setStep(next) - }, [step, barriers, name, level, goal, frequency]) - - const prevStep = useCallback(() => { - if (step > 1) { - const prev = step - 1 - stepStartTime.current = Date.now() - track('onboarding_step_back', { from_step: step, to_step: prev }) - setStep(prev) - } - }, [step]) - - const renderStep = () => { - switch (step) { - case 1: - return - case 2: - return ( - - ) - case 3: - return - case 4: - return - case 5: - return ( - - ) - case 6: - return ( - finishOnboarding(plan)} - onSkip={() => finishOnboarding('free')} - /> - ) - default: - return null - } - } - - return ( - - {renderStep()} - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// STYLES -// ═══════════════════════════════════════════════════════════════════════════ - -function createStyles(colors: ThemeColors) { - return StyleSheet.create({ - // Layout helpers - screenCenter: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - screenFull: { - flex: 1, - }, - titleCenter: { - textAlign: 'center', - }, - subtitle: { - textAlign: 'center', - }, - bottomAction: { - position: 'absolute', - bottom: SPACING[4], - left: 0, - right: 0, - }, - - // CTA Button - ctaButton: { - height: LAYOUT.BUTTON_HEIGHT, - backgroundColor: GREEN[500], - borderRadius: RADIUS.MD, - alignItems: 'center', - justifyContent: 'center', - }, - ctaButtonDisabled: { - backgroundColor: colors.bg.elevated, - }, - - // ── Screen 2: Barriers ── - barrierGrid: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: SPACING[3], - marginTop: SPACING[8], - justifyContent: 'center', - }, - barrierCard: { - width: (SCREEN_WIDTH - LAYOUT.SCREEN_PADDING * 2 - SPACING[3]) / 2, - paddingVertical: SPACING[6], - alignItems: 'center', - borderRadius: RADIUS.LG, - backgroundColor: NAVY[800], - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - }, - barrierCardSelected: { - borderColor: GREEN.BORDER, - backgroundColor: GREEN.DIM, - }, - - // ── Screen 3: Comparison ── - comparisonContainer: { - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'flex-end', - marginTop: SPACING[10], - paddingHorizontal: SPACING[8], - gap: SPACING[4], - }, - barColumn: { - alignItems: 'center', - flex: 1, - }, - barTrack: { - width: 60, - height: 160, - backgroundColor: colors.bg.overlay1, - borderRadius: RADIUS.SM, - overflow: 'hidden', - marginVertical: SPACING[3], - justifyContent: 'flex-end', - }, - barFill: { - width: '100%', - borderRadius: RADIUS.SM, - }, - barTabata: { - backgroundColor: GREEN[500], - }, - barCardio: { - backgroundColor: PHASE.REST, - }, - vsContainer: { - paddingBottom: 80, - }, - citation: { - marginTop: SPACING[8], - paddingHorizontal: SPACING[4], - }, - citationText: { - textAlign: 'center', - fontStyle: 'italic', - lineHeight: 20, - }, - citationAuthor: { - textAlign: 'center', - marginTop: SPACING[2], - }, - - // ── Screen 5: Personalization ── - personalizationContent: { - paddingBottom: SPACING[10], - }, - fieldGroup: { - marginTop: SPACING[6], - }, - fieldLabel: { - letterSpacing: 1.5, - marginBottom: SPACING[2], - }, - textInput: { - height: LAYOUT.BUTTON_HEIGHT_SM, - backgroundColor: colors.bg.surface, - borderRadius: RADIUS.MD, - paddingHorizontal: SPACING[4], - color: colors.text.primary, - fontSize: 17, - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - }, - segmentRow: { - flexDirection: 'row', - backgroundColor: colors.bg.surface, - borderRadius: RADIUS.MD, - padding: 3, - gap: 2, - }, - segmentButton: { - flex: 1, - height: 36, - alignItems: 'center', - justifyContent: 'center', - borderRadius: RADIUS.SM, - }, - segmentButtonActive: { - backgroundColor: colors.bg.elevated, - }, - readyMessage: { - textAlign: 'center', - marginTop: SPACING[6], - }, - - // ── Screen 6: Paywall ── - paywallContent: { - paddingBottom: SPACING[10], - }, - featuresList: { - marginTop: SPACING[8], - gap: SPACING[4], - }, - featureRow: { - flexDirection: 'row', - alignItems: 'center', - }, - pricingCards: { - flexDirection: 'row', - gap: SPACING[3], - marginTop: SPACING[8], - }, - pricingCard: { - flex: 1, - paddingVertical: SPACING[5], - alignItems: 'center', - justifyContent: 'center', - borderRadius: RADIUS.LG, - backgroundColor: NAVY[800], - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - }, - pricingCardSelected: { - borderColor: GREEN.BORDER, - borderWidth: 2, - backgroundColor: GREEN.DIM, - }, - bestValueBadge: { - backgroundColor: GREEN[500], - paddingHorizontal: SPACING[3], - paddingVertical: SPACING[1], - borderRadius: RADIUS.SM, - marginBottom: SPACING[2], - }, - trialButton: { - height: LAYOUT.BUTTON_HEIGHT, - backgroundColor: GREEN[500], - borderRadius: RADIUS.MD, - alignItems: 'center', - justifyContent: 'center', - marginTop: SPACING[6], - }, - guarantees: { - alignItems: 'center', - marginTop: SPACING[4], - }, - restoreButton: { - alignItems: 'center', - paddingVertical: SPACING[3], - }, - skipButton: { - alignItems: 'center', - paddingVertical: SPACING[5], - marginTop: SPACING[2], - }, - }) -} - -// ── Screen 4: Feature List Styles ── -function createWowStyles(colors: ThemeColors) { - return StyleSheet.create({ - list: { - gap: SPACING[5], - marginTop: SPACING[4], - }, - row: { - flexDirection: 'row', - alignItems: 'center', - gap: SPACING[4], - }, - iconCircle: { - width: 44, - height: 44, - borderRadius: RADIUS.FULL, - alignItems: 'center', - justifyContent: 'center', - }, - textCol: { - flex: 1, - }, - }) -} diff --git a/app/paywall.tsx b/app/paywall.tsx deleted file mode 100644 index 0f5fc5a..0000000 --- a/app/paywall.tsx +++ /dev/null @@ -1,460 +0,0 @@ -/** - * TabataFit Paywall Screen - * Premium subscription purchase flow - */ - -import React, { useMemo } from 'react' -import { - View, - StyleSheet, - ScrollView, - Pressable, -} from 'react-native' -import { useRouter } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { Icon, type IconName } from '@/src/shared/components/Icon' - -import { useTranslation } from 'react-i18next' -import { useHaptics, usePurchases } from '@/src/shared/hooks' -import { StyledText } from '@/src/shared/components/StyledText' -import { useThemeColors } from '@/src/shared/theme' -import { NativeButton } from '@/src/shared/components/native' -import type { ThemeColors } from '@/src/shared/theme/types' -import { SPACING } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { GREEN, NAVY, BORDER_COLORS } from '@/src/shared/constants/colors' - -// ═══════════════════════════════════════════════════════════════════════════ -// FEATURES LIST -// ═══════════════════════════════════════════════════════════════════════════ - -const PREMIUM_FEATURES: { icon: IconName; key: string }[] = [ - { icon: 'music.note.list', key: 'music' }, - { icon: 'infinity', key: 'workouts' }, - { icon: 'chart.bar.fill', key: 'stats' }, - { icon: 'flame.fill', key: 'calories' }, - { icon: 'bell.fill', key: 'reminders' }, - { icon: 'xmark.circle.fill', key: 'ads' }, -] - -// ═══════════════════════════════════════════════════════════════════════════ -// COMPONENTS -// ═══════════════════════════════════════════════════════════════════════════ - -interface PlanCardStyles { - planCard: object - planCardPressed: object - savingsBadge: object - savingsText: object - planInfo: object - planTitle: object - planPeriod: object - planPrice: object - checkmark: object -} - -function PlanCard({ - title, - price, - period, - savings, - isSelected, - onPress, - colors, - styles, -}: { - title: string - price: string - period: string - savings?: string - isSelected: boolean - onPress: () => void - colors: ThemeColors - styles: PlanCardStyles -}) { - const haptics = useHaptics() - - const handlePress = () => { - haptics.selection() - onPress() - } - - return ( - [ - styles.planCard, - isSelected && { borderColor: GREEN.BORDER }, - pressed && styles.planCardPressed, - { - backgroundColor: colors.bg.surface, - borderColor: isSelected ? GREEN.BORDER : BORDER_COLORS.DIM, - }, - ]} - > - {savings && ( - - {savings} - - )} - - - {title} - - - {period} - - - - {price} - - {isSelected && ( - - - - )} - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// MAIN SCREEN -// ═══════════════════════════════════════════════════════════════════════════ - -export default function PaywallScreen() { - const { t } = useTranslation('screens') - const router = useRouter() - const insets = useSafeAreaInsets() - const haptics = useHaptics() - const colors = useThemeColors() - const styles = useMemo(() => createStyles(colors), [colors]) - - // Extract plan card styles for the child component - const planCardStyles = useMemo( - () => ({ - planCard: styles.planCard, - planCardPressed: styles.planCardPressed, - savingsBadge: styles.savingsBadge, - savingsText: styles.savingsText, - planInfo: styles.planInfo, - planTitle: styles.planTitle, - planPeriod: styles.planPeriod, - planPrice: styles.planPrice, - checkmark: styles.checkmark, - }), - [styles], - ) - - const { - monthlyPackage, - annualPackage, - purchasePackage, - restorePurchases, - isLoading, - } = usePurchases() - - const [selectedPlan, setSelectedPlan] = React.useState<'monthly' | 'annual'>('annual') - - // Get prices from RevenueCat packages - const monthlyPrice = monthlyPackage?.product.priceString ?? '$4.99' - const annualPrice = annualPackage?.product.priceString ?? '$29.99' - const annualMonthlyEquivalent = annualPackage - ? (annualPackage.product.price / 12).toFixed(2) - : '2.49' - - const handlePurchase = async () => { - haptics.buttonTap() - const pkg = selectedPlan === 'monthly' ? monthlyPackage : annualPackage - if (!pkg) { - console.log('[Paywall] No package available for purchase') - return - } - - const result = await purchasePackage(pkg) - if (result.success) { - haptics.workoutComplete() - router.back() - } else if (!result.cancelled) { - console.log('[Paywall] Purchase error:', result.error) - } - } - - const handleRestore = async () => { - haptics.selection() - const restored = await restorePurchases() - if (restored) { - haptics.workoutComplete() - router.back() - } - } - - const handleClose = () => { - haptics.selection() - router.back() - } - - return ( - - {/* Close Button */} - - - - - - {/* Header */} - - - TabataFit+ - - - {t('paywall.subtitle')} - - - - {/* Features Grid */} - - {PREMIUM_FEATURES.map((feature) => ( - - - - - - {t(`paywall.features.${feature.key}`)} - - - ))} - - - {/* Plan Selection */} - - setSelectedPlan('annual')} - colors={colors} - styles={planCardStyles} - /> - setSelectedPlan('monthly')} - colors={colors} - styles={planCardStyles} - /> - - - {/* Price Note */} - {selectedPlan === 'annual' && ( - - {t('paywall.equivalent', { price: annualMonthlyEquivalent })} - - )} - - {/* CTA Button */} - - - {/* Restore & Terms */} - - - - - router.push('/terms')}> - - {t('paywall.termsLink')} - - - · - router.push('/terms')}> - - {t('paywall.privacyLink')} - - - - - - {t('paywall.terms')} - - - - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// STYLES -// ═══════════════════════════════════════════════════════════════════════════ - -function createStyles(colors: ThemeColors) { - return StyleSheet.create({ - container: { - flex: 1, - backgroundColor: colors.bg.base, - }, - closeButton: { - position: 'absolute', - top: SPACING[4], - right: SPACING[4], - width: 44, - height: 44, - alignItems: 'center', - justifyContent: 'center', - zIndex: 10, - }, - scrollView: { - flex: 1, - }, - scrollContent: { - paddingHorizontal: SPACING[5], - paddingTop: SPACING[8], - }, - header: { - alignItems: 'center', - }, - title: { - fontSize: 32, - fontWeight: '700', - color: colors.text.primary, - textAlign: 'center', - }, - subtitle: { - fontSize: 16, - color: colors.text.secondary, - textAlign: 'center', - marginTop: SPACING[2], - }, - featuresGrid: { - flexDirection: 'row', - flexWrap: 'wrap', - marginTop: SPACING[6], - marginHorizontal: -SPACING[2], - }, - featureItem: { - width: '33%', - alignItems: 'center', - paddingVertical: SPACING[3], - }, - featureIcon: { - width: 48, - height: 48, - borderRadius: 24, - alignItems: 'center', - justifyContent: 'center', - marginBottom: SPACING[2], - }, - featureText: { - fontSize: 13, - textAlign: 'center', - }, - plansContainer: { - marginTop: SPACING[6], - gap: SPACING[3], - }, - planCard: { - flexDirection: 'row', - alignItems: 'center', - borderRadius: RADIUS.LG, - padding: SPACING[4], - borderWidth: 2, - }, - planCardPressed: { - opacity: 0.8, - }, - savingsBadge: { - position: 'absolute', - top: -8, - right: SPACING[3], - backgroundColor: GREEN[500], - paddingHorizontal: SPACING[2], - paddingVertical: 2, - borderRadius: RADIUS.SM, - }, - savingsText: { - fontSize: 10, - fontWeight: '700', - color: colors.text.primary, - }, - planInfo: { - flex: 1, - }, - planTitle: { - fontSize: 16, - fontWeight: '600', - }, - planPeriod: { - fontSize: 13, - marginTop: 2, - }, - planPrice: { - fontSize: 20, - fontWeight: '700', - }, - checkmark: { - marginLeft: SPACING[2], - }, - priceNote: { - fontSize: 13, - textAlign: 'center', - marginTop: SPACING[3], - }, - ctaButton: { - borderRadius: RADIUS.LG, - marginTop: SPACING[6], - paddingVertical: SPACING[4], - alignItems: 'center', - backgroundColor: GREEN[500], - }, - ctaButtonDisabled: { - opacity: 0.6, - }, - ctaText: { - fontSize: 17, - fontWeight: '600', - }, - footer: { - marginTop: SPACING[5], - alignItems: 'center', - gap: SPACING[4], - }, - restoreText: { - fontSize: 14, - }, - termsText: { - fontSize: 11, - textAlign: 'center', - lineHeight: 18, - paddingHorizontal: SPACING[4], - }, - legalLinks: { - flexDirection: 'row', - alignItems: 'center', - gap: SPACING[1], - }, - }) -} diff --git a/app/player/CLAUDE.md b/app/player/CLAUDE.md deleted file mode 100644 index ab2a248..0000000 --- a/app/player/CLAUDE.md +++ /dev/null @@ -1,27 +0,0 @@ - -# Recent Activity - - - -### Feb 19, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #5000 | 9:35 AM | 🔵 | Reviewed Player Screen Implementation | ~522 | -| #4912 | 8:16 AM | 🔵 | Found doneButton component in player screen | ~104 | - -### Apr 9, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #5997 | 10:46 AM | 🟣 | Tabata Kine programs system fully implemented with four programs and specialized UI | ~563 | -| #5996 | 10:41 AM | 🟣 | Tabata Kine programs system implementation completed | ~460 | -| #5975 | 9:43 AM | 🟣 | Player screen updated to support kiné session detection and routing | ~316 | - -### Apr 10, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #6005 | 10:02 AM | 🔵 | Player Screen Routing Between Kine and Legacy Workouts | ~335 | -| #5998 | 9:52 AM | 🟣 | Tabata Kine programs system implementation completed | ~711 | - \ No newline at end of file diff --git a/app/player/[id].tsx b/app/player/[id].tsx deleted file mode 100644 index 30f52d1..0000000 --- a/app/player/[id].tsx +++ /dev/null @@ -1,82 +0,0 @@ -/** - * TabataFit Player Screen - * Loads a WorkoutProgram from Supabase and renders the Tabata player. - */ - -import React from 'react' -import { View, Text } from 'react-native' -import { useLocalSearchParams } from 'expo-router' -import { - isWorkoutProgramId, - parseWorkoutProgramId, - fetchProgramById, - workoutProgramToTabataSession, -} from '@/src/shared/data/workoutPrograms' -import { TabataPlayerScreen } from '@/src/features/player/TabataPlayerScreen' -import type { TabataSession } from '@/src/shared/types/program' -import type { WorkoutProgram } from '@/src/shared/types/workoutProgram' -import { NAVY, TEXT } from '@/src/shared/constants/colors' - -export default function PlayerScreen() { - const { id } = useLocalSearchParams<{ id: string }>() - const sessionId = id ?? '' - - if (!isWorkoutProgramId(sessionId)) { - return - } - - return -} - -function WorkoutProgramPlayerScreen({ compositeId }: { compositeId: string }) { - const [state, setState] = React.useState< - | { status: 'loading' } - | { status: 'error' } - | { status: 'ready'; session: TabataSession; program: WorkoutProgram } - >({ status: 'loading' }) - - React.useEffect(() => { - let cancelled = false - async function load() { - const parsed = parseWorkoutProgramId(compositeId) - if (!parsed) { - if (!cancelled) setState({ status: 'error' }) - return - } - const program = await fetchProgramById(parsed.programId) - if (cancelled) return - if (!program) { - setState({ status: 'error' }) - return - } - setState({ - status: 'ready', - session: workoutProgramToTabataSession(program), - program, - }) - } - load() - return () => { - cancelled = true - } - }, [compositeId]) - - if (state.status === 'loading') return - if (state.status === 'error') return - return -} - -function Message({ text }: { text: string }) { - return ( - - {text} - - ) -} diff --git a/app/privacy.tsx b/app/privacy.tsx deleted file mode 100644 index 9f3d213..0000000 --- a/app/privacy.tsx +++ /dev/null @@ -1,212 +0,0 @@ -/** - * TabataFit Privacy Policy Screen - * Required for App Store submission - */ - -import React from 'react' -import { View, ScrollView, StyleSheet, Text, Pressable } from 'react-native' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useRouter } from 'expo-router' -import { Icon } from '@/src/shared/components/Icon' - -import { useTranslation } from 'react-i18next' -import { useHaptics } from '@/src/shared/hooks' -import { darkColors, BRAND } from '@/src/shared/theme' -import { SPACING } from '@/src/shared/constants/spacing' -import { TYPOGRAPHY } from '@/src/shared/constants/typography' - -export default function PrivacyPolicyScreen() { - const { t } = useTranslation('screens') - const router = useRouter() - const insets = useSafeAreaInsets() - const haptics = useHaptics() - - const handleClose = () => { - haptics.selection() - router.back() - } - - return ( - - {/* Header */} - - - - - {t('privacy.title')} - - - - -
- -
- {t('privacy.intro.content')} -
- -
- {t('privacy.dataCollection.content')} - -
- -
- {t('privacy.usage.content')} -
- -
- {t('privacy.sharing.content')} -
- -
- {t('privacy.security.content')} -
- -
- {t('privacy.rights.content')} -
- -
- {t('privacy.contact.content')} - privacy@tabatafit.app -
- - - - TabataFit v1.0.0 - - - - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// HELPER COMPONENTS -// ═══════════════════════════════════════════════════════════════════════════ - -function Section({ - title, - children, -}: { - title: string - children?: React.ReactNode -}) { - return ( - - {title} - {children} - - ) -} - -function Paragraph({ children }: { children: string }) { - return {children} -} - -function BulletList({ items }: { items: string[] }) { - return ( - - {items.map((item, index) => ( - - - {item} - - ))} - - ) -} - -// ═══════════════════════════════════════════════════════════════════════════ -// STYLES -// ═══════════════════════════════════════════════════════════════════════════ - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: darkColors.bg.base, - }, - header: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: SPACING[4], - paddingVertical: SPACING[3], - borderBottomWidth: 1, - borderBottomColor: darkColors.border.dim, - }, - backButton: { - width: 44, - height: 44, - alignItems: 'center', - justifyContent: 'center', - }, - headerTitle: { - ...TYPOGRAPHY.HEADLINE, - color: darkColors.text.primary, - }, - scrollView: { - flex: 1, - }, - content: { - paddingHorizontal: SPACING[5], - paddingTop: SPACING[4], - }, - section: { - marginBottom: SPACING[6], - }, - sectionTitle: { - ...TYPOGRAPHY.TITLE_3, - fontWeight: '700', - color: darkColors.text.primary, - marginBottom: SPACING[3], - }, - paragraph: { - ...TYPOGRAPHY.BODY, - lineHeight: 22, - color: darkColors.text.secondary, - }, - bulletList: { - marginTop: SPACING[3], - }, - bulletItem: { - flexDirection: 'row', - marginBottom: SPACING[2], - }, - bullet: { - ...TYPOGRAPHY.BODY, - color: BRAND.PRIMARY, - marginRight: SPACING[2], - }, - bulletText: { - flex: 1, - ...TYPOGRAPHY.BODY, - lineHeight: 22, - color: darkColors.text.secondary, - }, - email: { - ...TYPOGRAPHY.BODY, - color: BRAND.PRIMARY, - marginTop: SPACING[2], - }, - footer: { - marginTop: SPACING[8], - alignItems: 'center', - }, - footerText: { - fontSize: 13, - color: darkColors.text.tertiary, - }, -}) diff --git a/app/program/CLAUDE.md b/app/program/CLAUDE.md deleted file mode 100644 index f4ebf54..0000000 --- a/app/program/CLAUDE.md +++ /dev/null @@ -1,28 +0,0 @@ - -# Recent Activity - - - -### Apr 9, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #5997 | 10:46 AM | 🟣 | Tabata Kine programs system fully implemented with four programs and specialized UI | ~563 | -| #5996 | 10:41 AM | 🟣 | Tabata Kine programs system implementation completed | ~460 | -| #5978 | 9:53 AM | 🟣 | Kine program detail screen implemented | ~452 | - -### Apr 10, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #6027 | 10:08 AM | 🔵 | Program Detail Screen Re-Referenced for Kine Program Display | ~458 | -| #6004 | 10:02 AM | 🔵 | Kine Program Detail Screen Architecture | ~337 | -| #5998 | 9:52 AM | 🟣 | Tabata Kine programs system implementation completed | ~711 | - -### Apr 11, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #6150 | 7:49 PM | 🔵 | Program detail unlock button contains hardcoded orange | ~253 | -| #6134 | 7:43 PM | 🔄 | Program detail screen added withOpacity import | ~237 | - \ No newline at end of file diff --git a/app/program/[id].tsx b/app/program/[id].tsx deleted file mode 100644 index bdcb43e..0000000 --- a/app/program/[id].tsx +++ /dev/null @@ -1,296 +0,0 @@ -/** - * Workout Program Detail Screen - * Shows Warmup → 3 Tabatas → Stretch preview, CTA to player. - */ - -import { useEffect, useState } from 'react' -import { View, Text, StyleSheet, ScrollView, Pressable, ActivityIndicator } from 'react-native' -import { Stack, useRouter, useLocalSearchParams } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useTranslation } from 'react-i18next' - -import { Icon } from '@/src/shared/components/Icon' -import { fetchProgramById, buildWorkoutProgramId } from '@/src/shared/data/workoutPrograms' -import type { WorkoutProgram } from '@/src/shared/types/workoutProgram' -import { BODY_ZONE_META, LEVEL_META } from '@/src/shared/types/workoutProgram' -import { useUserStore } from '@/src/shared/stores/userStore' -import { TYPOGRAPHY } from '@/src/shared/constants/typography' -import { SPACING } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { TEXT, NAVY, GREEN, BORDER_COLORS, DARK } from '@/src/shared/constants/colors' -import { withOpacity } from '@/src/shared/utils/color' - -const FALLBACK_ACCENT = '#FF6B35' - -export default function WorkoutProgramDetailScreen() { - const { id } = useLocalSearchParams<{ id: string }>() - const router = useRouter() - const insets = useSafeAreaInsets() - const { t } = useTranslation() - - const [program, setProgram] = useState(null) - const [loading, setLoading] = useState(true) - - const isPremium = useUserStore(s => s.profile.subscription) !== 'free' - - useEffect(() => { - let cancelled = false - setLoading(true) - fetchProgramById(id) - .then(p => { - if (!cancelled) setProgram(p) - }) - .finally(() => { - if (!cancelled) setLoading(false) - }) - return () => { - cancelled = true - } - }, [id]) - - if (loading) { - return ( - - - - - ) - } - - if (!program) { - return ( - - - {t('screens:program.notFound')} - - ) - } - - const accent = program.accentColor ?? BODY_ZONE_META[program.bodyZone].color ?? FALLBACK_ACCENT - const level = LEVEL_META[program.level] - const zone = BODY_ZONE_META[program.bodyZone] - const canAccess = program.isFree || isPremium - - const handleStart = () => { - if (!canAccess) { - router.push('/paywall') - return - } - router.push(`/player/${buildWorkoutProgramId(program.id)}`) - } - - const warmupMinutes = Math.round(program.warmup.totalDuration / 60) - const stretchMinutes = Math.round(program.stretch.totalDuration / 60) - - return ( - - - - - {/* Hero */} - - - - - {program.title} - {program.description && {program.description}} - - - - {level.label} - - - {zone.label} - - {!program.isFree && ( - - {t('screens:home.premiumBadge')} - - )} - - - - - - - - - - {/* Warmup */} -
- {program.warmup.exercises.map((ex, i) => ( - - ))} -
- - {/* Tabatas */} - {program.tabatas.map((tabata, i) => ( -
- - -
- ))} - - {/* Stretch */} -
- {program.stretch.exercises.map((ex, i) => ( - - ))} -
-
- - - - - {canAccess ? t('screens:program.startSession') : t('screens:program.unlockPremium')} - - - -
- ) -} - -function Stat({ value, label }: { value: number; label: string }) { - return ( - - {value} - {label} - - ) -} - -function Section({ - title, - subtitle, - accent, - children, -}: { - title: string - subtitle: string - accent: string - children: React.ReactNode -}) { - return ( - - - - {title} - {subtitle} - - {children} - - ) -} - -function Row({ label, detail }: { label: string; detail: string }) { - return ( - - - {label} - - {detail} - - ) -} - -const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: NAVY[900] }, - center: { alignItems: 'center', justifyContent: 'center' }, - scroll: { flex: 1 }, - errorText: { color: TEXT.SECONDARY, ...TYPOGRAPHY.BODY }, - - hero: { padding: SPACING[6], alignItems: 'center' }, - iconCircle: { - width: 64, - height: 64, - borderRadius: 32, - alignItems: 'center', - justifyContent: 'center', - marginBottom: SPACING[3], - }, - title: { ...TYPOGRAPHY.LARGE_TITLE, color: TEXT.PRIMARY, textAlign: 'center' }, - description: { - ...TYPOGRAPHY.BODY, - color: TEXT.SECONDARY, - textAlign: 'center', - marginTop: SPACING[2], - lineHeight: 22, - }, - - badgeRow: { flexDirection: 'row', gap: SPACING[2], marginTop: SPACING[3], flexWrap: 'wrap', justifyContent: 'center' }, - badge: { paddingHorizontal: SPACING[2], paddingVertical: 3, borderRadius: RADIUS.SM, borderWidth: 1 }, - badgeText: { ...TYPOGRAPHY.LABEL }, - - statsRow: { flexDirection: 'row', marginTop: SPACING[6], gap: SPACING[8] }, - statItem: { alignItems: 'center' }, - statValue: { ...TYPOGRAPHY.TITLE_2, color: TEXT.PRIMARY }, - statLabel: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.TERTIARY, marginTop: 2 }, - - section: { paddingHorizontal: SPACING[5], marginTop: SPACING[6] }, - sectionHeader: { flexDirection: 'row', alignItems: 'center', gap: SPACING[2], marginBottom: SPACING[3] }, - sectionDot: { width: 8, height: 8, borderRadius: 4 }, - sectionTitle: { ...TYPOGRAPHY.HEADLINE, color: TEXT.PRIMARY, flex: 1 }, - sectionSubtitle: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.TERTIARY }, - - sectionBody: { - backgroundColor: NAVY[800], - borderRadius: RADIUS.MD, - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - overflow: 'hidden', - }, - row: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: SPACING[3], - paddingVertical: SPACING[3], - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: BORDER_COLORS.DIM, - gap: SPACING[3], - }, - rowLabel: { ...TYPOGRAPHY.SUBHEADLINE, color: TEXT.PRIMARY, flex: 1 }, - rowDetail: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.TERTIARY }, - - ctaContainer: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, - paddingHorizontal: SPACING[5], - paddingTop: SPACING[3], - backgroundColor: DARK.SCRIM, - borderTopWidth: 1, - borderTopColor: BORDER_COLORS.DIM, - }, - ctaButton: { height: 52, borderRadius: RADIUS.MD, alignItems: 'center', justifyContent: 'center' }, - ctaText: { ...TYPOGRAPHY.BUTTON_MEDIUM, color: NAVY[900], letterSpacing: 0.5 }, -}) diff --git a/app/settings.tsx b/app/settings.tsx deleted file mode 100644 index 2289fa1..0000000 --- a/app/settings.tsx +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Settings FormSheet - * Profile, preferences, premium, legal, reset progress. - */ - -import { View, Text, StyleSheet, ScrollView, Pressable, Switch, Alert } from 'react-native' -import { useRouter } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useTranslation } from 'react-i18next' - -import { Icon, type IconName } from '@/src/shared/components/Icon' -import { useUserStore } from '@/src/shared/stores/userStore' -import { useProgressStore } from '@/src/shared/stores/progressStore' -import { TYPOGRAPHY } from '@/src/shared/constants/typography' -import { SPACING } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { TEXT, NAVY, GREEN, BORDER_COLORS } from '@/src/shared/constants/colors' - -export default function SettingsScreen() { - const router = useRouter() - const insets = useSafeAreaInsets() - const { t } = useTranslation() - - const profile = useUserStore(s => s.profile) - const settings = useUserStore(s => s.settings) - const updateSettings = useUserStore(s => s.updateSettings) - const resetProgress = useProgressStore(s => s.resetProgress) - - const isPremium = profile.subscription !== 'free' - - const handleResetProgress = () => { - Alert.alert( - t('screens:settings.resetTitle'), - t('screens:settings.resetMessage'), - [ - { text: t('common:cancel'), style: 'cancel' }, - { - text: t('screens:settings.resetConfirm'), - style: 'destructive', - onPress: () => { - resetProgress() - }, - }, - ], - ) - } - - return ( - - {t('screens:settings.title')} - - {/* Profile */} -
- - - {!isPremium && ( - router.push('/paywall')} - accent={GREEN[500]} - /> - )} -
- - {/* Preferences */} -
- updateSettings({ haptics: v })} - /> - updateSettings({ soundEffects: v })} - /> - updateSettings({ voiceCoaching: v })} - /> - updateSettings({ musicEnabled: v })} - /> -
- - {/* Legal */} -
- router.push('/terms')} /> - router.push('/privacy')} /> -
- - {/* Danger */} -
- - - {t('screens:settings.resetProgress')} - -
- - {t('screens:settings.version')} -
- ) -} - -function Section({ title, children }: { title: string; children: React.ReactNode }) { - return ( - - {title} - {children} - - ) -} - -function Row({ label, value, accent }: { label: string; value: string; accent?: string }) { - return ( - - {label} - {value} - - ) -} - -function SwitchRow({ - label, - value, - onChange, -}: { - label: string - value: boolean - onChange: (value: boolean) => void -}) { - return ( - - {label} - - - ) -} - -function LinkRow({ - icon, - label, - onPress, - accent, -}: { - icon: IconName - label: string - onPress: () => void - accent?: string -}) { - return ( - - - - {label} - - - - ) -} - -const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: NAVY[900] }, - header: { ...TYPOGRAPHY.LARGE_TITLE, color: TEXT.PRIMARY, marginBottom: SPACING[4] }, - - section: { marginBottom: SPACING[6] }, - sectionTitle: { - ...TYPOGRAPHY.CAPTION_1, - color: TEXT.TERTIARY, - textTransform: 'uppercase', - letterSpacing: 0.5, - marginBottom: SPACING[2], - paddingHorizontal: SPACING[2], - }, - sectionBody: { - backgroundColor: NAVY[800], - borderRadius: RADIUS.MD, - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - overflow: 'hidden', - }, - - row: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: SPACING[4], - paddingVertical: SPACING[3], - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: BORDER_COLORS.DIM, - gap: SPACING[3], - }, - rowLabel: { ...TYPOGRAPHY.BODY, color: TEXT.PRIMARY }, - rowValue: { ...TYPOGRAPHY.BODY, color: TEXT.SECONDARY }, - linkLeft: { flexDirection: 'row', alignItems: 'center', gap: SPACING[3] }, - - dangerRow: { - flexDirection: 'row', - alignItems: 'center', - gap: SPACING[3], - paddingHorizontal: SPACING[4], - paddingVertical: SPACING[3], - }, - dangerText: { ...TYPOGRAPHY.BODY, color: '#FF453A' }, - - version: { - ...TYPOGRAPHY.CAPTION_1, - color: TEXT.TERTIARY, - textAlign: 'center', - marginTop: SPACING[4], - }, -}) diff --git a/app/terms.tsx b/app/terms.tsx deleted file mode 100644 index f695e00..0000000 --- a/app/terms.tsx +++ /dev/null @@ -1,106 +0,0 @@ -/** - * TabataFit Terms of Service Screen - * Features: F-027, F-029, F-100, F-129 - */ - -import { useMemo } from 'react' -import { View, StyleSheet, ScrollView } from 'react-native' -import { Stack } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useTranslation } from 'react-i18next' -import { StyledText } from '@/src/shared/components/StyledText' -import { useThemeColors } from '@/src/shared/theme' -import type { ThemeColors } from '@/src/shared/theme/types' -import { SPACING, LAYOUT } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { NAVY, BORDER_COLORS, GREEN } from '@/src/shared/constants/colors' - -const SECTIONS = [ - 'acceptance', - 'service', - 'subscriptions', - 'cancellation', - 'liability', - 'contact', -] as const - -export default function TermsScreen() { - const { t } = useTranslation() - const colors = useThemeColors() - const insets = useSafeAreaInsets() - const styles = useMemo(() => createStyles(colors), [colors]) - - return ( - <> - - - - {t('screens:terms.lastUpdated')} - - - {SECTIONS.map((section) => ( - - - {t(`screens:terms.${section}.title`)} - - - {t(`screens:terms.${section}.content`)} - - - ))} - - - support@tabatafit.app - - - - ) -} - -function createStyles(colors: ThemeColors) { - return StyleSheet.create({ - container: { - flex: 1, - backgroundColor: colors.bg.base, - }, - content: { - paddingHorizontal: LAYOUT.SCREEN_PADDING, - paddingTop: SPACING[4], - gap: SPACING[6], - }, - lastUpdated: { - color: colors.text.tertiary, - }, - section: { - gap: SPACING[2], - }, - sectionTitle: { - color: colors.text.primary, - }, - sectionContent: { - color: colors.text.secondary, - lineHeight: 24, - }, - email: { - color: GREEN[500], - textAlign: 'center', - marginTop: SPACING[4], - }, - }) -} diff --git a/app/zone/[bodyZone].tsx b/app/zone/[bodyZone].tsx deleted file mode 100644 index 2575d0c..0000000 --- a/app/zone/[bodyZone].tsx +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Body Zone Detail Screen - * Segmented level selector (Beginner default) + program list filtered by zone+level. - */ - -import { useEffect, useMemo, useState } from 'react' -import { View, Text, StyleSheet, ScrollView, Pressable, ActivityIndicator } from 'react-native' -import { Stack, useRouter, useLocalSearchParams } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useTranslation } from 'react-i18next' - -import { Icon } from '@/src/shared/components/Icon' -import { fetchProgramsByBodyZone } from '@/src/shared/data/workoutPrograms' -import { - BODY_ZONE_META, - LEVEL_META, - type BodyZone, - type ProgramLevel, - type WorkoutProgram, -} from '@/src/shared/types/workoutProgram' -import { useProgressStore } from '@/src/shared/stores/progressStore' -import { useUserStore } from '@/src/shared/stores/userStore' -import { TYPOGRAPHY } from '@/src/shared/constants/typography' -import { SPACING } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { TEXT, NAVY, GREEN, BORDER_COLORS } from '@/src/shared/constants/colors' -import { withOpacity } from '@/src/shared/utils/color' - -const LEVELS: ProgramLevel[] = ['Beginner', 'Intermediate', 'Advanced'] -const VALID_ZONES: BodyZone[] = ['upper-body', 'lower-body', 'full-body'] - -export default function BodyZoneScreen() { - const { bodyZone } = useLocalSearchParams<{ bodyZone: string }>() - const router = useRouter() - const insets = useSafeAreaInsets() - const { t } = useTranslation() - - const zone = (VALID_ZONES.includes(bodyZone as BodyZone) ? bodyZone : 'full-body') as BodyZone - const meta = BODY_ZONE_META[zone] - - const [programs, setPrograms] = useState([]) - const [loading, setLoading] = useState(true) - const [selectedLevel, setSelectedLevel] = useState('Beginner') - - const isProgramCompleted = useProgressStore(s => s.isProgramCompleted) - const isPremium = useUserStore(s => s.profile.subscription) !== 'free' - - useEffect(() => { - let cancelled = false - setLoading(true) - fetchProgramsByBodyZone(zone) - .then(list => { - if (!cancelled) setPrograms(list) - }) - .finally(() => { - if (!cancelled) setLoading(false) - }) - return () => { - cancelled = true - } - }, [zone]) - - const filtered = useMemo( - () => programs.filter(p => p.level === selectedLevel).sort((a, b) => a.sortOrder - b.sortOrder), - [programs, selectedLevel], - ) - - return ( - - - - - {/* Zone header */} - - - - - - {meta.label} - {t('screens:zone.chooseLevel')} - - - - {/* Level segmented */} - - {LEVELS.map(level => { - const active = selectedLevel === level - const levelMeta = LEVEL_META[level] - return ( - setSelectedLevel(level)} - style={[ - styles.segment, - active && { - backgroundColor: withOpacity(levelMeta.color, 0.2), - borderColor: levelMeta.color, - }, - ]} - > - - {levelMeta.label} - - - ) - })} - - - {/* Program list */} - {loading ? ( - - ) : filtered.length === 0 ? ( - {t('screens:zone.emptyPrograms')} - ) : ( - - {filtered.map(program => ( - router.push(`/program/${program.id}`)} - /> - ))} - - )} - - - ) -} - -function ProgramCard({ - program, - completed, - locked, - onPress, -}: { - program: WorkoutProgram - completed: boolean - locked: boolean - onPress: () => void -}) { - const accent = program.accentColor ?? BODY_ZONE_META[program.bodyZone].color - return ( - [styles.programCard, pressed && { opacity: 0.85 }]} - > - - - - {program.title} - - - {program.estimatedDuration} min · {program.tabatas.length} tabatas · {program.estimatedCalories} cal - - - {completed && } - {locked && } - {!completed && !locked && } - - ) -} - -const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: NAVY[900] }, - scroll: { flex: 1 }, - - zoneHeader: { - flexDirection: 'row', - alignItems: 'center', - gap: SPACING[3], - padding: SPACING[4], - borderRadius: RADIUS.LG, - marginBottom: SPACING[5], - }, - iconCircle: { - width: 56, - height: 56, - borderRadius: 28, - alignItems: 'center', - justifyContent: 'center', - }, - zoneTitle: { ...TYPOGRAPHY.TITLE_2, color: TEXT.PRIMARY }, - zoneSubtitle: { ...TYPOGRAPHY.SUBHEADLINE, color: TEXT.SECONDARY, marginTop: 2 }, - - segmented: { - flexDirection: 'row', - backgroundColor: NAVY[800], - borderRadius: RADIUS.MD, - padding: 4, - gap: 4, - marginBottom: SPACING[5], - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - }, - segment: { - flex: 1, - paddingVertical: SPACING[2], - borderRadius: RADIUS.SM, - alignItems: 'center', - borderWidth: 1, - borderColor: 'transparent', - }, - segmentText: { ...TYPOGRAPHY.SUBHEADLINE, color: TEXT.SECONDARY }, - - programList: { gap: SPACING[3] }, - programCard: { - flexDirection: 'row', - alignItems: 'center', - gap: SPACING[3], - padding: SPACING[4], - backgroundColor: NAVY[800], - borderRadius: RADIUS.MD, - borderWidth: 1, - borderColor: BORDER_COLORS.DIM, - }, - programDot: { width: 10, height: 10, borderRadius: 5 }, - programTitle: { ...TYPOGRAPHY.HEADLINE, color: TEXT.PRIMARY }, - programMeta: { ...TYPOGRAPHY.CAPTION_1, color: TEXT.TERTIARY, marginTop: 2 }, - - empty: { - ...TYPOGRAPHY.BODY, - color: TEXT.SECONDARY, - textAlign: 'center', - marginTop: SPACING[8], - }, -}) diff --git a/assets/audio/complete.wav b/assets/audio/complete.wav deleted file mode 100644 index 8a4dac7..0000000 Binary files a/assets/audio/complete.wav and /dev/null differ diff --git a/assets/audio/countdown.wav b/assets/audio/countdown.wav deleted file mode 100644 index 4540ad3..0000000 Binary files a/assets/audio/countdown.wav and /dev/null differ diff --git a/assets/audio/music/electro_high.mp3 b/assets/audio/music/electro_high.mp3 deleted file mode 100644 index 3650371..0000000 Binary files a/assets/audio/music/electro_high.mp3 and /dev/null differ diff --git a/assets/audio/music/electro_low.mp3 b/assets/audio/music/electro_low.mp3 deleted file mode 100644 index 5bd61b5..0000000 Binary files a/assets/audio/music/electro_low.mp3 and /dev/null differ diff --git a/assets/audio/phase-start.wav b/assets/audio/phase-start.wav deleted file mode 100644 index 9db0128..0000000 Binary files a/assets/audio/phase-start.wav and /dev/null differ diff --git a/assets/audio/sounds/beep_double.mp3 b/assets/audio/sounds/beep_double.mp3 deleted file mode 100644 index ce72c91..0000000 Binary files a/assets/audio/sounds/beep_double.mp3 and /dev/null differ diff --git a/assets/audio/sounds/beep_long.mp3 b/assets/audio/sounds/beep_long.mp3 deleted file mode 100644 index 68232bf..0000000 Binary files a/assets/audio/sounds/beep_long.mp3 and /dev/null differ diff --git a/assets/audio/sounds/beep_short.mp3 b/assets/audio/sounds/beep_short.mp3 deleted file mode 100644 index bf09b46..0000000 Binary files a/assets/audio/sounds/beep_short.mp3 and /dev/null differ diff --git a/assets/audio/sounds/bell.mp3 b/assets/audio/sounds/bell.mp3 deleted file mode 100644 index d7df1f4..0000000 Binary files a/assets/audio/sounds/bell.mp3 and /dev/null differ diff --git a/assets/audio/sounds/count_1.mp3 b/assets/audio/sounds/count_1.mp3 deleted file mode 100644 index 8e002bb..0000000 Binary files a/assets/audio/sounds/count_1.mp3 and /dev/null differ diff --git a/assets/audio/sounds/count_2.mp3 b/assets/audio/sounds/count_2.mp3 deleted file mode 100644 index 984e7f8..0000000 Binary files a/assets/audio/sounds/count_2.mp3 and /dev/null differ diff --git a/assets/audio/sounds/count_3.mp3 b/assets/audio/sounds/count_3.mp3 deleted file mode 100644 index 984e7f8..0000000 Binary files a/assets/audio/sounds/count_3.mp3 and /dev/null differ diff --git a/assets/audio/sounds/fanfare.mp3 b/assets/audio/sounds/fanfare.mp3 deleted file mode 100644 index 1e36c05..0000000 Binary files a/assets/audio/sounds/fanfare.mp3 and /dev/null differ diff --git a/assets/images/android-icon-background.png b/assets/images/android-icon-background.png deleted file mode 100644 index 5ffefc5..0000000 Binary files a/assets/images/android-icon-background.png and /dev/null differ diff --git a/assets/images/android-icon-foreground.png b/assets/images/android-icon-foreground.png deleted file mode 100644 index 3a9e501..0000000 Binary files a/assets/images/android-icon-foreground.png and /dev/null differ diff --git a/assets/images/android-icon-monochrome.png b/assets/images/android-icon-monochrome.png deleted file mode 100644 index 77484eb..0000000 Binary files a/assets/images/android-icon-monochrome.png and /dev/null differ diff --git a/assets/images/favicon.png b/assets/images/favicon.png deleted file mode 100644 index 408bd74..0000000 Binary files a/assets/images/favicon.png and /dev/null differ diff --git a/assets/images/icon.png b/assets/images/icon.png deleted file mode 100644 index d21f091..0000000 Binary files a/assets/images/icon.png and /dev/null differ diff --git a/assets/images/icon.svg b/assets/images/icon.svg deleted file mode 100644 index 516dd2b..0000000 --- a/assets/images/icon.svg +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/partial-react-logo.png b/assets/images/partial-react-logo.png deleted file mode 100644 index 66fd957..0000000 Binary files a/assets/images/partial-react-logo.png and /dev/null differ diff --git a/assets/images/react-logo.png b/assets/images/react-logo.png deleted file mode 100644 index 9d72a9f..0000000 Binary files a/assets/images/react-logo.png and /dev/null differ diff --git a/assets/images/react-logo@2x.png b/assets/images/react-logo@2x.png deleted file mode 100644 index 2229b13..0000000 Binary files a/assets/images/react-logo@2x.png and /dev/null differ diff --git a/assets/images/react-logo@3x.png b/assets/images/react-logo@3x.png deleted file mode 100644 index a99b203..0000000 Binary files a/assets/images/react-logo@3x.png and /dev/null differ diff --git a/assets/images/splash-icon.png b/assets/images/splash-icon.png deleted file mode 100644 index 03d6f6b..0000000 Binary files a/assets/images/splash-icon.png and /dev/null differ diff --git a/assets/mascot.gif b/assets/mascot.gif deleted file mode 100644 index 0c7f993..0000000 Binary files a/assets/mascot.gif and /dev/null differ diff --git a/assets/mascot.png b/assets/mascot.png deleted file mode 100644 index f34c32d..0000000 Binary files a/assets/mascot.png and /dev/null differ diff --git a/assets/mascot_bak.gif b/assets/mascot_bak.gif deleted file mode 100644 index bd9dcc5..0000000 Binary files a/assets/mascot_bak.gif and /dev/null differ diff --git a/assets/model.glb b/assets/model.glb deleted file mode 100644 index fc2a685..0000000 Binary files a/assets/model.glb and /dev/null differ diff --git a/eas.json b/eas.json deleted file mode 100644 index 78e3fa0..0000000 --- a/eas.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "cli": { - "version": ">= 16.0.1", - "appVersionSource": "remote" - }, - "build": { - "development": { - "developmentClient": true, - "distribution": "internal", - "ios": { - "simulator": true - } - }, - "preview": { - "distribution": "internal", - "ios": { - "resourceClass": "m-medium" - } - }, - "production": { - "autoIncrement": true, - "ios": { - "resourceClass": "m-medium" - } - } - }, - "submit": { - "production": { - "ios": { - "appleId": "millianlmx@icloud.com", - "ascAppId": "REPLACE_WITH_APP_STORE_CONNECT_APP_ID", - "appleTeamId": "REPLACE_WITH_APPLE_TEAM_ID" - } - } - } -} diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 5025da6..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,10 +0,0 @@ -// https://docs.expo.dev/guides/using-eslint/ -const { defineConfig } = require('eslint/config'); -const expoConfig = require('eslint-config-expo/flat'); - -module.exports = defineConfig([ - expoConfig, - { - ignores: ['dist/*'], - }, -]); diff --git a/fix_i18n.js b/fix_i18n.js deleted file mode 100644 index 428df9e..0000000 --- a/fix_i18n.js +++ /dev/null @@ -1,20 +0,0 @@ -const fs = require('fs'); - -const langs = [ - { code: 'fr', text: 'Prêt à tout casser aujourd\'hui ?' }, - { code: 'es', text: '¿Listo para arrasar hoy?' }, - { code: 'de', text: 'Bereit, heute alles zu geben?' } -]; - -langs.forEach(({ code, text }) => { - const path = `src/shared/i18n/locales/${code}/screens.json`; - if (fs.existsSync(path)) { - let content = fs.readFileSync(path, 'utf8'); - content = content.replace( - /"home":\s*\{/, - `"home": {\n "readyToCrush": "${text}",` - ); - fs.writeFileSync(path, content); - console.log(`Updated ${code}`); - } -}); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index abfbc96..0000000 --- a/package-lock.json +++ /dev/null @@ -1,15598 +0,0 @@ -{ - "name": "tabatafit", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "tabatafit", - "version": "1.0.0", - "dependencies": { - "@expo-google-fonts/dm-mono": "^0.4.2", - "@expo-google-fonts/dm-serif-display": "^0.4.2", - "@expo-google-fonts/inter": "^0.4.2", - "@expo-google-fonts/outfit": "^0.4.3", - "@expo/ui": "~0.2.0-beta.9", - "@react-native-async-storage/async-storage": "^2.2.0", - "@react-navigation/bottom-tabs": "^7.4.0", - "@react-navigation/elements": "^2.6.3", - "@react-navigation/native": "^7.1.8", - "@supabase/supabase-js": "^2.98.0", - "@tanstack/query-async-storage-persister": "^5.90.24", - "@tanstack/react-query": "^5.90.21", - "@tanstack/react-query-persist-client": "^5.90.24", - "expo": "~54.0.33", - "expo-application": "~7.0.8", - "expo-av": "~16.0.8", - "expo-blur": "~15.0.8", - "expo-constants": "~18.0.13", - "expo-device": "~8.0.10", - "expo-file-system": "~19.0.21", - "expo-font": "~14.0.11", - "expo-gl": "~16.0.10", - "expo-haptics": "~15.0.8", - "expo-image": "~3.0.11", - "expo-keep-awake": "~15.0.8", - "expo-linear-gradient": "~15.0.8", - "expo-linking": "~8.0.11", - "expo-localization": "~17.0.8", - "expo-network": "~8.0.8", - "expo-notifications": "~0.32.16", - "expo-router": "~6.0.23", - "expo-sharing": "~14.0.8", - "expo-splash-screen": "~31.0.13", - "expo-status-bar": "~3.0.9", - "expo-store-review": "~9.0.9", - "expo-symbols": "~1.0.8", - "expo-system-ui": "~6.0.9", - "expo-video": "~3.0.16", - "expo-web-browser": "~15.0.10", - "i18next": "^25.8.12", - "posthog-react-native": "^4.36.0", - "posthog-react-native-session-replay": "^1.5.0", - "react": "19.1.0", - "react-dom": "19.1.0", - "react-i18next": "^16.5.4", - "react-native": "0.81.5", - "react-native-gesture-handler": "~2.28.0", - "react-native-purchases": "^9.10.3", - "react-native-reanimated": "~4.1.1", - "react-native-safe-area-context": "~5.6.0", - "react-native-screens": "~4.16.0", - "react-native-svg": "15.12.1", - "react-native-web": "~0.21.0", - "react-native-worklets": "0.5.1", - "zustand": "^5.0.11" - }, - "devDependencies": { - "@testing-library/jest-native": "^5.4.3", - "@testing-library/react-native": "^13.3.3", - "@types/react": "~19.1.0", - "@vitest/coverage-v8": "^4.1.1", - "eslint": "^9.25.0", - "eslint-config-expo": "~10.0.0", - "jsdom": "^29.0.1", - "react-test-renderer": "^19.1.0", - "typescript": "~5.9.2", - "vitest": "^4.1.1" - } - }, - "node_modules/@0no-co/graphql.web": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz", - "integrity": "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==", - "license": "MIT", - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - }, - "peerDependenciesMeta": { - "graphql": { - "optional": true - } - } - }, - "node_modules/@asamuzakjp/css-color": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.0.1.tgz", - "integrity": "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^3.1.1", - "@csstools/css-color-parser": "^4.0.2", - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0", - "lru-cache": "^11.2.6" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@asamuzakjp/dom-selector": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.4.tgz", - "integrity": "sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/nwsapi": "^2.3.9", - "bidi-js": "^1.0.3", - "css-tree": "^3.2.1", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.7" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@asamuzakjp/nwsapi": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", - "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", - "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "regexpu-core": "^6.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz", - "integrity": "sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "debug": "^4.4.3", - "lodash.debounce": "^4.0.8", - "resolve": "^1.22.11" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", - "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", - "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz", - "integrity": "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-syntax-decorators": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-default-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.27.1.tgz", - "integrity": "sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz", - "integrity": "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-default-from": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.28.6.tgz", - "integrity": "sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz", - "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", - "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", - "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", - "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", - "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", - "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", - "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz", - "integrity": "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-flow": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", - "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", - "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", - "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", - "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", - "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", - "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-syntax-jsx": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", - "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", - "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "babel-plugin-polyfill-corejs2": "^0.4.14", - "babel-plugin-polyfill-corejs3": "^0.13.0", - "babel-plugin-polyfill-regenerator": "^0.6.5", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", - "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", - "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", - "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.28.0", - "@babel/plugin-transform-react-jsx": "^7.27.1", - "@babel/plugin-transform-react-jsx-development": "^7.27.1", - "@babel/plugin-transform-react-pure-annotations": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", - "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse--for-generate-function-map": { - "name": "@babel/traverse", - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@bramus/specificity": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", - "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-tree": "^3.0.0" - }, - "bin": { - "specificity": "bin/cli.js" - } - }, - "node_modules/@csstools/color-helpers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", - "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/@csstools/css-calc": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", - "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz", - "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^6.0.2", - "@csstools/css-calc": "^3.1.1" - }, - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^4.0.0" - } - }, - "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.1.tgz", - "integrity": "sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "peerDependencies": { - "css-tree": "^3.2.1" - }, - "peerDependenciesMeta": { - "css-tree": { - "optional": true - } - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/@egjs/hammerjs": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", - "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", - "license": "MIT", - "dependencies": { - "@types/hammerjs": "^2.0.36" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@exodus/bytes": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", - "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@noble/hashes": "^1.8.0 || ^2.0.0" - }, - "peerDependenciesMeta": { - "@noble/hashes": { - "optional": true - } - } - }, - "node_modules/@expo-google-fonts/dm-mono": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@expo-google-fonts/dm-mono/-/dm-mono-0.4.2.tgz", - "integrity": "sha512-loMaZOkQRs1r7yt4rN39zcr9e0J+smwnSx929yuODkuiPfsY4PaW18C9SEZ0BvXfcBKoRhatGoIBl8V2MOYVPQ==", - "license": "MIT AND OFL-1.1" - }, - "node_modules/@expo-google-fonts/dm-serif-display": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@expo-google-fonts/dm-serif-display/-/dm-serif-display-0.4.2.tgz", - "integrity": "sha512-onlO8xAzsgMbKcwUE+fAgJ5AFHhk06VtaDN7eQOJwjV65QIciDKTiSNu1ymHc4m6g/x6D9OqPIYPXdTNIfaEaA==", - "license": "MIT AND OFL-1.1" - }, - "node_modules/@expo-google-fonts/inter": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@expo-google-fonts/inter/-/inter-0.4.2.tgz", - "integrity": "sha512-syfiImMaDmq7cFi0of+waE2M4uSCyd16zgyWxdPOY7fN2VBmSLKEzkfbZgeOjJq61kSqPBNNtXjggiQiSD6gMQ==", - "license": "MIT AND OFL-1.1" - }, - "node_modules/@expo-google-fonts/outfit": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@expo-google-fonts/outfit/-/outfit-0.4.3.tgz", - "integrity": "sha512-2uQmDVJencWLllxds6SG92E+SjxyZfvg7eZKZ5XLHggmm5AuUyQK7lzMAFOUzT6kheq2kJ7BAiubMdjKT32fJg==", - "license": "MIT AND OFL-1.1" - }, - "node_modules/@expo/code-signing-certificates": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz", - "integrity": "sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==", - "license": "MIT", - "dependencies": { - "node-forge": "^1.3.3" - } - }, - "node_modules/@expo/config": { - "version": "12.0.13", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.13.tgz", - "integrity": "sha512-Cu52arBa4vSaupIWsF0h7F/Cg//N374nYb7HAxV0I4KceKA7x2UXpYaHOL7EEYYvp7tZdThBjvGpVmr8ScIvaQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "~7.10.4", - "@expo/config-plugins": "~54.0.4", - "@expo/config-types": "^54.0.10", - "@expo/json-file": "^10.0.8", - "deepmerge": "^4.3.1", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "require-from-string": "^2.0.2", - "resolve-from": "^5.0.0", - "resolve-workspace-root": "^2.0.0", - "semver": "^7.6.0", - "slugify": "^1.3.4", - "sucrase": "~3.35.1" - } - }, - "node_modules/@expo/config-plugins": { - "version": "54.0.4", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.4.tgz", - "integrity": "sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q==", - "license": "MIT", - "dependencies": { - "@expo/config-types": "^54.0.10", - "@expo/json-file": "~10.0.8", - "@expo/plist": "^0.4.8", - "@expo/sdk-runtime-versions": "^1.0.0", - "chalk": "^4.1.2", - "debug": "^4.3.5", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.5.4", - "slash": "^3.0.0", - "slugify": "^1.6.6", - "xcode": "^3.0.1", - "xml2js": "0.6.0" - } - }, - "node_modules/@expo/config-plugins/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/config-types": { - "version": "54.0.10", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.10.tgz", - "integrity": "sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA==", - "license": "MIT" - }, - "node_modules/@expo/config/node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@expo/config/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/devcert": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.2.1.tgz", - "integrity": "sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==", - "license": "MIT", - "dependencies": { - "@expo/sudo-prompt": "^9.3.1", - "debug": "^3.1.0" - } - }, - "node_modules/@expo/devcert/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/@expo/devtools": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@expo/devtools/-/devtools-0.1.8.tgz", - "integrity": "sha512-SVLxbuanDjJPgc0sy3EfXUMLb/tXzp6XIHkhtPVmTWJAp+FOr6+5SeiCfJrCzZFet0Ifyke2vX3sFcKwEvCXwQ==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/@expo/env": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.0.8.tgz", - "integrity": "sha512-5VQD6GT8HIMRaSaB5JFtOXuvfDVU80YtZIuUT/GDhUF782usIXY13Tn3IdDz1Tm/lqA9qnRZQ1BF4t7LlvdJPA==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "debug": "^4.3.4", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", - "getenv": "^2.0.0" - } - }, - "node_modules/@expo/fingerprint": { - "version": "0.15.4", - "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.15.4.tgz", - "integrity": "sha512-eYlxcrGdR2/j2M6pEDXo9zU9KXXF1vhP+V+Tl+lyY+bU8lnzrN6c637mz6Ye3em2ANy8hhUR03Raf8VsT9Ogng==", - "license": "MIT", - "dependencies": { - "@expo/spawn-async": "^1.7.2", - "arg": "^5.0.2", - "chalk": "^4.1.2", - "debug": "^4.3.4", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "ignore": "^5.3.1", - "minimatch": "^9.0.0", - "p-limit": "^3.1.0", - "resolve-from": "^5.0.0", - "semver": "^7.6.0" - }, - "bin": { - "fingerprint": "bin/cli.js" - } - }, - "node_modules/@expo/fingerprint/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@expo/fingerprint/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/fingerprint/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/image-utils": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.8.8.tgz", - "integrity": "sha512-HHHaG4J4nKjTtVa1GG9PCh763xlETScfEyNxxOvfTRr8IKPJckjTyqSLEtdJoFNJ1vqiABEjW7tqGhqGibZLeA==", - "license": "MIT", - "dependencies": { - "@expo/spawn-async": "^1.7.2", - "chalk": "^4.0.0", - "getenv": "^2.0.0", - "jimp-compact": "0.16.1", - "parse-png": "^2.1.0", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0", - "semver": "^7.6.0", - "temp-dir": "~2.0.0", - "unique-string": "~2.0.0" - } - }, - "node_modules/@expo/image-utils/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/json-file": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.8.tgz", - "integrity": "sha512-9LOTh1PgKizD1VXfGQ88LtDH0lRwq9lsTb4aichWTWSWqy3Ugfkhfm3BhzBIkJJfQQ5iJu3m/BoRlEIjoCGcnQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "~7.10.4", - "json5": "^2.2.3" - } - }, - "node_modules/@expo/json-file/node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@expo/metro": { - "version": "54.2.0", - "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-54.2.0.tgz", - "integrity": "sha512-h68TNZPGsk6swMmLm9nRSnE2UXm48rWwgcbtAHVMikXvbxdS41NDHHeqg1rcQ9AbznDRp6SQVC2MVpDnsRKU1w==", - "license": "MIT", - "dependencies": { - "metro": "0.83.3", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-config": "0.83.3", - "metro-core": "0.83.3", - "metro-file-map": "0.83.3", - "metro-minify-terser": "0.83.3", - "metro-resolver": "0.83.3", - "metro-runtime": "0.83.3", - "metro-source-map": "0.83.3", - "metro-symbolicate": "0.83.3", - "metro-transform-plugins": "0.83.3", - "metro-transform-worker": "0.83.3" - } - }, - "node_modules/@expo/metro-config": { - "version": "54.0.14", - "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-54.0.14.tgz", - "integrity": "sha512-hxpLyDfOR4L23tJ9W1IbJJsG7k4lv2sotohBm/kTYyiG+pe1SYCAWsRmgk+H42o/wWf/HQjE5k45S5TomGLxNA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.20.0", - "@babel/generator": "^7.20.5", - "@expo/config": "~12.0.13", - "@expo/env": "~2.0.8", - "@expo/json-file": "~10.0.8", - "@expo/metro": "~54.2.0", - "@expo/spawn-async": "^1.7.2", - "browserslist": "^4.25.0", - "chalk": "^4.1.0", - "debug": "^4.3.2", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "hermes-parser": "^0.29.1", - "jsc-safe-url": "^0.2.4", - "lightningcss": "^1.30.1", - "minimatch": "^9.0.0", - "postcss": "~8.4.32", - "resolve-from": "^5.0.0" - }, - "peerDependencies": { - "expo": "*" - }, - "peerDependenciesMeta": { - "expo": { - "optional": true - } - } - }, - "node_modules/@expo/metro-config/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@expo/metro-config/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/metro-runtime": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-6.1.2.tgz", - "integrity": "sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g==", - "license": "MIT", - "dependencies": { - "anser": "^1.4.9", - "pretty-format": "^29.7.0", - "stacktrace-parser": "^0.1.10", - "whatwg-fetch": "^3.0.0" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-dom": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/@expo/osascript": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.3.8.tgz", - "integrity": "sha512-/TuOZvSG7Nn0I8c+FcEaoHeBO07yu6vwDgk7rZVvAXoeAK5rkA09jRyjYsZo+0tMEFaToBeywA6pj50Mb3ny9w==", - "license": "MIT", - "dependencies": { - "@expo/spawn-async": "^1.7.2", - "exec-async": "^2.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@expo/package-manager": { - "version": "1.9.10", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.9.10.tgz", - "integrity": "sha512-axJm+NOj3jVxep49va/+L3KkF3YW/dkV+RwzqUJedZrv4LeTqOG4rhrCaCPXHTvLqCTDKu6j0Xyd28N7mnxsGA==", - "license": "MIT", - "dependencies": { - "@expo/json-file": "^10.0.8", - "@expo/spawn-async": "^1.7.2", - "chalk": "^4.0.0", - "npm-package-arg": "^11.0.0", - "ora": "^3.4.0", - "resolve-workspace-root": "^2.0.0" - } - }, - "node_modules/@expo/plist": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.8.tgz", - "integrity": "sha512-pfNtErGGzzRwHP+5+RqswzPDKkZrx+Cli0mzjQaus1ZWFsog5ibL+nVT3NcporW51o8ggnt7x813vtRbPiyOrQ==", - "license": "MIT", - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.2.3", - "xmlbuilder": "^15.1.1" - } - }, - "node_modules/@expo/prebuild-config": { - "version": "54.0.8", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-54.0.8.tgz", - "integrity": "sha512-EA7N4dloty2t5Rde+HP0IEE+nkAQiu4A/+QGZGT9mFnZ5KKjPPkqSyYcRvP5bhQE10D+tvz6X0ngZpulbMdbsg==", - "license": "MIT", - "dependencies": { - "@expo/config": "~12.0.13", - "@expo/config-plugins": "~54.0.4", - "@expo/config-types": "^54.0.10", - "@expo/image-utils": "^0.8.8", - "@expo/json-file": "^10.0.8", - "@react-native/normalize-colors": "0.81.5", - "debug": "^4.3.1", - "resolve-from": "^5.0.0", - "semver": "^7.6.0", - "xml2js": "0.6.0" - }, - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/@expo/prebuild-config/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/schema-utils": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-0.1.8.tgz", - "integrity": "sha512-9I6ZqvnAvKKDiO+ZF8BpQQFYWXOJvTAL5L/227RUbWG1OVZDInFifzCBiqAZ3b67NRfeAgpgvbA7rejsqhY62A==", - "license": "MIT" - }, - "node_modules/@expo/sdk-runtime-versions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz", - "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==", - "license": "MIT" - }, - "node_modules/@expo/spawn-async": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", - "integrity": "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@expo/sudo-prompt": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@expo/sudo-prompt/-/sudo-prompt-9.3.2.tgz", - "integrity": "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==", - "license": "MIT" - }, - "node_modules/@expo/ui": { - "version": "0.2.0-beta.9", - "resolved": "https://registry.npmjs.org/@expo/ui/-/ui-0.2.0-beta.9.tgz", - "integrity": "sha512-RaBcp0cMe5GykQogJwRZGy4o4JHDLtrr+HaurDPhwPKqVATsV0rR11ysmFe4QX8XWLP/L3od7NOkXUi5ailvaw==", - "license": "MIT", - "dependencies": { - "sf-symbols-typescript": "^2.1.0" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/@expo/vector-icons": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-15.0.3.tgz", - "integrity": "sha512-SBUyYKphmlfUBqxSfDdJ3jAdEVSALS2VUPOUyqn48oZmb2TL/O7t7/PQm5v4NQujYEPLPMTLn9KVw6H7twwbTA==", - "license": "MIT", - "peerDependencies": { - "expo-font": ">=14.0.4", - "react": "*", - "react-native": "*" - } - }, - "node_modules/@expo/ws-tunnel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.6.tgz", - "integrity": "sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==", - "license": "MIT" - }, - "node_modules/@expo/xcpretty": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.4.0.tgz", - "integrity": "sha512-o2qDlTqJ606h4xR36H2zWTywmZ3v3842K6TU8Ik2n1mfW0S580VHlt3eItVYdLYz+klaPp7CXqanja8eASZjRw==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "chalk": "^4.1.0", - "js-yaml": "^4.1.0" - }, - "bin": { - "excpretty": "build/cli.js" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@ide/backoff": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ide/backoff/-/backoff-1.0.0.tgz", - "integrity": "sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", - "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@isaacs/ttlcache": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", - "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/create-cache-key-function": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", - "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/diff-sequences": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", - "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.4.0" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@posthog/core": { - "version": "1.23.1", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.23.1.tgz", - "integrity": "sha512-GViD5mOv/mcbZcyzz3z9CS0R79JzxVaqEz4sP5Dsea178M/j3ZWe6gaHDZB9yuyGfcmIMQ/8K14yv+7QrK4sQQ==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.6" - } - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@react-native-async-storage/async-storage": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz", - "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==", - "license": "MIT", - "dependencies": { - "merge-options": "^3.0.4" - }, - "peerDependencies": { - "react-native": "^0.0.0-0 || >=0.65 <1.0" - } - }, - "node_modules/@react-native/assets-registry": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.5.tgz", - "integrity": "sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==", - "license": "MIT", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/babel-plugin-codegen": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.81.5.tgz", - "integrity": "sha512-oF71cIH6je3fSLi6VPjjC3Sgyyn57JLHXs+mHWc9MoCiJJcM4nqsS5J38zv1XQ8d3zOW2JtHro+LF0tagj2bfQ==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.3", - "@react-native/codegen": "0.81.5" - }, - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/babel-preset": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.81.5.tgz", - "integrity": "sha512-UoI/x/5tCmi+pZ3c1+Ypr1DaRMDLI3y+Q70pVLLVgrnC3DHsHRIbHcCHIeG/IJvoeFqFM2sTdhSOLJrf8lOPrA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.25.2", - "@babel/plugin-transform-react-jsx-self": "^7.24.7", - "@babel/plugin-transform-react-jsx-source": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.25.2", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/template": "^7.25.0", - "@react-native/babel-plugin-codegen": "0.81.5", - "babel-plugin-syntax-hermes-parser": "0.29.1", - "babel-plugin-transform-flow-enums": "^0.0.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/@react-native/codegen": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.81.5.tgz", - "integrity": "sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/parser": "^7.25.3", - "glob": "^7.1.1", - "hermes-parser": "0.29.1", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "yargs": "^17.6.2" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/@react-native/codegen/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@react-native/community-cli-plugin": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.81.5.tgz", - "integrity": "sha512-yWRlmEOtcyvSZ4+OvqPabt+NS36vg0K/WADTQLhrYrm9qdZSuXmq8PmdJWz/68wAqKQ+4KTILiq2kjRQwnyhQw==", - "license": "MIT", - "dependencies": { - "@react-native/dev-middleware": "0.81.5", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "metro": "^0.83.1", - "metro-config": "^0.83.1", - "metro-core": "^0.83.1", - "semver": "^7.1.3" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@react-native-community/cli": "*", - "@react-native/metro-config": "*" - }, - "peerDependenciesMeta": { - "@react-native-community/cli": { - "optional": true - }, - "@react-native/metro-config": { - "optional": true - } - } - }, - "node_modules/@react-native/community-cli-plugin/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@react-native/debugger-frontend": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.81.5.tgz", - "integrity": "sha512-bnd9FSdWKx2ncklOetCgrlwqSGhMHP2zOxObJbOWXoj7GHEmih4MKarBo5/a8gX8EfA1EwRATdfNBQ81DY+h+w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/dev-middleware": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.81.5.tgz", - "integrity": "sha512-WfPfZzboYgo/TUtysuD5xyANzzfka8Ebni6RIb2wDxhb56ERi7qDrE4xGhtPsjCL4pQBXSVxyIlCy0d8I6EgGA==", - "license": "MIT", - "dependencies": { - "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.81.5", - "chrome-launcher": "^0.15.2", - "chromium-edge-launcher": "^0.2.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "open": "^7.0.3", - "serve-static": "^1.16.2", - "ws": "^6.2.3" - }, - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/dev-middleware/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/@react-native/gradle-plugin": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.81.5.tgz", - "integrity": "sha512-hORRlNBj+ReNMLo9jme3yQ6JQf4GZpVEBLxmTXGGlIL78MAezDZr5/uq9dwElSbcGmLEgeiax6e174Fie6qPLg==", - "license": "MIT", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/js-polyfills": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.81.5.tgz", - "integrity": "sha512-fB7M1CMOCIUudTRuj7kzxIBTVw2KXnsgbQ6+4cbqSxo8NmRRhA0Ul4ZUzZj3rFd3VznTL4Brmocv1oiN0bWZ8w==", - "license": "MIT", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/normalize-colors": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.5.tgz", - "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", - "license": "MIT" - }, - "node_modules/@react-navigation/bottom-tabs": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.14.0.tgz", - "integrity": "sha512-oG2VdoInuIyK0o9o90Yo47hTCS+sPyVE7k8eSB37vt3pq3uQxjh8V3xJpsQfOfNlRUXOPB/ejH93nSBlP7ZHmQ==", - "license": "MIT", - "dependencies": { - "@react-navigation/elements": "^2.9.5", - "color": "^4.2.3", - "sf-symbols-typescript": "^2.1.0" - }, - "peerDependencies": { - "@react-navigation/native": "^7.1.28", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-screens": ">= 4.0.0" - } - }, - "node_modules/@react-navigation/core": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.14.0.tgz", - "integrity": "sha512-tMpzskBzVp0E7CRNdNtJIdXjk54Kwe/TF9ViXAef+YFM1kSfGv4e/B2ozfXE+YyYgmh4WavTv8fkdJz1CNyu+g==", - "license": "MIT", - "dependencies": { - "@react-navigation/routers": "^7.5.3", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "query-string": "^7.1.3", - "react-is": "^19.1.0", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" - }, - "peerDependencies": { - "react": ">= 18.2.0" - } - }, - "node_modules/@react-navigation/elements": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.5.tgz", - "integrity": "sha512-iHZU8rRN1014Upz73AqNVXDvSMZDh5/ktQ1CMe21rdgnOY79RWtHHBp9qOS3VtqlUVYGkuX5GEw5mDt4tKdl0g==", - "license": "MIT", - "dependencies": { - "color": "^4.2.3", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" - }, - "peerDependencies": { - "@react-native-masked-view/masked-view": ">= 0.2.0", - "@react-navigation/native": "^7.1.28", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0" - }, - "peerDependenciesMeta": { - "@react-native-masked-view/masked-view": { - "optional": true - } - } - }, - "node_modules/@react-navigation/native": { - "version": "7.1.28", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.28.tgz", - "integrity": "sha512-d1QDn+KNHfHGt3UIwOZvupvdsDdiHYZBEj7+wL2yDVo3tMezamYy60H9s3EnNVE1Ae1ty0trc7F2OKqo/RmsdQ==", - "license": "MIT", - "dependencies": { - "@react-navigation/core": "^7.14.0", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "use-latest-callback": "^0.2.4" - }, - "peerDependencies": { - "react": ">= 18.2.0", - "react-native": "*" - } - }, - "node_modules/@react-navigation/native-stack": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.13.0.tgz", - "integrity": "sha512-5OOp1IKEd5woHl9hGBU0qCAfrQ4+7Tqej0HzDzGQeXzS8tg9gq84x1qUdRvFk5BXbhuAyvJliY9F1/I07d2X0A==", - "license": "MIT", - "dependencies": { - "@react-navigation/elements": "^2.9.5", - "color": "^4.2.3", - "sf-symbols-typescript": "^2.1.0", - "warn-once": "^0.1.1" - }, - "peerDependencies": { - "@react-navigation/native": "^7.1.28", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-screens": ">= 4.0.0" - } - }, - "node_modules/@react-navigation/routers": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.3.tgz", - "integrity": "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==", - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11" - } - }, - "node_modules/@revenuecat/purchases-js": { - "version": "1.26.1", - "resolved": "https://registry.npmjs.org/@revenuecat/purchases-js/-/purchases-js-1.26.1.tgz", - "integrity": "sha512-+RO6G/AW6/Tm7RFqz2sdkHBAAyV4T2Lj4KGQm8vVUvR8g9hxuDeenCAIKssAqAgLTyq8uqUl4dH6ncbCzHBA0g==", - "license": "MIT" - }, - "node_modules/@revenuecat/purchases-js-hybrid-mappings": { - "version": "17.41.0", - "resolved": "https://registry.npmjs.org/@revenuecat/purchases-js-hybrid-mappings/-/purchases-js-hybrid-mappings-17.41.0.tgz", - "integrity": "sha512-rjPX+k1ryiSu//hYpSPU0+M5rryVoNaMxr6raFDyc5bB1My2aCH4b7zUnVBiyGa6ZUayUMJtZxOsz/THzS4WqA==", - "license": "MIT", - "dependencies": { - "@revenuecat/purchases-js": "1.26.1" - } - }, - "node_modules/@revenuecat/purchases-typescript-internal": { - "version": "17.41.0", - "resolved": "https://registry.npmjs.org/@revenuecat/purchases-typescript-internal/-/purchases-typescript-internal-17.41.0.tgz", - "integrity": "sha512-1UhQaQcHQ4TxTdMRXagWhN0ecFC61Wy6ula+aSHuUDLJwyKsMl4Dt4vocuo1Ja9o5Jc1Ty4AXx4MnqEnuLrr6A==", - "license": "MIT" - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.11.tgz", - "integrity": "sha512-SJ+/g+xNnOh6NqYxD0V3uVN4W3VfnrGsC9/hoglicgTNfABFG9JjISvkkU0dNY84MNHLWyOgxP9v9Y9pX4S7+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.11.tgz", - "integrity": "sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.11.tgz", - "integrity": "sha512-39Ks6UvIHq4rEogIfQBoBRusj0Q0nPVWIvqmwBLaT6aqQGIakHdESBVOPRRLacy4WwUPIx4ZKzfZ9PMW+IeyUQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.11.tgz", - "integrity": "sha512-jfsm0ZHfhiqrvWjJAmzsqiIFPz5e7mAoCOPBNTcNgkiid/LaFKiq92+0ojH+nmJmKYkre4t71BWXUZDNp7vsag==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.11.tgz", - "integrity": "sha512-zjQaUtSyq1nVe3nxmlSCuR96T1LPlpvmJ0SZy0WJFEsV4kFbXcq2u68L4E6O0XeFj4aex9bEauqjW8UQBeAvfQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.11.tgz", - "integrity": "sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-ZlFgw46NOAGMgcdvdYwAGu2Q+SLFA9LzbJLW+iyMOJyhj5wk6P3KEE9Gct4xWwSzFoPI7JCdYmYMzVtlgQ+zfw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-hIOYmuT6ofM4K04XAZd3OzMySEO4K0/nc9+jmNcxNAxRi6c5UWpqfw3KMFV4MVFWL+jQsSh+bGw2VqmaPMTLyw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.11.tgz", - "integrity": "sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.11.tgz", - "integrity": "sha512-mcp3Rio2w72IvdZG0oQ4bM2c2oumtwHfUfKncUM6zGgz0KgPz4YmDPQfnXEiY5t3+KD/i8HG2rOB/LxdmieK2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.11.tgz", - "integrity": "sha512-LXk5Hii1Ph9asuGRjBuz8TUxdc1lWzB7nyfdoRgI0WGPZKmCxvlKk8KfYysqtr4MfGElu/f/pEQRh8fcEgkrWw==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", - "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.11.tgz", - "integrity": "sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.11.tgz", - "integrity": "sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.11.tgz", - "integrity": "sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@supabase/auth-js": { - "version": "2.98.0", - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.98.0.tgz", - "integrity": "sha512-GBH361T0peHU91AQNzOlIrjUZw9TZbB9YDRiyFgk/3Kvr3/Z1NWUZ2athWTfHhwNNi8IrW00foyFxQD9IO/Trg==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/functions-js": { - "version": "2.98.0", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.98.0.tgz", - "integrity": "sha512-N/xEyiNU5Org+d+PNCpv+TWniAXRzxIURxDYsS/m2I/sfAB/HcM9aM2Dmf5edj5oWb9GxID1OBaZ8NMmPXL+Lg==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/postgrest-js": { - "version": "2.98.0", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.98.0.tgz", - "integrity": "sha512-v6e9WeZuJijzUut8HyXu6gMqWFepIbaeaMIm1uKzei4yLg9bC9OtEW9O14LE/9ezqNbSAnSLO5GtOLFdm7Bpkg==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/realtime-js": { - "version": "2.98.0", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.98.0.tgz", - "integrity": "sha512-rOWt28uGyFipWOSd+n0WVMr9kUXiWaa7J4hvyLCIHjRFqWm1z9CaaKAoYyfYMC1Exn3WT8WePCgiVhlAtWC2yw==", - "license": "MIT", - "dependencies": { - "@types/phoenix": "^1.6.6", - "@types/ws": "^8.18.1", - "tslib": "2.8.1", - "ws": "^8.18.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/realtime-js/node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@supabase/storage-js": { - "version": "2.98.0", - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.98.0.tgz", - "integrity": "sha512-tzr2mG+v7ILSAZSfZMSL9OPyIH4z1ikgQ8EcQTKfMRz4EwmlFt3UnJaGzSOxyvF5b+fc9So7qdSUWTqGgeLokQ==", - "license": "MIT", - "dependencies": { - "iceberg-js": "^0.8.1", - "tslib": "2.8.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/supabase-js": { - "version": "2.98.0", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.98.0.tgz", - "integrity": "sha512-Ohc97CtInLwZyiSASz7tT9/Abm/vqnIbO9REp+PivVUII8UZsuI3bngRQnYgJdFoOIwvaEII1fX1qy8x0CyNiw==", - "license": "MIT", - "dependencies": { - "@supabase/auth-js": "2.98.0", - "@supabase/functions-js": "2.98.0", - "@supabase/postgrest-js": "2.98.0", - "@supabase/realtime-js": "2.98.0", - "@supabase/storage-js": "2.98.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@tanstack/query-async-storage-persister": { - "version": "5.90.24", - "resolved": "https://registry.npmjs.org/@tanstack/query-async-storage-persister/-/query-async-storage-persister-5.90.24.tgz", - "integrity": "sha512-3mljhSqeyu4xqF6BzNAzCe5MbteWPlOWHegLLmgiyppAENjaE0HpJcJAHlKaGhrP5IUhh3zytxW0gSydjmgwIw==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.90.20", - "@tanstack/query-persist-client-core": "5.92.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/query-core": { - "version": "5.90.20", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz", - "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/query-persist-client-core": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-persist-client-core/-/query-persist-client-core-5.92.1.tgz", - "integrity": "sha512-XGzB1lulFrGc8UwQnMI12r71R7ock/XOZvDaz3Fu3xrxCFwLHuFcABAOkIolS/6hFHe0pRdsBRXd4Q8ECqiCug==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.90.20" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.90.21", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.21.tgz", - "integrity": "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.90.20" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, - "node_modules/@tanstack/react-query-persist-client": { - "version": "5.90.24", - "resolved": "https://registry.npmjs.org/@tanstack/react-query-persist-client/-/react-query-persist-client-5.90.24.tgz", - "integrity": "sha512-FkfU37vHq61Efr/qGiz+CUNmGfCky1jjsaZFuS5MsWwA9vPHudCwmdirgyTx+RfcQxyHON904q/pc48zrIEhxg==", - "license": "MIT", - "dependencies": { - "@tanstack/query-persist-client-core": "5.92.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@tanstack/react-query": "^5.90.21", - "react": "^18 || ^19" - } - }, - "node_modules/@testing-library/jest-native": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-native/-/jest-native-5.4.3.tgz", - "integrity": "sha512-/sSDGaOuE+PJ1Z9Kp4u7PQScSVVXGud59I/qsBFFJvIbcn4P6yYw6cBnBmbPF+X9aRIsTJRDl6gzw5ZkJNm66w==", - "deprecated": "DEPRECATED: This package is no longer maintained.\nPlease use the built-in Jest matchers available in @testing-library/react-native v12.4+.\n\nSee migration guide: https://callstack.github.io/react-native-testing-library/docs/migration/jest-matchers", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "jest-diff": "^29.0.1", - "jest-matcher-utils": "^29.0.1", - "pretty-format": "^29.0.3", - "redent": "^3.0.0" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-native": ">=0.59", - "react-test-renderer": ">=16.0.0" - } - }, - "node_modules/@testing-library/react-native": { - "version": "13.3.3", - "resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-13.3.3.tgz", - "integrity": "sha512-k6Mjsd9dbZgvY4Bl7P1NIpePQNi+dfYtlJ5voi9KQlynxSyQkfOgJmYGCYmw/aSgH/rUcFvG8u5gd4npzgRDyg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "jest-matcher-utils": "^30.0.5", - "picocolors": "^1.1.1", - "pretty-format": "^30.0.5", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "jest": ">=29.0.0", - "react": ">=18.2.0", - "react-native": ">=0.71", - "react-test-renderer": ">=18.2.0" - }, - "peerDependenciesMeta": { - "jest": { - "optional": true - } - } - }, - "node_modules/@testing-library/react-native/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@testing-library/react-native/node_modules/@sinclair/typebox": { - "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@testing-library/react-native/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@testing-library/react-native/node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.3.0", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@testing-library/react-native/node_modules/jest-matcher-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", - "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.3.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@testing-library/react-native/node_modules/pretty-format": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", - "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@testing-library/react-native/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/hammerjs": { - "version": "2.0.46", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", - "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", - "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/phoenix": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", - "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.1.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", - "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", - "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.56.0", - "@typescript-eslint/type-utils": "8.56.0", - "@typescript-eslint/utils": "8.56.0", - "@typescript-eslint/visitor-keys": "8.56.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.56.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", - "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.56.0", - "@typescript-eslint/types": "8.56.0", - "@typescript-eslint/typescript-estree": "8.56.0", - "@typescript-eslint/visitor-keys": "8.56.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz", - "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.56.0", - "@typescript-eslint/types": "^8.56.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz", - "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.56.0", - "@typescript-eslint/visitor-keys": "8.56.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz", - "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz", - "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.56.0", - "@typescript-eslint/typescript-estree": "8.56.0", - "@typescript-eslint/utils": "8.56.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz", - "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz", - "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.56.0", - "@typescript-eslint/tsconfig-utils": "8.56.0", - "@typescript-eslint/types": "8.56.0", - "@typescript-eslint/visitor-keys": "8.56.0", - "debug": "^4.4.3", - "minimatch": "^9.0.5", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz", - "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.56.0", - "@typescript-eslint/types": "8.56.0", - "@typescript-eslint/typescript-estree": "8.56.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz", - "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.56.0", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", - "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@urql/core": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.2.0.tgz", - "integrity": "sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==", - "license": "MIT", - "dependencies": { - "@0no-co/graphql.web": "^1.0.13", - "wonka": "^6.3.2" - } - }, - "node_modules/@urql/exchange-retry": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.3.2.tgz", - "integrity": "sha512-TQMCz2pFJMfpNxmSfX1VSfTjwUIFx/mL+p1bnfM1xjjdla7Z+KnGMW/EhFbpckp3LyWAH4PgOsMwOMnIN+MBFg==", - "license": "MIT", - "dependencies": { - "@urql/core": "^5.1.2", - "wonka": "^6.3.2" - }, - "peerDependencies": { - "@urql/core": "^5.0.0" - } - }, - "node_modules/@vitest/coverage-v8": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.1.tgz", - "integrity": "sha512-nZ4RWwGCoGOQRMmU/Q9wlUY540RVRxJZ9lxFsFfy0QV7Zmo5VVBhB6Sl9Xa0KIp2iIs3zWfPlo9LcY1iqbpzCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.1", - "ast-v8-to-istanbul": "^1.0.0", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.2.0", - "magicast": "^0.5.2", - "obug": "^2.1.1", - "std-env": "^4.0.0-rc.1", - "tinyrainbow": "^3.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "4.1.1", - "vitest": "4.1.1" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@vitest/expect": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.1.tgz", - "integrity": "sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.1", - "@vitest/utils": "4.1.1", - "chai": "^6.2.2", - "tinyrainbow": "^3.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.1.tgz", - "integrity": "sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "4.1.1", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.1.tgz", - "integrity": "sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^3.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.1.tgz", - "integrity": "sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.1", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.1.tgz", - "integrity": "sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.1", - "@vitest/utils": "4.1.1", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.1.tgz", - "integrity": "sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.1.tgz", - "integrity": "sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.1", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", - "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/anser": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", - "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", - "license": "MIT" - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT" - }, - "node_modules/assert": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", - "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/ast-v8-to-istanbul": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", - "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.31", - "estree-walker": "^3.0.3", - "js-tokens": "^10.0.0" - } - }, - "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz", - "integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.6", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz", - "integrity": "sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.6" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-react-compiler": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz", - "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.26.0" - } - }, - "node_modules/babel-plugin-react-native-web": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.21.2.tgz", - "integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==", - "license": "MIT" - }, - "node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.29.1.tgz", - "integrity": "sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==", - "license": "MIT", - "dependencies": { - "hermes-parser": "0.29.1" - } - }, - "node_modules/babel-plugin-transform-flow-enums": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", - "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-flow": "^7.12.1" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-expo": { - "version": "54.0.10", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-54.0.10.tgz", - "integrity": "sha512-wTt7POavLFypLcPW/uC5v8y+mtQKDJiyGLzYCjqr9tx0Qc3vCXcDKk1iCFIj/++Iy5CWhhTflEa7VvVPNWeCfw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/plugin-proposal-decorators": "^7.12.9", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.23.0", - "@react-native/babel-preset": "0.81.5", - "babel-plugin-react-compiler": "^1.0.0", - "babel-plugin-react-native-web": "~0.21.0", - "babel-plugin-syntax-hermes-parser": "^0.29.1", - "babel-plugin-transform-flow-enums": "^0.0.2", - "debug": "^4.3.4", - "resolve-from": "^5.0.0" - }, - "peerDependencies": { - "@babel/runtime": "^7.20.0", - "expo": "*", - "react-refresh": ">=0.14.0 <1.0.0" - }, - "peerDependenciesMeta": { - "@babel/runtime": { - "optional": true - }, - "expo": { - "optional": true - } - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/badgin": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/badgin/-/badgin-1.2.3.tgz", - "integrity": "sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==", - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/better-opn": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", - "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", - "license": "MIT", - "dependencies": { - "open": "^8.0.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/better-opn/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bidi-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "require-from-string": "^2.0.2" - } - }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "license": "Unlicense", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/bplist-creator": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", - "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", - "license": "MIT", - "dependencies": { - "stream-buffers": "2.2.x" - } - }, - "node_modules/bplist-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", - "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", - "license": "MIT", - "dependencies": { - "big-integer": "1.6.x" - }, - "engines": { - "node": ">= 5.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001770", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", - "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/chrome-launcher": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", - "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0" - }, - "bin": { - "print-chrome-path": "bin/print-chrome-path.js" - }, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/chromium-edge-launcher": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.2.0.tgz", - "integrity": "sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "license": "MIT" - }, - "node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.1.0", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/compression/node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.48.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.48.0.tgz", - "integrity": "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-fetch": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", - "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/css-in-js-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", - "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==", - "license": "MIT", - "dependencies": { - "hyphenate-style-name": "^1.0.3" - } - }, - "node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.27.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/data-urls": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", - "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^16.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/data-urls/node_modules/tr46": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/data-urls/node_modules/webidl-conversions": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", - "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=20" - } - }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", - "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@exodus/bytes": "^1.11.0", - "tr46": "^6.0.0", - "webidl-conversions": "^8.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "dev": true, - "license": "MIT" - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dotenv-expand": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", - "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", - "license": "BSD-2-Clause", - "dependencies": { - "dotenv": "^16.4.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-editor": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz", - "integrity": "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" - } - }, - "node_modules/es-abstract": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", - "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.1", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.1.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.3.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.5", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", - "dev": true, - "license": "MIT", - "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" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-expo": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-expo/-/eslint-config-expo-10.0.0.tgz", - "integrity": "sha512-/XC/DvniUWTzU7Ypb/cLDhDD4DXqEio4lug1ObD/oQ9Hcx3OVOR8Mkp4u6U4iGoZSJyIQmIk3WVHe/P1NYUXKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "^8.18.2", - "@typescript-eslint/parser": "^8.18.2", - "eslint-import-resolver-typescript": "^3.6.3", - "eslint-plugin-expo": "^1.0.0", - "eslint-plugin-import": "^2.30.0", - "eslint-plugin-react": "^7.37.3", - "eslint-plugin-react-hooks": "^5.1.0", - "globals": "^16.0.0" - }, - "peerDependencies": { - "eslint": ">=8.10" - } - }, - "node_modules/eslint-config-expo/node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", - "dev": true, - "license": "ISC", - "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" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-expo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-expo/-/eslint-plugin-expo-1.0.0.tgz", - "integrity": "sha512-qLtunR+cNFtC+jwYCBia5c/PJurMjSLMOV78KrEOyQK02ohZapU4dCFFnS2hfrJuw0zxfsjVkjqg3QBqi933QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "^8.29.1", - "@typescript-eslint/utils": "^8.29.1", - "eslint": "^9.24.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "eslint": ">=8.10" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", - "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", - "dev": true, - "license": "MIT", - "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", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.1", - "hasown": "^2.0.2", - "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.1", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.9", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", - "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/exec-async": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz", - "integrity": "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==", - "license": "MIT" - }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/expo": { - "version": "54.0.33", - "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.33.tgz", - "integrity": "sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.0", - "@expo/cli": "54.0.23", - "@expo/config": "~12.0.13", - "@expo/config-plugins": "~54.0.4", - "@expo/devtools": "0.1.8", - "@expo/fingerprint": "0.15.4", - "@expo/metro": "~54.2.0", - "@expo/metro-config": "54.0.14", - "@expo/vector-icons": "^15.0.3", - "@ungap/structured-clone": "^1.3.0", - "babel-preset-expo": "~54.0.10", - "expo-asset": "~12.0.12", - "expo-constants": "~18.0.13", - "expo-file-system": "~19.0.21", - "expo-font": "~14.0.11", - "expo-keep-awake": "~15.0.8", - "expo-modules-autolinking": "3.0.24", - "expo-modules-core": "3.0.29", - "pretty-format": "^29.7.0", - "react-refresh": "^0.14.2", - "whatwg-url-without-unicode": "8.0.0-3" - }, - "bin": { - "expo": "bin/cli", - "expo-modules-autolinking": "bin/autolinking", - "fingerprint": "bin/fingerprint" - }, - "peerDependencies": { - "@expo/dom-webview": "*", - "@expo/metro-runtime": "*", - "react": "*", - "react-native": "*", - "react-native-webview": "*" - }, - "peerDependenciesMeta": { - "@expo/dom-webview": { - "optional": true - }, - "@expo/metro-runtime": { - "optional": true - }, - "react-native-webview": { - "optional": true - } - } - }, - "node_modules/expo-application": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-7.0.8.tgz", - "integrity": "sha512-qFGyxk7VJbrNOQWBbE09XUuGuvkOgFS9QfToaK2FdagM2aQ+x3CvGV2DuVgl/l4ZxPgIf3b/MNh9xHpwSwn74Q==", - "license": "MIT", - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-asset": { - "version": "12.0.12", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-12.0.12.tgz", - "integrity": "sha512-CsXFCQbx2fElSMn0lyTdRIyKlSXOal6ilLJd+yeZ6xaC7I9AICQgscY5nj0QcwgA+KYYCCEQEBndMsmj7drOWQ==", - "license": "MIT", - "dependencies": { - "@expo/image-utils": "^0.8.8", - "expo-constants": "~18.0.12" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-av": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/expo-av/-/expo-av-16.0.8.tgz", - "integrity": "sha512-cmVPftGR/ca7XBgs7R6ky36lF3OC0/MM/lpgX/yXqfv0jASTsh7AYX9JxHCwFmF+Z6JEB1vne9FDx4GiLcGreQ==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*", - "react-native-web": "*" - }, - "peerDependenciesMeta": { - "react-native-web": { - "optional": true - } - } - }, - "node_modules/expo-blur": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-15.0.8.tgz", - "integrity": "sha512-rWyE1NBRZEu9WD+X+5l7gyPRszw7n12cW3IRNAb5i6KFzaBp8cxqT5oeaphJapqURvcqhkOZn2k5EtBSbsuU7w==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-constants": { - "version": "18.0.13", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.13.tgz", - "integrity": "sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ==", - "license": "MIT", - "dependencies": { - "@expo/config": "~12.0.13", - "@expo/env": "~2.0.8" - }, - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, - "node_modules/expo-device": { - "version": "8.0.10", - "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-8.0.10.tgz", - "integrity": "sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA==", - "license": "MIT", - "dependencies": { - "ua-parser-js": "^0.7.33" - }, - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-device/node_modules/ua-parser-js": { - "version": "0.7.41", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz", - "integrity": "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "MIT", - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/expo-file-system": { - "version": "19.0.21", - "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.21.tgz", - "integrity": "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, - "node_modules/expo-font": { - "version": "14.0.11", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.11.tgz", - "integrity": "sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==", - "license": "MIT", - "dependencies": { - "fontfaceobserver": "^2.1.0" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-gl": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/expo-gl/-/expo-gl-16.0.10.tgz", - "integrity": "sha512-/pPlSJvfmrGuW+UXBRVADr52nhiHFwRGXB8shhQb+b6KKreCuTmQZUASznAXS6YaHNjkOghmkaUW0hRnyiAwBQ==", - "license": "MIT", - "dependencies": { - "invariant": "^2.2.4" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-dom": "*", - "react-native": "*", - "react-native-reanimated": "*", - "react-native-web": "*" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native-reanimated": { - "optional": true - }, - "react-native-web": { - "optional": true - } - } - }, - "node_modules/expo-haptics": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-15.0.8.tgz", - "integrity": "sha512-lftutojy8Qs8zaDzzjwM3gKHFZ8bOOEZDCkmh2Ddpe95Ra6kt2izeOfOfKuP/QEh0MZ1j9TfqippyHdRd1ZM9g==", - "license": "MIT", - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-image": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-3.0.11.tgz", - "integrity": "sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*", - "react-native-web": "*" - }, - "peerDependenciesMeta": { - "react-native-web": { - "optional": true - } - } - }, - "node_modules/expo-keep-awake": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.8.tgz", - "integrity": "sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*" - } - }, - "node_modules/expo-linear-gradient": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-15.0.8.tgz", - "integrity": "sha512-V2d8Wjn0VzhPHO+rrSBtcl+Fo+jUUccdlmQ6OoL9/XQB7Qk3d9lYrqKDJyccwDxmQT10JdST3Tmf2K52NLc3kw==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-linking": { - "version": "8.0.11", - "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.11.tgz", - "integrity": "sha512-+VSaNL5om3kOp/SSKO5qe6cFgfSIWnnQDSbA7XLs3ECkYzXRquk5unxNS3pg7eK5kNUmQ4kgLI7MhTggAEUBLA==", - "license": "MIT", - "dependencies": { - "expo-constants": "~18.0.12", - "invariant": "^2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-localization": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-17.0.8.tgz", - "integrity": "sha512-UrdwklZBDJ+t+ZszMMiE0SXZ2eJxcquCuQcl6EvGHM9K+e6YqKVRQ+w8qE+iIB3H75v2RJy6MHAaLK+Mqeo04g==", - "license": "MIT", - "dependencies": { - "rtl-detect": "^1.0.2" - }, - "peerDependencies": { - "expo": "*", - "react": "*" - } - }, - "node_modules/expo-modules-autolinking": { - "version": "3.0.24", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.24.tgz", - "integrity": "sha512-TP+6HTwhL7orDvsz2VzauyQlXJcAWyU3ANsZ7JGL4DQu8XaZv/A41ZchbtAYLfozNA2Ya1Hzmhx65hXryBMjaQ==", - "license": "MIT", - "dependencies": { - "@expo/spawn-async": "^1.7.2", - "chalk": "^4.1.0", - "commander": "^7.2.0", - "require-from-string": "^2.0.2", - "resolve-from": "^5.0.0" - }, - "bin": { - "expo-modules-autolinking": "bin/expo-modules-autolinking.js" - } - }, - "node_modules/expo-modules-core": { - "version": "3.0.29", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-3.0.29.tgz", - "integrity": "sha512-LzipcjGqk8gvkrOUf7O2mejNWugPkf3lmd9GkqL9WuNyeN2fRwU0Dn77e3ZUKI3k6sI+DNwjkq4Nu9fNN9WS7Q==", - "license": "MIT", - "dependencies": { - "invariant": "^2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-network": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/expo-network/-/expo-network-8.0.8.tgz", - "integrity": "sha512-dgrL8UHAmWofqeY4UEjWskCl/RoQAM0DG6PZR8xz2WZt+6aQEboQgFRXowCfhbKZ71d16sNuKXtwBEsp2DtdNw==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*" - } - }, - "node_modules/expo-notifications": { - "version": "0.32.16", - "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.32.16.tgz", - "integrity": "sha512-QQD/UA6v7LgvwIJ+tS7tSvqJZkdp0nCSj9MxsDk/jU1GttYdK49/5L2LvE/4U0H7sNBz1NZAyhDZozg8xgBLXw==", - "license": "MIT", - "dependencies": { - "@expo/image-utils": "^0.8.8", - "@ide/backoff": "^1.0.0", - "abort-controller": "^3.0.0", - "assert": "^2.0.0", - "badgin": "^1.1.5", - "expo-application": "~7.0.8", - "expo-constants": "~18.0.13" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-router": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-6.0.23.tgz", - "integrity": "sha512-qCxVAiCrCyu0npky6azEZ6dJDMt77OmCzEbpF6RbUTlfkaCA417LvY14SBkk0xyGruSxy/7pvJOI6tuThaUVCA==", - "license": "MIT", - "dependencies": { - "@expo/metro-runtime": "^6.1.2", - "@expo/schema-utils": "^0.1.8", - "@radix-ui/react-slot": "1.2.0", - "@radix-ui/react-tabs": "^1.1.12", - "@react-navigation/bottom-tabs": "^7.4.0", - "@react-navigation/native": "^7.1.8", - "@react-navigation/native-stack": "^7.3.16", - "client-only": "^0.0.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "expo-server": "^1.0.5", - "fast-deep-equal": "^3.1.3", - "invariant": "^2.2.4", - "nanoid": "^3.3.8", - "query-string": "^7.1.3", - "react-fast-compare": "^3.2.2", - "react-native-is-edge-to-edge": "^1.1.6", - "semver": "~7.6.3", - "server-only": "^0.0.1", - "sf-symbols-typescript": "^2.1.0", - "shallowequal": "^1.1.0", - "use-latest-callback": "^0.2.1", - "vaul": "^1.1.2" - }, - "peerDependencies": { - "@expo/metro-runtime": "^6.1.2", - "@react-navigation/drawer": "^7.5.0", - "@testing-library/react-native": ">= 12.0.0", - "expo": "*", - "expo-constants": "^18.0.13", - "expo-linking": "^8.0.11", - "react": "*", - "react-dom": "*", - "react-native": "*", - "react-native-gesture-handler": "*", - "react-native-reanimated": "*", - "react-native-safe-area-context": ">= 5.4.0", - "react-native-screens": "*", - "react-native-web": "*", - "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4" - }, - "peerDependenciesMeta": { - "@react-navigation/drawer": { - "optional": true - }, - "@testing-library/react-native": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native-gesture-handler": { - "optional": true - }, - "react-native-reanimated": { - "optional": true - }, - "react-native-web": { - "optional": true - }, - "react-server-dom-webpack": { - "optional": true - } - } - }, - "node_modules/expo-router/node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/expo-router/node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/expo-router/node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/expo-router/node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/expo-router/node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/expo-router/node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/expo-router/node_modules/@radix-ui/react-tabs": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/expo-router/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/expo-server": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.5.tgz", - "integrity": "sha512-IGR++flYH70rhLyeXF0Phle56/k4cee87WeQ4mamS+MkVAVP+dDlOHf2nN06Z9Y2KhU0Gp1k+y61KkghF7HdhA==", - "license": "MIT", - "engines": { - "node": ">=20.16.0" - } - }, - "node_modules/expo-sharing": { - "version": "14.0.8", - "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-14.0.8.tgz", - "integrity": "sha512-A1pPr2iBrxypFDCWVAESk532HK+db7MFXbvO2sCV9ienaFXAk7lIBm6bkqgE6vzRd9O3RGdEGzYx80cYlc089Q==", - "license": "MIT", - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-splash-screen": { - "version": "31.0.13", - "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-31.0.13.tgz", - "integrity": "sha512-1epJLC1cDlwwj089R2h8cxaU5uk4ONVAC+vzGiTZH4YARQhL4Stlz1MbR6yAS173GMosvkE6CAeihR7oIbCkDA==", - "license": "MIT", - "dependencies": { - "@expo/prebuild-config": "^54.0.8" - }, - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-status-bar": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-3.0.9.tgz", - "integrity": "sha512-xyYyVg6V1/SSOZWh4Ni3U129XHCnFHBTcUo0dhWtFDrZbNp/duw5AGsQfb2sVeU0gxWHXSY1+5F0jnKYC7WuOw==", - "license": "MIT", - "dependencies": { - "react-native-is-edge-to-edge": "^1.2.1" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-store-review": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/expo-store-review/-/expo-store-review-9.0.9.tgz", - "integrity": "sha512-99vS7edXlKzPcdjrzVlMQWc4zOyq4khQfFjhNqJgpGP+AgRn4U0LaZkHIrVjmzolryD3rcHJSiUQH9Vi0sD0MQ==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, - "node_modules/expo-symbols": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-1.0.8.tgz", - "integrity": "sha512-7bNjK350PaQgxBf0owpmSYkdZIpdYYmaPttDBb2WIp6rIKtcEtdzdfmhsc2fTmjBURHYkg36+eCxBFXO25/1hw==", - "license": "MIT", - "dependencies": { - "sf-symbols-typescript": "^2.0.0" - }, - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, - "node_modules/expo-system-ui": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-6.0.9.tgz", - "integrity": "sha512-eQTYGzw1V4RYiYHL9xDLYID3Wsec2aZS+ypEssmF64D38aDrqbDgz1a2MSlHLQp2jHXSs3FvojhZ9FVela1Zcg==", - "license": "MIT", - "dependencies": { - "@react-native/normalize-colors": "0.81.5", - "debug": "^4.3.2" - }, - "peerDependencies": { - "expo": "*", - "react-native": "*", - "react-native-web": "*" - }, - "peerDependenciesMeta": { - "react-native-web": { - "optional": true - } - } - }, - "node_modules/expo-video": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/expo-video/-/expo-video-3.0.16.tgz", - "integrity": "sha512-H1HlxcHGomZItqisGfW3YL/G9BHtNBfVSimDJcLuyxyU87wFnV8loO9tCjuhufkfh/aTa2sW5BYAjLjg9DvnBQ==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-web-browser": { - "version": "15.0.10", - "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-15.0.10.tgz", - "integrity": "sha512-fvDhW4bhmXAeWFNFiInmsGCK83PAqAcQaFyp/3pE/jbdKmFKoRCWr46uZGIfN4msLK/OODhaQ/+US7GSJNDHJg==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, - "node_modules/expo/node_modules/@expo/cli": { - "version": "54.0.23", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.23.tgz", - "integrity": "sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g==", - "license": "MIT", - "dependencies": { - "@0no-co/graphql.web": "^1.0.8", - "@expo/code-signing-certificates": "^0.0.6", - "@expo/config": "~12.0.13", - "@expo/config-plugins": "~54.0.4", - "@expo/devcert": "^1.2.1", - "@expo/env": "~2.0.8", - "@expo/image-utils": "^0.8.8", - "@expo/json-file": "^10.0.8", - "@expo/metro": "~54.2.0", - "@expo/metro-config": "~54.0.14", - "@expo/osascript": "^2.3.8", - "@expo/package-manager": "^1.9.10", - "@expo/plist": "^0.4.8", - "@expo/prebuild-config": "^54.0.8", - "@expo/schema-utils": "^0.1.8", - "@expo/spawn-async": "^1.7.2", - "@expo/ws-tunnel": "^1.0.1", - "@expo/xcpretty": "^4.3.0", - "@react-native/dev-middleware": "0.81.5", - "@urql/core": "^5.0.6", - "@urql/exchange-retry": "^1.3.0", - "accepts": "^1.3.8", - "arg": "^5.0.2", - "better-opn": "~3.0.2", - "bplist-creator": "0.1.0", - "bplist-parser": "^0.3.1", - "chalk": "^4.0.0", - "ci-info": "^3.3.0", - "compression": "^1.7.4", - "connect": "^3.7.0", - "debug": "^4.3.4", - "env-editor": "^0.4.1", - "expo-server": "^1.0.5", - "freeport-async": "^2.0.0", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "lan-network": "^0.1.6", - "minimatch": "^9.0.0", - "node-forge": "^1.3.3", - "npm-package-arg": "^11.0.0", - "ora": "^3.4.0", - "picomatch": "^3.0.1", - "pretty-bytes": "^5.6.0", - "pretty-format": "^29.7.0", - "progress": "^2.0.3", - "prompts": "^2.3.2", - "qrcode-terminal": "0.11.0", - "require-from-string": "^2.0.2", - "requireg": "^0.2.2", - "resolve": "^1.22.2", - "resolve-from": "^5.0.0", - "resolve.exports": "^2.0.3", - "semver": "^7.6.0", - "send": "^0.19.0", - "slugify": "^1.3.4", - "source-map-support": "~0.5.21", - "stacktrace-parser": "^0.1.10", - "structured-headers": "^0.4.1", - "tar": "^7.5.2", - "terminal-link": "^2.1.1", - "undici": "^6.18.2", - "wrap-ansi": "^7.0.0", - "ws": "^8.12.1" - }, - "bin": { - "expo-internal": "build/bin/cli" - }, - "peerDependencies": { - "expo": "*", - "expo-router": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "expo-router": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/expo/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/expo/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/expo/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/expo/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/expo/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/expo/node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/exponential-backoff": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", - "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", - "license": "Apache-2.0" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fbjs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", - "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", - "license": "MIT", - "dependencies": { - "cross-fetch": "^3.1.5", - "fbjs-css-vars": "^1.0.0", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^1.0.35" - } - }, - "node_modules/fbjs-css-vars": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", - "license": "MIT" - }, - "node_modules/fbjs/node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "license": "MIT", - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/flow-enums-runtime": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", - "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", - "license": "MIT" - }, - "node_modules/fontfaceobserver": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", - "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", - "license": "BSD-2-Clause" - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/freeport-async": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", - "integrity": "sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/getenv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", - "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/glob": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.5.tgz", - "integrity": "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/balanced-match": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.2.tgz", - "integrity": "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==", - "license": "MIT", - "dependencies": { - "jackspeak": "^4.2.3" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", - "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.1.tgz", - "integrity": "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", - "license": "MIT", - "dependencies": { - "ini": "^1.3.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hermes-estree": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", - "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", - "license": "MIT" - }, - "node_modules/hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", - "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", - "license": "MIT", - "dependencies": { - "hermes-estree": "0.29.1" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/html-encoding-sniffer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", - "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@exodus/bytes": "^1.6.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", - "license": "MIT", - "dependencies": { - "void-elements": "3.1.0" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/hyphenate-style-name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", - "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", - "license": "BSD-3-Clause" - }, - "node_modules/i18next": { - "version": "25.8.12", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.12.tgz", - "integrity": "sha512-hw59RF5QaH9i3l47hTXjeLLtzgVO8OtznlTJZbulmaLbz+itA1hIKWHTEiajY9W2SNPzvL8U5nTBVt7SsOGNRA==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4" - }, - "peerDependencies": { - "typescript": "^5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/iceberg-js": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", - "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", - "license": "MIT", - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/image-size": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", - "license": "MIT", - "dependencies": { - "queue": "6.0.2" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/inline-style-prefixer": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz", - "integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==", - "license": "MIT", - "dependencies": { - "css-in-js-utils": "^3.1.0" - } - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT" - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bun-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", - "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.7.1" - } - }, - "node_modules/is-bun-module/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/jackspeak": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", - "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^9.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jimp-compact": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", - "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", - "license": "MIT" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsc-safe-url": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", - "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", - "license": "0BSD" - }, - "node_modules/jsdom": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.1.tgz", - "integrity": "sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/css-color": "^5.0.1", - "@asamuzakjp/dom-selector": "^7.0.3", - "@bramus/specificity": "^2.4.2", - "@csstools/css-syntax-patches-for-csstree": "^1.1.1", - "@exodus/bytes": "^1.15.0", - "css-tree": "^3.2.1", - "data-urls": "^7.0.0", - "decimal.js": "^10.6.0", - "html-encoding-sniffer": "^6.0.0", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.7", - "parse5": "^8.0.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^6.0.1", - "undici": "^7.24.5", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^8.0.1", - "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^16.0.1", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/jsdom/node_modules/tr46": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/jsdom/node_modules/undici": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.5.tgz", - "integrity": "sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, - "node_modules/jsdom/node_modules/webidl-conversions": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", - "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=20" - } - }, - "node_modules/jsdom/node_modules/whatwg-url": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", - "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@exodus/bytes": "^1.11.0", - "tr46": "^6.0.0", - "webidl-conversions": "^8.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lan-network": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.1.7.tgz", - "integrity": "sha512-mnIlAEMu4OyEvUNdzco9xpuB9YVcPkQec+QsgycBCtPZvEqWPCDPfbAE4OJMdBBWpZWtpCn1xw9jJYlwjWI5zQ==", - "license": "MIT", - "bin": { - "lan-network": "dist/lan-network-cli.js" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lighthouse-logger": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", - "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", - "license": "Apache-2.0", - "dependencies": { - "debug": "^2.6.9", - "marky": "^1.2.2" - } - }, - "node_modules/lighthouse-logger/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/lighthouse-logger/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "license": "MIT", - "dependencies": { - "chalk": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/log-symbols/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magicast": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", - "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "source-map-js": "^1.2.1" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/marky": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", - "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", - "license": "Apache-2.0" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/memoize-one": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", - "license": "MIT" - }, - "node_modules/merge-options": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", - "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", - "license": "MIT", - "dependencies": { - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" - }, - "node_modules/metro": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.3.tgz", - "integrity": "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", - "@babel/types": "^7.25.2", - "accepts": "^1.3.7", - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "error-stack-parser": "^2.0.6", - "flow-enums-runtime": "^0.0.6", - "graceful-fs": "^4.2.4", - "hermes-parser": "0.32.0", - "image-size": "^1.0.2", - "invariant": "^2.2.4", - "jest-worker": "^29.7.0", - "jsc-safe-url": "^0.2.2", - "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-config": "0.83.3", - "metro-core": "0.83.3", - "metro-file-map": "0.83.3", - "metro-resolver": "0.83.3", - "metro-runtime": "0.83.3", - "metro-source-map": "0.83.3", - "metro-symbolicate": "0.83.3", - "metro-transform-plugins": "0.83.3", - "metro-transform-worker": "0.83.3", - "mime-types": "^2.1.27", - "nullthrows": "^1.1.1", - "serialize-error": "^2.1.0", - "source-map": "^0.5.6", - "throat": "^5.0.0", - "ws": "^7.5.10", - "yargs": "^17.6.2" - }, - "bin": { - "metro": "src/cli.js" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-babel-transformer": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.3.tgz", - "integrity": "sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "hermes-parser": "0.32.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-babel-transformer/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", - "license": "MIT" - }, - "node_modules/metro-babel-transformer/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", - "license": "MIT", - "dependencies": { - "hermes-estree": "0.32.0" - } - }, - "node_modules/metro-cache": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.3.tgz", - "integrity": "sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q==", - "license": "MIT", - "dependencies": { - "exponential-backoff": "^3.1.1", - "flow-enums-runtime": "^0.0.6", - "https-proxy-agent": "^7.0.5", - "metro-core": "0.83.3" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-cache-key": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.3.tgz", - "integrity": "sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-config": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.3.tgz", - "integrity": "sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA==", - "license": "MIT", - "dependencies": { - "connect": "^3.6.5", - "flow-enums-runtime": "^0.0.6", - "jest-validate": "^29.7.0", - "metro": "0.83.3", - "metro-cache": "0.83.3", - "metro-core": "0.83.3", - "metro-runtime": "0.83.3", - "yaml": "^2.6.1" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-core": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.3.tgz", - "integrity": "sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6", - "lodash.throttle": "^4.1.1", - "metro-resolver": "0.83.3" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-file-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.3.tgz", - "integrity": "sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "fb-watchman": "^2.0.0", - "flow-enums-runtime": "^0.0.6", - "graceful-fs": "^4.2.4", - "invariant": "^2.2.4", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "nullthrows": "^1.1.1", - "walker": "^1.0.7" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-minify-terser": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.3.tgz", - "integrity": "sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6", - "terser": "^5.15.0" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-resolver": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.3.tgz", - "integrity": "sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-runtime": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.3.tgz", - "integrity": "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.0", - "flow-enums-runtime": "^0.0.6" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-source-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.3.tgz", - "integrity": "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.3", - "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", - "@babel/types": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "invariant": "^2.2.4", - "metro-symbolicate": "0.83.3", - "nullthrows": "^1.1.1", - "ob1": "0.83.3", - "source-map": "^0.5.6", - "vlq": "^1.0.0" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-symbolicate": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.3.tgz", - "integrity": "sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6", - "invariant": "^2.2.4", - "metro-source-map": "0.83.3", - "nullthrows": "^1.1.1", - "source-map": "^0.5.6", - "vlq": "^1.0.0" - }, - "bin": { - "metro-symbolicate": "src/index.js" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-transform-plugins": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.3.tgz", - "integrity": "sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", - "flow-enums-runtime": "^0.0.6", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-transform-worker": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.3.tgz", - "integrity": "sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/types": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "metro": "0.83.3", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-minify-terser": "0.83.3", - "metro-source-map": "0.83.3", - "metro-transform-plugins": "0.83.3", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", - "license": "MIT" - }, - "node_modules/metro/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", - "license": "MIT", - "dependencies": { - "hermes-estree": "0.32.0" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/napi-postinstall": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", - "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", - "dev": true, - "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nested-error-stacks": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", - "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", - "license": "MIT" - }, - "node_modules/node-exports-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", - "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array.prototype.flatmap": "^1.3.3", - "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-package-arg": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", - "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", - "license": "ISC", - "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-package-arg/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nullthrows": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", - "license": "MIT" - }, - "node_modules/ob1": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.3.tgz", - "integrity": "sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", - "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", - "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", - "license": "MIT", - "dependencies": { - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-spinners": "^2.0.0", - "log-symbols": "^2.2.0", - "strip-ansi": "^5.2.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/ora/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ora/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-png": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", - "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", - "license": "MIT", - "dependencies": { - "pngjs": "^3.3.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", - "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/plist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", - "license": "MIT", - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" - }, - "engines": { - "node": ">=10.4.0" - } - }, - "node_modules/pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/posthog-react-native": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/posthog-react-native/-/posthog-react-native-4.36.0.tgz", - "integrity": "sha512-BiwUXNa5+y5SzZGaCeowca86b7D9MOrqdERzILTBYcF0FbiQS3XhM4AWmxuzdfpH4MPyGtuaCSRxJOmyna7NEA==", - "license": "MIT", - "dependencies": { - "@posthog/core": "1.23.1" - }, - "peerDependencies": { - "@react-native-async-storage/async-storage": ">=1.0.0", - "@react-navigation/native": ">= 5.0.0", - "expo-application": ">= 4.0.0", - "expo-device": ">= 4.0.0", - "expo-file-system": ">= 13.0.0", - "expo-localization": ">= 11.0.0", - "posthog-react-native-session-replay": ">= 1.3.0", - "react-native-device-info": ">= 10.0.0", - "react-native-localize": ">= 3.0.0", - "react-native-navigation": ">= 6.0.0", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-svg": ">= 15.0.0" - }, - "peerDependenciesMeta": { - "@react-native-async-storage/async-storage": { - "optional": true - }, - "@react-navigation/native": { - "optional": true - }, - "expo-application": { - "optional": true - }, - "expo-device": { - "optional": true - }, - "expo-file-system": { - "optional": true - }, - "expo-localization": { - "optional": true - }, - "posthog-react-native-session-replay": { - "optional": true - }, - "react-native-device-info": { - "optional": true - }, - "react-native-localize": { - "optional": true - }, - "react-native-navigation": { - "optional": true - }, - "react-native-safe-area-context": { - "optional": true - } - } - }, - "node_modules/posthog-react-native-session-replay": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/posthog-react-native-session-replay/-/posthog-react-native-session-replay-1.5.0.tgz", - "integrity": "sha512-3XYGSpaWDfB0s4WrZlekN+dNO/kVSWCPAUBDmayIbFfL7SJ1OTCoYQrJp+JJdm8Wf+wJmrAv7LoPOvl/mY5A0g==", - "license": "MIT", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "license": "MIT", - "dependencies": { - "asap": "~2.0.6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qrcode-terminal": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", - "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==", - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, - "node_modules/query-string": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", - "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", - "license": "MIT", - "dependencies": { - "decode-uri-component": "^0.2.2", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-devtools-core": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", - "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", - "license": "MIT", - "dependencies": { - "shell-quote": "^1.6.1", - "ws": "^7" - } - }, - "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", - "license": "MIT" - }, - "node_modules/react-freeze": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", - "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=17.0.0" - } - }, - "node_modules/react-i18next": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.4.tgz", - "integrity": "sha512-6yj+dcfMncEC21QPhOTsW8mOSO+pzFmT6uvU7XXdvM/Cp38zJkmTeMeKmTrmCMD5ToT79FmiE/mRWiYWcJYW4g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "html-parse-stringify": "^3.0.1", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "i18next": ">= 25.6.2", - "react": ">= 16.8.0", - "typescript": "^5" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/react-is": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", - "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", - "license": "MIT" - }, - "node_modules/react-native": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", - "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", - "license": "MIT", - "dependencies": { - "@jest/create-cache-key-function": "^29.7.0", - "@react-native/assets-registry": "0.81.5", - "@react-native/codegen": "0.81.5", - "@react-native/community-cli-plugin": "0.81.5", - "@react-native/gradle-plugin": "0.81.5", - "@react-native/js-polyfills": "0.81.5", - "@react-native/normalize-colors": "0.81.5", - "@react-native/virtualized-lists": "0.81.5", - "abort-controller": "^3.0.0", - "anser": "^1.4.9", - "ansi-regex": "^5.0.0", - "babel-jest": "^29.7.0", - "babel-plugin-syntax-hermes-parser": "0.29.1", - "base64-js": "^1.5.1", - "commander": "^12.0.0", - "flow-enums-runtime": "^0.0.6", - "glob": "^7.1.1", - "invariant": "^2.2.4", - "jest-environment-node": "^29.7.0", - "memoize-one": "^5.0.0", - "metro-runtime": "^0.83.1", - "metro-source-map": "^0.83.1", - "nullthrows": "^1.1.1", - "pretty-format": "^29.7.0", - "promise": "^8.3.0", - "react-devtools-core": "^6.1.5", - "react-refresh": "^0.14.0", - "regenerator-runtime": "^0.13.2", - "scheduler": "0.26.0", - "semver": "^7.1.3", - "stacktrace-parser": "^0.1.10", - "whatwg-fetch": "^3.0.0", - "ws": "^6.2.3", - "yargs": "^17.6.2" - }, - "bin": { - "react-native": "cli.js" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@types/react": "^19.1.0", - "react": "^19.1.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-native-gesture-handler": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz", - "integrity": "sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==", - "license": "MIT", - "dependencies": { - "@egjs/hammerjs": "^2.0.17", - "hoist-non-react-statics": "^3.3.0", - "invariant": "^2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-is-edge-to-edge": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz", - "integrity": "sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==", - "license": "MIT", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-purchases": { - "version": "9.10.3", - "resolved": "https://registry.npmjs.org/react-native-purchases/-/react-native-purchases-9.10.3.tgz", - "integrity": "sha512-YMdueV9SNTT8bLeFN7pFe27tBMusUP6dXit7yU0opjN2XJw//bSJYrk1snBv8e33At2i6Yq1wNXyHipth8dBgg==", - "license": "MIT", - "workspaces": [ - "examples/purchaseTesterTypescript", - "react-native-purchases-ui" - ], - "dependencies": { - "@revenuecat/purchases-js-hybrid-mappings": "17.41.0", - "@revenuecat/purchases-typescript-internal": "17.41.0" - }, - "peerDependencies": { - "react": ">= 16.6.3", - "react-native": ">= 0.73.0", - "react-native-web": "*" - }, - "peerDependenciesMeta": { - "react-native-web": { - "optional": true - } - } - }, - "node_modules/react-native-reanimated": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.6.tgz", - "integrity": "sha512-F+ZJBYiok/6Jzp1re75F/9aLzkgoQCOh4yxrnwATa8392RvM3kx+fiXXFvwcgE59v48lMwd9q0nzF1oJLXpfxQ==", - "license": "MIT", - "dependencies": { - "react-native-is-edge-to-edge": "^1.2.1", - "semver": "7.7.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0", - "react": "*", - "react-native": "*", - "react-native-worklets": ">=0.5.0" - } - }, - "node_modules/react-native-reanimated/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-native-safe-area-context": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", - "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", - "license": "MIT", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-screens": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", - "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", - "license": "MIT", - "dependencies": { - "react-freeze": "^1.0.0", - "react-native-is-edge-to-edge": "^1.2.1", - "warn-once": "^0.1.0" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-svg": { - "version": "15.12.1", - "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.1.tgz", - "integrity": "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g==", - "license": "MIT", - "dependencies": { - "css-select": "^5.1.0", - "css-tree": "^1.1.3", - "warn-once": "0.1.1" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-svg/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/react-native-svg/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "license": "CC0-1.0" - }, - "node_modules/react-native-svg/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-native-web": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz", - "integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.6", - "@react-native/normalize-colors": "^0.74.1", - "fbjs": "^3.0.4", - "inline-style-prefixer": "^7.0.1", - "memoize-one": "^6.0.0", - "nullthrows": "^1.1.1", - "postcss-value-parser": "^4.2.0", - "styleq": "^0.1.3" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-native-web/node_modules/@react-native/normalize-colors": { - "version": "0.74.89", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.89.tgz", - "integrity": "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==", - "license": "MIT" - }, - "node_modules/react-native-web/node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", - "license": "MIT" - }, - "node_modules/react-native-worklets": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz", - "integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==", - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-arrow-functions": "^7.0.0-0", - "@babel/plugin-transform-class-properties": "^7.0.0-0", - "@babel/plugin-transform-classes": "^7.0.0-0", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", - "@babel/plugin-transform-optional-chaining": "^7.0.0-0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", - "@babel/plugin-transform-template-literals": "^7.0.0-0", - "@babel/plugin-transform-unicode-regex": "^7.0.0-0", - "@babel/preset-typescript": "^7.16.7", - "convert-source-map": "^2.0.0", - "semver": "7.7.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0", - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-worklets/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-native/node_modules/@react-native/virtualized-lists": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz", - "integrity": "sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw==", - "license": "MIT", - "dependencies": { - "invariant": "^2.2.4", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@types/react": "^19.1.0", - "react": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-native/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/react-native/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/react-native/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-native/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-remove-scroll": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-test-renderer": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-19.1.0.tgz", - "integrity": "sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "react-is": "^19.1.0", - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", - "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpu-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", - "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.2", - "regjsgen": "^0.8.0", - "regjsparser": "^0.13.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.2.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", - "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.1.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requireg": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz", - "integrity": "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==", - "dependencies": { - "nested-error-stacks": "~2.0.1", - "rc": "~1.2.7", - "resolve": "~1.7.1" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/requireg/node_modules/resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", - "license": "MIT", - "dependencies": { - "path-parse": "^1.0.5" - } - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", - "license": "MIT", - "dependencies": { - "global-dirs": "^0.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/resolve-workspace-root": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.1.tgz", - "integrity": "sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==", - "license": "MIT" - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "license": "MIT", - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rolldown": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.11.tgz", - "integrity": "sha512-NRjoKMusSjfRbSYiH3VSumlkgFe7kYAa3pzVOsVYVFY3zb5d7nS+a3KGQ7hJKXuYWbzJKPVQ9Wxq2UvyK+ENpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.11" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.11", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.11", - "@rolldown/binding-darwin-x64": "1.0.0-rc.11", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.11", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.11", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.11", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.11", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.11", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.11", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.11", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.11" - } - }, - "node_modules/rtl-detect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", - "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==", - "license": "BSD-3-Clause" - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sax": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", - "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serialize-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", - "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-static/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/server-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", - "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", - "license": "MIT" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sf-symbols-typescript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz", - "integrity": "sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/simple-plist": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", - "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", - "license": "MIT", - "dependencies": { - "bplist-creator": "0.1.0", - "bplist-parser": "0.3.1", - "plist": "^3.0.5" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/slugify": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", - "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "license": "MIT" - }, - "node_modules/stacktrace-parser": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", - "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/std-env": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", - "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/stream-buffers": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", - "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", - "license": "Unlicense", - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/structured-headers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", - "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", - "license": "MIT" - }, - "node_modules/styleq": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/styleq/-/styleq-0.1.3.tgz", - "integrity": "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==", - "license": "MIT" - }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tar": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz", - "integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "license": "MIT" - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", - "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tldts": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", - "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^7.0.27" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", - "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", - "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^7.0.5" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ua-parser-js": { - "version": "1.0.41", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", - "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "MIT", - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", - "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", - "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", - "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "license": "MIT", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.0" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-latest-callback": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.6.tgz", - "integrity": "sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vaul": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", - "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-dialog": "^1.1.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/vaul/node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/vaul/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/vaul/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/vaul/node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/vaul/node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/vaul/node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/vaul/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/vite": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.2.tgz", - "integrity": "sha512-1gFhNi+bHhRE/qKZOJXACm6tX4bA3Isy9KuKF15AgSRuRazNBOJfdDemPBU16/mpMxApDPrWvZ08DcLPEoRnuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.11", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/vite/node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/vitest": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.1.tgz", - "integrity": "sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.1", - "@vitest/mocker": "4.1.1", - "@vitest/pretty-format": "4.1.1", - "@vitest/runner": "4.1.1", - "@vitest/snapshot": "4.1.1", - "@vitest/spy": "4.1.1", - "@vitest/utils": "4.1.1", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.1", - "@vitest/browser-preview": "4.1.1", - "@vitest/browser-webdriverio": "4.1.1", - "@vitest/ui": "4.1.1", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "vite": { - "optional": false - } - } - }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/vlq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", - "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", - "license": "MIT" - }, - "node_modules/void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/warn-once": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz", - "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==", - "license": "MIT" - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", - "license": "MIT" - }, - "node_modules/whatwg-mimetype": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", - "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/whatwg-url-without-unicode": { - "version": "8.0.0-3", - "resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz", - "integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==", - "license": "MIT", - "dependencies": { - "buffer": "^5.4.3", - "punycode": "^2.1.1", - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/whatwg-url-without-unicode/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wonka": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", - "integrity": "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==", - "license": "MIT" - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xcode": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", - "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", - "license": "Apache-2.0", - "dependencies": { - "simple-plist": "^1.1.0", - "uuid": "^7.0.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/xml2js": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", - "integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xml2js/node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "license": "MIT", - "engines": { - "node": ">=8.0" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zustand": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.11.tgz", - "integrity": "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==", - "license": "MIT", - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=18.0.0", - "immer": ">=9.0.6", - "react": ">=18.0.0", - "use-sync-external-store": ">=1.2.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - }, - "use-sync-external-store": { - "optional": true - } - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 3574219..0000000 --- a/package.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "name": "tabatafit", - "main": "expo-router/entry", - "version": "1.0.0", - "scripts": { - "start": "expo start", - "reset-project": "node ./scripts/reset-project.js", - "android": "expo run:android", - "ios": "expo run:ios", - "web": "expo start --web", - "lint": "expo lint", - "test": "vitest run", - "test:watch": "vitest", - "test:coverage": "vitest run --coverage", - "test:render": "vitest run --config vitest.config.render.ts", - "test:maestro": "maestro test .maestro/flows", - "test:maestro:onboarding": "maestro test .maestro/flows/onboarding.yaml", - "test:maestro:programs": "maestro test .maestro/flows/program-browse.yaml", - "test:maestro:tabs": "maestro test .maestro/flows/tab-navigation.yaml", - "test:maestro:paywall": "maestro test .maestro/flows/subscription.yaml", - "test:maestro:player": "maestro test .maestro/flows/workout-player.yaml", - "test:maestro:activity": "maestro test .maestro/flows/activity-tab.yaml", - "test:maestro:profile": "maestro test .maestro/flows/profile-settings.yaml", - "test:maestro:all": "maestro test .maestro/flows/all-tests.yaml", - "test:maestro:reset": "maestro test .maestro/flows/reset-state.yaml" - }, - "dependencies": { - "@expo-google-fonts/dm-mono": "^0.4.2", - "@expo-google-fonts/dm-serif-display": "^0.4.2", - "@expo-google-fonts/inter": "^0.4.2", - "@expo-google-fonts/outfit": "^0.4.3", - "@expo/ui": "~0.2.0-beta.9", - "@react-native-async-storage/async-storage": "^2.2.0", - "@react-navigation/bottom-tabs": "^7.4.0", - "@react-navigation/elements": "^2.6.3", - "@react-navigation/native": "^7.1.8", - "@supabase/supabase-js": "^2.98.0", - "@tanstack/query-async-storage-persister": "^5.90.24", - "@tanstack/react-query": "^5.90.21", - "@tanstack/react-query-persist-client": "^5.90.24", - "expo": "~54.0.33", - "expo-application": "~7.0.8", - "expo-av": "~16.0.8", - "expo-blur": "~15.0.8", - "expo-constants": "~18.0.13", - "expo-device": "~8.0.10", - "expo-file-system": "~19.0.21", - "expo-font": "~14.0.11", - "expo-gl": "~16.0.10", - "expo-haptics": "~15.0.8", - "expo-image": "~3.0.11", - "expo-keep-awake": "~15.0.8", - "expo-linear-gradient": "~15.0.8", - "expo-linking": "~8.0.11", - "expo-localization": "~17.0.8", - "expo-network": "~8.0.8", - "expo-notifications": "~0.32.16", - "expo-router": "~6.0.23", - "expo-sharing": "~14.0.8", - "expo-splash-screen": "~31.0.13", - "expo-status-bar": "~3.0.9", - "expo-store-review": "~9.0.9", - "expo-symbols": "~1.0.8", - "expo-system-ui": "~6.0.9", - "expo-video": "~3.0.16", - "expo-web-browser": "~15.0.10", - "i18next": "^25.8.12", - "posthog-react-native": "^4.36.0", - "posthog-react-native-session-replay": "^1.5.0", - "react": "19.1.0", - "react-dom": "19.1.0", - "react-i18next": "^16.5.4", - "react-native": "0.81.5", - "react-native-gesture-handler": "~2.28.0", - "react-native-purchases": "^9.10.3", - "react-native-reanimated": "~4.1.1", - "react-native-safe-area-context": "~5.6.0", - "react-native-screens": "~4.16.0", - "react-native-svg": "15.12.1", - "react-native-web": "~0.21.0", - "react-native-worklets": "0.5.1", - "zustand": "^5.0.11" - }, - "devDependencies": { - "@testing-library/jest-native": "^5.4.3", - "@testing-library/react-native": "^13.3.3", - "@types/react": "~19.1.0", - "@vitest/coverage-v8": "^4.1.1", - "eslint": "^9.25.0", - "eslint-config-expo": "~10.0.0", - "jsdom": "^29.0.1", - "react-test-renderer": "^19.1.0", - "typescript": "~5.9.2", - "vitest": "^4.1.1" - }, - "private": true -} diff --git a/plugins/CLAUDE.md b/plugins/CLAUDE.md deleted file mode 100644 index adfdcb1..0000000 --- a/plugins/CLAUDE.md +++ /dev/null @@ -1,7 +0,0 @@ - -# Recent Activity - - - -*No recent activity* - \ No newline at end of file diff --git a/plugins/withStoreKitConfig.js b/plugins/withStoreKitConfig.js deleted file mode 100644 index 11fb6a9..0000000 --- a/plugins/withStoreKitConfig.js +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Expo Config Plugin: StoreKit Configuration for Sandbox Testing - * - * Adds TabataFit.storekit to the Xcode project and configures the scheme - * to use it for StoreKit testing. This enables purchase testing in the - * iOS simulator without real Apple Pay charges. - */ - -const { - withXcodeProject, - withDangerousMod, -} = require('@expo/config-plugins') -const fs = require('fs') -const path = require('path') - -const STOREKIT_FILENAME = 'TabataFit.storekit' - -/** - * Step 1: Copy the .storekit file and add it to the Xcode project (project.pbxproj) - */ -function withStoreKitXcodeProject(config) { - return withXcodeProject(config, (config) => { - const project = config.modResults - const projectName = config.modRequest.projectName - const platformRoot = config.modRequest.platformProjectRoot - - // Copy .storekit file into the iOS app directory - const sourceFile = path.resolve(__dirname, '..', 'storekit', STOREKIT_FILENAME) - const destDir = path.join(platformRoot, projectName) - const destFile = path.join(destDir, STOREKIT_FILENAME) - fs.copyFileSync(sourceFile, destFile) - - // Add file to the Xcode project manually via pbxproj APIs - // Find the app's PBXGroup - const mainGroupKey = project.getFirstProject().firstProject.mainGroup - const mainGroup = project.getPBXGroupByKey(mainGroupKey) - - // Find the app target group (e.g., "tabatago") - let appGroupKey = null - if (mainGroup && mainGroup.children) { - for (const child of mainGroup.children) { - if (child.comment === projectName) { - appGroupKey = child.value - break - } - } - } - - if (!appGroupKey) { - console.warn('[withStoreKitConfig] Could not find app group in Xcode project') - return config - } - - const appGroup = project.getPBXGroupByKey(appGroupKey) - - // Check if already added - const alreadyExists = appGroup.children?.some( - (child) => child.comment === STOREKIT_FILENAME - ) - - if (!alreadyExists) { - // Generate a unique UUID for the file reference - const fileRefUuid = project.generateUuid() - - // Add PBXFileReference — NOT added to any build phase - // .storekit files are testing configs, not app resources - const pbxFileRef = project.hash.project.objects['PBXFileReference'] - pbxFileRef[fileRefUuid] = { - isa: 'PBXFileReference', - lastKnownFileType: 'text.json', - path: STOREKIT_FILENAME, - sourceTree: '""', - } - pbxFileRef[`${fileRefUuid}_comment`] = STOREKIT_FILENAME - - // Add to the app group's children (visible in Xcode navigator) - appGroup.children.push({ - value: fileRefUuid, - comment: STOREKIT_FILENAME, - }) - - console.log('[withStoreKitConfig] Added', STOREKIT_FILENAME, 'to Xcode project') - } - - return config - }) -} - -/** - * Step 2: Configure the Xcode scheme to use the StoreKit configuration - */ -function withStoreKitScheme(config) { - return withDangerousMod(config, [ - 'ios', - (config) => { - const projectName = config.modRequest.projectName - const schemePath = path.join( - config.modRequest.platformProjectRoot, - `${projectName}.xcodeproj`, - 'xcshareddata', - 'xcschemes', - `${projectName}.xcscheme` - ) - - if (!fs.existsSync(schemePath)) { - console.warn('[withStoreKitConfig] Scheme not found:', schemePath) - return config - } - - let scheme = fs.readFileSync(schemePath, 'utf8') - - // Skip if already configured - if (scheme.includes('storeKitConfigurationFileReference')) { - console.log('[withStoreKitConfig] StoreKit config already in scheme') - return config - } - - // Insert StoreKitConfigurationFileReference as a child element of LaunchAction - // The identifier path is relative to the workspace root (ios/ directory) - const storeKitRef = ` - - ` - - // Insert before the closing tag - scheme = scheme.replace( - '', - `${storeKitRef}\n ` - ) - - fs.writeFileSync(schemePath, scheme, 'utf8') - console.log('[withStoreKitConfig] Added StoreKit config to scheme') - - return config - }, - ]) -} - -/** - * Main plugin - */ -function withStoreKitConfig(config) { - config = withStoreKitXcodeProject(config) - config = withStoreKitScheme(config) - return config -} - -module.exports = withStoreKitConfig diff --git a/skills-lock.json b/skills-lock.json deleted file mode 100644 index cf87c09..0000000 --- a/skills-lock.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "version": 1, - "skills": { - "building-native-ui": { - "source": "expo/skills", - "sourceType": "github", - "computedHash": "342df93f481a0dba919f372d6c7b40d2b4bf5b51dd24363aea2e5d0bae27a6fa" - }, - "expo-api-routes": { - "source": "expo/skills", - "sourceType": "github", - "computedHash": "015c6b849507fda73fcc32d2448f033aaaaa21f5229085342b8421727a90cafb" - }, - "expo-cicd-workflows": { - "source": "expo/skills", - "sourceType": "github", - "computedHash": "700b20b575fcbe75ad238b41a0bd57938abe495e62dc53e05400712ab01ee7c0" - }, - "expo-deployment": { - "source": "expo/skills", - "sourceType": "github", - "computedHash": "9ea9f16374765c1b16764a51bd43a64098921b33f48e94d9c5c1cce24b335c10" - }, - "expo-dev-client": { - "source": "expo/skills", - "sourceType": "github", - "computedHash": "234e2633b7fbcef2d479f8fe8ab20d53d08ed3e4beec7c965da4aff5b43affe7" - }, - "expo-tailwind-setup": { - "source": "expo/skills", - "sourceType": "github", - "computedHash": "d39e806942fe880347f161056729b588a3cb0f1796270eebf52633fe11cfdce1" - }, - "native-data-fetching": { - "source": "expo/skills", - "sourceType": "github", - "computedHash": "6c14e4efb34a9c4759e8b959f82dec328f87dd89a022957c6737086984b9b106" - } - } -} diff --git a/src/__tests__/components/StyledText.test.tsx b/src/__tests__/components/StyledText.test.tsx deleted file mode 100644 index 7e3a7a2..0000000 --- a/src/__tests__/components/StyledText.test.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { describe, it, expect } from 'vitest' - -type FontWeight = 'regular' | 'medium' | 'semibold' | 'bold' - -const WEIGHT_MAP: Record = { - regular: '400', - medium: '500', - semibold: '600', - bold: '700', -} - -describe('StyledText', () => { - describe('weight mapping', () => { - it('should map regular to 400', () => { - expect(WEIGHT_MAP['regular']).toBe('400') - }) - - it('should map medium to 500', () => { - expect(WEIGHT_MAP['medium']).toBe('500') - }) - - it('should map semibold to 600', () => { - expect(WEIGHT_MAP['semibold']).toBe('600') - }) - - it('should map bold to 700', () => { - expect(WEIGHT_MAP['bold']).toBe('700') - }) - }) - - describe('default values', () => { - it('should have default size of 17', () => { - const defaultSize = 17 - expect(defaultSize).toBe(17) - }) - - it('should have default weight of regular', () => { - const defaultWeight: FontWeight = 'regular' - expect(WEIGHT_MAP[defaultWeight]).toBe('400') - }) - }) - - describe('style computation', () => { - const computeTextStyle = (size: number, weight: FontWeight, color: string) => ({ - fontSize: size, - fontWeight: WEIGHT_MAP[weight], - color, - }) - - it('should compute correct style with defaults', () => { - const style = computeTextStyle(17, 'regular', '#FFFFFF') - expect(style.fontSize).toBe(17) - expect(style.fontWeight).toBe('400') - expect(style.color).toBe('#FFFFFF') - }) - - it('should compute correct style with custom size', () => { - const style = computeTextStyle(24, 'regular', '#FFFFFF') - expect(style.fontSize).toBe(24) - }) - - it('should compute correct style with bold weight', () => { - const style = computeTextStyle(17, 'bold', '#FFFFFF') - expect(style.fontWeight).toBe('700') - }) - - it('should compute correct style with custom color', () => { - const style = computeTextStyle(17, 'regular', '#FF0000') - expect(style.color).toBe('#FF0000') - }) - - it('should compute correct style with all custom props', () => { - const style = computeTextStyle(20, 'semibold', '#5AC8FA') - expect(style.fontSize).toBe(20) - expect(style.fontWeight).toBe('600') - expect(style.color).toBe('#5AC8FA') - }) - }) - - describe('numberOfLines handling', () => { - it('should accept numberOfLines prop', () => { - const numberOfLines = 2 - expect(numberOfLines).toBe(2) - }) - - it('should handle undefined numberOfLines', () => { - const numberOfLines: number | undefined = undefined - expect(numberOfLines).toBeUndefined() - }) - }) - - describe('style merging', () => { - const mergeStyles = (baseStyle: object, customStyle: object | undefined) => { - return customStyle ? [baseStyle, customStyle] : [baseStyle] - } - - it('should merge custom style with base style', () => { - const base = { fontSize: 17, fontWeight: '400' } - const custom = { marginTop: 10 } - const merged = mergeStyles(base, custom) - - expect(merged).toHaveLength(2) - expect(merged[0]).toEqual(base) - expect(merged[1]).toEqual(custom) - }) - - it('should return only base style when no custom style', () => { - const base = { fontSize: 17, fontWeight: '400' } - const merged = mergeStyles(base, undefined) - - expect(merged).toHaveLength(1) - expect(merged[0]).toEqual(base) - }) - }) - - describe('theme color integration', () => { - const mockThemeColors = { - text: { - primary: '#FFFFFF', - secondary: '#8E8E93', - tertiary: '#636366', - }, - } - - it('should use primary text color as default', () => { - const defaultColor = mockThemeColors.text.primary - expect(defaultColor).toBe('#FFFFFF') - }) - - it('should allow color override', () => { - const customColor = '#FF0000' - const resolvedColor = customColor || mockThemeColors.text.primary - expect(resolvedColor).toBe('#FF0000') - }) - - it('should fallback to theme color when no override', () => { - const customColor: string | undefined = undefined - const resolvedColor = customColor || mockThemeColors.text.primary - expect(resolvedColor).toBe('#FFFFFF') - }) - }) -}) diff --git a/src/__tests__/components/VideoPlayer.test.tsx b/src/__tests__/components/VideoPlayer.test.tsx deleted file mode 100644 index e205e9e..0000000 --- a/src/__tests__/components/VideoPlayer.test.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest' -import { BRAND } from '../../shared/constants/colors' - -type VideoPlayerMode = 'preview' | 'background' - -interface VideoPlayerConfig { - loop: boolean - muted: boolean - volume: number -} - -function getVideoPlayerConfig(mode: VideoPlayerMode): VideoPlayerConfig { - return { - loop: true, - muted: mode === 'preview', - volume: mode === 'background' ? 0.3 : 0, - } -} - -function shouldShowGradient(videoUrl: string | undefined): boolean { - return !videoUrl -} - -function shouldPlayVideo(isPlaying: boolean, videoUrl: string | undefined): boolean { - return isPlaying && !!videoUrl -} - -describe('VideoPlayer', () => { - describe('video player configuration', () => { - it('should configure preview mode with muted audio', () => { - const config = getVideoPlayerConfig('preview') - expect(config.loop).toBe(true) - expect(config.muted).toBe(true) - expect(config.volume).toBe(0) - }) - - it('should configure background mode with low volume', () => { - const config = getVideoPlayerConfig('background') - expect(config.loop).toBe(true) - expect(config.muted).toBe(false) - expect(config.volume).toBe(0.3) - }) - - it('should always loop regardless of mode', () => { - expect(getVideoPlayerConfig('preview').loop).toBe(true) - expect(getVideoPlayerConfig('background').loop).toBe(true) - }) - }) - - describe('gradient fallback', () => { - it('should show gradient when no video URL', () => { - expect(shouldShowGradient(undefined)).toBe(true) - expect(shouldShowGradient('')).toBe(true) - }) - - it('should not show gradient when video URL exists', () => { - expect(shouldShowGradient('https://example.com/video.m3u8')).toBe(false) - expect(shouldShowGradient('https://example.com/video.mp4')).toBe(false) - }) - }) - - describe('playback control', () => { - it('should play when isPlaying is true and video exists', () => { - expect(shouldPlayVideo(true, 'https://example.com/video.mp4')).toBe(true) - }) - - it('should not play when isPlaying is false', () => { - expect(shouldPlayVideo(false, 'https://example.com/video.mp4')).toBe(false) - }) - - it('should not play when no video URL', () => { - expect(shouldPlayVideo(true, undefined)).toBe(false) - expect(shouldPlayVideo(true, '')).toBe(false) - }) - }) - - describe('default gradient colors', () => { - it('should use brand colors as default gradient', () => { - const defaultColors = [BRAND.PRIMARY, BRAND.SECONDARY] - expect(defaultColors[0]).toBe(BRAND.PRIMARY) - expect(defaultColors[1]).toBe(BRAND.SECONDARY) - }) - }) - - describe('video URL validation', () => { - it('should accept HLS streams', () => { - const hlsUrl = 'https://example.com/video.m3u8' - expect(shouldShowGradient(hlsUrl)).toBe(false) - }) - - it('should accept MP4 files', () => { - const mp4Url = 'https://example.com/video.mp4' - expect(shouldShowGradient(mp4Url)).toBe(false) - }) - - it('should handle null/undefined', () => { - expect(shouldShowGradient(null as any)).toBe(true) - expect(shouldShowGradient(undefined)).toBe(true) - }) - }) - - describe('mode-specific behavior', () => { - it('preview mode should be silent', () => { - const previewConfig = getVideoPlayerConfig('preview') - expect(previewConfig.muted || previewConfig.volume === 0).toBe(true) - }) - - it('background mode should have audible audio', () => { - const bgConfig = getVideoPlayerConfig('background') - expect(bgConfig.volume).toBeGreaterThan(0) - }) - }) -}) diff --git a/src/__tests__/components/rendering/DataDeletionModal.test.tsx b/src/__tests__/components/rendering/DataDeletionModal.test.tsx deleted file mode 100644 index 04eab39..0000000 --- a/src/__tests__/components/rendering/DataDeletionModal.test.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest' -import React from 'react' -import { render, screen, fireEvent, waitFor, act } from '@testing-library/react-native' -import { DataDeletionModal } from '@/src/shared/components/DataDeletionModal' - -describe('DataDeletionModal', () => { - const defaultProps = { - visible: true, - onDelete: vi.fn().mockResolvedValue(undefined), - onCancel: vi.fn(), - } - - beforeEach(() => { - vi.clearAllMocks() - }) - - it('renders when visible is true', () => { - render() - // Title key from i18n mock - expect(screen.getByText('dataDeletion.title')).toBeTruthy() - }) - - it('renders warning icon', () => { - render() - expect(screen.getByTestId('icon-warning')).toBeTruthy() - }) - - it('renders description and note text', () => { - render() - expect(screen.getByText('dataDeletion.description')).toBeTruthy() - expect(screen.getByText('dataDeletion.note')).toBeTruthy() - }) - - it('renders delete and cancel buttons', () => { - render() - expect(screen.getByText('dataDeletion.deleteButton')).toBeTruthy() - expect(screen.getByText('dataDeletion.cancelButton')).toBeTruthy() - }) - - it('calls onCancel when cancel button is pressed', () => { - render() - fireEvent.press(screen.getByText('dataDeletion.cancelButton')) - expect(defaultProps.onCancel).toHaveBeenCalledTimes(1) - }) - - it('calls onDelete when delete button is pressed', async () => { - render() - await act(async () => { - fireEvent.press(screen.getByText('dataDeletion.deleteButton')) - }) - expect(defaultProps.onDelete).toHaveBeenCalledTimes(1) - }) - - it('shows loading text while deleting', async () => { - let resolveDelete: () => void - const slowDelete = new Promise((resolve) => { - resolveDelete = resolve - }) - const onDelete = vi.fn(() => slowDelete) - - render() - - // Start delete - await act(async () => { - fireEvent.press(screen.getByText('dataDeletion.deleteButton')) - }) - - // Should show 'Deleting...' while in progress - expect(screen.getByText('Deleting...')).toBeTruthy() - - // Complete the delete - await act(async () => { - resolveDelete!() - }) - }) - - it('does not render content when visible is false', () => { - render() - // Modal with visible=false won't render its children - expect(screen.queryByText('dataDeletion.title')).toBeNull() - }) - - it('full modal structure snapshot', () => { - const { toJSON } = render() - expect(toJSON()).toMatchSnapshot() - }) - - it('delete button shows disabled state while deleting', async () => { - let resolveDelete: () => void - const slowDelete = new Promise((resolve) => { - resolveDelete = resolve - }) - const onDelete = vi.fn(() => slowDelete) - - const { toJSON } = render( - - ) - - await act(async () => { - fireEvent.press(screen.getByText('dataDeletion.deleteButton')) - }) - - // While deleting, the button text changes to loading state - expect(screen.getByText('Deleting...')).toBeTruthy() - - // Verify the tree has the disabled styling applied (opacity: 0.6) - const tree = toJSON() - const treeStr = JSON.stringify(tree) - expect(treeStr).toContain('"opacity":0.6') - - await act(async () => { - resolveDelete!() - }) - }) -}) diff --git a/src/__tests__/components/rendering/GlassCard.test.tsx b/src/__tests__/components/rendering/GlassCard.test.tsx deleted file mode 100644 index 1f7bf4c..0000000 --- a/src/__tests__/components/rendering/GlassCard.test.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { describe, it, expect } from 'vitest' -import React from 'react' -import { render, screen } from '@testing-library/react-native' -import { Text } from 'react-native' -import { Card, CardAccent, CardTip, GlassCard, GlassCardElevated, GlassCardInset, GlassCardTinted } from '@/src/shared/components/GlassCard' - -describe('Card', () => { - it('renders children', () => { - render( - - Hello - - ) - expect(screen.getByTestId('child')).toBeTruthy() - }) - - it('applies custom style prop to root container', () => { - const customStyle = { padding: 20 } - const { toJSON } = render( - - Content - - ) - const tree = toJSON() - const rootStyle = tree?.props?.style - expect(rootStyle).toBeDefined() - const flatStyles = Array.isArray(rootStyle) ? rootStyle : [rootStyle] - const hasPadding = flatStyles.some( - (s: any) => s && typeof s === 'object' && s.padding === 20 - ) - expect(hasPadding).toBe(true) - }) -}) - -describe('Card variants', () => { - it('renders default variant (snapshot)', () => { - const { toJSON } = render( - - Default - - ) - expect(toJSON()).toMatchSnapshot() - }) - - it('renders accent variant (snapshot)', () => { - const { toJSON } = render( - - Accent - - ) - expect(toJSON()).toMatchSnapshot() - }) - - it('renders tip variant (snapshot)', () => { - const { toJSON } = render( - - Tip - - ) - expect(toJSON()).toMatchSnapshot() - }) -}) - -describe('Backward-compatible aliases', () => { - it('GlassCard renders children', () => { - render( - - Backward compat - - ) - expect(screen.getByTestId('bc-child')).toBeTruthy() - }) - - it('GlassCardElevated renders children', () => { - render( - - Elevated - - ) - expect(screen.getByTestId('elevated-child')).toBeTruthy() - }) - - it('GlassCardInset renders children', () => { - render( - - Inset - - ) - expect(screen.getByTestId('inset-child')).toBeTruthy() - }) - - it('GlassCardTinted renders children', () => { - render( - - Tinted - - ) - expect(screen.getByTestId('tinted-child')).toBeTruthy() - }) -}) diff --git a/src/__tests__/components/rendering/OnboardingStep.test.tsx b/src/__tests__/components/rendering/OnboardingStep.test.tsx deleted file mode 100644 index e78601d..0000000 --- a/src/__tests__/components/rendering/OnboardingStep.test.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { describe, it, expect } from 'vitest' -import React from 'react' -import { render, screen } from '@testing-library/react-native' -import { Text } from 'react-native' -import { OnboardingStep } from '@/src/shared/components/OnboardingStep' - -/** - * Helper to recursively find a node in the rendered tree by its element type name. - * Returns the first match or null. - */ -function findByType(tree: any, typeName: string): any { - if (!tree) return null - if (tree.type === typeName) return tree - if (tree.children && Array.isArray(tree.children)) { - for (const child of tree.children) { - if (typeof child === 'object') { - const found = findByType(child, typeName) - if (found) return found - } - } - } - return null -} - -/** - * Helper to count nodes of a given type in the tree - */ -function countByType(tree: any, typeName: string): number { - if (!tree) return 0 - let count = tree.type === typeName ? 1 : 0 - if (tree.children && Array.isArray(tree.children)) { - for (const child of tree.children) { - if (typeof child === 'object') { - count += countByType(child, typeName) - } - } - } - return count -} - -describe('OnboardingStep', () => { - it('renders children', () => { - render( - - Welcome - - ) - expect(screen.getByTestId('child-content')).toBeTruthy() - }) - - it('renders progress bar with track and fill Views', () => { - const { toJSON } = render( - - Step 1 - - ) - const tree = toJSON() - // OnboardingStep should have: - // - A root View (container) - // - A View (progressTrack) - // - An Animated.View (progressFill) — rendered as View by mock - // - An Animated.View (content wrapper) - expect(tree).toBeTruthy() - expect(tree?.type).toBe('View') // root container - expect(tree?.children).toBeDefined() - expect(tree!.children!.length).toBeGreaterThanOrEqual(2) // progress track + content - }) - - it('step 1 of 6 snapshot', () => { - const { toJSON } = render( - - First step - - ) - expect(toJSON()).toMatchSnapshot() - }) - - it('step 6 of 6 (final step) snapshot', () => { - const { toJSON } = render( - - Final step - - ) - expect(toJSON()).toMatchSnapshot() - }) - - it('renders multiple children inside content area', () => { - render( - - Title - Description - - ) - expect(screen.getByTestId('title')).toBeTruthy() - expect(screen.getByTestId('description')).toBeTruthy() - }) - - it('does not crash with step 0 (edge case snapshot)', () => { - const { toJSON } = render( - - Edge case - - ) - // Should render without error — snapshot captures structure - expect(toJSON()).toMatchSnapshot() - }) - - it('container uses safe area top inset for paddingTop', () => { - const { toJSON } = render( - - Check padding - - ) - const tree = toJSON() - // Root container should have paddingTop accounting for safe area (mock returns top=47) - const rootStyle = tree?.props?.style - const flatStyles = Array.isArray(rootStyle) ? rootStyle : [rootStyle] - const hasPaddingTop = flatStyles.some( - (s: any) => s && typeof s === 'object' && typeof s.paddingTop === 'number' && s.paddingTop > 0 - ) - expect(hasPaddingTop).toBe(true) - }) -}) diff --git a/src/__tests__/components/rendering/SyncConsentModal.test.tsx b/src/__tests__/components/rendering/SyncConsentModal.test.tsx deleted file mode 100644 index 36a4b5b..0000000 --- a/src/__tests__/components/rendering/SyncConsentModal.test.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest' -import React from 'react' -import { render, screen, fireEvent, act } from '@testing-library/react-native' -import { SyncConsentModal } from '@/src/shared/components/SyncConsentModal' - -describe('SyncConsentModal', () => { - const defaultProps = { - visible: true, - onAccept: vi.fn().mockResolvedValue(undefined), - onDecline: vi.fn(), - } - - beforeEach(() => { - vi.clearAllMocks() - }) - - it('renders when visible is true', () => { - render() - expect(screen.getByText('sync.title')).toBeTruthy() - }) - - it('renders sparkles icon', () => { - render() - expect(screen.getByTestId('icon-sparkles')).toBeTruthy() - }) - - it('renders benefit rows', () => { - render() - expect(screen.getByText('sync.benefits.recommendations')).toBeTruthy() - expect(screen.getByText('sync.benefits.adaptive')).toBeTruthy() - expect(screen.getByText('sync.benefits.sync')).toBeTruthy() - expect(screen.getByText('sync.benefits.secure')).toBeTruthy() - }) - - it('renders benefit icons', () => { - render() - expect(screen.getByTestId('icon-trending-up')).toBeTruthy() - expect(screen.getByTestId('icon-fitness')).toBeTruthy() - expect(screen.getByTestId('icon-sync')).toBeTruthy() - expect(screen.getByTestId('icon-shield-checkmark')).toBeTruthy() - }) - - it('renders privacy note', () => { - render() - expect(screen.getByText('sync.privacy')).toBeTruthy() - }) - - it('renders primary and secondary buttons', () => { - render() - expect(screen.getByText('sync.primaryButton')).toBeTruthy() - expect(screen.getByText('sync.secondaryButton')).toBeTruthy() - }) - - it('calls onDecline when secondary button is pressed', () => { - render() - fireEvent.press(screen.getByText('sync.secondaryButton')) - expect(defaultProps.onDecline).toHaveBeenCalledTimes(1) - }) - - it('calls onAccept when primary button is pressed', async () => { - render() - await act(async () => { - fireEvent.press(screen.getByText('sync.primaryButton')) - }) - expect(defaultProps.onAccept).toHaveBeenCalledTimes(1) - }) - - it('shows loading text while accepting', async () => { - let resolveAccept: () => void - const slowAccept = new Promise((resolve) => { - resolveAccept = resolve - }) - const onAccept = vi.fn(() => slowAccept) - - render() - - await act(async () => { - fireEvent.press(screen.getByText('sync.primaryButton')) - }) - - expect(screen.getByText('Setting up...')).toBeTruthy() - - await act(async () => { - resolveAccept!() - }) - }) - - it('does not render content when visible is false', () => { - render() - expect(screen.queryByText('sync.title')).toBeNull() - }) - - it('full modal structure snapshot', () => { - const { toJSON } = render() - expect(toJSON()).toMatchSnapshot() - }) - - it('primary button shows disabled state while loading', async () => { - let resolveAccept: () => void - const slowAccept = new Promise((resolve) => { - resolveAccept = resolve - }) - const onAccept = vi.fn(() => slowAccept) - - const { toJSON } = render( - - ) - - await act(async () => { - fireEvent.press(screen.getByText('sync.primaryButton')) - }) - - // While loading, button text changes to loading state - expect(screen.getByText('Setting up...')).toBeTruthy() - - // Verify the tree has the disabled styling applied (opacity: 0.6) - const tree = toJSON() - const treeStr = JSON.stringify(tree) - expect(treeStr).toContain('"opacity":0.6') - - await act(async () => { - resolveAccept!() - }) - }) -}) diff --git a/src/__tests__/components/rendering/VideoPlayerPreview.test.tsx b/src/__tests__/components/rendering/VideoPlayerPreview.test.tsx deleted file mode 100644 index ab788a8..0000000 --- a/src/__tests__/components/rendering/VideoPlayerPreview.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { describe, it, expect } from 'vitest' -import React from 'react' -import { render } from '@testing-library/react-native' -import { VideoPlayer } from '@/src/shared/components/VideoPlayer' - -describe('VideoPlayer rendering', () => { - describe('preview mode', () => { - it('renders gradient fallback when no videoUrl', () => { - const { toJSON } = render( - - ) - const tree = toJSON() - expect(tree).toBeTruthy() - expect(tree).toMatchSnapshot() - }) - - it('renders video view when videoUrl is provided', () => { - const { toJSON } = render( - - ) - const tree = toJSON() - expect(tree).toBeTruthy() - expect(tree).toMatchSnapshot() - }) - - it('renders with custom style', () => { - const { toJSON } = render( - - ) - expect(toJSON()).toMatchSnapshot() - }) - - it('renders with testID prop', () => { - const { getByTestId } = render( - - ) - expect(getByTestId('my-video-player')).toBeTruthy() - }) - }) - - describe('background mode', () => { - it('renders gradient fallback when no videoUrl', () => { - const { toJSON } = render( - - ) - expect(toJSON()).toMatchSnapshot() - }) - - it('renders video view when videoUrl is provided', () => { - const { toJSON } = render( - - ) - expect(toJSON()).toMatchSnapshot() - }) - }) - - describe('custom gradient colors', () => { - it('renders with custom gradient colors when no video', () => { - const { toJSON } = render( - - ) - expect(toJSON()).toMatchSnapshot() - }) - }) -}) diff --git a/src/__tests__/components/rendering/__snapshots__/CollectionCard.test.tsx.snap b/src/__tests__/components/rendering/__snapshots__/CollectionCard.test.tsx.snap deleted file mode 100644 index 9444e64..0000000 --- a/src/__tests__/components/rendering/__snapshots__/CollectionCard.test.tsx.snap +++ /dev/null @@ -1,324 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`CollectionCard > renders without onPress (no crash) 1`] = ` - - - - - - - - - 💪 - - - - Upper Body Blast - - - 3 - workouts - - - -`; - -exports[`CollectionCard > snapshot with imageUrl (different rendering path) 1`] = ` - - - - - - - - - - - 💪 - - - - Upper Body Blast - - - 3 - workouts - - - -`; diff --git a/src/__tests__/components/rendering/__snapshots__/GlassCard.test.tsx.snap b/src/__tests__/components/rendering/__snapshots__/GlassCard.test.tsx.snap deleted file mode 100644 index 52ff0e4..0000000 --- a/src/__tests__/components/rendering/__snapshots__/GlassCard.test.tsx.snap +++ /dev/null @@ -1,283 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`GlassCard presets > GlassCardElevated snapshot 1`] = ` - - - - - Elevated preset - - - -`; - -exports[`GlassCard variants > renders base variant (snapshot) 1`] = ` - - - - - Base - - - -`; - -exports[`GlassCard variants > renders elevated variant (snapshot) 1`] = ` - - - - - Elevated - - - -`; - -exports[`GlassCard variants > renders inset variant (snapshot) 1`] = ` - - - - - Inset - - - -`; - -exports[`GlassCard variants > renders tinted variant (snapshot) 1`] = ` - - - - - Tinted - - - -`; diff --git a/src/__tests__/components/rendering/__snapshots__/OnboardingStep.test.tsx.snap b/src/__tests__/components/rendering/__snapshots__/OnboardingStep.test.tsx.snap deleted file mode 100644 index 345b769..0000000 --- a/src/__tests__/components/rendering/__snapshots__/OnboardingStep.test.tsx.snap +++ /dev/null @@ -1,277 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`OnboardingStep > does not crash with step 0 (edge case snapshot) 1`] = ` - - - - - - - Edge case - - - -`; - -exports[`OnboardingStep > step 1 of 6 snapshot 1`] = ` - - - - - - - First step - - - -`; - -exports[`OnboardingStep > step 6 of 6 (final step) snapshot 1`] = ` - - - - - - - Final step - - - -`; diff --git a/src/__tests__/components/rendering/__snapshots__/Skeleton.test.tsx.snap b/src/__tests__/components/rendering/__snapshots__/Skeleton.test.tsx.snap deleted file mode 100644 index 30299eb..0000000 --- a/src/__tests__/components/rendering/__snapshots__/Skeleton.test.tsx.snap +++ /dev/null @@ -1,799 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`CollectionCardSkeleton > renders correct structure (snapshot) 1`] = ` - - - - - - - - -`; - -exports[`Skeleton > renders with default dimensions (snapshot) 1`] = ` - - - -`; - -exports[`StatsCardSkeleton > renders correct structure (snapshot) 1`] = ` - - - - - - - - - - - - - -`; - -exports[`TrainerCardSkeleton > renders correct structure (snapshot) 1`] = ` - - - - - - - - - - - - - -`; - -exports[`WorkoutCardSkeleton > renders correct structure (snapshot) 1`] = ` - - - - - - - - - - - - - - - - - - -`; diff --git a/src/__tests__/components/rendering/__snapshots__/VideoPlayerPreview.test.tsx.snap b/src/__tests__/components/rendering/__snapshots__/VideoPlayerPreview.test.tsx.snap deleted file mode 100644 index 29e569a..0000000 --- a/src/__tests__/components/rendering/__snapshots__/VideoPlayerPreview.test.tsx.snap +++ /dev/null @@ -1,292 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`VideoPlayer rendering > background mode > renders gradient fallback when no videoUrl 1`] = ` - - - -`; - -exports[`VideoPlayer rendering > background mode > renders video view when videoUrl is provided 1`] = ` - - - -`; - -exports[`VideoPlayer rendering > custom gradient colors > renders with custom gradient colors when no video 1`] = ` - - - -`; - -exports[`VideoPlayer rendering > preview mode > renders gradient fallback when no videoUrl 1`] = ` - - - -`; - -exports[`VideoPlayer rendering > preview mode > renders video view when videoUrl is provided 1`] = ` - - - -`; - -exports[`VideoPlayer rendering > preview mode > renders with custom style 1`] = ` - - - -`; diff --git a/src/__tests__/features/player.test.ts b/src/__tests__/features/player.test.ts deleted file mode 100644 index 5e1f469..0000000 --- a/src/__tests__/features/player.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Player feature unit tests - * Tests constants, getCoachMessage, and component prop contracts - */ - -import { describe, it, expect } from 'vitest' -import { - TIMER_RING_SIZE, - TIMER_RING_STROKE, - COACH_MESSAGES, - getCoachMessage, -} from '../../features/player/constants' - -describe('Player constants', () => { - describe('TIMER_RING_SIZE', () => { - it('should be a positive number', () => { - expect(TIMER_RING_SIZE).toBeGreaterThan(0) - expect(TIMER_RING_SIZE).toBe(280) - }) - }) - - describe('TIMER_RING_STROKE', () => { - it('should be a positive number', () => { - expect(TIMER_RING_STROKE).toBeGreaterThan(0) - expect(TIMER_RING_STROKE).toBe(12) - }) - - it('should be smaller than half the ring size', () => { - expect(TIMER_RING_STROKE).toBeLessThan(TIMER_RING_SIZE / 2) - }) - }) - - describe('COACH_MESSAGES', () => { - it('should have early, mid, late, and prep pools', () => { - expect(COACH_MESSAGES.early).toBeDefined() - expect(COACH_MESSAGES.mid).toBeDefined() - expect(COACH_MESSAGES.late).toBeDefined() - expect(COACH_MESSAGES.prep).toBeDefined() - }) - - it('each pool should have at least one message', () => { - expect(COACH_MESSAGES.early.length).toBeGreaterThan(0) - expect(COACH_MESSAGES.mid.length).toBeGreaterThan(0) - expect(COACH_MESSAGES.late.length).toBeGreaterThan(0) - expect(COACH_MESSAGES.prep.length).toBeGreaterThan(0) - }) - - it('all messages should be non-empty strings', () => { - const allMessages = [ - ...COACH_MESSAGES.early, - ...COACH_MESSAGES.mid, - ...COACH_MESSAGES.late, - ...COACH_MESSAGES.prep, - ] - for (const msg of allMessages) { - expect(typeof msg).toBe('string') - expect(msg.length).toBeGreaterThan(0) - } - }) - }) -}) - -describe('getCoachMessage', () => { - it('should return an early message for round 1 of 10', () => { - const msg = getCoachMessage(1, 10) - expect(COACH_MESSAGES.early).toContain(msg) - }) - - it('should return an early message for round 3 of 10 (30%)', () => { - const msg = getCoachMessage(3, 10) - expect(COACH_MESSAGES.early).toContain(msg) - }) - - it('should return a mid message for round 5 of 10 (50%)', () => { - const msg = getCoachMessage(5, 10) - expect(COACH_MESSAGES.mid).toContain(msg) - }) - - it('should return a mid message for round 6 of 10 (60%)', () => { - const msg = getCoachMessage(6, 10) - expect(COACH_MESSAGES.mid).toContain(msg) - }) - - it('should return a late message for round 7 of 10 (70%)', () => { - const msg = getCoachMessage(7, 10) - expect(COACH_MESSAGES.late).toContain(msg) - }) - - it('should return a late message for round 10 of 10 (100%)', () => { - const msg = getCoachMessage(10, 10) - expect(COACH_MESSAGES.late).toContain(msg) - }) - - it('should return a string for edge case round 1 of 1', () => { - const msg = getCoachMessage(1, 1) - expect(typeof msg).toBe('string') - expect(msg.length).toBeGreaterThan(0) - }) - - it('should not throw for very large round numbers', () => { - expect(() => getCoachMessage(100, 200)).not.toThrow() - const msg = getCoachMessage(100, 200) - expect(typeof msg).toBe('string') - }) - - it('should cycle through messages deterministically', () => { - // Same round/total should always return the same message - const msg1 = getCoachMessage(3, 10) - const msg2 = getCoachMessage(3, 10) - expect(msg1).toBe(msg2) - }) - - it('boundary: 33% should be early', () => { - const msg = getCoachMessage(33, 100) - expect(COACH_MESSAGES.early).toContain(msg) - }) - - it('boundary: 34% should be mid', () => { - const msg = getCoachMessage(34, 100) - expect(COACH_MESSAGES.mid).toContain(msg) - }) - - it('boundary: 66% should be mid', () => { - const msg = getCoachMessage(66, 100) - expect(COACH_MESSAGES.mid).toContain(msg) - }) - - it('boundary: 67% should be late', () => { - const msg = getCoachMessage(67, 100) - expect(COACH_MESSAGES.late).toContain(msg) - }) -}) - -describe('Player barrel exports', () => { - // NOTE: Dynamically importing components triggers react-native/index.js - // parsing (Flow syntax) which Rolldown/Vite cannot handle. We verify - // constants are re-exported correctly (they don't import RN) and check - // that the barrel index file declares all expected export lines. - it('should re-export constants from barrel', async () => { - // Import constants directly through barrel — these don't touch RN - const { TIMER_RING_SIZE, TIMER_RING_STROKE, COACH_MESSAGES, getCoachMessage } = - await import('../../features/player/constants') - - expect(TIMER_RING_SIZE).toBe(280) - expect(TIMER_RING_STROKE).toBe(12) - expect(COACH_MESSAGES).toBeDefined() - expect(typeof getCoachMessage).toBe('function') - }) - - it('should declare all component exports in barrel index', async () => { - // Read barrel source to verify all components are listed - // This is a static check that the barrel file has the right exports - const fs = await import('node:fs') - const path = await import('node:path') - const barrelPath = path.resolve(__dirname, '../../features/player/index.ts') - const barrelSource = fs.readFileSync(barrelPath, 'utf-8') - - const expectedComponents = [ - 'TimerRing', - 'PhaseIndicator', - 'ExerciseDisplay', - 'RoundIndicator', - 'ControlButton', - 'PlayerControls', - 'BurnBar', - 'StatsOverlay', - 'CoachEncouragement', - 'NowPlaying', - ] - - for (const comp of expectedComponents) { - expect(barrelSource).toContain(`export { ${comp} }`) - } - - // Constants - expect(barrelSource).toContain('TIMER_RING_SIZE') - expect(barrelSource).toContain('TIMER_RING_STROKE') - expect(barrelSource).toContain('COACH_MESSAGES') - expect(barrelSource).toContain('getCoachMessage') - }) -}) diff --git a/src/__tests__/hooks/useAudio.test.ts b/src/__tests__/hooks/useAudio.test.ts deleted file mode 100644 index 73b2f4b..0000000 --- a/src/__tests__/hooks/useAudio.test.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest' -import { Audio } from 'expo-av' -import { useUserStore } from '../../shared/stores/userStore' - -vi.mock('expo-av', () => ({ - Audio: { - Sound: { - createAsync: vi.fn().mockResolvedValue({ - sound: { - playAsync: vi.fn(), - pauseAsync: vi.fn(), - stopAsync: vi.fn(), - unloadAsync: vi.fn(), - setPositionAsync: vi.fn(), - setVolumeAsync: vi.fn(), - }, - status: { isLoaded: true }, - }), - }, - setAudioModeAsync: vi.fn(), - }, -})) - -describe('useAudio logic', () => { - beforeEach(() => { - vi.clearAllMocks() - useUserStore.setState({ - settings: { - haptics: true, - soundEffects: true, - voiceCoaching: true, - musicEnabled: true, - musicVolume: 0.5, - reminders: false, - reminderTime: '09:00', - hasPromptedReview: false, - }, - }) - }) - - describe('audio mode configuration', () => { - it('should configure audio with correct settings', async () => { - const expectedConfig = { - playsInSilentModeIOS: true, - staysActiveInBackground: false, - shouldDuckAndroid: true, - } - - await Audio.setAudioModeAsync(expectedConfig) - - expect(Audio.setAudioModeAsync).toHaveBeenCalledWith(expectedConfig) - }) - }) - - describe('sound creation', () => { - it('should create sound with createAsync', async () => { - const mockSound = { - playAsync: vi.fn(), - setPositionAsync: vi.fn(), - unloadAsync: vi.fn(), - } - - vi.mocked(Audio.Sound.createAsync).mockResolvedValueOnce({ - sound: mockSound, - status: { isLoaded: true }, - } as any) - - const result = await Audio.Sound.createAsync({} as any) - - expect(result.sound).toBeDefined() - expect(result.status.isLoaded).toBe(true) - }) - - it('should handle sound creation failure gracefully', async () => { - vi.mocked(Audio.Sound.createAsync).mockRejectedValueOnce(new Error('Failed to load')) - - await expect(Audio.Sound.createAsync({} as any)).rejects.toThrow('Failed to load') - }) - }) - - describe('sound playback', () => { - const createSoundCallbacks = (soundEnabled: boolean) => { - const play = async (soundKey: string) => { - if (!soundEnabled) return - - try { - const { sound } = await Audio.Sound.createAsync({} as any) - await sound.setPositionAsync(0) - await sound.playAsync() - } catch (error) { - // Handle error silently - } - } - - return { - countdownBeep: () => play('countdown'), - phaseStart: () => play('phaseStart'), - workoutComplete: () => play('complete'), - } - } - - describe('when sound enabled', () => { - it('should play countdown beep', async () => { - const mockSound = { - playAsync: vi.fn(), - setPositionAsync: vi.fn(), - unloadAsync: vi.fn(), - } - vi.mocked(Audio.Sound.createAsync).mockResolvedValueOnce({ - sound: mockSound, - status: { isLoaded: true }, - } as any) - - const callbacks = createSoundCallbacks(true) - await callbacks.countdownBeep() - - expect(Audio.Sound.createAsync).toHaveBeenCalled() - expect(mockSound.setPositionAsync).toHaveBeenCalledWith(0) - expect(mockSound.playAsync).toHaveBeenCalled() - }) - - it('should play phase start sound', async () => { - const mockSound = { - playAsync: vi.fn(), - setPositionAsync: vi.fn(), - unloadAsync: vi.fn(), - } - vi.mocked(Audio.Sound.createAsync).mockResolvedValueOnce({ - sound: mockSound, - status: { isLoaded: true }, - } as any) - - const callbacks = createSoundCallbacks(true) - await callbacks.phaseStart() - - expect(Audio.Sound.createAsync).toHaveBeenCalled() - expect(mockSound.playAsync).toHaveBeenCalled() - }) - - it('should play workout complete sound', async () => { - const mockSound = { - playAsync: vi.fn(), - setPositionAsync: vi.fn(), - unloadAsync: vi.fn(), - } - vi.mocked(Audio.Sound.createAsync).mockResolvedValueOnce({ - sound: mockSound, - status: { isLoaded: true }, - } as any) - - const callbacks = createSoundCallbacks(true) - await callbacks.workoutComplete() - - expect(Audio.Sound.createAsync).toHaveBeenCalled() - expect(mockSound.playAsync).toHaveBeenCalled() - }) - }) - - describe('when sound disabled', () => { - it('should not play countdown beep', async () => { - const callbacks = createSoundCallbacks(false) - await callbacks.countdownBeep() - - expect(Audio.Sound.createAsync).not.toHaveBeenCalled() - }) - - it('should not play phase start sound', async () => { - const callbacks = createSoundCallbacks(false) - await callbacks.phaseStart() - - expect(Audio.Sound.createAsync).not.toHaveBeenCalled() - }) - - it('should not play workout complete sound', async () => { - const callbacks = createSoundCallbacks(false) - await callbacks.workoutComplete() - - expect(Audio.Sound.createAsync).not.toHaveBeenCalled() - }) - }) - }) - - describe('sound cleanup', () => { - it('should unload sound on cleanup', async () => { - const mockUnload = vi.fn() - const mockSound = { - playAsync: vi.fn(), - setPositionAsync: vi.fn(), - unloadAsync: mockUnload, - } - - vi.mocked(Audio.Sound.createAsync).mockResolvedValueOnce({ - sound: mockSound, - status: { isLoaded: true }, - } as any) - - const { sound } = await Audio.Sound.createAsync({} as any) - await sound.unloadAsync() - - expect(mockUnload).toHaveBeenCalled() - }) - }) - - describe('error handling', () => { - it('should handle playback errors gracefully', async () => { - vi.mocked(Audio.Sound.createAsync).mockRejectedValueOnce(new Error('Playback failed')) - - const play = async () => { - try { - await Audio.Sound.createAsync({} as any) - } catch { - // Silently handle - } - } - - await expect(play()).resolves.not.toThrow() - }) - }) -}) diff --git a/src/__tests__/hooks/useHaptics.test.ts b/src/__tests__/hooks/useHaptics.test.ts deleted file mode 100644 index 0fac384..0000000 --- a/src/__tests__/hooks/useHaptics.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest' -import * as Haptics from 'expo-haptics' -import { useUserStore } from '../../shared/stores/userStore' - -vi.mock('expo-haptics', () => ({ - impactAsync: vi.fn(), - ImpactFeedbackStyle: { - Light: 'light', - Medium: 'medium', - Heavy: 'heavy', - }, - notificationAsync: vi.fn(), - NotificationFeedbackType: { - Success: 'success', - Warning: 'warning', - Error: 'error', - }, - selectionAsync: vi.fn(), -})) - -describe('useHaptics logic', () => { - beforeEach(() => { - vi.clearAllMocks() - useUserStore.setState({ - settings: { - haptics: true, - soundEffects: true, - voiceCoaching: true, - musicEnabled: true, - musicVolume: 0.5, - reminders: false, - reminderTime: '09:00', - hasPromptedReview: false, - }, - }) - }) - - describe('haptic feedback functions', () => { - const createHapticCallbacks = () => { - const hapticsEnabled = useUserStore.getState().settings.haptics - - const phaseChange = () => { - if (!hapticsEnabled) return - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy) - } - - const buttonTap = () => { - if (!hapticsEnabled) return - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium) - } - - const countdownTick = () => { - if (!hapticsEnabled) return - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light) - } - - const workoutComplete = () => { - if (!hapticsEnabled) return - Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success) - } - - const selection = () => { - if (!hapticsEnabled) return - Haptics.selectionAsync() - } - - return { phaseChange, buttonTap, countdownTick, workoutComplete, selection } - } - - describe('when haptics enabled', () => { - it('should call impactAsync with Heavy for phaseChange', () => { - const callbacks = createHapticCallbacks() - callbacks.phaseChange() - - expect(Haptics.impactAsync).toHaveBeenCalledWith(Haptics.ImpactFeedbackStyle.Heavy) - }) - - it('should call impactAsync with Medium for buttonTap', () => { - const callbacks = createHapticCallbacks() - callbacks.buttonTap() - - expect(Haptics.impactAsync).toHaveBeenCalledWith(Haptics.ImpactFeedbackStyle.Medium) - }) - - it('should call impactAsync with Light for countdownTick', () => { - const callbacks = createHapticCallbacks() - callbacks.countdownTick() - - expect(Haptics.impactAsync).toHaveBeenCalledWith(Haptics.ImpactFeedbackStyle.Light) - }) - - it('should call notificationAsync with Success for workoutComplete', () => { - const callbacks = createHapticCallbacks() - callbacks.workoutComplete() - - expect(Haptics.notificationAsync).toHaveBeenCalledWith(Haptics.NotificationFeedbackType.Success) - }) - - it('should call selectionAsync for selection', () => { - const callbacks = createHapticCallbacks() - callbacks.selection() - - expect(Haptics.selectionAsync).toHaveBeenCalled() - }) - }) - - describe('when haptics disabled', () => { - beforeEach(() => { - useUserStore.setState({ - settings: { - haptics: false, - soundEffects: true, - voiceCoaching: true, - musicEnabled: true, - musicVolume: 0.5, - reminders: false, - reminderTime: '09:00', - hasPromptedReview: false, - }, - }) - }) - - it('should not call impactAsync for phaseChange', () => { - const callbacks = createHapticCallbacks() - callbacks.phaseChange() - - expect(Haptics.impactAsync).not.toHaveBeenCalled() - }) - - it('should not call impactAsync for buttonTap', () => { - const callbacks = createHapticCallbacks() - callbacks.buttonTap() - - expect(Haptics.impactAsync).not.toHaveBeenCalled() - }) - - it('should not call impactAsync for countdownTick', () => { - const callbacks = createHapticCallbacks() - callbacks.countdownTick() - - expect(Haptics.impactAsync).not.toHaveBeenCalled() - }) - - it('should not call notificationAsync for workoutComplete', () => { - const callbacks = createHapticCallbacks() - callbacks.workoutComplete() - - expect(Haptics.notificationAsync).not.toHaveBeenCalled() - }) - - it('should not call selectionAsync for selection', () => { - const callbacks = createHapticCallbacks() - callbacks.selection() - - expect(Haptics.selectionAsync).not.toHaveBeenCalled() - }) - }) - }) - - describe('feedback style mapping', () => { - it('should map phase change to heavy impact', () => { - expect(Haptics.ImpactFeedbackStyle.Heavy).toBe('heavy') - }) - - it('should map button tap to medium impact', () => { - expect(Haptics.ImpactFeedbackStyle.Medium).toBe('medium') - }) - - it('should map countdown tick to light impact', () => { - expect(Haptics.ImpactFeedbackStyle.Light).toBe('light') - }) - - it('should map workout complete to success notification', () => { - expect(Haptics.NotificationFeedbackType.Success).toBe('success') - }) - }) -}) diff --git a/src/__tests__/hooks/useMusicPlayer.test.ts b/src/__tests__/hooks/useMusicPlayer.test.ts deleted file mode 100644 index 64ef83c..0000000 --- a/src/__tests__/hooks/useMusicPlayer.test.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' -import { Audio } from 'expo-av' -import { useUserStore } from '../../shared/stores/userStore' -import type { MusicTrack } from '../../shared/services/music' -import type { MusicVibe } from '../../shared/types' - -const mockTracks: MusicTrack[] = [ - { id: '1', title: 'Energy Pulse', artist: 'Neon Dreams', duration: 240, url: '', vibe: 'electronic' }, - { id: '2', title: 'Cyber Sprint', artist: 'Digital Flux', duration: 180, url: '', vibe: 'electronic' }, - { id: '3', title: 'High Voltage', artist: 'Circuit Breakers', duration: 200, url: '', vibe: 'electronic' }, -] - -const mockHipHopTracks: MusicTrack[] = [ - { id: '4', title: 'Street Heat', artist: 'Urban Flow', duration: 210, url: '', vibe: 'hip-hop' }, -] - -function getRandomTrackIndex(tracks: MusicTrack[]): number { - if (tracks.length === 0) return -1 - return Math.floor(Math.random() * tracks.length) -} - -function getNextTrackIndex(currentIndex: number, tracksLength: number): number { - if (tracksLength <= 1) return 0 - return (currentIndex + 1) % tracksLength -} - -function clampVolume(volume: number): number { - return Math.max(0, Math.min(1, volume)) -} - -describe('useMusicPlayer', () => { - beforeEach(() => { - vi.clearAllMocks() - useUserStore.setState({ - settings: { - haptics: true, - soundEffects: true, - voiceCoaching: true, - musicEnabled: true, - musicVolume: 0.5, - reminders: false, - reminderTime: '09:00', - hasPromptedReview: false, - }, - }) - }) - - afterEach(() => { - vi.restoreAllMocks() - }) - - describe('audio mode configuration', () => { - it('should configure audio with correct settings', async () => { - const expectedConfig = { - playsInSilentModeIOS: true, - staysActiveInBackground: true, - shouldDuckAndroid: true, - interruptionModeIOS: 1, - interruptionModeAndroid: 1, - } - - await Audio.setAudioModeAsync(expectedConfig) - - expect(Audio.setAudioModeAsync).toHaveBeenCalledWith(expectedConfig) - }) - }) - - describe('track selection', () => { - it('should return valid random track index', () => { - const index = getRandomTrackIndex(mockTracks) - expect(index).toBeGreaterThanOrEqual(0) - expect(index).toBeLessThan(mockTracks.length) - }) - - it('should return -1 for empty track list', () => { - const index = getRandomTrackIndex([]) - expect(index).toBe(-1) - }) - - it('should cycle to next track', () => { - const nextIndex = getNextTrackIndex(0, 3) - expect(nextIndex).toBe(1) - }) - - it('should wrap around to first track', () => { - const nextIndex = getNextTrackIndex(2, 3) - expect(nextIndex).toBe(0) - }) - - it('should return 0 for single track list', () => { - const nextIndex = getNextTrackIndex(0, 1) - expect(nextIndex).toBe(0) - }) - }) - - describe('volume control', () => { - it('should clamp volume above 1 to 1', () => { - expect(clampVolume(1.5)).toBe(1) - }) - - it('should clamp volume below 0 to 0', () => { - expect(clampVolume(-0.5)).toBe(0) - }) - - it('should keep valid volume unchanged', () => { - expect(clampVolume(0.7)).toBe(0.7) - }) - - it('should handle edge cases', () => { - expect(clampVolume(0)).toBe(0) - expect(clampVolume(1)).toBe(1) - }) - }) - - describe('music enabled state', () => { - it('should check music enabled from store', () => { - const musicEnabled = useUserStore.getState().settings.musicEnabled - expect(musicEnabled).toBe(true) - }) - - it('should respect music disabled state', () => { - useUserStore.setState({ - settings: { - ...useUserStore.getState().settings, - musicEnabled: false, - }, - }) - - const musicEnabled = useUserStore.getState().settings.musicEnabled - expect(musicEnabled).toBe(false) - }) - }) - - describe('track filtering by vibe', () => { - it('should filter tracks by vibe', () => { - const electronicTracks = mockTracks.filter(t => t.vibe === 'electronic') - expect(electronicTracks).toHaveLength(3) - }) - - it('should return empty array for unmatched vibe', () => { - const rockTracks = mockTracks.filter(t => t.vibe === 'rock') - expect(rockTracks).toHaveLength(0) - }) - }) - - describe('playback status', () => { - it('should create sound with correct initial status', async () => { - const mockSound = { - playAsync: vi.fn(), - pauseAsync: vi.fn(), - stopAsync: vi.fn(), - unloadAsync: vi.fn(), - getStatusAsync: vi.fn().mockResolvedValue({ isLoaded: true, isPlaying: false }), - setVolumeAsync: vi.fn(), - } - - vi.mocked(Audio.Sound.createAsync).mockResolvedValueOnce({ - sound: mockSound, - status: { isLoaded: true }, - } as any) - - const result = await Audio.Sound.createAsync({} as any, { - shouldPlay: false, - volume: 0.5, - isLooping: false, - }) - - expect(result.status.isLoaded).toBe(true) - }) - }) - - describe('error handling', () => { - it('should handle empty track list', () => { - const tracks: MusicTrack[] = [] - expect(tracks.length).toBe(0) - }) - - it('should handle tracks without URL', () => { - const tracksWithoutUrl = mockTracks.filter(t => !t.url) - expect(tracksWithoutUrl).toHaveLength(3) - }) - }) - - describe('vibe type', () => { - it('should accept valid vibe types', () => { - const vibes: MusicVibe[] = ['electronic', 'hip-hop', 'pop', 'rock', 'chill'] - expect(vibes).toHaveLength(5) - }) - }) - - describe('sound cleanup', () => { - it('should unload sound on cleanup', async () => { - const mockUnload = vi.fn() - const mockSound = { - playAsync: vi.fn(), - pauseAsync: vi.fn(), - stopAsync: vi.fn(), - unloadAsync: mockUnload, - getStatusAsync: vi.fn().mockResolvedValue({ isLoaded: true }), - setVolumeAsync: vi.fn(), - } - - vi.mocked(Audio.Sound.createAsync).mockResolvedValueOnce({ - sound: mockSound, - status: { isLoaded: true }, - } as any) - - const { sound } = await Audio.Sound.createAsync({} as any) - await sound.unloadAsync() - - expect(mockUnload).toHaveBeenCalled() - }) - }) -}) diff --git a/src/__tests__/hooks/useNotifications.test.ts b/src/__tests__/hooks/useNotifications.test.ts deleted file mode 100644 index 8ece9af..0000000 --- a/src/__tests__/hooks/useNotifications.test.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { describe, it, expect, beforeEach, vi } from 'vitest' -import * as Notifications from 'expo-notifications' - -// Mock useUserStore before importing the hook -const mockUserStoreState = { - settings: { - reminders: false, - reminderTime: '09:00', - hasPromptedReview: false, - }, -} - -vi.mock('@/src/shared/stores', () => ({ - useUserStore: (selector: (s: typeof mockUserStoreState) => any) => selector(mockUserStoreState), -})) - -vi.mock('@/src/shared/i18n', () => ({ - default: { - t: (key: string) => key, - }, -})) - -// Additional expo-notifications mocks beyond setup.ts -vi.mock('expo-notifications', () => ({ - getPermissionsAsync: vi.fn(), - requestPermissionsAsync: vi.fn(), - scheduleNotificationAsync: vi.fn().mockResolvedValue('notification-id'), - cancelAllScheduledNotificationsAsync: vi.fn().mockResolvedValue(undefined), - cancelScheduledNotificationAsync: vi.fn(), - getAllScheduledNotificationsAsync: vi.fn().mockResolvedValue([]), - setNotificationHandler: vi.fn(), - SchedulableTriggerInputTypes: { - DAILY: 'daily', - }, -})) - -import { requestNotificationPermissions } from '../../shared/hooks/useNotifications' - -describe('useNotifications', () => { - beforeEach(() => { - vi.clearAllMocks() - mockUserStoreState.settings.reminders = false - mockUserStoreState.settings.reminderTime = '09:00' - }) - - describe('requestNotificationPermissions', () => { - it('should return true if permissions already granted', async () => { - vi.mocked(Notifications.getPermissionsAsync).mockResolvedValue({ - status: 'granted', - expires: 'never', - granted: true, - canAskAgain: true, - } as any) - - const result = await requestNotificationPermissions() - expect(result).toBe(true) - expect(Notifications.getPermissionsAsync).toHaveBeenCalled() - expect(Notifications.requestPermissionsAsync).not.toHaveBeenCalled() - }) - - it('should request permissions when not yet granted', async () => { - vi.mocked(Notifications.getPermissionsAsync).mockResolvedValue({ - status: 'undetermined', - expires: 'never', - granted: false, - canAskAgain: true, - } as any) - vi.mocked(Notifications.requestPermissionsAsync).mockResolvedValue({ - status: 'granted', - expires: 'never', - granted: true, - canAskAgain: true, - } as any) - - const result = await requestNotificationPermissions() - expect(result).toBe(true) - expect(Notifications.requestPermissionsAsync).toHaveBeenCalled() - }) - - it('should return false when permissions denied', async () => { - vi.mocked(Notifications.getPermissionsAsync).mockResolvedValue({ - status: 'denied', - expires: 'never', - granted: false, - canAskAgain: false, - } as any) - vi.mocked(Notifications.requestPermissionsAsync).mockResolvedValue({ - status: 'denied', - expires: 'never', - granted: false, - canAskAgain: false, - } as any) - - const result = await requestNotificationPermissions() - expect(result).toBe(false) - }) - }) - - describe('scheduling logic', () => { - // We test the scheduleDaily and cancelAll functions through their - // observable effects since they're module-private. We import the - // module dynamically to trigger the useEffect side effects. - - it('should parse time string correctly and schedule notification', async () => { - // Directly test the scheduling by calling the internal logic - // We can't directly call scheduleDaily since it's not exported, - // but we can verify the mock calls pattern - const { scheduleNotificationAsync, cancelAllScheduledNotificationsAsync } = Notifications - - // Simulate what scheduleDaily('08:30') would do - await cancelAllScheduledNotificationsAsync() - await scheduleNotificationAsync({ - identifier: 'daily-reminder', - content: { - title: 'notifications:dailyReminder.title', - body: 'notifications:dailyReminder.body', - sound: true, - }, - trigger: { - type: Notifications.SchedulableTriggerInputTypes.DAILY, - hour: 8, - minute: 30, - }, - }) - - expect(cancelAllScheduledNotificationsAsync).toHaveBeenCalled() - expect(scheduleNotificationAsync).toHaveBeenCalledWith( - expect.objectContaining({ - identifier: 'daily-reminder', - content: expect.objectContaining({ - sound: true, - }), - trigger: expect.objectContaining({ - type: 'daily', - hour: 8, - minute: 30, - }), - }) - ) - }) - - it('should handle midnight time (00:00)', () => { - const time = '00:00' - const [hour, minute] = time.split(':').map(Number) - expect(hour).toBe(0) - expect(minute).toBe(0) - }) - - it('should handle evening time (23:59)', () => { - const time = '23:59' - const [hour, minute] = time.split(':').map(Number) - expect(hour).toBe(23) - expect(minute).toBe(59) - }) - - it('should handle typical morning time (09:00)', () => { - const time = '09:00' - const [hour, minute] = time.split(':').map(Number) - expect(hour).toBe(9) - expect(minute).toBe(0) - }) - }) - - describe('useNotifications hook behavior', () => { - it('should read reminders setting from user store', () => { - mockUserStoreState.settings.reminders = true - mockUserStoreState.settings.reminderTime = '08:30' - - // Verify store is accessible - expect(mockUserStoreState.settings.reminders).toBe(true) - expect(mockUserStoreState.settings.reminderTime).toBe('08:30') - }) - - it('should have reminders disabled by default', () => { - mockUserStoreState.settings.reminders = false - expect(mockUserStoreState.settings.reminders).toBe(false) - }) - - it('should use correct default reminder time', () => { - expect(mockUserStoreState.settings.reminderTime).toBe('09:00') - }) - }) - - describe('cancelAll', () => { - it('should call cancelAllScheduledNotificationsAsync', async () => { - await Notifications.cancelAllScheduledNotificationsAsync() - expect(Notifications.cancelAllScheduledNotificationsAsync).toHaveBeenCalledTimes(1) - }) - }) -}) diff --git a/src/__tests__/hooks/usePurchases.test.ts b/src/__tests__/hooks/usePurchases.test.ts deleted file mode 100644 index 76e05db..0000000 --- a/src/__tests__/hooks/usePurchases.test.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' -import { useUserStore } from '../../shared/stores/userStore' -import { ENTITLEMENT_ID } from '../../shared/services/purchases' -import type { SubscriptionPlan } from '../../shared/types' - -interface MockCustomerInfo { - entitlements: { - active: Record - all: Record - } - activeSubscriptions: string[] - allPurchasedProductIdentifiers: string[] -} - -const mockCustomerInfoFree: MockCustomerInfo = { - entitlements: { active: {}, all: {} }, - activeSubscriptions: [], - allPurchasedProductIdentifiers: [], -} - -const mockCustomerInfoPremium: MockCustomerInfo = { - entitlements: { - active: { - [ENTITLEMENT_ID]: { - identifier: ENTITLEMENT_ID, - isActive: true, - }, - }, - all: {}, - }, - activeSubscriptions: ['tabatafit.premium.yearly'], - allPurchasedProductIdentifiers: ['tabatafit.premium.yearly'], -} - -const mockCustomerInfoMonthly: MockCustomerInfo = { - entitlements: { - active: { - [ENTITLEMENT_ID]: { - identifier: ENTITLEMENT_ID, - isActive: true, - }, - }, - all: {}, - }, - activeSubscriptions: ['tabatafit.premium.monthly'], - allPurchasedProductIdentifiers: ['tabatafit.premium.monthly'], -} - -function hasPremiumEntitlement(info: MockCustomerInfo | null): boolean { - if (!info) return false - return ENTITLEMENT_ID in info.entitlements.active -} - -function determineSubscriptionPlan(info: MockCustomerInfo): SubscriptionPlan { - if (!hasPremiumEntitlement(info)) return 'free' - - const activeSubscriptions = info.activeSubscriptions - if (activeSubscriptions.length === 0) return 'free' - - const subId = activeSubscriptions[0].toLowerCase() - if (subId.includes('yearly') || subId.includes('annual')) { - return 'premium-yearly' - } else if (subId.includes('monthly')) { - return 'premium-monthly' - } - return 'premium-yearly' -} - -describe('usePurchases', () => { - beforeEach(() => { - vi.clearAllMocks() - useUserStore.setState({ - profile: { - name: 'Test User', - email: 'test@example.com', - joinDate: new Date().toISOString(), - subscription: 'free', - onboardingCompleted: true, - fitnessLevel: 'beginner', - goal: 'strength', - weeklyFrequency: 3, - barriers: [], - syncStatus: 'never-synced', - supabaseUserId: null, - savedWorkouts: [], - }, - }) - }) - - afterEach(() => { - vi.restoreAllMocks() - }) - - describe('hasPremiumEntitlement', () => { - it('should return false for null customerInfo', () => { - expect(hasPremiumEntitlement(null)).toBe(false) - }) - - it('should return false for free user', () => { - expect(hasPremiumEntitlement(mockCustomerInfoFree)).toBe(false) - }) - - it('should return true for premium user', () => { - expect(hasPremiumEntitlement(mockCustomerInfoPremium)).toBe(true) - }) - }) - - describe('determineSubscriptionPlan', () => { - it('should return free for user without entitlement', () => { - const plan = determineSubscriptionPlan(mockCustomerInfoFree) - expect(plan).toBe('free') - }) - - it('should return premium-yearly for annual subscription', () => { - const plan = determineSubscriptionPlan(mockCustomerInfoPremium) - expect(plan).toBe('premium-yearly') - }) - - it('should return premium-monthly for monthly subscription', () => { - const plan = determineSubscriptionPlan(mockCustomerInfoMonthly) - expect(plan).toBe('premium-monthly') - }) - - it('should return free when activeSubscriptions is empty', () => { - const info: MockCustomerInfo = { - ...mockCustomerInfoPremium, - activeSubscriptions: [], - } - const plan = determineSubscriptionPlan(info) - expect(plan).toBe('free') - }) - }) - - describe('purchasePackage', () => { - it('should detect user cancellation', () => { - const error = { userCancelled: true } - expect(error.userCancelled).toBe(true) - }) - - it('should detect purchase success', () => { - const success = hasPremiumEntitlement(mockCustomerInfoPremium) - expect(success).toBe(true) - }) - - it('should detect purchase failure', () => { - const success = hasPremiumEntitlement(mockCustomerInfoFree) - expect(success).toBe(false) - }) - }) - - describe('restorePurchases', () => { - it('should return true when premium is restored', () => { - const hasPremium = hasPremiumEntitlement(mockCustomerInfoPremium) - expect(hasPremium).toBe(true) - }) - - it('should return false when no purchases to restore', () => { - const hasPremium = hasPremiumEntitlement(mockCustomerInfoFree) - expect(hasPremium).toBe(false) - }) - }) - - describe('subscription sync to store', () => { - it('should sync premium-yearly to userStore', () => { - const plan = determineSubscriptionPlan(mockCustomerInfoPremium) - useUserStore.getState().setSubscription(plan) - - expect(useUserStore.getState().profile.subscription).toBe('premium-yearly') - }) - - it('should sync premium-monthly to userStore', () => { - const plan = determineSubscriptionPlan(mockCustomerInfoMonthly) - useUserStore.getState().setSubscription(plan) - - expect(useUserStore.getState().profile.subscription).toBe('premium-monthly') - }) - - it('should sync free to userStore', () => { - const plan = determineSubscriptionPlan(mockCustomerInfoFree) - useUserStore.getState().setSubscription(plan) - - expect(useUserStore.getState().profile.subscription).toBe('free') - }) - }) - - describe('package identification', () => { - it('should identify monthly package by identifier', () => { - const pkg = { - identifier: 'monthly', - product: { identifier: 'tabatafit.premium.monthly', priceString: '$9.99' }, - } - const isMonthly = pkg.identifier === 'monthly' - expect(isMonthly).toBe(true) - }) - - it('should identify annual package by identifier', () => { - const pkg = { - identifier: 'annual', - product: { identifier: 'tabatafit.premium.yearly', priceString: '$79.99' }, - } - const isAnnual = pkg.identifier === 'annual' - expect(isAnnual).toBe(true) - }) - }) - - describe('price calculations', () => { - it('should format monthly price', () => { - const price = '$9.99' - expect(price).toBe('$9.99') - }) - - it('should format annual price', () => { - const price = '$79.99' - expect(price).toBe('$79.99') - }) - - it('should calculate annual savings', () => { - const monthlyPrice = 9.99 - const annualPrice = 79.99 - const yearlyFromMonthly = monthlyPrice * 12 - const savings = yearlyFromMonthly - annualPrice - const savingsPercent = Math.round((savings / yearlyFromMonthly) * 100) - - expect(savings).toBeCloseTo(39.89, 0) - expect(savingsPercent).toBe(33) - }) - - it('should calculate monthly equivalent of annual', () => { - const annualPrice = 79.99 - const monthlyEquivalent = annualPrice / 12 - expect(monthlyEquivalent).toBeCloseTo(6.67, 1) - }) - }) - - describe('entitlement ID', () => { - it('should use correct entitlement ID', () => { - expect(ENTITLEMENT_ID).toBe('1000 Corp Pro') - }) - }) -}) diff --git a/src/__tests__/hooks/useTimer.integration.test.ts b/src/__tests__/hooks/useTimer.integration.test.ts deleted file mode 100644 index 72f924e..0000000 --- a/src/__tests__/hooks/useTimer.integration.test.ts +++ /dev/null @@ -1,460 +0,0 @@ -/** - * useTimer integration tests - * - * Tests the timer's phase-transition state machine by simulating interval ticks - * through the playerStore. Because renderHook from @testing-library/react-native - * tries to import real react-native (with Flow syntax that Vite/Rolldown can't - * parse), we replicate the interval-tick logic from useTimer.ts directly here - * and drive it with vi.advanceTimersByTime. - * - * This gives us true integration coverage of PREP→WORK→REST→COMPLETE transitions, - * calorie accumulation, skip, pause/resume, and progress calculation — without - * needing a React render tree. - */ - -import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest' -import { usePlayerStore } from '../../shared/stores/playerStore' -import type { Workout } from '../../shared/types' - -// --------------------------------------------------------------------------- -// Helpers that mirror the core useTimer logic (src/shared/hooks/useTimer.ts) -// --------------------------------------------------------------------------- - -const mockWorkout: Workout = { - id: 'integration-test', - title: 'Integration Test Workout', - trainerId: 'trainer-1', - category: 'full-body', - level: 'Beginner', - duration: 4, - calories: 48, - rounds: 4, - prepTime: 3, - workTime: 5, - restTime: 3, - equipment: [], - musicVibe: 'electronic', - exercises: [ - { name: 'Jumping Jacks', duration: 5 }, - { name: 'Squats', duration: 5 }, - { name: 'Push-ups', duration: 5 }, - { name: 'High Knees', duration: 5 }, - ], -} - -/** Replicates the setInterval tick logic from useTimer.ts */ -function tick(workout: Workout): void { - const s = usePlayerStore.getState() - - // Don't tick when paused or complete - if (s.isPaused || s.phase === 'COMPLETE') return - - if (s.timeRemaining <= 0) { - if (s.phase === 'PREP') { - s.setPhase('WORK') - s.setTimeRemaining(workout.workTime) - } else if (s.phase === 'WORK') { - const caloriesPerRound = Math.round(workout.calories / workout.rounds) - s.addCalories(caloriesPerRound) - s.setPhase('REST') - s.setTimeRemaining(workout.restTime) - } else if (s.phase === 'REST') { - if (s.currentRound >= workout.rounds) { - s.setPhase('COMPLETE') - s.setTimeRemaining(0) - s.setRunning(false) - } else { - s.setPhase('WORK') - s.setTimeRemaining(workout.workTime) - s.setCurrentRound(s.currentRound + 1) - } - } - } else { - s.setTimeRemaining(s.timeRemaining - 1) - } -} - -/** Replicates the skip logic from useTimer.ts */ -function skip(workout: Workout): void { - const s = usePlayerStore.getState() - if (s.phase === 'PREP') { - s.setPhase('WORK') - s.setTimeRemaining(workout.workTime) - } else if (s.phase === 'WORK') { - s.setPhase('REST') - s.setTimeRemaining(workout.restTime) - } else if (s.phase === 'REST') { - if (s.currentRound >= workout.rounds) { - s.setPhase('COMPLETE') - s.setTimeRemaining(0) - s.setRunning(false) - } else { - s.setPhase('WORK') - s.setTimeRemaining(workout.workTime) - s.setCurrentRound(s.currentRound + 1) - } - } -} - -function getPhaseDuration(phase: string, workout: Workout): number { - switch (phase) { - case 'PREP': return workout.prepTime - case 'WORK': return workout.workTime - case 'REST': return workout.restTime - default: return 0 - } -} - -function calcProgress(timeRemaining: number, phaseDuration: number): number { - return phaseDuration > 0 ? 1 - timeRemaining / phaseDuration : 1 -} - -function currentExercise(round: number, workout: Workout): string { - const idx = (round - 1) % workout.exercises.length - return workout.exercises[idx]?.name ?? '' -} - -function nextExercise(round: number, workout: Workout): string | undefined { - const idx = round % workout.exercises.length - return workout.exercises[idx]?.name -} - -/** Start an interval that calls tick() every 1 s (fake-timer aware) */ -let intervalId: ReturnType | null = null - -function startInterval(workout: Workout): void { - stopInterval() - intervalId = setInterval(() => tick(workout), 1000) -} - -function stopInterval(): void { - if (intervalId !== null) { - clearInterval(intervalId) - intervalId = null - } -} - -/** Advance fake timers by `seconds` full seconds */ -function advanceSeconds(seconds: number): void { - vi.advanceTimersByTime(seconds * 1000) -} - -// --------------------------------------------------------------------------- -// Tests -// --------------------------------------------------------------------------- - -describe('useTimer integration', () => { - beforeEach(() => { - vi.useFakeTimers() - usePlayerStore.getState().reset() - usePlayerStore.getState().loadWorkout(mockWorkout) - }) - - afterEach(() => { - stopInterval() - vi.useRealTimers() - vi.clearAllMocks() - }) - - // ── Initial state ────────────────────────────────────────────────────── - - describe('initial state', () => { - it('should initialize in PREP phase with correct time', () => { - const s = usePlayerStore.getState() - expect(s.phase).toBe('PREP') - expect(s.timeRemaining).toBe(mockWorkout.prepTime) - expect(s.currentRound).toBe(1) - expect(s.isRunning).toBe(false) - expect(s.isPaused).toBe(false) - expect(s.calories).toBe(0) - }) - - it('should show correct exercise for round 1', () => { - expect(currentExercise(1, mockWorkout)).toBe('Jumping Jacks') - }) - - it('should return totalRounds from workout', () => { - expect(mockWorkout.rounds).toBe(4) - }) - - it('should calculate progress as 0 at phase start', () => { - const s = usePlayerStore.getState() - const dur = getPhaseDuration(s.phase, mockWorkout) - expect(calcProgress(s.timeRemaining, dur)).toBe(0) - }) - }) - - // ── Start / Pause / Resume ───────────────────────────────────────────── - - describe('start / pause / resume', () => { - it('should start timer when start is called', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - s.setPaused(false) - startInterval(mockWorkout) - - expect(usePlayerStore.getState().isRunning).toBe(true) - expect(usePlayerStore.getState().isPaused).toBe(false) - }) - - it('should pause timer', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - s.setPaused(true) - - expect(usePlayerStore.getState().isRunning).toBe(true) - expect(usePlayerStore.getState().isPaused).toBe(true) - }) - - it('should resume timer after pause', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - s.setPaused(true) - s.setPaused(false) - - expect(usePlayerStore.getState().isPaused).toBe(false) - }) - - it('should stop and reset timer', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - - advanceSeconds(2) // advance a bit - - stopInterval() - usePlayerStore.getState().reset() - - const after = usePlayerStore.getState() - expect(after.isRunning).toBe(false) - expect(after.phase).toBe('PREP') - expect(after.calories).toBe(0) - }) - - it('should not tick when paused', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - s.setPaused(true) - - const timeBefore = usePlayerStore.getState().timeRemaining - - advanceSeconds(5) // 5 ticks fire but tick() early-returns because isPaused - - expect(usePlayerStore.getState().timeRemaining).toBe(timeBefore) - }) - }) - - // ── Countdown & Phase Transitions ────────────────────────────────────── - - describe('countdown', () => { - it('should decrement timeRemaining each second', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - - const initial = usePlayerStore.getState().timeRemaining - - advanceSeconds(1) - - expect(usePlayerStore.getState().timeRemaining).toBe(initial - 1) - }) - - it('should transition from PREP to WORK when time expires', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - - // PREP is 3s: tick at 1s→2, 2s→1, 3s→0, 4s triggers transition - advanceSeconds(mockWorkout.prepTime + 1) - - const after = usePlayerStore.getState() - expect(after.phase).toBe('WORK') - expect(after.timeRemaining).toBeLessThanOrEqual(mockWorkout.workTime) - }) - - it('should transition from WORK to REST and add calories', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - - // Through PREP (3s + 1 transition tick) - advanceSeconds(mockWorkout.prepTime + 1) - expect(usePlayerStore.getState().phase).toBe('WORK') - - // Through WORK (5s + 1 transition tick) - advanceSeconds(mockWorkout.workTime + 1) - - const after = usePlayerStore.getState() - expect(after.phase).toBe('REST') - expect(after.calories).toBeGreaterThan(0) - }) - - it('should advance rounds after REST phase', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - - // PREP→WORK→REST→WORK(round 2) - // prep: 3+1, work: 5+1, rest: 3+1 = 14s - advanceSeconds(mockWorkout.prepTime + 1 + mockWorkout.workTime + 1 + mockWorkout.restTime + 1) - - const after = usePlayerStore.getState() - expect(after.currentRound).toBeGreaterThanOrEqual(2) - expect(after.phase).not.toBe('COMPLETE') - }) - }) - - // ── Workout Completion ───────────────────────────────────────────────── - - describe('workout completion', () => { - it('should complete after all rounds', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - - // Total = prep + (work + rest) * rounds + enough transition ticks - // Each phase needs +1 tick for the transition at 0 - // PREP: 3+1 = 4 - // Per round: WORK 5+1 + REST 3+1 = 10 (except last round REST→COMPLETE) - // 4 rounds × 10 + 4 (prep) = 44, add generous buffer - advanceSeconds(60) - - const after = usePlayerStore.getState() - expect(after.phase).toBe('COMPLETE') - expect(after.isRunning).toBe(false) - }) - - it('should accumulate calories for all rounds', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - - advanceSeconds(60) - - const after = usePlayerStore.getState() - const caloriesPerRound = Math.round(mockWorkout.calories / mockWorkout.rounds) - expect(after.calories).toBe(caloriesPerRound * mockWorkout.rounds) - }) - }) - - // ── Skip ─────────────────────────────────────────────────────────────── - - describe('skip', () => { - it('should skip from PREP to WORK', () => { - skip(mockWorkout) - - const after = usePlayerStore.getState() - expect(after.phase).toBe('WORK') - expect(after.timeRemaining).toBe(mockWorkout.workTime) - }) - - it('should skip from WORK to REST', () => { - skip(mockWorkout) // PREP → WORK - skip(mockWorkout) // WORK → REST - - const after = usePlayerStore.getState() - expect(after.phase).toBe('REST') - expect(after.timeRemaining).toBe(mockWorkout.restTime) - }) - - it('should skip from REST to next WORK round', () => { - skip(mockWorkout) // PREP → WORK - skip(mockWorkout) // WORK → REST - skip(mockWorkout) // REST → WORK (round 2) - - const after = usePlayerStore.getState() - expect(after.phase).toBe('WORK') - expect(after.currentRound).toBe(2) - }) - - it('should complete when skipping REST on final round', () => { - // Manually set to final round REST - const s = usePlayerStore.getState() - s.setCurrentRound(mockWorkout.rounds) - s.setPhase('REST') - s.setTimeRemaining(mockWorkout.restTime) - - skip(mockWorkout) - - const after = usePlayerStore.getState() - expect(after.phase).toBe('COMPLETE') - expect(after.isRunning).toBe(false) - }) - }) - - // ── Progress ─────────────────────────────────────────────────────────── - - describe('progress calculation', () => { - it('should be 0 at phase start', () => { - const s = usePlayerStore.getState() - const dur = getPhaseDuration(s.phase, mockWorkout) - expect(calcProgress(s.timeRemaining, dur)).toBe(0) - }) - - it('should increase as time counts down', () => { - const s = usePlayerStore.getState() - s.setRunning(true) - startInterval(mockWorkout) - - advanceSeconds(1) - - const after = usePlayerStore.getState() - const dur = getPhaseDuration(after.phase, mockWorkout) - const progress = calcProgress(after.timeRemaining, dur) - - expect(progress).toBeGreaterThan(0) - expect(progress).toBeLessThan(1) - }) - - it('should be 1 when COMPLETE (phaseDuration 0)', () => { - const progress = calcProgress(0, 0) - expect(progress).toBe(1) - }) - }) - - // ── Next Exercise ────────────────────────────────────────────────────── - - describe('nextExercise', () => { - it('should return next exercise based on round', () => { - // Round 1 → next is index 1 = Squats - expect(nextExercise(1, mockWorkout)).toBe('Squats') - }) - - it('should cycle back to first exercise', () => { - // Round 4 → next is index 0 = Jumping Jacks - expect(nextExercise(4, mockWorkout)).toBe('Jumping Jacks') - }) - - it('should only be shown during REST phase (hook returns undefined otherwise)', () => { - // Simulate what the hook does: nextExercise only when phase === 'REST' - const s = usePlayerStore.getState() - const showNext = s.phase === 'REST' ? nextExercise(s.currentRound, mockWorkout) : undefined - expect(showNext).toBeUndefined() // phase is PREP - - s.setPhase('REST') - const showNextRest = usePlayerStore.getState().phase === 'REST' - ? nextExercise(usePlayerStore.getState().currentRound, mockWorkout) - : undefined - expect(showNextRest).toBeDefined() - }) - }) - - // ── Exercise cycling ─────────────────────────────────────────────────── - - describe('exercise cycling', () => { - it('should return correct exercise per round', () => { - expect(currentExercise(1, mockWorkout)).toBe('Jumping Jacks') - expect(currentExercise(2, mockWorkout)).toBe('Squats') - expect(currentExercise(3, mockWorkout)).toBe('Push-ups') - expect(currentExercise(4, mockWorkout)).toBe('High Knees') - }) - - it('should wrap around when rounds exceed exercise count', () => { - expect(currentExercise(5, mockWorkout)).toBe('Jumping Jacks') - expect(currentExercise(8, mockWorkout)).toBe('High Knees') - }) - }) -}) diff --git a/src/__tests__/hooks/useTimer.test.ts b/src/__tests__/hooks/useTimer.test.ts deleted file mode 100644 index 779b3b1..0000000 --- a/src/__tests__/hooks/useTimer.test.ts +++ /dev/null @@ -1,428 +0,0 @@ -import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest' -import { usePlayerStore } from '../../shared/stores/playerStore' -import type { Workout } from '../../shared/types' - -const mockWorkout: Workout = { - id: 'test-workout', - title: 'Test Workout', - trainerId: 'trainer-1', - category: 'full-body', - level: 'Beginner', - duration: 4, - calories: 48, - rounds: 8, - prepTime: 10, - workTime: 20, - restTime: 10, - equipment: [], - musicVibe: 'electronic', - exercises: [ - { name: 'Jumping Jacks', duration: 20 }, - { name: 'Squats', duration: 20 }, - { name: 'Push-ups', duration: 20 }, - { name: 'High Knees', duration: 20 }, - ], -} - -function getExerciseForRound(round: number, exercises: typeof mockWorkout.exercises): string { - const index = (round - 1) % exercises.length - return exercises[index]?.name ?? '' -} - -function getNextExercise(round: number, exercises: typeof mockWorkout.exercises): string | undefined { - const index = round % exercises.length - return exercises[index]?.name -} - -function calculateProgress(timeRemaining: number, phaseDuration: number): number { - return phaseDuration > 0 ? 1 - timeRemaining / phaseDuration : 1 -} - -function getPhaseDuration(phase: string, workout: typeof mockWorkout): number { - switch (phase) { - case 'PREP': return workout.prepTime - case 'WORK': return workout.workTime - case 'REST': return workout.restTime - default: return 0 - } -} - -describe('useTimer', () => { - beforeEach(() => { - vi.useFakeTimers() - usePlayerStore.getState().reset() - }) - - afterEach(() => { - vi.useRealTimers() - vi.clearAllMocks() - }) - - describe('initialization', () => { - it('should have correct default values', () => { - const state = usePlayerStore.getState() - - expect(state.phase).toBe('PREP') - expect(state.timeRemaining).toBe(10) - expect(state.currentRound).toBe(1) - expect(state.isPaused).toBe(false) - expect(state.isRunning).toBe(false) - expect(state.calories).toBe(0) - }) - - it('should load workout and set prepTime', () => { - usePlayerStore.getState().loadWorkout(mockWorkout) - - const state = usePlayerStore.getState() - expect(state.workout).toEqual(mockWorkout) - expect(state.timeRemaining).toBe(mockWorkout.prepTime) - }) - - it('should return correct totalRounds from workout', () => { - const totalRounds = mockWorkout.rounds - expect(totalRounds).toBe(8) - }) - - it('should return correct currentExercise for round 1', () => { - const exercise = getExerciseForRound(1, mockWorkout.exercises) - expect(exercise).toBe('Jumping Jacks') - }) - }) - - describe('progress calculation', () => { - it('should calculate progress as 0 at start', () => { - usePlayerStore.getState().loadWorkout(mockWorkout) - const state = usePlayerStore.getState() - - const phaseDuration = getPhaseDuration(state.phase, mockWorkout) - const progress = calculateProgress(state.timeRemaining, phaseDuration) - - expect(progress).toBe(0) - }) - - it('should calculate progress correctly mid-phase', () => { - usePlayerStore.getState().loadWorkout(mockWorkout) - usePlayerStore.getState().setTimeRemaining(5) - - const state = usePlayerStore.getState() - const phaseDuration = getPhaseDuration(state.phase, mockWorkout) - const progress = calculateProgress(state.timeRemaining, phaseDuration) - - expect(progress).toBe(0.5) - }) - - it('should calculate progress as 1 when time is 0', () => { - usePlayerStore.getState().loadWorkout(mockWorkout) - usePlayerStore.getState().setTimeRemaining(0) - - const state = usePlayerStore.getState() - const phaseDuration = getPhaseDuration(state.phase, mockWorkout) - const progress = calculateProgress(state.timeRemaining, phaseDuration) - - expect(progress).toBe(1) - }) - }) - - describe('exercise tracking', () => { - it('should return correct exercise for each round', () => { - const expectedOrder = [ - 'Jumping Jacks', 'Squats', 'Push-ups', 'High Knees', - 'Jumping Jacks', 'Squats', 'Push-ups', 'High Knees' - ] - - for (let i = 0; i < 8; i++) { - const round = i + 1 - const exercise = getExerciseForRound(round, mockWorkout.exercises) - expect(exercise).toBe(expectedOrder[i]) - } - }) - - it('should cycle through exercises continuously', () => { - expect(getExerciseForRound(1, mockWorkout.exercises)).toBe('Jumping Jacks') - expect(getExerciseForRound(5, mockWorkout.exercises)).toBe('Jumping Jacks') - expect(getExerciseForRound(9, mockWorkout.exercises)).toBe('Jumping Jacks') - }) - }) - - describe('controls', () => { - describe('start', () => { - it('should set running and clear pause', () => { - const store = usePlayerStore.getState() - - store.setRunning(true) - store.setPaused(false) - - const state = usePlayerStore.getState() - expect(state.isRunning).toBe(true) - expect(state.isPaused).toBe(false) - }) - }) - - describe('pause', () => { - it('should set paused state', () => { - const store = usePlayerStore.getState() - - store.setRunning(true) - store.setPaused(true) - - const state = usePlayerStore.getState() - expect(state.isRunning).toBe(true) - expect(state.isPaused).toBe(true) - }) - }) - - describe('resume', () => { - it('should clear paused state', () => { - const store = usePlayerStore.getState() - - store.setRunning(true) - store.setPaused(true) - store.setPaused(false) - - const state = usePlayerStore.getState() - expect(state.isPaused).toBe(false) - }) - }) - - describe('stop', () => { - it('should reset all state', () => { - const store = usePlayerStore.getState() - - store.loadWorkout(mockWorkout) - store.setRunning(true) - store.addCalories(25) - store.setPhase('WORK') - store.reset() - - const state = usePlayerStore.getState() - expect(state.isRunning).toBe(false) - expect(state.phase).toBe('PREP') - expect(state.calories).toBe(0) - }) - }) - }) - - describe('skip functionality', () => { - it('should skip from PREP to WORK', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - expect(store.phase).toBe('PREP') - - store.setPhase('WORK') - store.setTimeRemaining(mockWorkout.workTime) - - const state = usePlayerStore.getState() - expect(state.phase).toBe('WORK') - expect(state.timeRemaining).toBe(mockWorkout.workTime) - }) - - it('should skip from WORK to REST', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - store.setPhase('WORK') - - store.setPhase('REST') - store.setTimeRemaining(mockWorkout.restTime) - - const state = usePlayerStore.getState() - expect(state.phase).toBe('REST') - expect(state.timeRemaining).toBe(mockWorkout.restTime) - }) - - it('should skip from REST to next WORK round', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - store.setPhase('REST') - store.setCurrentRound(1) - - store.setPhase('WORK') - store.setTimeRemaining(mockWorkout.workTime) - store.setCurrentRound(2) - - const state = usePlayerStore.getState() - expect(state.phase).toBe('WORK') - expect(state.currentRound).toBe(2) - }) - - it('should complete workout when skipping REST on final round', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - store.setPhase('REST') - store.setCurrentRound(mockWorkout.rounds) - - store.setPhase('COMPLETE') - store.setRunning(false) - - const state = usePlayerStore.getState() - expect(state.phase).toBe('COMPLETE') - expect(state.isRunning).toBe(false) - }) - }) - - describe('timer tick simulation', () => { - it('should decrement timeRemaining', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - - const initial = store.timeRemaining - store.setTimeRemaining(initial - 1) - - expect(usePlayerStore.getState().timeRemaining).toBe(initial - 1) - }) - - it('should transition from PREP to WORK when time expires', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - store.setRunning(true) - - store.setTimeRemaining(0) - store.setPhase('WORK') - store.setTimeRemaining(mockWorkout.workTime) - - const state = usePlayerStore.getState() - expect(state.phase).toBe('WORK') - expect(state.timeRemaining).toBe(mockWorkout.workTime) - }) - - it('should transition from WORK to REST and add calories', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - store.setPhase('WORK') - store.setRunning(true) - - store.setTimeRemaining(0) - const caloriesPerRound = Math.round(mockWorkout.calories / mockWorkout.rounds) - store.addCalories(caloriesPerRound) - store.setPhase('REST') - store.setTimeRemaining(mockWorkout.restTime) - - const state = usePlayerStore.getState() - expect(state.phase).toBe('REST') - expect(state.timeRemaining).toBe(mockWorkout.restTime) - - const expectedCalories = Math.round(mockWorkout.calories / mockWorkout.rounds) - expect(state.calories).toBe(expectedCalories) - }) - - it('should transition from REST to WORK for next round', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - store.setPhase('REST') - store.setCurrentRound(1) - store.setRunning(true) - - store.setTimeRemaining(0) - store.setPhase('WORK') - store.setTimeRemaining(mockWorkout.workTime) - store.setCurrentRound(2) - - const state = usePlayerStore.getState() - expect(state.phase).toBe('WORK') - expect(state.currentRound).toBe(2) - }) - - it('should complete workout after final round', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - store.setPhase('REST') - store.setCurrentRound(mockWorkout.rounds) - store.setRunning(true) - - store.setTimeRemaining(0) - store.setPhase('COMPLETE') - store.setTimeRemaining(0) - store.setRunning(false) - - const state = usePlayerStore.getState() - expect(state.phase).toBe('COMPLETE') - expect(state.isRunning).toBe(false) - }) - }) - - describe('pause/resume behavior', () => { - it('should not update timeRemaining when paused', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - store.setRunning(true) - store.setPaused(true) - - const pausedTime = store.timeRemaining - - vi.advanceTimersByTime(5000) - - expect(usePlayerStore.getState().timeRemaining).toBe(pausedTime) - }) - - it('should resume timer when resumed', () => { - const store = usePlayerStore.getState() - store.loadWorkout(mockWorkout) - store.setRunning(true) - store.setPaused(false) - - expect(store.isPaused).toBe(false) - }) - }) - - describe('isComplete flag', () => { - it('should be false initially', () => { - const store = usePlayerStore.getState() - expect(store.phase === 'COMPLETE').toBe(false) - }) - - it('should be true when phase is COMPLETE', () => { - const store = usePlayerStore.getState() - store.setPhase('COMPLETE') - - expect(usePlayerStore.getState().phase === 'COMPLETE').toBe(true) - }) - }) - - describe('nextExercise', () => { - it('should return next exercise during REST phase', () => { - const nextExercise = getNextExercise(1, mockWorkout.exercises) - expect(nextExercise).toBe('Squats') - }) - - it('should cycle to first exercise after last', () => { - const nextExercise = getNextExercise(4, mockWorkout.exercises) - expect(nextExercise).toBe('Jumping Jacks') - }) - }) - - describe('calorie tracking', () => { - it('should accumulate calories for each WORK phase completed', () => { - const store = usePlayerStore.getState() - const caloriesPerRound = Math.round(mockWorkout.calories / mockWorkout.rounds) - - store.addCalories(caloriesPerRound) - expect(usePlayerStore.getState().calories).toBe(caloriesPerRound) - - store.addCalories(caloriesPerRound) - expect(usePlayerStore.getState().calories).toBe(caloriesPerRound * 2) - }) - }) - - describe('startedAt tracking', () => { - it('should set startedAt when first running', () => { - const store = usePlayerStore.getState() - const beforeSet = Date.now() - - store.setRunning(true) - - const state = usePlayerStore.getState() - expect(state.startedAt).toBeGreaterThanOrEqual(beforeSet) - }) - - it('should not update startedAt if already set', () => { - const store = usePlayerStore.getState() - - store.setRunning(true) - const firstStartedAt = usePlayerStore.getState().startedAt - - store.setRunning(false) - store.setRunning(true) - - expect(usePlayerStore.getState().startedAt).toBe(firstStartedAt) - }) - }) -}) diff --git a/src/__tests__/mocks/preload-rn-mock.cjs b/src/__tests__/mocks/preload-rn-mock.cjs deleted file mode 100644 index 6573918..0000000 --- a/src/__tests__/mocks/preload-rn-mock.cjs +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Node.js --require preload script - * - * Patches Module._resolveFilename so that ANY require('react-native') call - * (including CJS requires from @testing-library/react-native's build files) - * gets redirected to our compiled mock at react-native.cjs. - * - * This runs before vitest starts, so it intercepts at the earliest possible point. - */ -'use strict' - -const Module = require('module') -const path = require('path') - -const mockPath = path.resolve(__dirname, 'react-native.cjs') -const originalResolveFilename = Module._resolveFilename - -Module._resolveFilename = function (request, parent, isMain, options) { - // Intercept 'react-native' and any subpath like 'react-native/index' - if (request === 'react-native' || request.startsWith('react-native/')) { - return mockPath - } - return originalResolveFilename.call(this, request, parent, isMain, options) -} diff --git a/src/__tests__/mocks/react-native.cjs b/src/__tests__/mocks/react-native.cjs deleted file mode 100644 index bf97d62..0000000 --- a/src/__tests__/mocks/react-native.cjs +++ /dev/null @@ -1,423 +0,0 @@ -"use strict"; -/** - * react-native mock for component rendering tests (vitest + jsdom) - * - * This file is used as a resolve.alias for 'react-native' in vitest.config.render.ts. - * It provides real React component implementations so @testing-library/react-native - * can render and query them in jsdom. The real react-native package cannot be loaded - * in Node 22 due to ESM/typeof issues. - */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LayoutAnimation = exports.I18nManager = exports.AccessibilityInfo = exports.NativeModules = exports.Appearance = exports.BackHandler = exports.Linking = exports.Keyboard = exports.AppState = exports.PixelRatio = exports.Easing = exports.Animated = exports.Alert = exports.Platform = exports.Dimensions = exports.StyleSheet = exports.FlatList = exports.Modal = exports.Pressable = exports.SectionList = exports.RefreshControl = exports.KeyboardAvoidingView = exports.StatusBar = exports.Switch = exports.TouchableWithoutFeedback = exports.TouchableHighlight = exports.TouchableOpacity = exports.ActivityIndicator = exports.SafeAreaView = exports.ScrollView = exports.ImageBackground = exports.Image = exports.TextInput = exports.Text = exports.View = void 0; -exports.useWindowDimensions = useWindowDimensions; -exports.PlatformColor = PlatformColor; -exports.useColorScheme = useColorScheme; -const react_1 = __importDefault(require("react")); -// --------------------------------------------------------------------------- -// Helper: create a simple host component that forwards props to a DOM element -// --------------------------------------------------------------------------- -function createMockComponent(name) { - const Component = react_1.default.forwardRef((props, ref) => { - const { children, testID, ...rest } = props; - return react_1.default.createElement(name, { ...rest, testID, 'data-testid': testID, ref }, children); - }); - Component.displayName = name; - return Component; -} -// --------------------------------------------------------------------------- -// Core RN Components -// --------------------------------------------------------------------------- -exports.View = createMockComponent('View'); -exports.Text = createMockComponent('Text'); -exports.TextInput = createMockComponent('TextInput'); -exports.Image = createMockComponent('Image'); -exports.ImageBackground = createMockComponent('ImageBackground'); -exports.ScrollView = createMockComponent('ScrollView'); -exports.SafeAreaView = createMockComponent('SafeAreaView'); -exports.ActivityIndicator = createMockComponent('ActivityIndicator'); -exports.TouchableOpacity = createMockComponent('TouchableOpacity'); -exports.TouchableHighlight = createMockComponent('TouchableHighlight'); -exports.TouchableWithoutFeedback = createMockComponent('TouchableWithoutFeedback'); -exports.Switch = createMockComponent('Switch'); -exports.StatusBar = createMockComponent('StatusBar'); -exports.KeyboardAvoidingView = createMockComponent('KeyboardAvoidingView'); -exports.RefreshControl = createMockComponent('RefreshControl'); -exports.SectionList = createMockComponent('SectionList'); -// Pressable needs onPress / disabled support -exports.Pressable = react_1.default.forwardRef((props, ref) => { - const { children, testID, onPress, disabled, ...rest } = props; - return react_1.default.createElement('Pressable', { - ...rest, - testID, - 'data-testid': testID, - onClick: disabled ? undefined : onPress, - disabled, - onPress, - ref, - }, typeof children === 'function' ? children({ pressed: false }) : children); -}); -exports.Pressable.displayName = 'Pressable'; -// Modal -exports.Modal = react_1.default.forwardRef((props, ref) => { - const { children, visible, testID, onRequestClose, ...rest } = props; - if (!visible) - return null; - return react_1.default.createElement('Modal', { ...rest, testID, 'data-testid': testID, visible, onRequestClose, ref }, children); -}); -exports.Modal.displayName = 'Modal'; -// FlatList — simplified: just render items in a ScrollView-like wrapper -exports.FlatList = react_1.default.forwardRef((props, ref) => { - const { data, renderItem, keyExtractor, testID, ListHeaderComponent, ListFooterComponent, ListEmptyComponent, ...rest } = props; - const items = data?.map((item, index) => { - const key = keyExtractor ? keyExtractor(item, index) : String(index); - return react_1.default.createElement(react_1.default.Fragment, { key }, renderItem({ item, index, separators: {} })); - }) ?? []; - const children = [ - ListHeaderComponent ? react_1.default.createElement(react_1.default.Fragment, { key: '__header' }, typeof ListHeaderComponent === 'function' ? react_1.default.createElement(ListHeaderComponent) : ListHeaderComponent) : null, - ...(items.length === 0 && ListEmptyComponent - ? [react_1.default.createElement(react_1.default.Fragment, { key: '__empty' }, typeof ListEmptyComponent === 'function' ? react_1.default.createElement(ListEmptyComponent) : ListEmptyComponent)] - : items), - ListFooterComponent ? react_1.default.createElement(react_1.default.Fragment, { key: '__footer' }, typeof ListFooterComponent === 'function' ? react_1.default.createElement(ListFooterComponent) : ListFooterComponent) : null, - ].filter(Boolean); - return react_1.default.createElement('FlatList', { ...rest, testID, 'data-testid': testID, ref }, ...children); -}); -exports.FlatList.displayName = 'FlatList'; -// --------------------------------------------------------------------------- -// StyleSheet -// --------------------------------------------------------------------------- -const absoluteFillValue = { - position: 'absolute', - left: 0, - right: 0, - top: 0, - bottom: 0, -}; -exports.StyleSheet = { - create: (styles) => styles, - flatten: (style) => { - if (Array.isArray(style)) { - return Object.assign({}, ...style.filter(Boolean).map((s) => exports.StyleSheet.flatten(s))); - } - return style ?? {}; - }, - absoluteFill: absoluteFillValue, - absoluteFillObject: absoluteFillValue, - hairlineWidth: 1, - compose: (a, b) => [a, b], -}; -// --------------------------------------------------------------------------- -// Dimensions -// --------------------------------------------------------------------------- -const dimensionsValues = { width: 375, height: 812, scale: 2, fontScale: 1 }; -exports.Dimensions = { - get: (_dim) => dimensionsValues, - addEventListener: () => ({ remove: () => { } }), - set: () => { }, -}; -// --------------------------------------------------------------------------- -// useWindowDimensions -// --------------------------------------------------------------------------- -function useWindowDimensions() { - return dimensionsValues; -} -// --------------------------------------------------------------------------- -// Platform -// --------------------------------------------------------------------------- -exports.Platform = { - OS: 'ios', - Version: 18, - isPad: false, - isTVOS: false, - isTV: false, - select: (obj) => obj.ios ?? obj.default, - constants: { - reactNativeVersion: { major: 0, minor: 81, patch: 5 }, - }, -}; -// --------------------------------------------------------------------------- -// Alert -// --------------------------------------------------------------------------- -exports.Alert = { - alert: (() => { }), -}; -// --------------------------------------------------------------------------- -// Animated -// --------------------------------------------------------------------------- -class AnimatedValue { - _value; - _listeners = new Map(); - constructor(value = 0) { - this._value = value; - } - setValue(value) { - this._value = value; - } - setOffset(_offset) { } - flattenOffset() { } - extractOffset() { } - addListener(cb) { - const id = String(Math.random()); - this._listeners.set(id, cb); - return id; - } - removeListener(id) { - this._listeners.delete(id); - } - removeAllListeners() { - this._listeners.clear(); - } - stopAnimation(cb) { - cb?.(this._value); - } - resetAnimation(cb) { - cb?.(this._value); - } - interpolate(config) { - return { - ...config, - __isAnimatedInterpolation: true, - interpolate: (c) => ({ ...c, __isAnimatedInterpolation: true }), - }; - } - // Arithmetic methods for combined animations - __getValue() { return this._value; } -} -class AnimatedValueXY { - x; - y; - constructor(value) { - this.x = new AnimatedValue(value?.x ?? 0); - this.y = new AnimatedValue(value?.y ?? 0); - } - setValue(value) { - this.x.setValue(value.x); - this.y.setValue(value.y); - } - setOffset(offset) { - this.x.setOffset(offset.x); - this.y.setOffset(offset.y); - } - flattenOffset() { - this.x.flattenOffset(); - this.y.flattenOffset(); - } - extractOffset() { - this.x.extractOffset(); - this.y.extractOffset(); - } - stopAnimation(cb) { - this.x.stopAnimation(); - this.y.stopAnimation(); - cb?.({ x: this.x._value, y: this.y._value }); - } - addListener() { return ''; } - removeListener() { } - removeAllListeners() { } - getLayout() { - return { left: this.x, top: this.y }; - } - getTranslateTransform() { - return [{ translateX: this.x }, { translateY: this.y }]; - } -} -const mockAnimationResult = { start: (cb) => cb?.({ finished: true }), stop: () => { }, reset: () => { } }; -exports.Animated = { - Value: AnimatedValue, - ValueXY: AnimatedValueXY, - View: createMockComponent('Animated.View'), - Text: createMockComponent('Animated.Text'), - Image: createMockComponent('Animated.Image'), - ScrollView: createMockComponent('Animated.ScrollView'), - FlatList: createMockComponent('Animated.FlatList'), - SectionList: createMockComponent('Animated.SectionList'), - createAnimatedComponent: (comp) => comp, - timing: (_value, _config) => mockAnimationResult, - spring: (_value, _config) => mockAnimationResult, - decay: (_value, _config) => mockAnimationResult, - parallel: (_animations) => mockAnimationResult, - sequence: (_animations) => mockAnimationResult, - stagger: (_delay, _animations) => mockAnimationResult, - delay: (_time) => mockAnimationResult, - loop: (_animation, _config) => mockAnimationResult, - event: (_argMapping, _config) => () => { }, - add: (a, b) => a, - subtract: (a, b) => a, - divide: (a, b) => a, - multiply: (a, b) => a, - diffClamp: (a, _min, _max) => a, -}; -// --------------------------------------------------------------------------- -// Easing -// --------------------------------------------------------------------------- -exports.Easing = { - linear: (t) => t, - ease: (t) => t, - quad: (t) => t * t, - cubic: (t) => t * t * t, - poly: (_n) => (t) => t, - sin: (t) => t, - circle: (t) => t, - exp: (t) => t, - elastic: (_bounciness) => (t) => t, - back: (_s) => (t) => t, - bounce: (t) => t, - bezier: (_x1, _y1, _x2, _y2) => (t) => t, - in: (fn) => fn, - out: (fn) => fn, - inOut: (fn) => fn, - step0: (t) => t, - step1: (t) => t, -}; -// --------------------------------------------------------------------------- -// PixelRatio -// --------------------------------------------------------------------------- -exports.PixelRatio = { - get: () => 2, - getFontScale: () => 1, - getPixelSizeForLayoutSize: (size) => size * 2, - roundToNearestPixel: (size) => Math.round(size * 2) / 2, -}; -// --------------------------------------------------------------------------- -// AppState -// --------------------------------------------------------------------------- -exports.AppState = { - currentState: 'active', - addEventListener: () => ({ remove: () => { } }), - removeEventListener: () => { }, -}; -// --------------------------------------------------------------------------- -// Keyboard -// --------------------------------------------------------------------------- -exports.Keyboard = { - addListener: () => ({ remove: () => { } }), - removeListener: () => { }, - dismiss: () => { }, - isVisible: () => false, - metrics: () => undefined, -}; -// --------------------------------------------------------------------------- -// Linking -// --------------------------------------------------------------------------- -exports.Linking = { - openURL: async () => { }, - canOpenURL: async () => true, - getInitialURL: async () => null, - addEventListener: () => ({ remove: () => { } }), -}; -// --------------------------------------------------------------------------- -// BackHandler -// --------------------------------------------------------------------------- -exports.BackHandler = { - addEventListener: () => ({ remove: () => { } }), - removeEventListener: () => { }, - exitApp: () => { }, -}; -// --------------------------------------------------------------------------- -// Appearance -// --------------------------------------------------------------------------- -exports.Appearance = { - getColorScheme: () => 'dark', - addChangeListener: () => ({ remove: () => { } }), - setColorScheme: () => { }, -}; -// --------------------------------------------------------------------------- -// PlatformColor (no-op stub) -// --------------------------------------------------------------------------- -function PlatformColor(..._args) { - return ''; -} -// --------------------------------------------------------------------------- -// NativeModules -// --------------------------------------------------------------------------- -exports.NativeModules = {}; -// --------------------------------------------------------------------------- -// AccessibilityInfo -// --------------------------------------------------------------------------- -exports.AccessibilityInfo = { - isScreenReaderEnabled: async () => false, - addEventListener: () => ({ remove: () => { } }), - setAccessibilityFocus: () => { }, - announceForAccessibility: () => { }, - isReduceMotionEnabled: async () => false, - isBoldTextEnabled: async () => false, - isGrayscaleEnabled: async () => false, - isInvertColorsEnabled: async () => false, - prefersCrossFadeTransitions: async () => false, -}; -// --------------------------------------------------------------------------- -// I18nManager -// --------------------------------------------------------------------------- -exports.I18nManager = { - isRTL: false, - doLeftAndRightSwapInRTL: true, - allowRTL: () => { }, - forceRTL: () => { }, - swapLeftAndRightInRTL: () => { }, -}; -// --------------------------------------------------------------------------- -// LayoutAnimation -// --------------------------------------------------------------------------- -exports.LayoutAnimation = { - configureNext: () => { }, - create: () => ({}), - Types: { spring: 'spring', linear: 'linear', easeInEaseOut: 'easeInEaseOut', easeIn: 'easeIn', easeOut: 'easeOut' }, - Properties: { opacity: 'opacity', scaleX: 'scaleX', scaleY: 'scaleY', scaleXY: 'scaleXY' }, - Presets: { - easeInEaseOut: {}, - linear: {}, - spring: {}, - }, -}; -// --------------------------------------------------------------------------- -// useColorScheme -// --------------------------------------------------------------------------- -function useColorScheme() { - return 'dark'; -} -// --------------------------------------------------------------------------- -// Default export (some code does `import RN from 'react-native'`) -// --------------------------------------------------------------------------- -const RN = { - View: exports.View, - Text: exports.Text, - TextInput: exports.TextInput, - Image: exports.Image, - ImageBackground: exports.ImageBackground, - ScrollView: exports.ScrollView, - FlatList: exports.FlatList, - SectionList: exports.SectionList, - SafeAreaView: exports.SafeAreaView, - ActivityIndicator: exports.ActivityIndicator, - TouchableOpacity: exports.TouchableOpacity, - TouchableHighlight: exports.TouchableHighlight, - TouchableWithoutFeedback: exports.TouchableWithoutFeedback, - Pressable: exports.Pressable, - Modal: exports.Modal, - Switch: exports.Switch, - StatusBar: exports.StatusBar, - KeyboardAvoidingView: exports.KeyboardAvoidingView, - RefreshControl: exports.RefreshControl, - StyleSheet: exports.StyleSheet, - Dimensions: exports.Dimensions, - Platform: exports.Platform, - Alert: exports.Alert, - Animated: exports.Animated, - Easing: exports.Easing, - PixelRatio: exports.PixelRatio, - AppState: exports.AppState, - Keyboard: exports.Keyboard, - Linking: exports.Linking, - BackHandler: exports.BackHandler, - Appearance: exports.Appearance, - PlatformColor, - NativeModules: exports.NativeModules, - AccessibilityInfo: exports.AccessibilityInfo, - I18nManager: exports.I18nManager, - LayoutAnimation: exports.LayoutAnimation, - useColorScheme, - useWindowDimensions, -}; -exports.default = RN; diff --git a/src/__tests__/mocks/react-native.ts b/src/__tests__/mocks/react-native.ts deleted file mode 100644 index 1c6bbb9..0000000 --- a/src/__tests__/mocks/react-native.ts +++ /dev/null @@ -1,458 +0,0 @@ -/** - * react-native mock for component rendering tests (vitest + jsdom) - * - * This file is used as a resolve.alias for 'react-native' in vitest.config.render.ts. - * It provides real React component implementations so @testing-library/react-native - * can render and query them in jsdom. The real react-native package cannot be loaded - * in Node 22 due to ESM/typeof issues. - */ - -import React from 'react' - -// --------------------------------------------------------------------------- -// Helper: create a simple host component that forwards props to a DOM element -// --------------------------------------------------------------------------- -function createMockComponent(name: string) { - const Component = React.forwardRef((props: any, ref: any) => { - const { children, testID, ...rest } = props - return React.createElement( - name, - { ...rest, testID, 'data-testid': testID, ref }, - children - ) - }) - Component.displayName = name - return Component -} - -// --------------------------------------------------------------------------- -// Core RN Components -// --------------------------------------------------------------------------- -export const View = createMockComponent('View') -export const Text = createMockComponent('Text') -export const TextInput = createMockComponent('TextInput') -export const Image = createMockComponent('Image') -export const ImageBackground = createMockComponent('ImageBackground') -export const ScrollView = createMockComponent('ScrollView') -export const SafeAreaView = createMockComponent('SafeAreaView') -export const ActivityIndicator = createMockComponent('ActivityIndicator') -export const TouchableOpacity = createMockComponent('TouchableOpacity') -export const TouchableHighlight = createMockComponent('TouchableHighlight') -export const TouchableWithoutFeedback = createMockComponent('TouchableWithoutFeedback') -export const Switch = createMockComponent('Switch') -export const StatusBar = createMockComponent('StatusBar') -export const KeyboardAvoidingView = createMockComponent('KeyboardAvoidingView') -export const RefreshControl = createMockComponent('RefreshControl') -export const SectionList = createMockComponent('SectionList') - -// Pressable needs onPress / disabled support -export const Pressable = React.forwardRef((props: any, ref: any) => { - const { children, testID, onPress, disabled, ...rest } = props - return React.createElement( - 'Pressable', - { - ...rest, - testID, - 'data-testid': testID, - onClick: disabled ? undefined : onPress, - disabled, - onPress, - ref, - }, - typeof children === 'function' ? children({ pressed: false }) : children - ) -}) -;(Pressable as any).displayName = 'Pressable' - -// Modal -export const Modal = React.forwardRef((props: any, ref: any) => { - const { children, visible, testID, onRequestClose, ...rest } = props - if (!visible) return null - return React.createElement( - 'Modal', - { ...rest, testID, 'data-testid': testID, visible, onRequestClose, ref }, - children - ) -}) -;(Modal as any).displayName = 'Modal' - -// FlatList — simplified: just render items in a ScrollView-like wrapper -export const FlatList = React.forwardRef((props: any, ref: any) => { - const { data, renderItem, keyExtractor, testID, ListHeaderComponent, ListFooterComponent, ListEmptyComponent, ...rest } = props - const items = data?.map((item: any, index: number) => { - const key = keyExtractor ? keyExtractor(item, index) : String(index) - return React.createElement(React.Fragment, { key }, renderItem({ item, index, separators: {} })) - }) ?? [] - - const children = [ - ListHeaderComponent ? React.createElement(React.Fragment, { key: '__header' }, typeof ListHeaderComponent === 'function' ? React.createElement(ListHeaderComponent) : ListHeaderComponent) : null, - ...(items.length === 0 && ListEmptyComponent - ? [React.createElement(React.Fragment, { key: '__empty' }, typeof ListEmptyComponent === 'function' ? React.createElement(ListEmptyComponent) : ListEmptyComponent)] - : items), - ListFooterComponent ? React.createElement(React.Fragment, { key: '__footer' }, typeof ListFooterComponent === 'function' ? React.createElement(ListFooterComponent) : ListFooterComponent) : null, - ].filter(Boolean) - - return React.createElement('FlatList', { ...rest, testID, 'data-testid': testID, ref }, ...children) -}) -;(FlatList as any).displayName = 'FlatList' - -// --------------------------------------------------------------------------- -// StyleSheet -// --------------------------------------------------------------------------- -const absoluteFillValue = { - position: 'absolute' as const, - left: 0, - right: 0, - top: 0, - bottom: 0, -} - -export const StyleSheet = { - create: >(styles: T): T => styles, - flatten: (style: any): any => { - if (Array.isArray(style)) { - return Object.assign({}, ...style.filter(Boolean).map((s: any) => StyleSheet.flatten(s))) - } - return style ?? {} - }, - absoluteFill: absoluteFillValue, - absoluteFillObject: absoluteFillValue, - hairlineWidth: 1, - compose: (a: any, b: any) => [a, b], -} - -// --------------------------------------------------------------------------- -// Dimensions -// --------------------------------------------------------------------------- -const dimensionsValues = { width: 375, height: 812, scale: 2, fontScale: 1 } -export const Dimensions = { - get: (_dim?: string) => dimensionsValues, - addEventListener: () => ({ remove: () => {} }), - set: () => {}, -} - -// --------------------------------------------------------------------------- -// useWindowDimensions -// --------------------------------------------------------------------------- -export function useWindowDimensions() { - return dimensionsValues -} - -// --------------------------------------------------------------------------- -// Platform -// --------------------------------------------------------------------------- -export const Platform = { - OS: 'ios' as const, - Version: 18, - isPad: false, - isTVOS: false, - isTV: false, - select: (obj: any) => obj.ios ?? obj.default, - constants: { - reactNativeVersion: { major: 0, minor: 81, patch: 5 }, - }, -} - -// --------------------------------------------------------------------------- -// Alert -// --------------------------------------------------------------------------- -export const Alert = { - alert: (() => {}) as any, -} - -// --------------------------------------------------------------------------- -// Animated -// --------------------------------------------------------------------------- -class AnimatedValue { - _value: number - _listeners: Map = new Map() - constructor(value: number = 0) { - this._value = value - } - setValue(value: number) { - this._value = value - } - setOffset(_offset: number) {} - flattenOffset() {} - extractOffset() {} - addListener(cb: Function) { - const id = String(Math.random()) - this._listeners.set(id, cb) - return id - } - removeListener(id: string) { - this._listeners.delete(id) - } - removeAllListeners() { - this._listeners.clear() - } - stopAnimation(cb?: Function) { - cb?.(this._value) - } - resetAnimation(cb?: Function) { - cb?.(this._value) - } - interpolate(config: any) { - return { - ...config, - __isAnimatedInterpolation: true, - interpolate: (c: any) => ({ ...c, __isAnimatedInterpolation: true }), - } - } - // Arithmetic methods for combined animations - __getValue() { return this._value } -} - -class AnimatedValueXY { - x: AnimatedValue - y: AnimatedValue - constructor(value?: { x?: number; y?: number }) { - this.x = new AnimatedValue(value?.x ?? 0) - this.y = new AnimatedValue(value?.y ?? 0) - } - setValue(value: { x: number; y: number }) { - this.x.setValue(value.x) - this.y.setValue(value.y) - } - setOffset(offset: { x: number; y: number }) { - this.x.setOffset(offset.x) - this.y.setOffset(offset.y) - } - flattenOffset() { - this.x.flattenOffset() - this.y.flattenOffset() - } - extractOffset() { - this.x.extractOffset() - this.y.extractOffset() - } - stopAnimation(cb?: Function) { - this.x.stopAnimation() - this.y.stopAnimation() - cb?.({ x: this.x._value, y: this.y._value }) - } - addListener() { return '' } - removeListener() {} - removeAllListeners() {} - getLayout() { - return { left: this.x, top: this.y } - } - getTranslateTransform() { - return [{ translateX: this.x }, { translateY: this.y }] - } -} - -const mockAnimationResult = { start: (cb?: Function) => cb?.({ finished: true }), stop: () => {}, reset: () => {} } - -export const Animated = { - Value: AnimatedValue, - ValueXY: AnimatedValueXY, - View: createMockComponent('Animated.View'), - Text: createMockComponent('Animated.Text'), - Image: createMockComponent('Animated.Image'), - ScrollView: createMockComponent('Animated.ScrollView'), - FlatList: createMockComponent('Animated.FlatList'), - SectionList: createMockComponent('Animated.SectionList'), - createAnimatedComponent: (comp: any) => comp, - timing: (_value: any, _config: any) => mockAnimationResult, - spring: (_value: any, _config: any) => mockAnimationResult, - decay: (_value: any, _config: any) => mockAnimationResult, - parallel: (_animations: any[]) => mockAnimationResult, - sequence: (_animations: any[]) => mockAnimationResult, - stagger: (_delay: number, _animations: any[]) => mockAnimationResult, - delay: (_time: number) => mockAnimationResult, - loop: (_animation: any, _config?: any) => mockAnimationResult, - event: (_argMapping: any[], _config?: any) => () => {}, - add: (a: any, b: any) => a, - subtract: (a: any, b: any) => a, - divide: (a: any, b: any) => a, - multiply: (a: any, b: any) => a, - diffClamp: (a: any, _min: number, _max: number) => a, -} - -// --------------------------------------------------------------------------- -// Easing -// --------------------------------------------------------------------------- -export const Easing = { - linear: (t: number) => t, - ease: (t: number) => t, - quad: (t: number) => t * t, - cubic: (t: number) => t * t * t, - poly: (_n: number) => (t: number) => t, - sin: (t: number) => t, - circle: (t: number) => t, - exp: (t: number) => t, - elastic: (_bounciness?: number) => (t: number) => t, - back: (_s?: number) => (t: number) => t, - bounce: (t: number) => t, - bezier: (_x1: number, _y1: number, _x2: number, _y2: number) => (t: number) => t, - in: (fn: Function) => fn, - out: (fn: Function) => fn, - inOut: (fn: Function) => fn, - step0: (t: number) => t, - step1: (t: number) => t, -} - -// --------------------------------------------------------------------------- -// PixelRatio -// --------------------------------------------------------------------------- -export const PixelRatio = { - get: () => 2, - getFontScale: () => 1, - getPixelSizeForLayoutSize: (size: number) => size * 2, - roundToNearestPixel: (size: number) => Math.round(size * 2) / 2, -} - -// --------------------------------------------------------------------------- -// AppState -// --------------------------------------------------------------------------- -export const AppState = { - currentState: 'active', - addEventListener: () => ({ remove: () => {} }), - removeEventListener: () => {}, -} - -// --------------------------------------------------------------------------- -// Keyboard -// --------------------------------------------------------------------------- -export const Keyboard = { - addListener: () => ({ remove: () => {} }), - removeListener: () => {}, - dismiss: () => {}, - isVisible: () => false, - metrics: () => undefined, -} - -// --------------------------------------------------------------------------- -// Linking -// --------------------------------------------------------------------------- -export const Linking = { - openURL: async () => {}, - canOpenURL: async () => true, - getInitialURL: async () => null, - addEventListener: () => ({ remove: () => {} }), -} - -// --------------------------------------------------------------------------- -// BackHandler -// --------------------------------------------------------------------------- -export const BackHandler = { - addEventListener: () => ({ remove: () => {} }), - removeEventListener: () => {}, - exitApp: () => {}, -} - -// --------------------------------------------------------------------------- -// Appearance -// --------------------------------------------------------------------------- -export const Appearance = { - getColorScheme: () => 'dark', - addChangeListener: () => ({ remove: () => {} }), - setColorScheme: () => {}, -} - -// --------------------------------------------------------------------------- -// PlatformColor (no-op stub) -// --------------------------------------------------------------------------- -export function PlatformColor(..._args: string[]) { - return '' -} - -// --------------------------------------------------------------------------- -// NativeModules -// --------------------------------------------------------------------------- -export const NativeModules = {} - -// --------------------------------------------------------------------------- -// AccessibilityInfo -// --------------------------------------------------------------------------- -export const AccessibilityInfo = { - isScreenReaderEnabled: async () => false, - addEventListener: () => ({ remove: () => {} }), - setAccessibilityFocus: () => {}, - announceForAccessibility: () => {}, - isReduceMotionEnabled: async () => false, - isBoldTextEnabled: async () => false, - isGrayscaleEnabled: async () => false, - isInvertColorsEnabled: async () => false, - prefersCrossFadeTransitions: async () => false, -} - -// --------------------------------------------------------------------------- -// I18nManager -// --------------------------------------------------------------------------- -export const I18nManager = { - isRTL: false, - doLeftAndRightSwapInRTL: true, - allowRTL: () => {}, - forceRTL: () => {}, - swapLeftAndRightInRTL: () => {}, -} - -// --------------------------------------------------------------------------- -// LayoutAnimation -// --------------------------------------------------------------------------- -export const LayoutAnimation = { - configureNext: () => {}, - create: () => ({}), - Types: { spring: 'spring', linear: 'linear', easeInEaseOut: 'easeInEaseOut', easeIn: 'easeIn', easeOut: 'easeOut' }, - Properties: { opacity: 'opacity', scaleX: 'scaleX', scaleY: 'scaleY', scaleXY: 'scaleXY' }, - Presets: { - easeInEaseOut: {}, - linear: {}, - spring: {}, - }, -} - -// --------------------------------------------------------------------------- -// useColorScheme -// --------------------------------------------------------------------------- -export function useColorScheme() { - return 'dark' -} - -// --------------------------------------------------------------------------- -// Default export (some code does `import RN from 'react-native'`) -// --------------------------------------------------------------------------- -const RN = { - View, - Text, - TextInput, - Image, - ImageBackground, - ScrollView, - FlatList, - SectionList, - SafeAreaView, - ActivityIndicator, - TouchableOpacity, - TouchableHighlight, - TouchableWithoutFeedback, - Pressable, - Modal, - Switch, - StatusBar, - KeyboardAvoidingView, - RefreshControl, - StyleSheet, - Dimensions, - Platform, - Alert, - Animated, - Easing, - PixelRatio, - AppState, - Keyboard, - Linking, - BackHandler, - Appearance, - PlatformColor, - NativeModules, - AccessibilityInfo, - I18nManager, - LayoutAnimation, - useColorScheme, - useWindowDimensions, -} - -export default RN diff --git a/src/__tests__/services/access.test.ts b/src/__tests__/services/access.test.ts deleted file mode 100644 index 2ead285..0000000 --- a/src/__tests__/services/access.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { describe, it, expect } from 'vitest' -import { - FREE_WORKOUT_IDS, - FREE_WORKOUT_COUNT, - isFreeWorkout, - canAccessWorkout, -} from '../../shared/services/access' - -describe('access service', () => { - describe('FREE_WORKOUT_IDS', () => { - it('should contain exactly 3 free workout IDs', () => { - expect(FREE_WORKOUT_IDS).toHaveLength(3) - }) - - it('should include workout 1 (Full Body Ignite)', () => { - expect(FREE_WORKOUT_IDS).toContain('1') - }) - - it('should include workout 11 (Core Crusher)', () => { - expect(FREE_WORKOUT_IDS).toContain('11') - }) - - it('should include workout 43 (Dance Cardio)', () => { - expect(FREE_WORKOUT_IDS).toContain('43') - }) - - it('should be readonly (immutable)', () => { - // TypeScript enforces readonly at compile time; at runtime we verify the array content is stable - const snapshot = [...FREE_WORKOUT_IDS] - expect(FREE_WORKOUT_IDS).toEqual(snapshot) - }) - }) - - describe('FREE_WORKOUT_COUNT', () => { - it('should equal the length of FREE_WORKOUT_IDS', () => { - expect(FREE_WORKOUT_COUNT).toBe(FREE_WORKOUT_IDS.length) - }) - - it('should be 3', () => { - expect(FREE_WORKOUT_COUNT).toBe(3) - }) - }) - - describe('isFreeWorkout', () => { - it('should return true for free workout ID "1"', () => { - expect(isFreeWorkout('1')).toBe(true) - }) - - it('should return true for free workout ID "11"', () => { - expect(isFreeWorkout('11')).toBe(true) - }) - - it('should return true for free workout ID "43"', () => { - expect(isFreeWorkout('43')).toBe(true) - }) - - it('should return false for non-free workout ID "2"', () => { - expect(isFreeWorkout('2')).toBe(false) - }) - - it('should return false for non-free workout ID "10"', () => { - expect(isFreeWorkout('10')).toBe(false) - }) - - it('should return false for non-free workout ID "44"', () => { - expect(isFreeWorkout('44')).toBe(false) - }) - - it('should return false for empty string', () => { - expect(isFreeWorkout('')).toBe(false) - }) - - it('should return false for non-existent ID', () => { - expect(isFreeWorkout('999')).toBe(false) - }) - }) - - describe('canAccessWorkout', () => { - describe('premium user', () => { - it('should access free workout', () => { - expect(canAccessWorkout('1', true)).toBe(true) - }) - - it('should access non-free workout', () => { - expect(canAccessWorkout('2', true)).toBe(true) - }) - - it('should access any workout ID', () => { - expect(canAccessWorkout('999', true)).toBe(true) - }) - }) - - describe('free user', () => { - it('should access free workout ID "1"', () => { - expect(canAccessWorkout('1', false)).toBe(true) - }) - - it('should access free workout ID "11"', () => { - expect(canAccessWorkout('11', false)).toBe(true) - }) - - it('should access free workout ID "43"', () => { - expect(canAccessWorkout('43', false)).toBe(true) - }) - - it('should NOT access non-free workout ID "2"', () => { - expect(canAccessWorkout('2', false)).toBe(false) - }) - - it('should NOT access non-free workout ID "10"', () => { - expect(canAccessWorkout('10', false)).toBe(false) - }) - - it('should NOT access non-free workout ID "50"', () => { - expect(canAccessWorkout('50', false)).toBe(false) - }) - - it('should NOT access empty string ID', () => { - expect(canAccessWorkout('', false)).toBe(false) - }) - }) - }) -}) diff --git a/src/__tests__/services/analytics.test.ts b/src/__tests__/services/analytics.test.ts deleted file mode 100644 index 49b8a7c..0000000 --- a/src/__tests__/services/analytics.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' - -vi.mock('posthog-react-native', () => { - const mockPostHog = { - capture: vi.fn(), - identify: vi.fn(), - setPersonProperties: vi.fn(), - startSessionRecording: vi.fn(), - stopSessionRecording: vi.fn(), - isSessionReplayActive: vi.fn().mockResolvedValue(true), - } - - return { - default: vi.fn().mockImplementation(() => mockPostHog), - } -}) - -describe('analytics service', () => { - beforeEach(() => { - vi.clearAllMocks() - vi.resetModules() - }) - - afterEach(() => { - vi.restoreAllMocks() - }) - - describe('initializeAnalytics', () => { - it('should initialize PostHog client', async () => { - const { initializeAnalytics } = await import('../../shared/services/analytics') - - const client = await initializeAnalytics() - - expect(client).toBeDefined() - }) - - it('should return existing client if already initialized', async () => { - const { initializeAnalytics } = await import('../../shared/services/analytics') - - const client1 = await initializeAnalytics() - const client2 = await initializeAnalytics() - - expect(client1).toBe(client2) - }) - }) - - describe('getPostHogClient', () => { - it('should return null before initialization', async () => { - vi.resetModules() - const { getPostHogClient } = await import('../../shared/services/analytics') - - expect(getPostHogClient()).toBeNull() - }) - - it('should return client after initialization', async () => { - vi.resetModules() - const { initializeAnalytics, getPostHogClient } = await import('../../shared/services/analytics') - - await initializeAnalytics() - - expect(getPostHogClient()).toBeDefined() - }) - }) - - describe('track', () => { - it('should track event with properties', async () => { - const { initializeAnalytics, track } = await import('../../shared/services/analytics') - - await initializeAnalytics() - track('test_event', { prop1: 'value1' }) - - }) - - it('should track event without properties', async () => { - const { initializeAnalytics, track } = await import('../../shared/services/analytics') - - await initializeAnalytics() - track('test_event') - - }) - }) - - describe('trackScreen', () => { - it('should track screen view', async () => { - const { initializeAnalytics, trackScreen } = await import('../../shared/services/analytics') - - await initializeAnalytics() - trackScreen('home', { source: 'tab' }) - - }) - }) - - describe('identifyUser', () => { - it('should identify user with traits', async () => { - const { initializeAnalytics, identifyUser } = await import('../../shared/services/analytics') - - await initializeAnalytics() - identifyUser('user-123', { name: 'Test User' }) - - }) - - it('should identify user without traits', async () => { - const { initializeAnalytics, identifyUser } = await import('../../shared/services/analytics') - - await initializeAnalytics() - identifyUser('user-123') - - }) - }) - - describe('setUserProperties', () => { - it('should set user properties', async () => { - const { initializeAnalytics, setUserProperties } = await import('../../shared/services/analytics') - - await initializeAnalytics() - setUserProperties({ plan: 'pro', workouts: 10 }) - - }) - }) - - describe('session recording', () => { - it('should start session recording', async () => { - const { initializeAnalytics, startSessionRecording } = await import('../../shared/services/analytics') - - await initializeAnalytics() - startSessionRecording() - - }) - - it('should stop session recording', async () => { - const { initializeAnalytics, stopSessionRecording } = await import('../../shared/services/analytics') - - await initializeAnalytics() - stopSessionRecording() - - }) - - it('should check session replay status', async () => { - const { initializeAnalytics, isSessionReplayActive } = await import('../../shared/services/analytics') - - await initializeAnalytics() - const isActive = await isSessionReplayActive() - - expect(typeof isActive).toBe('boolean') - }) - }) -}) diff --git a/src/__tests__/services/music.test.ts b/src/__tests__/services/music.test.ts deleted file mode 100644 index dc16eac..0000000 --- a/src/__tests__/services/music.test.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest' -import { musicService, MusicTrack } from '../../shared/services/music' -import type { MusicVibe } from '../../shared/types' - -vi.mock('../../shared/supabase', () => ({ - isSupabaseConfigured: () => false, - supabase: {}, -})) - -describe('music service', () => { - const vibes: MusicVibe[] = ['electronic', 'hip-hop', 'pop', 'rock', 'chill'] - - beforeEach(() => { - vi.clearAllMocks() - musicService.clearCache() - }) - - describe('loadTracksForVibe', () => { - it('should return mock tracks when Supabase not configured', async () => { - const tracks = await musicService.loadTracksForVibe('electronic') - - expect(tracks).toBeDefined() - expect(tracks.length).toBeGreaterThan(0) - }) - - it('should return tracks for each vibe', async () => { - for (const vibe of vibes) { - const tracks = await musicService.loadTracksForVibe(vibe) - expect(tracks).toBeDefined() - expect(tracks.length).toBeGreaterThan(0) - } - }) - - it('should cache tracks after first load', async () => { - const tracks1 = await musicService.loadTracksForVibe('electronic') - const tracks2 = await musicService.loadTracksForVibe('electronic') - - expect(tracks1).toBe(tracks2) - }) - - it('should return tracks with correct vibe property', async () => { - for (const vibe of vibes) { - const tracks = await musicService.loadTracksForVibe(vibe) - tracks.forEach(track => { - expect(track.vibe).toBe(vibe) - }) - } - }) - - it('should return tracks with required properties', async () => { - const tracks = await musicService.loadTracksForVibe('electronic') - - tracks.forEach(track => { - expect(track.id).toBeDefined() - expect(track.title).toBeDefined() - expect(track.artist).toBeDefined() - expect(track.duration).toBeDefined() - expect(track.url).toBeDefined() - expect(track.vibe).toBeDefined() - }) - }) - }) - - describe('clearCache', () => { - it('should clear specific vibe cache', async () => { - await musicService.loadTracksForVibe('electronic') - await musicService.loadTracksForVibe('hip-hop') - - musicService.clearCache('electronic') - - const tracks = await musicService.loadTracksForVibe('hip-hop') - expect(tracks).toBeDefined() - }) - - it('should clear all cache when no vibe specified', async () => { - await musicService.loadTracksForVibe('electronic') - await musicService.loadTracksForVibe('hip-hop') - - musicService.clearCache() - - expect(musicService).toBeDefined() - }) - }) - - describe('getRandomTrack', () => { - it('should return null for empty array', () => { - const track = musicService.getRandomTrack([]) - expect(track).toBeNull() - }) - - it('should return a track from the array', () => { - const tracks = [ - { id: '1', title: 'Track 1', artist: 'Artist 1', duration: 180, url: '', vibe: 'electronic' as MusicVibe }, - { id: '2', title: 'Track 2', artist: 'Artist 2', duration: 200, url: '', vibe: 'electronic' as MusicVibe }, - ] - - const track = musicService.getRandomTrack(tracks) - expect(track).not.toBeNull() - expect(['1', '2']).toContain(track!.id) - }) - - it('should return the only track for single-element array', () => { - const tracks = [ - { id: '1', title: 'Track 1', artist: 'Artist 1', duration: 180, url: '', vibe: 'electronic' as MusicVibe }, - ] - - const track = musicService.getRandomTrack(tracks) - expect(track).not.toBeNull() - expect(track!.id).toBe('1') - }) - }) - - describe('getNextTrack', () => { - const tracks = [ - { id: '1', title: 'Track 1', artist: 'Artist 1', duration: 180, url: '', vibe: 'electronic' as MusicVibe }, - { id: '2', title: 'Track 2', artist: 'Artist 2', duration: 200, url: '', vibe: 'electronic' as MusicVibe }, - { id: '3', title: 'Track 3', artist: 'Artist 3', duration: 220, url: '', vibe: 'electronic' as MusicVibe }, - ] - - it('should return null for empty array', () => { - const track = musicService.getNextTrack([], '1') - expect(track).toBeNull() - }) - - it('should return the only track for single-element array', () => { - const singleTrack = [tracks[0]] - const track = musicService.getNextTrack(singleTrack, '1') - expect(track).not.toBeNull() - expect(track!.id).toBe('1') - }) - - it('should return next track in sequence', () => { - const track = musicService.getNextTrack(tracks, '1', false) - expect(track).not.toBeNull() - expect(track!.id).toBe('2') - }) - - it('should wrap around to first track', () => { - const track = musicService.getNextTrack(tracks, '3', false) - expect(track).not.toBeNull() - expect(track!.id).toBe('1') - }) - - it('should return random track when shuffle is true', () => { - const track = musicService.getNextTrack(tracks, '1', true) - expect(track).not.toBeNull() - expect(track!.id).not.toBe('1') - }) - - it('should return different track when shuffling single remaining', () => { - const twoTracks = [tracks[0], tracks[1]] - const track = musicService.getNextTrack(twoTracks, '1', true) - expect(track).not.toBeNull() - expect(track!.id).toBe('2') - }) - }) -}) diff --git a/src/__tests__/services/purchases.test.ts b/src/__tests__/services/purchases.test.ts deleted file mode 100644 index eab86c1..0000000 --- a/src/__tests__/services/purchases.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' -import Purchases, { LOG_LEVEL } from 'react-native-purchases' - -vi.mock('react-native-purchases', () => ({ - default: { - configure: vi.fn(), - setLogLevel: vi.fn(), - }, - LOG_LEVEL: { - VERBOSE: 'VERBOSE', - DEBUG: 'DEBUG', - WARN: 'WARN', - ERROR: 'ERROR', - }, -})) - -describe('purchases service', () => { - beforeEach(() => { - vi.clearAllMocks() - vi.resetModules() - }) - - afterEach(() => { - vi.restoreAllMocks() - }) - - describe('constants', () => { - it('should export REVENUECAT_API_KEY', async () => { - const { REVENUECAT_API_KEY } = await import('../../shared/services/purchases') - expect(REVENUECAT_API_KEY).toBeDefined() - expect(REVENUECAT_API_KEY).toContain('test_') - }) - - it('should export ENTITLEMENT_ID', async () => { - const { ENTITLEMENT_ID } = await import('../../shared/services/purchases') - expect(ENTITLEMENT_ID).toBeDefined() - expect(ENTITLEMENT_ID).toBe('1000 Corp Pro') - }) - }) - - describe('initializePurchases', () => { - it('should call configure with API key', async () => { - const { initializePurchases } = await import('../../shared/services/purchases') - - await initializePurchases() - - expect(Purchases.configure).toHaveBeenCalled() - }) - - it('should set log level in dev mode', async () => { - const { initializePurchases } = await import('../../shared/services/purchases') - - await initializePurchases() - - if (__DEV__) { - expect(Purchases.setLogLevel).toHaveBeenCalledWith(LOG_LEVEL.VERBOSE) - } - }) - - it('should only initialize once', async () => { - const { initializePurchases } = await import('../../shared/services/purchases') - - await initializePurchases() - await initializePurchases() - - expect(Purchases.configure).toHaveBeenCalledTimes(1) - }) - - it('should throw on configuration error', async () => { - vi.mocked(Purchases.configure).mockRejectedValueOnce(new Error('Config failed')) - - vi.resetModules() - const { initializePurchases } = await import('../../shared/services/purchases') - - await expect(initializePurchases()).rejects.toThrow('Config failed') - }) - }) - - describe('isPurchasesInitialized', () => { - it('should return false before initialization', async () => { - vi.resetModules() - const { isPurchasesInitialized } = await import('../../shared/services/purchases') - - expect(isPurchasesInitialized()).toBe(false) - }) - - it('should return true after initialization', async () => { - vi.resetModules() - const { initializePurchases, isPurchasesInitialized } = await import('../../shared/services/purchases') - - await initializePurchases() - - expect(isPurchasesInitialized()).toBe(true) - }) - }) -}) diff --git a/src/__tests__/services/sync.test.ts b/src/__tests__/services/sync.test.ts deleted file mode 100644 index d787ccc..0000000 --- a/src/__tests__/services/sync.test.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest' -import { enableSync, syncWorkoutSession, deleteSyncedData, getSyncState, hasSyncedData, isAuthenticated } from '../../shared/services/sync' -import { supabase } from '@/src/shared/supabase' - -vi.mock('@/src/shared/supabase', () => ({ - supabase: { - auth: { - signInAnonymously: vi.fn(), - getUser: vi.fn(), - getSession: vi.fn(), - signOut: vi.fn(), - }, - from: vi.fn(), - }, -})) - -describe('sync service', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - describe('enableSync', () => { - it('should return success with userId when sync enabled', async () => { - vi.mocked(supabase.auth.signInAnonymously).mockResolvedValue({ - data: { user: { id: 'test-user-id' } }, - error: null, - } as any) - - vi.mocked(supabase.from).mockReturnValue({ - insert: vi.fn().mockResolvedValue({ error: null }), - } as any) - - const profileData = { - name: 'Test User', - fitnessLevel: 'intermediate' as const, - goal: 'strength' as const, - weeklyFrequency: 3 as const, - barriers: ['no-time'], - onboardingCompletedAt: new Date().toISOString(), - } - - const workoutHistory: Array<{ - workoutId: string - completedAt: string - durationSeconds: number - caloriesBurned: number - }> = [] - - const result = await enableSync(profileData, workoutHistory) - - expect(result.success).toBe(true) - expect(result.userId).toBe('test-user-id') - }) - - it('should return error when auth fails', async () => { - vi.mocked(supabase.auth.signInAnonymously).mockResolvedValue({ - data: { user: null }, - error: { message: 'Auth failed' }, - } as any) - - const result = await enableSync({ - name: 'Test', - fitnessLevel: 'beginner', - goal: 'cardio', - weeklyFrequency: 3, - barriers: [], - onboardingCompletedAt: new Date().toISOString(), - }, []) - - expect(result.success).toBe(false) - expect(result.error).toBeDefined() - }) - - it('should sync workout history when provided', async () => { - vi.mocked(supabase.auth.signInAnonymously).mockResolvedValue({ - data: { user: { id: 'test-user-id' } }, - error: null, - } as any) - - const mockInsert = vi.fn().mockResolvedValue({ error: null }) - vi.mocked(supabase.from).mockReturnValue({ - insert: mockInsert, - } as any) - - const profileData = { - name: 'Test User', - fitnessLevel: 'intermediate' as const, - goal: 'strength' as const, - weeklyFrequency: 3 as const, - barriers: ['no-time'], - onboardingCompletedAt: new Date().toISOString(), - } - - const workoutHistory = [ - { - workoutId: 'workout-1', - completedAt: new Date().toISOString(), - durationSeconds: 240, - caloriesBurned: 45, - }, - ] - - await enableSync(profileData, workoutHistory) - - expect(mockInsert).toHaveBeenCalled() - }) - }) - - describe('syncWorkoutSession', () => { - it('should return error when no authenticated user', async () => { - vi.mocked(supabase.auth.getUser).mockResolvedValue({ - data: { user: null }, - } as any) - - const result = await syncWorkoutSession({ - workoutId: 'workout-1', - completedAt: new Date().toISOString(), - durationSeconds: 240, - caloriesBurned: 45, - }) - - expect(result.success).toBe(false) - expect(result.error).toBe('No authenticated user') - }) - - it('should sync session when user is authenticated', async () => { - vi.mocked(supabase.auth.getUser).mockResolvedValue({ - data: { user: { id: 'test-user-id' } }, - } as any) - - vi.mocked(supabase.from).mockReturnValue({ - insert: vi.fn().mockResolvedValue({ error: null }), - } as any) - - const result = await syncWorkoutSession({ - workoutId: 'workout-1', - completedAt: new Date().toISOString(), - durationSeconds: 240, - caloriesBurned: 45, - }) - - expect(result.success).toBe(true) - }) - }) - - describe('deleteSyncedData', () => { - it('should return error when no authenticated user', async () => { - vi.mocked(supabase.auth.getUser).mockResolvedValue({ - data: { user: null }, - } as any) - - const result = await deleteSyncedData() - - expect(result.success).toBe(false) - expect(result.error).toBe('No authenticated user') - }) - - it('should delete all user data when authenticated', async () => { - vi.mocked(supabase.auth.getUser).mockResolvedValue({ - data: { user: { id: 'test-user-id' } }, - } as any) - - const mockDelete = vi.fn().mockReturnThis() - const mockEq = vi.fn().mockResolvedValue({ error: null }) - - vi.mocked(supabase.from).mockReturnValue({ - delete: mockDelete, - eq: mockEq, - } as any) - - vi.mocked(supabase.auth.signOut).mockResolvedValue({ error: null } as any) - - const result = await deleteSyncedData() - - expect(result.success).toBe(true) - expect(mockDelete).toHaveBeenCalledTimes(3) - }) - }) - - describe('getSyncState', () => { - it('should return never-synced when no session', async () => { - vi.mocked(supabase.auth.getSession).mockResolvedValue({ - data: { session: null }, - } as any) - - const state = await getSyncState() - - expect(state.status).toBe('never-synced') - expect(state.userId).toBeNull() - }) - - it('should return synced state with user info', async () => { - vi.mocked(supabase.auth.getSession).mockResolvedValue({ - data: { session: { user: { id: 'test-user-id' } } }, - } as any) - - vi.mocked(supabase.from).mockReturnValue({ - select: vi.fn().mockReturnThis(), - eq: vi.fn().mockReturnThis(), - order: vi.fn().mockReturnThis(), - limit: vi.fn().mockResolvedValue({ data: [] }), - } as any) - - const state = await getSyncState() - - expect(state.status).toBe('synced') - expect(state.userId).toBe('test-user-id') - }) - }) - - describe('hasSyncedData', () => { - it('should return false when no session', async () => { - vi.mocked(supabase.auth.getSession).mockResolvedValue({ - data: { session: null }, - } as any) - - const result = await hasSyncedData() - - expect(result).toBe(false) - }) - - it('should return true when session exists', async () => { - vi.mocked(supabase.auth.getSession).mockResolvedValue({ - data: { session: { user: { id: 'test-user-id' } } }, - } as any) - - const result = await hasSyncedData() - - expect(result).toBe(true) - }) - }) - - describe('isAuthenticated', () => { - it('should return false when no session', async () => { - vi.mocked(supabase.auth.getSession).mockResolvedValue({ - data: { session: null }, - } as any) - - const result = await isAuthenticated() - - expect(result).toBe(false) - }) - - it('should return true when session exists', async () => { - vi.mocked(supabase.auth.getSession).mockResolvedValue({ - data: { session: { user: { id: 'test-user-id' } } }, - } as any) - - const result = await isAuthenticated() - - expect(result).toBe(true) - }) - }) -}) diff --git a/src/__tests__/setup-render.tsx b/src/__tests__/setup-render.tsx deleted file mode 100644 index ec6b488..0000000 --- a/src/__tests__/setup-render.tsx +++ /dev/null @@ -1,431 +0,0 @@ -import { vi, afterEach, expect } from 'vitest' -import React from 'react' -import { cleanup } from '@testing-library/react-native' - -// Mock __DEV__ -vi.stubGlobal('__DEV__', true) - -// Quieter test output -vi.stubGlobal('console', { - ...console, - log: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), -}) - -afterEach(() => { - cleanup() - vi.clearAllMocks() -}) - -vi.mock('expo-blur', () => ({ - BlurView: ({ intensity, tint, children, style }: any) => { - return React.createElement('BlurView', { intensity, tint, style, testID: 'blur-view' }, children) - }, -})) - -vi.mock('expo-linear-gradient', () => ({ - LinearGradient: ({ colors, start, end, style, children }: any) => { - return React.createElement( - 'LinearGradient', - { colors, start, end, style, testID: 'linear-gradient' }, - children - ) - }, -})) - -vi.mock('expo-video', () => ({ - useVideoPlayer: vi.fn(() => ({ - play: vi.fn(), - pause: vi.fn(), - replace: vi.fn(), - currentTime: 0, - duration: 100, - playing: false, - muted: false, - volume: 1, - })), - VideoView: ({ player, style, contentFit, nativeControls }: any) => { - return React.createElement('VideoView', { player, style, contentFit, nativeControls, testID: 'video-view' }) - }, -})) - -vi.mock('expo-symbols', () => ({ - SymbolView: ({ name, size, tintColor, style, weight, type }: any) => { - return React.createElement('SymbolView', { name, size, tintColor, style, weight, type, testID: `icon-${name}` }) - }, -})) - -vi.mock('react-native-safe-area-context', () => ({ - useSafeAreaInsets: () => ({ top: 47, bottom: 34, left: 0, right: 0 }), - SafeAreaProvider: ({ children }: any) => children, -})) - -vi.mock('expo-constants', () => ({ - default: { - expoConfig: { version: '1.0.0' }, - systemFonts: [], - }, -})) - -vi.mock('expo-haptics', () => ({ - impactAsync: vi.fn(), - ImpactFeedbackStyle: { Light: 'light', Medium: 'medium', Heavy: 'heavy' }, - notificationAsync: vi.fn(), - NotificationFeedbackType: { Success: 'success', Warning: 'warning', Error: 'error' }, -})) - -vi.mock('expo-linking', () => ({ - default: { - openURL: vi.fn(), - createURL: vi.fn(), - }, -})) - -const mockThemeColors = { - bg: { - base: '#0D1B2A', - surface: '#112240', - elevated: '#1A3050', - overlay1: 'rgba(168,178,216,0.06)', - overlay2: 'rgba(168,178,216,0.10)', - overlay3: 'rgba(168,178,216,0.15)', - scrim: 'rgba(0,0,0,0.6)', - }, - text: { - primary: '#E6F1FF', - secondary: '#A8B2D8', - tertiary: '#8892B0', - muted: '#8892B0', - hint: '#8892B0', - disabled: '#3A3A3C', - }, - surface: { - default: { - backgroundColor: '#112240', - borderColor: 'rgba(168,178,216,0.15)', - borderWidth: 1, - }, - accent: { - backgroundColor: 'rgba(0,200,150,0.05)', - borderColor: 'rgba(0,200,150,0.35)', - borderWidth: 1.5, - }, - tip: { - backgroundColor: 'rgba(255,138,92,0.12)', - borderColor: '#FF8A5C', - borderWidth: 1, - }, - }, - border: { - dim: 'rgba(168,178,216,0.15)', - hover: 'rgba(168,178,216,0.25)', - brand: 'rgba(0,200,150,0.35)', - }, - gradients: { - videoOverlay: ['transparent', 'rgba(0,0,0,0.8)'], - videoTop: ['rgba(0,0,0,0.5)', 'transparent'], - }, - colorScheme: 'dark' as const, - statusBarStyle: 'light' as const, -} - -vi.mock('@/src/shared/theme', () => ({ - useThemeColors: () => mockThemeColors, - ThemeProvider: ({ children }: any) => children, -})) - -vi.mock('@/src/shared/constants/borderRadius', () => ({ - RADIUS: { - NONE: 0, - SM: 4, - MD: 8, - LG: 12, - XL: 16, - PILL: 9999, - FULL: 9999, - }, -})) - -vi.mock('@/src/shared/constants/spacing', () => ({ - SPACING: { - 1: 4, - 2: 8, - 3: 12, - 4: 16, - 5: 20, - 6: 24, - 8: 32, - }, - LAYOUT: { - SCREEN_PADDING: 24, - BUTTON_HEIGHT: 52, - }, -})) - -vi.mock('@/src/shared/constants/animations', () => ({ - DURATION: { - FAST: 150, - NORMAL: 250, - SLOW: 400, - }, - EASE: { - EASE_OUT: { x: 0.25, y: 0.1, x2: 0.25, y2: 1 }, - }, -})) - -vi.mock('react-i18next', () => ({ - useTranslation: () => ({ - t: (key: string) => key, - i18n: { changeLanguage: vi.fn(), language: 'en' }, - }), -})) - -// --------------------------------------------------------------------------- -// Mocks from setup.ts that are needed for render tests -// (react-native is NOT mocked here — it's provided by resolve.alias) -// --------------------------------------------------------------------------- - -vi.mock('expo-av', () => ({ - Audio: { - Sound: { - createAsync: vi.fn().mockResolvedValue({ - sound: { - playAsync: vi.fn(), - pauseAsync: vi.fn(), - stopAsync: vi.fn(), - unloadAsync: vi.fn(), - setPositionAsync: vi.fn(), - setVolumeAsync: vi.fn(), - }, - status: { isLoaded: true }, - }), - }, - setAudioModeAsync: vi.fn(), - }, - Video: 'Video', -})) - -vi.mock('expo-image', () => ({ - Image: 'Image', -})) - -vi.mock('expo-router', () => ({ - useRouter: vi.fn(() => ({ - push: vi.fn(), - replace: vi.fn(), - back: vi.fn(), - })), - useLocalSearchParams: vi.fn(() => ({})), - Link: 'Link', - Stack: { - Screen: 'Screen', - }, - Tabs: { - Tab: 'Tab', - }, -})) - -vi.mock('expo-localization', () => ({ - getLocales: vi.fn(() => [{ languageTag: 'en-US' }]), -})) - -vi.mock('expo-keep-awake', () => ({ - activateKeepAwakeAsync: vi.fn(), - deactivateKeepAwake: vi.fn(), -})) - -vi.mock('expo-notifications', () => ({ - scheduleNotificationAsync: vi.fn(), - cancelScheduledNotificationAsync: vi.fn(), - getAllScheduledNotificationsAsync: vi.fn().mockResolvedValue([]), - setNotificationHandler: vi.fn(), -})) - -vi.mock('expo-splash-screen', () => ({ - preventAutoHideAsync: vi.fn(), - hideAsync: vi.fn(), -})) - -vi.mock('expo-font', () => ({ - loadAsync: vi.fn(), -})) - -vi.mock('expo-file-system', () => ({ - documentDirectory: '/tmp/', - cacheDirectory: '/tmp/cache/', - readAsStringAsync: vi.fn(), - writeAsStringAsync: vi.fn(), - deleteAsync: vi.fn(), - getInfoAsync: vi.fn().mockResolvedValue({ exists: false }), -})) - -vi.mock('expo-device', () => ({ - isDevice: true, - model: 'iPhone 15', -})) - -vi.mock('expo-application', () => ({ - nativeApplicationVersion: '1.0.0', - nativeBuildVersion: '1', -})) - -vi.mock('@react-native-async-storage/async-storage', () => ({ - default: { - getItem: vi.fn().mockResolvedValue(null), - setItem: vi.fn().mockResolvedValue(undefined), - removeItem: vi.fn().mockResolvedValue(undefined), - clear: vi.fn().mockResolvedValue(undefined), - getAllKeys: vi.fn().mockResolvedValue([]), - }, -})) - -vi.mock('react-native-purchases', () => ({ - default: { - configure: vi.fn().mockResolvedValue(undefined), - getOfferings: vi.fn().mockResolvedValue({ - current: { - monthly: { - identifier: 'monthly', - product: { - identifier: 'tabatafit.premium.monthly', - priceString: '$9.99', - }, - }, - annual: { - identifier: 'annual', - product: { - identifier: 'tabatafit.premium.yearly', - priceString: '$79.99', - }, - }, - }, - }), - getCustomerInfo: vi.fn().mockResolvedValue({ - entitlements: { active: {}, all: {} }, - activeSubscriptions: [], - allPurchasedProductIdentifiers: [], - }), - purchasePackage: vi.fn().mockResolvedValue({ - customerInfo: { - entitlements: { active: {}, all: {} }, - activeSubscriptions: [], - }, - }), - restorePurchases: vi.fn().mockResolvedValue({ - entitlements: { active: {}, all: {} }, - activeSubscriptions: [], - }), - addCustomerInfoUpdateListener: vi.fn(), - removeCustomerInfoUpdateListener: vi.fn(), - setLogLevel: vi.fn(), - LOG_LEVEL: { - VERBOSE: 'verbose', - DEBUG: 'debug', - INFO: 'info', - WARN: 'warn', - ERROR: 'error', - }, - }, - LOG_LEVEL: { - VERBOSE: 'verbose', - DEBUG: 'debug', - INFO: 'info', - WARN: 'warn', - ERROR: 'error', - }, -})) - -vi.mock('react-native-reanimated', () => ({ - default: { - createAnimatedComponent: (comp: any) => comp, - View: 'View', - Text: 'Text', - Image: 'Image', - ScrollView: 'ScrollView', - }, - useAnimatedStyle: vi.fn(() => ({})), - useSharedValue: vi.fn(() => ({ value: 0 })), - withSpring: vi.fn((v) => v), - withTiming: vi.fn((v) => v), - withRepeat: vi.fn((v) => v), - withSequence: vi.fn((...v) => v[0]), - withDelay: vi.fn((_, v) => v), - Easing: { - linear: vi.fn(), - ease: vi.fn(), - bezier: vi.fn(), - }, - runOnJS: vi.fn((fn) => fn), -})) - -vi.mock('react-native-gesture-handler', () => ({ - Gesture: { - Pan: vi.fn(() => ({ onStart: vi.fn(), onUpdate: vi.fn(), onEnd: vi.fn() })), - Tap: vi.fn(() => ({ onStart: vi.fn(), onEnd: vi.fn() })), - }, - GestureDetector: 'GestureDetector', - PanGestureHandler: 'PanGestureHandler', - TapGestureHandler: 'TapGestureHandler', - State: {}, -})) - -vi.mock('react-native-screens', () => ({ - enableScreens: vi.fn(), -})) - -vi.mock('react-native-svg', () => ({ - Svg: 'Svg', - Path: 'Path', - Circle: 'Circle', - Rect: 'Rect', - G: 'G', - Defs: 'Defs', - Use: 'Use', -})) - -vi.mock('@/src/shared/supabase', () => ({ - supabase: { - auth: { - signInAnonymously: vi.fn().mockResolvedValue({ - data: { user: { id: 'test-user-id' } }, - error: null, - }), - getUser: vi.fn().mockResolvedValue({ - data: { user: { id: 'test-user-id' } }, - }), - getSession: vi.fn().mockResolvedValue({ - data: { session: { user: { id: 'test-user-id' } } }, - }), - signOut: vi.fn().mockResolvedValue({ error: null }), - }, - from: vi.fn(() => ({ - insert: vi.fn().mockResolvedValue({ error: null }), - select: vi.fn().mockReturnThis(), - eq: vi.fn().mockReturnThis(), - order: vi.fn().mockReturnThis(), - limit: vi.fn().mockResolvedValue({ data: [], error: null }), - delete: vi.fn().mockReturnThis(), - update: vi.fn().mockReturnThis(), - })), - }, -})) - -vi.mock('posthog-react-native', () => ({ - PostHogProvider: ({ children }: { children: React.ReactNode }) => children, - usePostHog: vi.fn(() => ({ - capture: vi.fn(), - identify: vi.fn(), - screen: vi.fn(), - reset: vi.fn(), - })), -})) - -vi.mock('@/src/shared/i18n', () => ({ - default: { - t: vi.fn((key: string) => key), - changeLanguage: vi.fn(), - language: 'en', - }, -})) diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts deleted file mode 100644 index a52f5f0..0000000 --- a/src/__tests__/setup.ts +++ /dev/null @@ -1,406 +0,0 @@ -import { vi, afterEach } from 'vitest' -import React from 'react' - -// Extend globals -vi.stubGlobal('vi', vi) - -// Mock React Native core -vi.mock('react-native', () => { - const RN = vi.importActual('react-native') - return { - ...RN, - Alert: { - alert: vi.fn(), - }, - Platform: { - OS: 'ios', - select: vi.fn((obj) => obj.ios), - }, - StyleSheet: { - create: (styles: any) => styles, - hairlineWidth: 1, - }, - Dimensions: { - get: vi.fn(() => ({ width: 375, height: 812 })), - addEventListener: vi.fn(() => ({ remove: vi.fn() })), - }, - View: 'View', - Text: 'Text', - TouchableOpacity: 'TouchableOpacity', - Pressable: 'Pressable', - Image: 'Image', - ScrollView: 'ScrollView', - FlatList: 'FlatList', - ActivityIndicator: 'ActivityIndicator', - SafeAreaView: 'SafeAreaView', - Easing: { - linear: vi.fn((v: number) => v), - ease: vi.fn((v: number) => v), - bezier: vi.fn(() => vi.fn((v: number) => v)), - quad: vi.fn((v: number) => v), - cubic: vi.fn((v: number) => v), - poly: vi.fn(() => vi.fn((v: number) => v)), - sin: vi.fn((v: number) => v), - circle: vi.fn((v: number) => v), - exp: vi.fn((v: number) => v), - elastic: vi.fn(() => vi.fn((v: number) => v)), - back: vi.fn(() => vi.fn((v: number) => v)), - bounce: vi.fn((v: number) => v), - in: vi.fn((easing: any) => easing), - out: vi.fn((easing: any) => easing), - inOut: vi.fn((easing: any) => easing), - }, - Animated: { - Value: vi.fn(() => ({ - interpolate: vi.fn(() => 0), - setValue: vi.fn(), - })), - View: 'Animated.View', - Text: 'Animated.Text', - Image: 'Animated.Image', - ScrollView: 'Animated.ScrollView', - FlatList: 'Animated.FlatList', - createAnimatedComponent: vi.fn((comp: any) => comp), - timing: vi.fn(() => ({ start: vi.fn() })), - spring: vi.fn(() => ({ start: vi.fn() })), - decay: vi.fn(() => ({ start: vi.fn() })), - sequence: vi.fn(() => ({ start: vi.fn() })), - parallel: vi.fn(() => ({ start: vi.fn() })), - loop: vi.fn(() => ({ start: vi.fn() })), - event: vi.fn(), - add: vi.fn(), - multiply: vi.fn(), - diffClamp: vi.fn(), - }, - } -}) - -// Mock expo modules -vi.mock('expo-constants', () => ({ - default: { - expoConfig: { version: '1.0.0' }, - systemFonts: [], - }, -})) - -vi.mock('expo-linking', () => ({ - default: { - openURL: vi.fn(), - createURL: vi.fn(), - }, -})) - -vi.mock('expo-haptics', () => ({ - impactAsync: vi.fn(), - ImpactFeedbackStyle: { - Light: 'light', - Medium: 'medium', - Heavy: 'heavy', - }, - notificationAsync: vi.fn(), - NotificationFeedbackType: { - Success: 'success', - Warning: 'warning', - Error: 'error', - }, -})) - -vi.mock('expo-av', () => ({ - Audio: { - Sound: { - createAsync: vi.fn().mockResolvedValue({ - sound: { - playAsync: vi.fn(), - pauseAsync: vi.fn(), - stopAsync: vi.fn(), - unloadAsync: vi.fn(), - setPositionAsync: vi.fn(), - setVolumeAsync: vi.fn(), - }, - status: { isLoaded: true }, - }), - }, - setAudioModeAsync: vi.fn(), - }, - Video: 'Video', -})) - -vi.mock('expo-video', () => ({ - useVideoPlayer: vi.fn(() => ({ - play: vi.fn(), - pause: vi.fn(), - replace: vi.fn(), - currentTime: 0, - duration: 100, - playing: false, - muted: false, - volume: 1, - })), - VideoView: 'VideoView', - VideoSource: vi.fn(), -})) - -vi.mock('expo-image', () => ({ - Image: 'Image', -})) - -vi.mock('expo-linear-gradient', () => ({ - LinearGradient: 'LinearGradient', -})) - -vi.mock('expo-blur', () => ({ - BlurView: 'BlurView', -})) - -vi.mock('expo-router', () => ({ - useRouter: vi.fn(() => ({ - push: vi.fn(), - replace: vi.fn(), - back: vi.fn(), - })), - useLocalSearchParams: vi.fn(() => ({})), - Link: 'Link', - Stack: { - Screen: 'Screen', - }, - Tabs: { - Tab: 'Tab', - }, -})) - -vi.mock('expo-localization', () => ({ - getLocales: vi.fn(() => [{ languageTag: 'en-US' }]), -})) - -vi.mock('expo-keep-awake', () => ({ - activateKeepAwakeAsync: vi.fn(), - deactivateKeepAwake: vi.fn(), -})) - -vi.mock('expo-notifications', () => ({ - scheduleNotificationAsync: vi.fn(), - cancelScheduledNotificationAsync: vi.fn(), - getAllScheduledNotificationsAsync: vi.fn().mockResolvedValue([]), - setNotificationHandler: vi.fn(), -})) - -vi.mock('expo-splash-screen', () => ({ - preventAutoHideAsync: vi.fn(), - hideAsync: vi.fn(), -})) - -vi.mock('expo-font', () => ({ - loadAsync: vi.fn(), -})) - -vi.mock('expo-file-system', () => ({ - documentDirectory: '/tmp/', - cacheDirectory: '/tmp/cache/', - readAsStringAsync: vi.fn(), - writeAsStringAsync: vi.fn(), - deleteAsync: vi.fn(), - getInfoAsync: vi.fn().mockResolvedValue({ exists: false }), -})) - -vi.mock('expo-device', () => ({ - isDevice: true, - model: 'iPhone 15', -})) - -vi.mock('expo-application', () => ({ - nativeApplicationVersion: '1.0.0', - nativeBuildVersion: '1', -})) - -// Mock AsyncStorage -vi.mock('@react-native-async-storage/async-storage', () => ({ - default: { - getItem: vi.fn().mockResolvedValue(null), - setItem: vi.fn().mockResolvedValue(undefined), - removeItem: vi.fn().mockResolvedValue(undefined), - clear: vi.fn().mockResolvedValue(undefined), - getAllKeys: vi.fn().mockResolvedValue([]), - }, -})) - -// Mock react-native-purchases (RevenueCat) -vi.mock('react-native-purchases', () => ({ - default: { - configure: vi.fn().mockResolvedValue(undefined), - getOfferings: vi.fn().mockResolvedValue({ - current: { - monthly: { - identifier: 'monthly', - product: { - identifier: 'tabatafit.premium.monthly', - priceString: '$9.99', - }, - }, - annual: { - identifier: 'annual', - product: { - identifier: 'tabatafit.premium.yearly', - priceString: '$79.99', - }, - }, - }, - }), - getCustomerInfo: vi.fn().mockResolvedValue({ - entitlements: { active: {}, all: {} }, - activeSubscriptions: [], - allPurchasedProductIdentifiers: [], - }), - purchasePackage: vi.fn().mockResolvedValue({ - customerInfo: { - entitlements: { active: {}, all: {} }, - activeSubscriptions: [], - }, - }), - restorePurchases: vi.fn().mockResolvedValue({ - entitlements: { active: {}, all: {} }, - activeSubscriptions: [], - }), - addCustomerInfoUpdateListener: vi.fn(), - removeCustomerInfoUpdateListener: vi.fn(), - setLogLevel: vi.fn(), - LOG_LEVEL: { - VERBOSE: 'verbose', - DEBUG: 'debug', - INFO: 'info', - WARN: 'warn', - ERROR: 'error', - }, - }, - LOG_LEVEL: { - VERBOSE: 'verbose', - DEBUG: 'debug', - INFO: 'info', - WARN: 'warn', - ERROR: 'error', - }, -})) - -// Mock react-native-reanimated -vi.mock('react-native-reanimated', () => ({ - default: { - createAnimatedComponent: (comp: any) => comp, - View: 'View', - Text: 'Text', - Image: 'Image', - ScrollView: 'ScrollView', - }, - useAnimatedStyle: vi.fn(() => ({})), - useSharedValue: vi.fn(() => ({ value: 0 })), - withSpring: vi.fn((v) => v), - withTiming: vi.fn((v) => v), - withRepeat: vi.fn((v) => v), - withSequence: vi.fn((...v) => v[0]), - withDelay: vi.fn((_, v) => v), - Easing: { - linear: vi.fn(), - ease: vi.fn(), - bezier: vi.fn(), - }, - runOnJS: vi.fn((fn) => fn), -})) - -// Mock react-native-gesture-handler -vi.mock('react-native-gesture-handler', () => ({ - Gesture: { - Pan: vi.fn(() => ({ onStart: vi.fn(), onUpdate: vi.fn(), onEnd: vi.fn() })), - Tap: vi.fn(() => ({ onStart: vi.fn(), onEnd: vi.fn() })), - }, - GestureDetector: 'GestureDetector', - PanGestureHandler: 'PanGestureHandler', - TapGestureHandler: 'TapGestureHandler', - State: {}, -})) - -// Mock react-native-screens -vi.mock('react-native-screens', () => ({ - enableScreens: vi.fn(), -})) - -// Mock react-native-safe-area-context -vi.mock('react-native-safe-area-context', () => ({ - SafeAreaProvider: ({ children }: { children: React.ReactNode }) => children, - useSafeAreaInsets: vi.fn(() => ({ top: 44, bottom: 34, left: 0, right: 0 })), - SafeAreaView: 'SafeAreaView', -})) - -// Mock react-native-svg -vi.mock('react-native-svg', () => ({ - Svg: 'Svg', - Path: 'Path', - Circle: 'Circle', - Rect: 'Rect', - G: 'G', - Defs: 'Defs', - Use: 'Use', -})) - -// Mock Supabase -vi.mock('@/src/shared/supabase', () => ({ - supabase: { - auth: { - signInAnonymously: vi.fn().mockResolvedValue({ - data: { user: { id: 'test-user-id' } }, - error: null, - }), - getUser: vi.fn().mockResolvedValue({ - data: { user: { id: 'test-user-id' } }, - }), - getSession: vi.fn().mockResolvedValue({ - data: { session: { user: { id: 'test-user-id' } } }, - }), - signOut: vi.fn().mockResolvedValue({ error: null }), - }, - from: vi.fn(() => ({ - insert: vi.fn().mockResolvedValue({ error: null }), - select: vi.fn().mockReturnThis(), - eq: vi.fn().mockReturnThis(), - order: vi.fn().mockReturnThis(), - limit: vi.fn().mockResolvedValue({ data: [], error: null }), - delete: vi.fn().mockReturnThis(), - update: vi.fn().mockReturnThis(), - })), - }, -})) - -// Mock PostHog -vi.mock('posthog-react-native', () => ({ - PostHogProvider: ({ children }: { children: React.ReactNode }) => children, - usePostHog: vi.fn(() => ({ - capture: vi.fn(), - identify: vi.fn(), - screen: vi.fn(), - reset: vi.fn(), - })), -})) - -// Mock i18next -vi.mock('@/src/shared/i18n', () => ({ - default: { - t: vi.fn((key: string) => key), - changeLanguage: vi.fn(), - language: 'en', - }, -})) - -// Mock __DEV__ -vi.stubGlobal('__DEV__', true) - -// Mock console methods for cleaner test output -const originalConsole = { ...console } -vi.stubGlobal('console', { - ...console, - log: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), -}) - -// Restore console after each test if needed -afterEach(() => { - vi.clearAllMocks() -}) diff --git a/src/__tests__/stores/playerStore.test.ts b/src/__tests__/stores/playerStore.test.ts deleted file mode 100644 index a846666..0000000 --- a/src/__tests__/stores/playerStore.test.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { describe, it, expect, beforeEach } from 'vitest' -import { usePlayerStore } from '../../shared/stores/playerStore' -import type { Workout } from '../../shared/types' - -const mockWorkout: Workout = { - id: 'test-workout', - title: 'Test Workout', - trainerId: 'trainer-1', - category: 'full-body', - level: 'Beginner', - duration: 4, - calories: 45, - rounds: 8, - prepTime: 10, - workTime: 20, - restTime: 10, - equipment: [], - musicVibe: 'electronic', - exercises: [ - { name: 'Jumping Jacks', duration: 20 }, - { name: 'Squats', duration: 20 }, - { name: 'Push-ups', duration: 20 }, - { name: 'High Knees', duration: 20 }, - ], -} - -describe('playerStore', () => { - beforeEach(() => { - usePlayerStore.getState().reset() - }) - - describe('initial state', () => { - it('should have correct default values', () => { - const state = usePlayerStore.getState() - - expect(state.workout).toBeNull() - expect(state.phase).toBe('PREP') - expect(state.timeRemaining).toBe(10) - expect(state.currentRound).toBe(1) - expect(state.isPaused).toBe(false) - expect(state.isRunning).toBe(false) - expect(state.calories).toBe(0) - expect(state.startedAt).toBeNull() - }) - }) - - describe('loadWorkout', () => { - it('should load workout and reset all state', () => { - const store = usePlayerStore.getState() - - store.loadWorkout(mockWorkout) - - const state = usePlayerStore.getState() - expect(state.workout).toEqual(mockWorkout) - expect(state.phase).toBe('PREP') - expect(state.timeRemaining).toBe(mockWorkout.prepTime) - expect(state.currentRound).toBe(1) - expect(state.isPaused).toBe(false) - expect(state.isRunning).toBe(false) - expect(state.calories).toBe(0) - expect(state.startedAt).toBeNull() - }) - - it('should use workout prepTime for timeRemaining', () => { - const workoutWithCustomPrep: Workout = { ...mockWorkout, prepTime: 15 } - - usePlayerStore.getState().loadWorkout(workoutWithCustomPrep) - - expect(usePlayerStore.getState().timeRemaining).toBe(15) - }) - }) - - describe('setPhase', () => { - it('should update phase', () => { - const store = usePlayerStore.getState() - - store.setPhase('WORK') - expect(usePlayerStore.getState().phase).toBe('WORK') - - store.setPhase('REST') - expect(usePlayerStore.getState().phase).toBe('REST') - - store.setPhase('COMPLETE') - expect(usePlayerStore.getState().phase).toBe('COMPLETE') - }) - }) - - describe('setTimeRemaining', () => { - it('should update timeRemaining', () => { - const store = usePlayerStore.getState() - - store.setTimeRemaining(5) - expect(usePlayerStore.getState().timeRemaining).toBe(5) - - store.setTimeRemaining(0) - expect(usePlayerStore.getState().timeRemaining).toBe(0) - }) - }) - - describe('setCurrentRound', () => { - it('should update currentRound', () => { - const store = usePlayerStore.getState() - - store.setCurrentRound(3) - expect(usePlayerStore.getState().currentRound).toBe(3) - - store.setCurrentRound(8) - expect(usePlayerStore.getState().currentRound).toBe(8) - }) - }) - - describe('setPaused', () => { - it('should toggle pause state', () => { - const store = usePlayerStore.getState() - - store.setPaused(true) - expect(usePlayerStore.getState().isPaused).toBe(true) - - store.setPaused(false) - expect(usePlayerStore.getState().isPaused).toBe(false) - }) - }) - - describe('setRunning', () => { - it('should set running state', () => { - const store = usePlayerStore.getState() - - store.setRunning(true) - expect(usePlayerStore.getState().isRunning).toBe(true) - }) - - it('should set startedAt when first running', () => { - const store = usePlayerStore.getState() - const beforeSet = Date.now() - - store.setRunning(true) - - const state = usePlayerStore.getState() - expect(state.startedAt).toBeGreaterThanOrEqual(beforeSet) - expect(state.startedAt).toBeLessThanOrEqual(Date.now()) - }) - - it('should not update startedAt if already set', () => { - const store = usePlayerStore.getState() - - store.setRunning(true) - const firstStartedAt = usePlayerStore.getState().startedAt - - store.setRunning(false) - store.setRunning(true) - - expect(usePlayerStore.getState().startedAt).toBe(firstStartedAt) - }) - }) - - describe('addCalories', () => { - it('should accumulate calories', () => { - const store = usePlayerStore.getState() - - store.addCalories(5) - expect(usePlayerStore.getState().calories).toBe(5) - - store.addCalories(5) - expect(usePlayerStore.getState().calories).toBe(10) - - store.addCalories(3) - expect(usePlayerStore.getState().calories).toBe(13) - }) - }) - - describe('reset', () => { - it('should reset all state to defaults', () => { - const store = usePlayerStore.getState() - - store.loadWorkout(mockWorkout) - store.setPhase('WORK') - store.setCurrentRound(5) - store.setRunning(true) - store.addCalories(25) - - store.reset() - - const state = usePlayerStore.getState() - expect(state.workout).toBeNull() - expect(state.phase).toBe('PREP') - expect(state.timeRemaining).toBe(10) - expect(state.currentRound).toBe(1) - expect(state.isPaused).toBe(false) - expect(state.isRunning).toBe(false) - expect(state.calories).toBe(0) - expect(state.startedAt).toBeNull() - }) - }) - - describe('workout phase simulation', () => { - it('should track complete workout flow', () => { - const store = usePlayerStore.getState() - - store.loadWorkout(mockWorkout) - - expect(store.phase).toBe('PREP') - expect(store.timeRemaining).toBe(10) - - store.setPhase('WORK') - store.setTimeRemaining(20) - expect(usePlayerStore.getState().phase).toBe('WORK') - - store.setTimeRemaining(0) - store.addCalories(6) - store.setPhase('REST') - store.setTimeRemaining(10) - expect(usePlayerStore.getState().calories).toBe(6) - expect(usePlayerStore.getState().phase).toBe('REST') - - store.setCurrentRound(2) - store.setPhase('WORK') - expect(usePlayerStore.getState().currentRound).toBe(2) - }) - }) -}) diff --git a/src/__tests__/stores/userStore.test.ts b/src/__tests__/stores/userStore.test.ts deleted file mode 100644 index e3fb581..0000000 --- a/src/__tests__/stores/userStore.test.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { describe, it, expect, beforeEach } from 'vitest' -import { useUserStore } from '../../shared/stores/userStore' -import type { FitnessLevel, FitnessGoal, WeeklyFrequency } from '../../shared/types' - -describe('userStore', () => { - beforeEach(() => { - useUserStore.setState({ - profile: { - name: '', - email: '', - joinDate: 'March 2026', - subscription: 'free', - onboardingCompleted: false, - fitnessLevel: 'beginner', - goal: 'cardio', - weeklyFrequency: 3, - barriers: [], - syncStatus: 'never-synced', - supabaseUserId: null, - savedWorkouts: [], - }, - settings: { - haptics: true, - soundEffects: true, - voiceCoaching: true, - musicEnabled: true, - musicVolume: 0.5, - reminders: false, - reminderTime: '09:00', - hasPromptedReview: false, - }, - }) - }) - - describe('initial state', () => { - it('should have correct default profile values', () => { - const { profile } = useUserStore.getState() - - expect(profile.name).toBe('') - expect(profile.email).toBe('') - expect(profile.subscription).toBe('free') - expect(profile.onboardingCompleted).toBe(false) - expect(profile.fitnessLevel).toBe('beginner') - expect(profile.goal).toBe('cardio') - expect(profile.weeklyFrequency).toBe(3) - expect(profile.barriers).toEqual([]) - expect(profile.syncStatus).toBe('never-synced') - expect(profile.supabaseUserId).toBeNull() - }) - - it('should have correct default settings values', () => { - const { settings } = useUserStore.getState() - - expect(settings.haptics).toBe(true) - expect(settings.soundEffects).toBe(true) - expect(settings.voiceCoaching).toBe(true) - expect(settings.musicEnabled).toBe(true) - expect(settings.musicVolume).toBe(0.5) - expect(settings.reminders).toBe(false) - expect(settings.reminderTime).toBe('09:00') - }) - }) - - describe('updateProfile', () => { - it('should update profile fields', () => { - const store = useUserStore.getState() - - store.updateProfile({ name: 'John Doe' }) - expect(useUserStore.getState().profile.name).toBe('John Doe') - }) - - it('should merge updates with existing profile', () => { - const store = useUserStore.getState() - - store.updateProfile({ name: 'Jane' }) - store.updateProfile({ email: 'jane@example.com' }) - - const profile = useUserStore.getState().profile - expect(profile.name).toBe('Jane') - expect(profile.email).toBe('jane@example.com') - }) - - it('should update fitness level', () => { - const store = useUserStore.getState() - const levels: FitnessLevel[] = ['beginner', 'intermediate', 'advanced'] - - levels.forEach((level) => { - store.updateProfile({ fitnessLevel: level }) - expect(useUserStore.getState().profile.fitnessLevel).toBe(level) - }) - }) - - it('should update fitness goal', () => { - const store = useUserStore.getState() - const goals: FitnessGoal[] = ['weight-loss', 'cardio', 'strength', 'wellness'] - - goals.forEach((goal) => { - store.updateProfile({ goal }) - expect(useUserStore.getState().profile.goal).toBe(goal) - }) - }) - - it('should update weekly frequency', () => { - const store = useUserStore.getState() - const frequencies: WeeklyFrequency[] = [2, 3, 5] - - frequencies.forEach((freq) => { - store.updateProfile({ weeklyFrequency: freq }) - expect(useUserStore.getState().profile.weeklyFrequency).toBe(freq) - }) - }) - - it('should update barriers array', () => { - const store = useUserStore.getState() - - store.updateProfile({ barriers: ['no-time', 'low-motivation'] }) - expect(useUserStore.getState().profile.barriers).toEqual(['no-time', 'low-motivation']) - }) - }) - - describe('updateSettings', () => { - it('should update individual settings', () => { - const store = useUserStore.getState() - - store.updateSettings({ haptics: false }) - expect(useUserStore.getState().settings.haptics).toBe(false) - }) - - it('should merge updates with existing settings', () => { - const store = useUserStore.getState() - - store.updateSettings({ haptics: false }) - store.updateSettings({ soundEffects: false }) - store.updateSettings({ musicVolume: 0.8 }) - - const settings = useUserStore.getState().settings - expect(settings.haptics).toBe(false) - expect(settings.soundEffects).toBe(false) - expect(settings.musicVolume).toBe(0.8) - expect(settings.voiceCoaching).toBe(true) - }) - - it('should update reminder settings', () => { - const store = useUserStore.getState() - - store.updateSettings({ reminders: true, reminderTime: '07:30' }) - - const settings = useUserStore.getState().settings - expect(settings.reminders).toBe(true) - expect(settings.reminderTime).toBe('07:30') - }) - }) - - describe('setSubscription', () => { - it('should update subscription plan', () => { - const store = useUserStore.getState() - - store.setSubscription('premium-monthly') - expect(useUserStore.getState().profile.subscription).toBe('premium-monthly') - - store.setSubscription('premium-yearly') - expect(useUserStore.getState().profile.subscription).toBe('premium-yearly') - - store.setSubscription('free') - expect(useUserStore.getState().profile.subscription).toBe('free') - }) - }) - - describe('completeOnboarding', () => { - it('should set onboarding completed flag', () => { - const store = useUserStore.getState() - - store.completeOnboarding({ - name: 'Test User', - fitnessLevel: 'intermediate', - goal: 'strength', - weeklyFrequency: 5, - barriers: ['no-time'], - }) - - const profile = useUserStore.getState().profile - expect(profile.onboardingCompleted).toBe(true) - expect(profile.name).toBe('Test User') - expect(profile.fitnessLevel).toBe('intermediate') - expect(profile.goal).toBe('strength') - expect(profile.weeklyFrequency).toBe(5) - expect(profile.barriers).toEqual(['no-time']) - }) - }) - - describe('setSyncStatus', () => { - it('should update sync status', () => { - const store = useUserStore.getState() - - store.setSyncStatus('prompt-pending') - expect(useUserStore.getState().profile.syncStatus).toBe('prompt-pending') - - store.setSyncStatus('synced', 'user-123') - const profile = useUserStore.getState().profile - expect(profile.syncStatus).toBe('synced') - expect(profile.supabaseUserId).toBe('user-123') - }) - }) - - describe('setPromptPending', () => { - it('should set sync status to prompt-pending', () => { - const store = useUserStore.getState() - - store.setPromptPending() - expect(useUserStore.getState().profile.syncStatus).toBe('prompt-pending') - }) - }) -}) diff --git a/src/__tests__/utils/color.test.ts b/src/__tests__/utils/color.test.ts deleted file mode 100644 index 579dab0..0000000 --- a/src/__tests__/utils/color.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { describe, it, expect } from 'vitest' -import { withOpacity } from '../../shared/utils/color' - -describe('withOpacity', () => { - it('should convert 6-digit hex with opacity', () => { - expect(withOpacity('#FF6B35', 0.5)).toBe('rgba(255,107,53,0.5)') - }) - - it('should convert 3-digit hex with opacity', () => { - expect(withOpacity('#FFF', 1)).toBe('rgba(255,255,255,1)') - }) - - it('should handle hex without # prefix', () => { - expect(withOpacity('000000', 0)).toBe('rgba(0,0,0,0)') - }) - - it('should handle 3-digit hex without # prefix', () => { - expect(withOpacity('F00', 0.8)).toBe('rgba(255,0,0,0.8)') - }) - - it('should handle pure black', () => { - expect(withOpacity('#000000', 1)).toBe('rgba(0,0,0,1)') - }) - - it('should handle pure white', () => { - expect(withOpacity('#FFFFFF', 0.12)).toBe('rgba(255,255,255,0.12)') - }) - - it('should handle lowercase hex', () => { - expect(withOpacity('#ff6b35', 0.5)).toBe('rgba(255,107,53,0.5)') - }) -}) diff --git a/src/__tests__/utils/render-utils.tsx b/src/__tests__/utils/render-utils.tsx deleted file mode 100644 index f997a5c..0000000 --- a/src/__tests__/utils/render-utils.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, { ReactElement } from 'react' -import { render, RenderOptions } from '@testing-library/react-native' - -const mockThemeColors = { - bg: { - base: '#000000', - surface: '#1C1C1E', - elevated: '#2C2C2E', - }, - text: { - primary: '#FFFFFF', - secondary: '#8E8E93', - tertiary: '#636366', - }, - glass: { - base: { - backgroundColor: 'rgba(255, 255, 255, 0.05)', - borderColor: 'rgba(255, 255, 255, 0.1)', - borderWidth: 1, - }, - elevated: { - backgroundColor: 'rgba(255, 255, 255, 0.08)', - borderColor: 'rgba(255, 255, 255, 0.12)', - borderWidth: 1, - }, - inset: { - backgroundColor: 'rgba(0, 0, 0, 0.2)', - borderColor: 'rgba(255, 255, 255, 0.05)', - borderWidth: 1, - }, - tinted: { - backgroundColor: 'rgba(255, 107, 53, 0.1)', - borderColor: 'rgba(255, 107, 53, 0.2)', - borderWidth: 1, - }, - blurTint: 'dark', - blurMedium: 40, - }, - shadow: { - sm: { shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.2, shadowRadius: 1 }, - md: { shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.25, shadowRadius: 4 }, - lg: { shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 8 }, - }, -} - -interface CustomRenderOptions extends Omit { - theme?: typeof mockThemeColors -} - -export function renderWithTheme( - ui: ReactElement, - options: CustomRenderOptions = {} -) { - return render(ui, options) -} - -export { mockThemeColors } diff --git a/src/features/player/CLAUDE.md b/src/features/player/CLAUDE.md deleted file mode 100644 index ec0a57d..0000000 --- a/src/features/player/CLAUDE.md +++ /dev/null @@ -1,15 +0,0 @@ - -# Recent Activity - - - -### Apr 13, 2026 - -| ID | Time | T | Title | Read | -|----|------|---|-------|------| -| #6298 | 11:39 PM | 🟣 | YouTube music system fully integrated with workout timers - database-driven architecture replaces storage buckets | ~617 | -| #6282 | 11:06 PM | 🟣 | Music playback disabled during warm-up phase in Kine sessions | ~293 | -| #6275 | 10:53 PM | 🔴 | React hook execution order fixed in KinePlayerScreen | ~327 | -| #6272 | " | 🟣 | NowPlaying component integrated into KinePlayerScreen UI | ~368 | -| #6271 | 10:52 PM | 🟣 | useMusicPlayer hook added to KinePlayerScreen for music synchronization | ~359 | - \ No newline at end of file diff --git a/src/features/player/TabataPlayerScreen.tsx b/src/features/player/TabataPlayerScreen.tsx deleted file mode 100644 index abc94cf..0000000 --- a/src/features/player/TabataPlayerScreen.tsx +++ /dev/null @@ -1,346 +0,0 @@ -/** - * Tabata Player Screen - * Handles multi-block tabata sessions with warmup, blocks, inter-block rest, cooldown - */ - -import React, { useRef, useEffect, useCallback, useState } from 'react' -import { - View, Text, StyleSheet, Pressable, Animated, StatusBar, -} from 'react-native' -import { useRouter } from 'expo-router' -import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useKeepAwake } from 'expo-keep-awake' -import { useTranslation } from 'react-i18next' - -import { useTabataTimer } from '@/src/shared/hooks/useTabataTimer' -import { useHaptics } from '@/src/shared/hooks/useHaptics' -import { useAudio } from '@/src/shared/hooks/useAudio' -import { useMusicPlayer } from '@/src/shared/hooks/useMusicPlayer' -import { useProgressStore } from '@/src/shared/stores' -import { track } from '@/src/shared/services/analytics' -import type { TabataSession } from '@/src/shared/types/program' -import type { WorkoutProgram } from '@/src/shared/types/workoutProgram' -import { PHASE_COLORS } from '@/src/shared/theme' -import { TYPOGRAPHY } from '@/src/shared/constants/typography' -import { SPACING } from '@/src/shared/constants/spacing' -import { RADIUS } from '@/src/shared/constants/borderRadius' -import { GREEN, NAVY, BORDER_COLORS, TEXT, PHASE, AMBER } from '@/src/shared/constants/colors' -import { Icon } from '@/src/shared/components/Icon' - -import { TimerRing, PhaseIndicator, RoundIndicator, PlayerControls, StatsOverlay, CoachEncouragement, NowPlaying } from '@/src/features/player' -import { TabataTip } from '@/src/features/player/components/TabataTip' -import { BlockIndicator } from '@/src/features/player/components/BlockIndicator' -import { WarmupOverlay } from '@/src/features/player/components/WarmupOverlay' - -// Lavender for STRETCH (COOLDOWN) phase per design spec -const LAVENDER = '#B4A7E5' - -function formatTime(seconds: number) { - const mins = Math.floor(seconds / 60) - const secs = seconds % 60 - return `${mins}:${secs.toString().padStart(2, '0')}` -} - -const TABATA_PHASE_COLORS: Record = { - WARMUP: AMBER[500], - WORK: PHASE.WORK, - REST: PHASE.REST, - INTER_BLOCK_REST: AMBER[500], - COOLDOWN: LAVENDER, - COMPLETE: GREEN[500], -} - -interface TabataPlayerScreenProps { - session: TabataSession - program?: WorkoutProgram -} - -export function TabataPlayerScreen({ session, program }: TabataPlayerScreenProps) { - useKeepAwake() - const { t } = useTranslation() - const router = useRouter() - const insets = useSafeAreaInsets() - const haptics = useHaptics() - const audio = useAudio() - const completeProgress = useProgressStore(s => s.completeProgram) - - const timer = useTabataTimer(session) - const [showControls, setShowControls] = useState(true) - const [isMuted, setIsMuted] = useState(false) - - // Music muted during WARMUP/COOLDOWN (stretch) per spec - const musicActive = - timer.isRunning && - !timer.isPaused && - timer.phase !== 'WARMUP' && - timer.phase !== 'COOLDOWN' - - const music = useMusicPlayer({ - vibe: session.musicVibe ?? 'electronic', - isPlaying: musicActive, - volume: isMuted ? 0 : 0.5, - }) - const timerScaleAnim = useRef(new Animated.Value(0.8)).current - - const phaseColor = TABATA_PHASE_COLORS[timer.phase] ?? PHASE.WORK - - // ─── Actions ───────────────────────────────────────────────── - - const startTimer = useCallback(() => { - timer.start() - haptics.buttonTap() - track('tabata_session_started', { session_id: session.id, blocks: session.blocks.length }) - }, [timer, haptics, session]) - - const stopWorkout = useCallback(() => { - haptics.phaseChange() - timer.stop() - router.back() - }, [router, timer, haptics]) - - const completeWorkout = useCallback(() => { - haptics.workoutComplete() - track('tabata_session_completed', { - session_id: session.id, - calories: timer.calories, - total_rounds: timer.totalRounds, - blocks: timer.totalBlocks, - }) - // Record session in unified progress store - const programId = program?.id ?? session.id.replace(/^wp-/, '') - completeProgress({ - programId, - completedAt: Date.now(), - durationSeconds: session.totalDuration * 60, - bodyZone: program?.bodyZone ?? 'full', - level: program?.level ?? 'Beginner', - }).catch(() => {}) - router.replace(`/complete/${session.id}`) - }, [router, session, timer, haptics, completeProgress, program]) - - const handleSkip = useCallback(() => { - timer.skip() - haptics.selection() - }, [timer, haptics]) - - // ─── Animations & side-effects ─────────────────────────────── - - useEffect(() => { - Animated.spring(timerScaleAnim, { - toValue: 1, friction: 6, tension: 100, useNativeDriver: true, - }).start() - }, []) - - useEffect(() => { - timerScaleAnim.setValue(0.9) - Animated.spring(timerScaleAnim, { - toValue: 1, friction: 4, tension: 150, useNativeDriver: true, - }).start() - haptics.phaseChange() - if (timer.phase === 'COMPLETE') { - if (!isMuted) audio.workoutComplete() - } else if (timer.isRunning) { - if (!isMuted) audio.phaseStart() - } - }, [timer.phase]) - - useEffect(() => { - if (timer.isRunning && timer.timeRemaining <= 3 && timer.timeRemaining > 0) { - if (!isMuted) audio.countdownBeep() - haptics.countdownTick() - } - }, [timer.timeRemaining]) - - // ─── Render ────────────────────────────────────────────────── - - const isWarmup = timer.phase === 'WARMUP' - const isCooldown = timer.phase === 'COOLDOWN' - const isInterBlockRest = timer.phase === 'INTER_BLOCK_REST' - const isBlockPhase = timer.phase === 'WORK' || timer.phase === 'REST' - - return ( - -