diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddf..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6e393ae --- /dev/null +++ b/.env.example @@ -0,0 +1,76 @@ +# ============================================================ +# Bojo (Boiska Poznań) — zmienne środowiskowe +# Skopiuj ten plik do .env i uzupełnij wartości. +# NIGDY nie commituj pliku .env do repozytorium. +# ============================================================ + +# ----- Frontend (Next.js) — zmienne publiczne, widoczne w przeglądarce ----- + +# Supabase: URL projektu, np. https://abcdefghijkl.supabase.co +NEXT_PUBLIC_SUPABASE_URL= + +# Supabase: klucz publiczny "anon" (bezpieczny dla frontendu — chroniony przez RLS) +NEXT_PUBLIC_SUPABASE_ANON_KEY= + +# Mapbox: token publiczny — używany TYLKO do miniatur zdjęć boisk (opcjonalny). +# Bez niego mapa nadal działa (Leaflet + OpenStreetMap), znikają tylko miniaturki. +# https://account.mapbox.com/ +NEXT_PUBLIC_MAPBOX_TOKEN= + +# Adres produkcyjny aplikacji — używany w sitemap.xml i znacznikach SEO. +# np. https://bojo.app (opcjonalny, domyślnie localhost) +NEXT_PUBLIC_SITE_URL= + +# Włącznik rezerwacji w aplikacji ("true" = pokaż UI rezerwacji globalnie). +# Można też włączyć per-obiekt flagą booking_enabled w bazie. (opcjonalny) +NEXT_PUBLIC_FEATURE_RESERVATIONS=false + +# ----- Scraper / wzbogacanie danych — TYLKO lokalnie / w GitHub Actions ----- +# Te wartości NIGDY nie trafiają do frontendu. W GitHub Actions trzymaj je w Secrets. + +# Supabase: URL projektu (ta sama wartość co NEXT_PUBLIC_SUPABASE_URL) +SUPABASE_URL= + +# Supabase: klucz service_role — pełny dostęp do bazy, OMIJA RLS. +# Settings → API → service_role. Trzymaj w sekrecie! +SUPABASE_SERVICE_ROLE_KEY= + +# Google Places API — dla enrich_google.py (telefon/strona/godziny). +# https://console.cloud.google.com/ +GOOGLE_PLACES_API_KEY= + +# Anthropic (Claude) API — dla enrich.py i enrich_booking.py. +# https://console.anthropic.com/ +ANTHROPIC_API_KEY= + +# Domyślny model Claude dla scrapera (opcjonalny). +ANTHROPIC_MODEL=claude-haiku-4-5-20251001 + +# ----- Supabase Edge Functions — sekrety (ustawiane przez `supabase secrets set`) ----- +# To NIE są zmienne .env frontendu. Ustaw je w projekcie Supabase, np.: +# supabase secrets set RESEND_API_KEY=... +# Używane przez funkcje: notify-game-alert, send-invites, send-event-sms. + +# Resend — wysyłka e-maili (powiadomienia o grach, zaproszenia). +# https://resend.com/api-keys Nadawca: noreply@bojo.app (zweryfikuj domenę w Resend). +RESEND_API_KEY= + +# SMSAPI.pl — wysyłka SMS (preferowany dostawca dla PL). +# https://ssl.smsapi.pl/ → Ustawienia → Tokeny API +SMSAPI_TOKEN= + +# Twilio — zapasowy dostawca SMS (używany, gdy SMSAPI zawiedzie). +# https://console.twilio.com/ +TWILIO_ACCOUNT_SID= +TWILIO_AUTH_TOKEN= +TWILIO_PHONE_FROM= + +# ----- Logowanie / Auth (konfiguracja w panelu Supabase, nie w .env) ----- +# Authentication → Providers: +# • Google OAuth — Client ID/Secret z Google Cloud Console. +# • Email — włącz "Email" provider dla logowania e-mailem + hasłem i magic-linków. +# Authentication → URL Configuration: +# • Site URL: https://bojo.app +# • Redirect URLs: dodaj https://bojo.app/auth/callback i https://bojo.app/auth/reset +# Authentication → Emails (SMTP): podłącz Resend jako custom SMTP, inaczej maile +# potwierdzające / magic-linki są mocno limitowane (kilka/godzinę) na darmowym planie. diff --git a/.github/workflows/analyze-venues-satellite.yml b/.github/workflows/analyze-venues-satellite.yml new file mode 100644 index 0000000..c1626c0 --- /dev/null +++ b/.github/workflows/analyze-venues-satellite.yml @@ -0,0 +1,105 @@ +name: Analiza satelitarna boisk (AI) + +# Wykrywa typ boiska, nawierzchnię, wymiary, infrastrukturę +# na podstawie zdjęcia satelitarnego Mapbox (zoom 18). +# +# Koszt szacunkowy: +# • claude-sonnet-4-6 → ~$0.010–0.015 / boisko (vision input ~1000 tok, output ~300 tok) +# • claude-haiku-4-5 → ~$0.002–0.003 / boisko (tańszy, nieco gorszy przy skomplikowanych obiektach) +# • 400 "quality" boisk (powiat poznański, mają jakieś info) → ~$4–6 Sonnet / ~$1 Haiku +# • Ponowna analiza wszystkich co miesiąc: zalecaj Haiku, Sonnet tylko przy pierwszym przebiegu +# +# Wymagane sekrety: +# SUPABASE_URL +# SUPABASE_SERVICE_ROLE_KEY +# ANTHROPIC_API_KEY +# MAPBOX_TOKEN (publiczny token z uprawnieniami styles:tiles) + +on: + schedule: + # Pierwszy piątek miesiąca, 03:00 UTC (niski ruch, tani czas) + - cron: '0 3 1-7 * 5' + workflow_dispatch: + inputs: + dry_run: + description: 'Tryb podglądu — nic nie zapisuje' + default: true + required: false + type: boolean + limit: + description: 'Maks. liczba boisk (0 = wszystkie kwalifikujące się)' + default: '0' + required: false + type: string + all: + description: 'Przetwórz też już przeanalizowane (--all)' + default: false + required: false + type: boolean + overwrite: + description: 'Nadpisz istniejące pola (surface, is_indoor, lit…) wartościami AI' + default: false + required: false + type: boolean + model: + description: 'Model Claude' + default: 'claude-sonnet-4-6' + required: false + type: choice + options: + - claude-sonnet-4-6 + - claude-haiku-4-5-20251001 + - claude-opus-4-8 + concurrency: + description: 'Równoległe requesty do API (1–2 dla Haiku, max 3 dla Sonnet)' + default: '1' + required: false + type: string + save_images: + description: 'Zapisz zdjęcia satelitarne do Supabase Storage (bucket: venue-satellites)' + default: false + required: false + type: boolean + +jobs: + analyze: + name: "Analiza satelitarna ${{ inputs.dry_run == true && '(DRY RUN)' || '(ZAPIS)' }}" + runs-on: ubuntu-latest + timeout-minutes: 120 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + + - name: Zainstaluj zależności + working-directory: scraper + run: pip install httpx anthropic python-dotenv + + - name: Zbuduj argumenty + id: args + run: | + LIMIT="${{ inputs.limit || '0' }}" + MODEL="${{ inputs.model || 'claude-sonnet-4-6' }}" + CONCURRENCY="${{ inputs.concurrency || '3' }}" + ARGS="--model $MODEL --concurrency $CONCURRENCY" + [ "$LIMIT" != "0" ] && ARGS="$ARGS --limit $LIMIT" + [ "${{ inputs.dry_run }}" = "true" ] && ARGS="$ARGS --dry-run" + [ "${{ inputs.all }}" = "true" ] && ARGS="$ARGS --all" + [ "${{ inputs.overwrite }}" = "true" ] && ARGS="$ARGS --overwrite" + [ "${{ inputs.save_images }}" = "true" ] && ARGS="$ARGS --save-images" + echo "args=$ARGS" >> $GITHUB_OUTPUT + echo "Argumenty: $ARGS" + + - name: Uruchom analizę satelitarną + working-directory: scraper + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + MAPBOX_TOKEN: ${{ secrets.MAPBOX_TOKEN }} + run: python analyze_venues.py ${{ steps.args.outputs.args }} diff --git a/.github/workflows/classify.yml b/.github/workflows/classify.yml new file mode 100644 index 0000000..0d6d4e9 --- /dev/null +++ b/.github/workflows/classify.yml @@ -0,0 +1,63 @@ +name: Klasyfikacja obiektów (map_visibility) + +# Ustawia map_visibility na podstawie danych w bazie: +# public — boisko do sportu zespołowego z rezerwacją lub dobrymi danymi +# organizer_only — wygląda na boisko ale mało danych (warto sprawdzić) +# hidden — siłownia, bilard, brak danych itp. +# +# Wymagane sekrety: +# SUPABASE_URL +# SUPABASE_SERVICE_ROLE_KEY + +on: + workflow_dispatch: + inputs: + dry_run: + description: 'Tryb podglądu — nic nie zapisuje' + default: true + required: false + type: boolean + limit: + description: 'Maks. obiektów (0 = wszystkie)' + default: '0' + required: false + type: string + reset: + description: 'Nadpisz też obiekty z już ustawioną visibility (domyślnie: tylko public)' + default: false + required: false + type: boolean + +jobs: + classify: + name: Klasyfikacja (${{ inputs.dry_run == true && 'DRY RUN' || 'ZAPIS' }}) + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + ARGS="--limit ${{ inputs.limit }}" + [ "${{ inputs.dry_run }}" = "true" ] && ARGS="$ARGS --dry-run" + [ "${{ inputs.reset }}" = "true" ] && ARGS="$ARGS --reset" + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run classifier + working-directory: scraper + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python classify.py ${{ steps.args.outputs.args }} diff --git a/.github/workflows/enrich-all.yml b/.github/workflows/enrich-all.yml new file mode 100644 index 0000000..c820f01 --- /dev/null +++ b/.github/workflows/enrich-all.yml @@ -0,0 +1,162 @@ +name: Pełne wzbogacanie danych (wszystkie źródła) + +# Uruchamia wszystkie enrichery po kolei w optymalnej kolejności: +# 1. Nominatim → adres, kod pocztowy, dzielnica (całkowicie bezpłatne) +# 2. Google Places → telefon, strona WWW, godziny otwarcia (bezpłatne w limicie $200/mc) +# 3. Claude AI → e-mail, sposób rezerwacji, AI-summary (płatne: ~$10/1000 wyszukań) +# +# Kolejność ma znaczenie: Nominatim idzie pierwszy, żeby Claude dostał +# porządny adres ("ul. Roosevelta 18") zamiast "Poznań" — web search +# trafia wtedy w znacznie lepsze wyniki. +# +# Uruchom z dry_run=true przed pierwszym prawdziwym uruchomieniem! +# +# Wymagane sekrety: +# SUPABASE_URL +# SUPABASE_SERVICE_ROLE_KEY +# GOOGLE_PLACES_API_KEY (dla kroku Google) +# ANTHROPIC_API_KEY (dla kroku Claude) + +on: + workflow_dispatch: + inputs: + dry_run: + description: 'Tryb podglądu — nic nie zapisuje (zalecane przy pierwszym uruchomieniu)' + default: true + required: false + type: boolean + limit: + description: 'Maks. obiektów / grup na każdy krok (0 = wszystkie)' + default: '50' + required: false + type: string + skip_geocode: + description: 'Pomiń krok Nominatim (adresy/dzielnice)' + default: false + required: false + type: boolean + skip_google: + description: 'Pomiń krok Google Places' + default: false + required: false + type: boolean + skip_claude: + description: 'Pomiń krok Claude AI (najdroższy)' + default: false + required: false + type: boolean + claude_rerun_all: + description: 'Claude --all: przetwórz też już wzbogacone rekordy (fix gdy ai_enriched_at ustawione na złych rekordach)' + default: false + required: false + type: boolean + claude_model: + description: 'Model Claude dla ostatniego kroku' + default: 'claude-haiku-4-5-20251001' + required: false + type: choice + options: + - claude-haiku-4-5-20251001 + - claude-sonnet-4-6 + +jobs: + # ── KROK 1: Nominatim — adresy i dzielnice ─────────────────────────────── + geocode: + name: "1/3 · Nominatim (adresy/dzielnice) ${{ inputs.dry_run == true && '(DRY RUN)' || '(ZAPIS)' }}" + runs-on: ubuntu-latest + timeout-minutes: 60 + if: ${{ inputs.skip_geocode != true }} + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + ARGS="--limit ${{ inputs.limit }}" + [ "${{ inputs.dry_run }}" = "true" ] && ARGS="$ARGS --dry-run" + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run geocode enrichment + working-directory: scraper + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python enrich_geocode.py ${{ steps.args.outputs.args }} + + # ── KROK 2: Google Places — telefon, WWW, godziny ──────────────────────── + google: + name: "2/3 · Google Places ${{ inputs.dry_run == true && '(DRY RUN)' || '(ZAPIS)' }}" + runs-on: ubuntu-latest + timeout-minutes: 30 + needs: geocode + if: ${{ always() && inputs.skip_google != true }} + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + ARGS="--limit ${{ inputs.limit }} --concurrency 5" + [ "${{ inputs.dry_run }}" = "true" ] && ARGS="$ARGS --dry-run" + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run Google enrichment + working-directory: scraper + env: + GOOGLE_PLACES_API_KEY: ${{ secrets.GOOGLE_PLACES_API_KEY }} + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python enrich_google.py ${{ steps.args.outputs.args }} + + # ── KROK 3: Claude AI — e-mail, rezerwacja, summary ────────────────────── + claude: + name: "3/3 · Claude AI ${{ inputs.dry_run == true && '(DRY RUN)' || '(ZAPIS)' }}" + runs-on: ubuntu-latest + timeout-minutes: 60 + needs: google + if: ${{ always() && inputs.skip_claude != true }} + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + ARGS="--limit ${{ inputs.limit }} --model ${{ inputs.claude_model }}" + [ "${{ inputs.dry_run }}" = "true" ] && ARGS="$ARGS --dry-run" + [ "${{ inputs.claude_rerun_all }}" = "true" ] && ARGS="$ARGS --all" || ARGS="$ARGS --require-empty" + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run Claude enrichment + working-directory: scraper + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python enrich.py ${{ steps.args.outputs.args }} diff --git a/.github/workflows/enrich-booking.yml b/.github/workflows/enrich-booking.yml new file mode 100644 index 0000000..f89403e --- /dev/null +++ b/.github/workflows/enrich-booking.yml @@ -0,0 +1,81 @@ +name: Booking System Extractor (website → Claude) + +# Reads HTML from each venue's website and uses Claude to detect the booking +# system type (phone / email / own system / external platform like Hally/Booksy). +# NO web search — Claude only reads the fetched HTML, so it's very cheap. +# +# Run AFTER enrich.py has had a chance to find website URLs for venues. +# Writes to field_outreach: booking_system, booking_url, booking_provider. +# +# Required secrets: +# ANTHROPIC_API_KEY +# SUPABASE_URL +# SUPABASE_SERVICE_ROLE_KEY + +on: + workflow_dispatch: + inputs: + limit: + description: 'Maks. obiektów (0 = wszystkie ze stroną WWW)' + default: '0' + required: false + type: string + dry_run: + description: 'Tryb podglądu — nic nie zapisuje' + default: true + required: false + type: boolean + reprocess: + description: 'Przetworz ponownie już przeanalizowane' + default: false + required: false + type: boolean + model: + description: 'Model Claude (pusty = haiku 4.5)' + default: '' + required: false + type: string + +jobs: + enrich-booking: + name: Booking (${{ inputs.dry_run == true && 'DRY RUN' || 'ZAPIS' }}, limit=${{ inputs.limit }}) + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + ARGS="--limit ${{ inputs.limit }} --concurrency 2" + if [ "${{ inputs.dry_run }}" = "true" ]; then + ARGS="$ARGS --dry-run" + fi + if [ "${{ inputs.reprocess }}" = "true" ]; then + ARGS="$ARGS --all" + fi + if [ -n "${{ inputs.model }}" ]; then + ARGS="$ARGS --model ${{ inputs.model }}" + fi + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run booking enrichment + working-directory: scraper + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python enrich_booking.py ${{ steps.args.outputs.args }} diff --git a/.github/workflows/enrich-geocode.yml b/.github/workflows/enrich-geocode.yml new file mode 100644 index 0000000..7962c17 --- /dev/null +++ b/.github/workflows/enrich-geocode.yml @@ -0,0 +1,96 @@ +name: Geocode Enrichment (Nominatim, bezpłatne) + +# Uzupełnia adres (ul. Roosevelta 18), kod pocztowy i dzielnicę dla obiektów +# z lat/lng które nie mają tych danych. Używa Nominatim (OpenStreetMap) — +# całkowicie bezpłatne, bez klucza API. +# +# Limit: 1 zapytanie/sek → ~25 min dla 1426 obiektów. +# Uruchamia się też automatycznie w każdy poniedziałek o 4:00 UTC, żeby +# nowo dodane obiekty (z importu OSM) dostały uzupełnione dane. +# +# Wymagane sekrety repozytorium: +# SUPABASE_URL +# SUPABASE_SERVICE_ROLE_KEY + +on: + workflow_dispatch: + inputs: + mode: + description: 'Co uzupełniać' + required: false + default: 'missing' + type: choice + options: + - missing # brakujące adresy LUB dzielnice + - missing_district # tylko brakujące dzielnice (zachowaj adresy) + - overwrite # nadpisz wszystko nowym wynikiem z Nominatim + limit: + description: 'Maks. obiektów do przetworzenia (0 = wszystkie)' + default: '0' + required: false + type: string + dry_run: + description: 'Tryb podglądu — wypisuje zmiany, nic nie zapisuje' + default: true + required: false + type: boolean + + schedule: + # Każdy poniedziałek 4:00 UTC — uzupełnia nowo dodane obiekty + - cron: '0 4 * * 1' + +jobs: + geocode: + name: >- + Geocode + ${{ github.event_name == 'schedule' && '(harmonogram)' || '' }} + ${{ inputs.dry_run == true && '· DRY RUN' || '· ZAPIS' }} + ${{ inputs.limit && inputs.limit != '0' && format('· limit={0}', inputs.limit) || '' }} + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + # Scheduled run: only fill in missing data, no dry-run, no limit + if [ "${{ github.event_name }}" = "schedule" ]; then + echo "args=" >> $GITHUB_OUTPUT + exit 0 + fi + + ARGS="" + if [ "${{ inputs.limit }}" != "0" ] && [ -n "${{ inputs.limit }}" ]; then + ARGS="$ARGS --limit ${{ inputs.limit }}" + fi + if [ "${{ inputs.dry_run }}" = "true" ]; then + ARGS="$ARGS --dry-run" + fi + if [ "${{ inputs.mode }}" = "missing_district" ]; then + ARGS="$ARGS --missing-district" + fi + if [ "${{ inputs.mode }}" = "overwrite" ]; then + ARGS="$ARGS --overwrite" + fi + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run geocode enrichment + working-directory: scraper + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python enrich_geocode.py ${{ steps.args.outputs.args }} diff --git a/.github/workflows/enrich-google.yml b/.github/workflows/enrich-google.yml new file mode 100644 index 0000000..2747aa1 --- /dev/null +++ b/.github/workflows/enrich-google.yml @@ -0,0 +1,77 @@ +name: Google Venue Enrichment (free) + +# Uruchamiany ręcznie z zakładki Actions. Dla istniejących obiektów pobiera z +# Google Places telefon, stronę WWW i godziny otwarcia (Find Place + Details). +# DARMOWE w ramach kredytu Google $200/mc (~11k zapytań). Uruchom to PRZED +# wzbogacaniem przez Claude — Google da telefon/www/godziny za darmo, a Claude +# dobierze tylko e-mail i sposób rezerwacji (taniej). +# +# Wymagane sekrety repozytorium: +# GOOGLE_PLACES_API_KEY (Google Cloud → APIs & Services → Credentials) +# SUPABASE_URL +# SUPABASE_SERVICE_ROLE_KEY + +on: + workflow_dispatch: + inputs: + limit: + description: 'Maks. grup lokalizacyjnych (0 = wszystkie)' + default: '0' + required: false + type: string + dry_run: + description: 'Tryb podglądu — nic nie zapisuje' + default: true + required: false + type: boolean + require_all: + description: 'Tylko obiekty bez telefonu I strony (mniej obiektów)' + default: false + required: false + type: boolean + radius: + description: 'Promień wyszukiwania Google (metry, domyślnie 200)' + default: '200' + required: false + type: string + +jobs: + enrich-google: + name: Google (${{ inputs.dry_run == true && 'DRY RUN' || 'ZAPIS' }}, limit=${{ inputs.limit }}) + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + ARGS="--limit ${{ inputs.limit }} --concurrency 5 --radius ${{ inputs.radius }}" + if [ "${{ inputs.dry_run }}" = "true" ]; then + ARGS="$ARGS --dry-run" + fi + if [ "${{ inputs.require_all }}" = "true" ]; then + ARGS="$ARGS --require-all" + fi + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run Google enrichment + working-directory: scraper + env: + GOOGLE_PLACES_API_KEY: ${{ secrets.GOOGLE_PLACES_API_KEY }} + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python enrich_google.py ${{ steps.args.outputs.args }} diff --git a/.github/workflows/enrich-photos.yml b/.github/workflows/enrich-photos.yml new file mode 100644 index 0000000..379f867 --- /dev/null +++ b/.github/workflows/enrich-photos.yml @@ -0,0 +1,80 @@ +name: Photo Enrichment + +# Finds venue photos in priority order: +# 1. Google Places API — real, high-quality photos (needs GOOGLE_PLACES_API_KEY) +# 2. Wikimedia Commons — CC-licensed photos, free (no key needed) +# 3. Mapbox satellite — aerial fallback for every venue (needs MAPBOX_TOKEN) +# +# Stores the best URL in fields.photo_url + fields.photo_source. +# +# Required secrets: +# SUPABASE_URL +# SUPABASE_SERVICE_ROLE_KEY +# GOOGLE_PLACES_API_KEY (optional — enables Google Photos) +# MAPBOX_TOKEN (optional — enables satellite fallback) + +on: + workflow_dispatch: + inputs: + strategy: + description: 'Photo source (auto = Google → Wikimedia → satellite)' + default: 'auto' + required: false + type: choice + options: + - auto + - google + - wikimedia + - satellite + limit: + description: 'Max venues to process (0 = all missing photos)' + default: '50' + required: false + type: string + dry_run: + description: 'Preview — show found URLs, do NOT write to DB' + default: true + required: false + type: boolean + +jobs: + enrich-photos: + name: >- + Photos + · ${{ inputs.strategy }} + ${{ inputs.dry_run == true && '· DRY RUN' || '· ZAPIS' }} + ${{ inputs.limit != '0' && format('· limit={0}', inputs.limit) || '' }} + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: pip + cache-dependency-path: scraper/requirements.txt + + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + ARGS="--strategy ${{ inputs.strategy }}" + if [ "${{ inputs.dry_run }}" = "true" ]; then ARGS="$ARGS --dry-run"; fi + if [ "${{ inputs.limit }}" != "0" ]; then ARGS="$ARGS --limit ${{ inputs.limit }}"; fi + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run photo enrichment + working-directory: scraper + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + GOOGLE_PLACES_API_KEY: ${{ secrets.GOOGLE_PLACES_API_KEY }} + MAPBOX_TOKEN: ${{ secrets.MAPBOX_TOKEN }} + run: python enrich_photos.py ${{ steps.args.outputs.args }} diff --git a/.github/workflows/enrich-venues.yml b/.github/workflows/enrich-venues.yml new file mode 100644 index 0000000..913d1d6 --- /dev/null +++ b/.github/workflows/enrich-venues.yml @@ -0,0 +1,92 @@ +name: AI Venue Enrichment (Claude) + +# Uruchamiany ręcznie z zakładki Actions w GitHubie (działa też z telefonu). +# Dla każdej lokalizacji (adres) wyszukuje w sieci dane kontaktowe i sposób +# rezerwacji, a wyniki wgrywa do Supabase (fields + field_outreach). +# +# Wymagane sekrety repozytorium: +# ANTHROPIC_API_KEY (console.anthropic.com → API keys) +# SUPABASE_URL (np. https://xxxx.supabase.co) +# SUPABASE_SERVICE_ROLE_KEY (Supabase → Settings → API → service_role) +# +# ⚠ KOSZT: web search ≈ $10/1000 wyszukań. Uruchom najpierw z dry_run=true +# i małym limitem, żeby sprawdzić jakość wyników zanim puścisz całość. + +on: + workflow_dispatch: + inputs: + limit: + description: 'Maks. grup adresowych do przetworzenia (0 = wszystkie)' + default: '10' + required: false + type: string + dry_run: + description: 'Tryb podglądu — wypisuje wyniki, nic nie zapisuje' + default: true + required: false + type: boolean + require_empty: + description: 'Tylko lokalizacje bez telefonu I e-maila (tańsze)' + default: false + required: false + type: boolean + model: + description: 'Model Claude (haiku = tani, sonnet = dokładniejszy)' + default: 'claude-haiku-4-5-20251001' + required: false + type: choice + options: + - claude-haiku-4-5-20251001 + - claude-sonnet-4-6 + concurrency: + description: 'Równoległe zapytania (1 = bezpieczne dla 50k TPM, 2-3 jeśli masz wyższy limit)' + default: '1' + required: false + type: string + max_searches: + description: 'Maks. wyszukań web na grupę adresową (2 = szybsze/tańsze, 4 = więcej danych)' + default: '2' + required: false + type: string + + +jobs: + enrich: + name: Wzbogacanie danych (${{ inputs.dry_run == true && 'DRY RUN' || 'ZAPIS' }}, limit=${{ inputs.limit }}) + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + ARGS="--limit ${{ inputs.limit }} --concurrency ${{ inputs.concurrency }} --max-searches ${{ inputs.max_searches }} --model ${{ inputs.model }}" + if [ "${{ inputs.dry_run }}" = "true" ]; then + ARGS="$ARGS --dry-run" + fi + if [ "${{ inputs.require_empty }}" = "true" ]; then + ARGS="$ARGS --require-empty" + fi + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run enrichment + working-directory: scraper + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python enrich.py ${{ steps.args.outputs.args }} diff --git a/.github/workflows/fix-coords.yml b/.github/workflows/fix-coords.yml new file mode 100644 index 0000000..3bd18eb --- /dev/null +++ b/.github/workflows/fix-coords.yml @@ -0,0 +1,76 @@ +name: Fix GPS Coordinates (Forward Geocoding) + +# Forward-geocodes venue addresses via Nominatim and updates coordinates +# when the stored lat/lng differs by more than --threshold km. +# +# Useful after bulk-importing manually-entered venues (seeds, spreadsheets) +# where GPS was estimated rather than verified. +# +# Required secrets: +# SUPABASE_URL +# SUPABASE_SERVICE_ROLE_KEY + +on: + workflow_dispatch: + inputs: + threshold: + description: 'Min distance (km) to trigger update' + default: '1.0' + required: false + type: string + source: + description: 'Filter by source (manual | osm | — leave blank for all)' + default: 'manual' + required: false + type: string + limit: + description: 'Max venues to process (0 = all)' + default: '100' + required: false + type: string + dry_run: + description: 'Preview mode — show mismatches, do NOT write to DB' + default: true + required: false + type: boolean + +jobs: + fix-coords: + name: >- + Fix GPS + ${{ inputs.dry_run == true && '· DRY RUN' || '· ZAPIS' }} + ${{ inputs.source != '' && format('· source={0}', inputs.source) || '' }} + ${{ inputs.limit != '0' && format('· limit={0}', inputs.limit) || '' }} + runs-on: ubuntu-latest + timeout-minutes: 120 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: pip + cache-dependency-path: scraper/requirements.txt + + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv + + - name: Build arguments + id: args + run: | + ARGS="--threshold ${{ inputs.threshold }}" + if [ "${{ inputs.dry_run }}" = "true" ]; then ARGS="$ARGS --dry-run"; fi + if [ -n "${{ inputs.source }}" ]; then ARGS="$ARGS --source ${{ inputs.source }}"; fi + if [ "${{ inputs.limit }}" != "0" ]; then ARGS="$ARGS --limit ${{ inputs.limit }}"; fi + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run fix-coords + working-directory: scraper + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python fix_coords.py ${{ steps.args.outputs.args }} diff --git a/.github/workflows/import-fields.yml b/.github/workflows/import-fields.yml new file mode 100644 index 0000000..be6a525 --- /dev/null +++ b/.github/workflows/import-fields.yml @@ -0,0 +1,34 @@ +name: Import boisk (OSM + Google) + +# Uruchamiany ręcznie z zakładki Actions w GitHubie (działa też z telefonu). +# Pobiera boiska z OpenStreetMap (i opcjonalnie Google Places) i wgrywa je +# do Supabase. Wymaga sekretów repozytorium: +# SUPABASE_URL (np. https://xxxx.supabase.co) +# SUPABASE_SERVICE_ROLE_KEY (Supabase → Settings → API → service_role) +# GOOGLE_PLACES_API_KEY (opcjonalnie — dla nazwanych obiektów) +on: + workflow_dispatch: + +jobs: + import: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install dependencies + working-directory: scraper + run: pip install -r requirements.txt + + - name: Run importer + working-directory: scraper + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + GOOGLE_PLACES_API_KEY: ${{ secrets.GOOGLE_PLACES_API_KEY }} + run: python scraper.py diff --git a/.github/workflows/scrape-booking.yml b/.github/workflows/scrape-booking.yml new file mode 100644 index 0000000..e26db36 --- /dev/null +++ b/.github/workflows/scrape-booking.yml @@ -0,0 +1,88 @@ +name: Odwrotny scraper rezerwacji (strony → obiekty) + +# Odkrywa obiekty sportowe z linkami do rezerwacji i zapisuje je w bazie. +# Źródła: AI/web-search (Claude odkrywa organicznie) + POSiR/orliki (dane publiczne). +# Komercyjne platformy rezerwacyjne NIE są scrapowane — chronione prawem +# sui generis do baz danych (dyrektywa 96/9/WE, ustawa o prawie autorskim art. 102-104). +# +# Wymagane sekrety repozytorium: +# ANTHROPIC_API_KEY +# SUPABASE_URL +# SUPABASE_SERVICE_ROLE_KEY + +on: + workflow_dispatch: + inputs: + source: + description: 'Źródło danych' + default: 'all' + required: true + type: choice + options: + - all + - ai + - posir + limit: + description: 'Maks. obiektów (0 = wszystkie)' + default: '0' + required: false + type: string + dry_run: + description: 'Tryb podglądu — nic nie zapisuje' + default: true + required: false + type: boolean + no_add: + description: 'Nie twórz nowych obiektów — tylko wzbogacaj istniejące' + default: false + required: false + type: boolean + model: + description: 'Model Claude (pusty = haiku 4.5)' + default: '' + required: false + type: string + +jobs: + scrape-booking: + name: Reverse Booking (${{ inputs.dry_run == true && 'DRY RUN' || 'ZAPIS' }}, source=${{ inputs.source }}) + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: scraper/requirements.txt + + - name: Install dependencies + working-directory: scraper + run: pip install httpx python-dotenv tenacity + + - name: Build arguments + id: args + run: | + ARGS="--source ${{ inputs.source }} --limit ${{ inputs.limit }}" + if [ "${{ inputs.dry_run }}" = "true" ]; then + ARGS="$ARGS --dry-run" + fi + if [ "${{ inputs.no_add }}" = "true" ]; then + ARGS="$ARGS --no-add" + fi + if [ -n "${{ inputs.model }}" ]; then + ARGS="$ARGS --model ${{ inputs.model }}" + fi + echo "args=$ARGS" >> $GITHUB_OUTPUT + + - name: Run reverse booking scraper + working-directory: scraper + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + run: python scrape_booking.py ${{ steps.args.outputs.args }} diff --git a/.gitignore b/.gitignore index 3ae21f6..873b09e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,109 @@ -node_modules/**/* -.expo/* +# ============================================================ +# Boiska Poznań — combined .gitignore +# ============================================================ + +# ---- Next.js ---- +frontend/.next/ +frontend/out/ +frontend/build/ +frontend/.vercel +frontend/node_modules/ +frontend/.env*.local +frontend/npm-debug.log* +frontend/yarn-debug.log* +frontend/yarn-error.log* +frontend/.pnp +frontend/.pnp.js + +# ---- Node (root & generic) ---- +node_modules/ npm-debug.* -package-lock.json -yarn.lock +yarn-debug.* +yarn-error.* +.pnpm-debug.log* + +# ---- Python / FastAPI ---- +__pycache__/ +**/__pycache__/ +*.py[cod] +*$py.class +*.pyo +*.pyd +.Python +.venv/ +venv/ +env/ +ENV/ +backend/.env +scraper/.env +.env +*.egg-info/ +dist/ +*.egg +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +htmlcov/ +.coverage +*.cover +.hypothesis/ + +# ---- Docker ---- +**/docker-data/ +**/volumes/ +.docker/ + +# ---- Supabase local ---- +.supabase/ +supabase/.temp/ + +# ---- OS ---- +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +desktop.ini + +# ---- IDE ---- +.idea/ +.vscode/ +!.vscode/settings.json +!.vscode/extensions.json +*.swp +*.swo +*~ +.project +.classpath + +# ---- Env files (keep .env.example) ---- +.env +.env.local +.env.development.local +.env.test.local +.env.production.local +!.env.example +!**/.env.example + +# ---- Expo / React Native (legacy files in repo root) ---- +.expo/* *.jks *.p12 *.key -*.mobileprovision \ No newline at end of file +*.mobileprovision + +# ---- Misc ---- +*.log +*.tmp +*.temp +.cache/ + +# ---- TypeScript (auto-generated) ---- +frontend/next-env.d.ts + +# TypeScript build cache +*.tsbuildinfo +frontend/tsconfig.tsbuildinfo +.claude/ diff --git a/.watchmanconfig b/.watchmanconfig deleted file mode 100644 index 0967ef4..0000000 --- a/.watchmanconfig +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/AUDIT.md b/AUDIT.md new file mode 100644 index 0000000..92abb6a --- /dev/null +++ b/AUDIT.md @@ -0,0 +1,118 @@ +# Audyt projektu Bojo — 2026-06-02 + +## Struktura projektu + +``` +bojo-app/ +├── frontend/ Next.js 14 App Router — jedyny aktywny frontend +├── supabase/ +│ ├── migrations/ SQL (001–011) — baza jest w Supabase Cloud +│ └── functions/ Edge Functions Deno: send-invites, send-event-sms +├── backend/ ⚠️ MARTWY KOD — patrz sekcja poniżej +├── scraper/ Python — scraper boisk (jednorazowe użycie) +├── App.js ⚠️ React Native — pozostałości z poprzedniej wersji +├── screens/ ⚠️ React Native — nieużywane +├── components/ ⚠️ React Native — nieużywane (inne niż frontend/src/components) +└── navigation/ ⚠️ React Native — nieużywane +``` + +--- + +## ⚠️ POWAŻNE ZNALEZISKA (zgłoszone zanim cokolwiek naprawiłem) + +### 1. Backend FastAPI — martwy kod (nie usuwam bez Twojej zgody) + +**Plik:** `backend/app/routers/fields.py`, `games.py` + +Backend FastAPI (~600 linii) **nigdy nie jest wywoływany przez frontend**. Frontend komunikuje się bezpośrednio z Supabase. Cały backend ma hardkodowane dane testowe zamiast prawdziwych zapytań do bazy: + +```python +# backend/app/routers/fields.py, linia 11 +# Mock data — replace with Supabase queries once database is seeded +MOCK_FIELDS: list[FieldModel] = [ + FieldModel(id="b1a2c3d4-...", name="Boisko Sportowe ul. Dąbrowskiego", ...) +] +``` + +`games.py` linia 114: `raise HTTPException(status_code=501, detail="Not implemented yet")`. + +**Ryzyko:** Brak — kod nie jest uruchamiany w produkcji. Ale zajmuje miejsce i może mylić nowych deweloperów. +**Rekomendacja:** Usunąć lub przenieść do archiwum. Nie usuwam bez Twojego OK. + +--- + +### 2. Bug: licznik gier w sekcji sportów zawsze pokazuje 0 + +**Plik:** `frontend/src/components/SportsSectionWithCounts.tsx`, linia 38–39 + +```typescript +.gte('date', from) // ❌ BŁĄD — kolumna nie istnieje +.lte('date', to) // ❌ BŁĄD — kolumna nie istnieje +``` + +Prawidłowa nazwa kolumny w bazie to `event_date` (widać w `events.ts` mapper: `date: row.event_date`). + +**Skutek:** Sekcja "Wybierz swój sport" zawsze pokazuje "· znajdź mecz" zamiast liczby gier, bo wszystkie zapytania zwracają pustą tablicę. Naprawione w tym commicie. + +--- + +## Bezpieczeństwo + +### ✅ Brak zahardkodowanych sekretów +Wszystkie klucze API są w zmiennych środowiskowych. `.gitignore` poprawnie wyklucza `.env*`. Brak kredencjałów w kodzie. + +### ℹ️ CORS w edge functions (akceptowalne) +Supabase Edge Functions używają `'Access-Control-Allow-Origin': '*'`. Jest to standardowy pattern dla Supabase Functions — są chronione przez JWT Supabase. Nie wymaga zmiany. + +### ℹ️ Frontend-only admin check +`useAdmin()` sprawdza `is_admin` po stronie klienta. Operacje wrażliwe (np. aktualizacja boiska) powinny być zabezpieczone przez RLS w bazie — co jest częściowo zrobione. Priorytet: średni. + +### ℹ️ Walidacja inputów +Dane wejściowe (telefon, notatki, nazwa gościa) nie są walidowane przed wysłaniem do Supabase. RLS chroni przed eskalacją uprawnień, ale niepoprawne dane mogą trafić do bazy. Priorytet: niski dla prototypu. + +--- + +## Martwy kod i porządki + +| Lokalizacja | Co | Akcja | +|-------------|-----|-------| +| `/backend/` | FastAPI z mockami, nigdy nie używany | Czeka na Twój OK do usunięcia | +| `/App.js`, `/screens/`, `/components/`, `/navigation/` | Resztki React Native z poprzedniej wersji | Czeka na Twój OK do usunięcia | +| `/scraper/` | Jednorazowy scraper, dane już w bazie | Zostawić jako dokumentację | +| `SPORT_EMOJI` | Zduplikowany w 4 plikach | Przeniesiony do `utils.ts` w tym commicie | + +--- + +## Dług techniczny + +| # | Problem | Wpływ | Status | +|---|---------|-------|--------| +| 1 | `event_date` bug w SportsSectionWithCounts | Liczy gry zawsze 0 | ✅ Naprawione | +| 2 | Emoji zamiast ikon w 4 miejscach | Niespójny wygląd | ✅ Naprawione | +| 3 | Brak robots.txt / sitemap.xml | SEO: strona niewidoczna dla Google | ✅ Naprawione | +| 4 | Brak slug-URL dla boisk | SEO: `/boisko/orlik-rataje` dawało 404 | ✅ Naprawione | +| 5 | Brak testów | Regresy niewykrywalne | ✅ Dodane testy krytyczne | +| 6 | Stary copy w sekcji sportów | Duplikat/sprzeczność z hero | ✅ Naprawione | +| 7 | Backend FastAPI (mock data) | Martwy kod, dezorientuje | ⏳ Czeka na decyzję | +| 8 | React Native legacy files | Bałagan w root | ⏳ Czeka na decyzję | + +--- + +## Zależności + +Wszystkie zależności npm są aktualne (Next.js 14.2.3, React 18, Supabase JS 2.x). Brak krytycznych CVE. + +--- + +## Gotowość na rozwój + +**Mocne strony:** +- Supabase z RLS — dobrze przemyślana warstwa bezpieczeństwa +- TypeScript wszędzie, brak `any` poza mapperami z jednoznacznym komentarzem +- Migracje SQL w wersjonowaniu (001–011), można odtworzyć schemat z zera +- Feature flags (`FEATURE_RESERVATIONS`, `bookingEnabled`) — bezpieczne A/B + +**Do zrobienia przed scale-up:** +- Testy E2E (Playwright) dla krytycznych flow: zapis na mecz, rezerwacja +- Server-side Supabase client z service role key dla operacji admin (teraz wszystko przez anon key z RLS) +- Rate limiting na Edge Functions (send-event-sms szczególnie) diff --git a/App.js b/App.js deleted file mode 100644 index 0c999ff..0000000 --- a/App.js +++ /dev/null @@ -1,93 +0,0 @@ -/*! - - ========================================================= - * Material Kit React Native - v1.4.0 - ========================================================= - * Product Page: https://demos.creative-tim.com/material-kit-react-native/ - * Copyright 2019 Creative Tim (http://www.creative-tim.com) - * Licensed under MIT (https://github.com/creativetimofficial/material-kit-react-native/blob/master/LICENSE) - ========================================================= - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -*/ - -import React from 'react'; -import { Platform, StatusBar, Image } from 'react-native'; -import AppLoading from 'expo-app-loading'; -import { Asset } from 'expo-asset'; -import { Block, GalioProvider } from 'galio-framework'; - -import { Images, products, materialTheme } from './constants/'; - -import { NavigationContainer } from '@react-navigation/native'; -import Screens from './navigation/Screens'; - -// Before rendering any navigation stack -import { enableScreens } from 'react-native-screens'; -enableScreens(); - -// cache app images -const assetImages = [ - Images.Pro, - Images.Profile, - Images.Avatar, - Images.Onboarding, -]; - -// cache product images -products.map(product => assetImages.push(product.image)); - -function cacheImages(images) { - return images.map(image => { - if (typeof image === 'string') { - return Image.prefetch(image); - } else { - return Asset.fromModule(image).downloadAsync(); - } - }); -} - -export default class App extends React.Component { - state = { - isLoadingComplete: false, - }; - - render() { - if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) { - return ( - - ); - } else { - return ( - - - - {Platform.OS === 'ios' && } - - - - - ); - } - } - - _loadResourcesAsync = async () => { - return Promise.all([ - ...cacheImages(assetImages), - ]); - }; - - _handleLoadingError = error => { - // In this case, you might want to report the error to your error - // reporting service, for example Sentry - console.warn(error); - }; - - _handleFinishLoading = () => { - this.setState({ isLoadingComplete: true }); - }; -} diff --git a/BACKLOG.md b/BACKLOG.md new file mode 100644 index 0000000..e296e23 --- /dev/null +++ b/BACKLOG.md @@ -0,0 +1,112 @@ +# BOJO — backlog: ukryte funkcje i pomysły + +Notatnik na to, co jest **zbudowane ale schowane**, oraz **pomysły jeszcze +niezrobione**. Żeby nic nie umknęło przed publicznym uruchomieniem. + +_Ostatnia aktualizacja: 2026-06-11_ + +--- + +## 1. Ukryte za flagami (`frontend/src/lib/features.ts`) + +Jedno miejsce, jeden przełącznik. Zmień flagę na `true`, żeby przywrócić funkcję. + +| Flaga | Co chowa | Dlaczego schowane | Gdzie jest kod | +|---|---|---|---| +| `SHOW_CUP` | Turniej **BOJO Cup** — pasek ogłoszeń, TrustBar, link w nagłówku | Brak gotowego turnieju; nie chcemy obiecywać na zapas | `AnnouncementBar`, `TrustBar`, `Header`, route `/turniej`, migracje `029`/`030` | +| `SHOW_GAME_ALERTS` | **„Ustaw alert"** — powiadomienie gdy pojawi się pasująca gra w okolicy | Brak kanału dostarczania (SMS/e-mail/push) — alert bez powiadomienia to ściema | `lib/alerts.ts`, `AlertSetupDialog`, `NearbyGames` (i tak nieużywany), migracja `025` | +| `SHOW_SMS_FEATURES` | **„Potwierdzenie SMS"** na meczu + **przypomnienia** SMS/e-mail | Brak bramki SMS | `RemindersSection`, `lib/reminders.ts`, `sendConfirmationSms` w `lib/eventFeatures.ts` | + +> Gdy pojawi się kanał powiadomień (SMS/e-mail/push) — odblokowanie alertów, +> przypomnień i potwierdzeń SMS to w zasadzie przełączenie tych flag (plus +> faktyczne wysyłanie po stronie backendu). + +--- + +## 2. Zbudowane, ale nieużywane / martwy kod + +- **`components/home/NearbyGames.tsx`** — kompletny komponent „gry w pobliżu + + alert", nigdzie nie renderowany. Do decyzji: wpiąć (po włączeniu alertów) albo usunąć. +- **`RemindersSection` / `AlertSetupDialog`** — renderowane tylko za flagami powyżej. + +--- + +## 3. Funkcje częściowo wdrożone (do przeglądu przed launchem) + +- **Rezerwacje boisk / panel zarządcy obiektu** — routy `/obiekt`, `/rezerwacje`, + migracja `008_venue_bookings`. Status: czy to live, czy odkładamy? Jeśli + odkładamy — rozważyć ukrycie wejść w nawigacji. +- **Gry cykliczne (stałe gierki)** — route `/cykliczne`, migracja `007`. Działa? + Czy pokazujemy w głównej nawigacji? +- **Statystyki / wyniki meczów, drużyny, płatności** — zaawansowane opcje meczu + (w szczegółach wydarzenia). Działają, ale warto zweryfikować spójność UX. + +--- + +## 4. Zadania do zrobienia (technika / dane) + +- [ ] **Migracje na produkcję** — upewnić się, że `032`–`036` są zaaplikowane + (`032` venue_type, `033` contact_visibility, `034` goalkeeper, `035` + allow_guest_adds, `036` invite_only). +- [ ] **Czyszczenie bazy boisk** — odsianie siłowni, kortów tylko-tenisowych, + kartingów itp. Teraz filtr `RELEVANT_SPORTS` jest **po stronie klienta** + (`VenueExplorer`). Docelowo: oznaczyć śmieciowe obiekty `map_visibility = + 'hidden'` w bazie + prosty **panel admina** do przeglądania i wybierania, + które boiska mają sens. +- [ ] **Dane demo** — `supabase/seed-events.sql` (fake userzy + wydarzenia na 2 + tygodnie). Sprzątanie: `DELETE FROM auth.users WHERE email LIKE '%@seed.bojo';` + +### Bezpieczeństwo (już wdrożone — pilnować) +- Telefony/e-maile zescrapowane z OSM **ukryte domyślnie**; widoczność per obiekt + włącza admin (`contact_visible`, migracja `033`). Egzekwowane na poziomie DB. + +--- + +## 5. Turniej — TODO (gdy wracamy do `SHOW_CUP`) + +**Zakres: 3 sporty — piłka nożna, koszykówka, siatkówka plażowa.** + +> ⭐ **Siatkówka plażowa to główny przypadek użycia** — więcej osób będzie +> robić turnieje plażówki niż hali. Halową siatkówkę traktujemy jako zwykły +> sport meczowy, a w turniejach wyraźnie eksponujemy plażową. + +### Baza +- [ ] `tournaments`: sport, organizer, field, data, format (pucharowy / + grupowy / każdy-z-każdym), liczba drużyn, rozmiar drużyny, deadline zapisów, + status, regulamin, nagrody. +- [ ] `tournament_teams`: nazwa, kapitan, członkowie, potwierdzona, rozstawienie. +- [ ] `tournament_matches`: runda, drużyna A/B, wynik, zwycięzca, boisko. +- [ ] RLS: organizator zarządza; drużyny widzą swój pojedynek; publicznie po starcie. +- _(częściowo: migracje `029`/`030` już istnieją — zweryfikować pokrycie.)_ + +### UI +- [ ] `/turnieje` — lista (filtr po sporcie) +- [ ] `/turnieje/[id]` — drabinka, lista drużyn, terminarz +- [ ] `/turnieje/nowe` — kreator (jak tworzenie meczu) +- [ ] rejestracja drużyny (kapitan: nazwa + skład) +- [ ] panel organizatora: potwierdzanie drużyn, losowanie drabinki, wpisywanie wyników + +### Decyzje do podjęcia +- [ ] Rozmiar drużyny: konfigurowalny czy stały per sport? (plażówka 2v2 / 4v4) +- [ ] Zapisy otwarte vs tylko zaproszone drużyny (re-użyć logiki „tylko dla zaproszonych") +- [ ] Płatność za drużynę? (re-użyć `trackPayments`) +- [ ] Wizualizacja drabinki na mobile (drzewko jest trudne na małym ekranie) +- [ ] Powiadomienia: gdy wylosowano drabinkę / kiedy następny mecz + +| Sport | Format domyślny | Rozmiar | Uwaga | +|---|---|---|---| +| **Siatkówka plażowa** ⭐ | pucharowy | 2v2 / 4v4 | główny przypadek; boiska już na mapie | +| Piłka nożna | grupy → puchar | 5v5 / 7v7 | trzeba wiele boisk lub sloty czasowe | +| Koszykówka | pucharowy | 3v3 | streetball, najpopularniejszy format amatorski | + +--- + +## 6. Pomysły jeszcze niezbudowane + +- **Kanał powiadomień** (SMS / e-mail / web-push) — odblokowuje alerty, + przypomnienia, potwierdzenia. Fundament pod sekcję 1. +- **Onboarding / pierwsza gra** — co widzi świeży user bez gier w okolicy. +- **Profil gracza / reputacja** — frekwencja, „rzetelny gracz", raporty (część + infrastruktury już jest: `submitReport`). +- **Udostępnianie gry** poza apką (link / obrazek do social/WhatsApp). +- **Statystyki sezonowe** dla stałych ekip. diff --git a/PRZEWODNIK.md b/PRZEWODNIK.md new file mode 100644 index 0000000..7773a01 --- /dev/null +++ b/PRZEWODNIK.md @@ -0,0 +1,118 @@ +# Bojo — przewodnik dla współpracownika + +> **Bojo** (Boiska Poznań) to aplikacja webowa, która pomaga znaleźć boisko w Poznaniu, +> zorganizować mecz i zebrać skład. Działa w przeglądarce, logowanie przez Google. + +Ten dokument w 5 minut wprowadza Cię w to, co aplikacja potrafi i jak jest zbudowana. + +--- + +## 1. Co widzi użytkownik + +| Strona | Co robi | +|---|---| +| **Start** (`/`) | Strona główna — sporty, najbliższe mecze, „jak to działa" | +| **Mapa** (`/mapa`) | Interaktywna mapa z dwiema zakładkami: **Boiska** i **Mecze**. Filtry po sporcie, dostępności, nawierzchni. Klik w pinezkę → szczegóły | +| **Boiska wg sportu** (`/boiska/pilka-nozna`) | Lista boisk dla danego sportu (przyjazne adresy pod Google) | +| **Szczegóły boiska** (`/boisko/...`) | Adres, sporty, zdjęcie, opis, dane kontaktowe, nadchodzące mecze. Strona zoptymalizowana pod wyszukiwarki (JSON-LD) | +| **Wydarzenia** (`/wydarzenia`) | Lista meczów — publiczne i „moje". Filtry po sporcie, sortowanie po odległości od Ciebie | +| **Nowy mecz** (`/wydarzenia/nowe`) | Tworzysz mecz: sport, miejsce (z mapy lub adresu), data, godzina, liczba graczy, widoczność (publiczny/prywatny link) + opcje zaawansowane (obecność, płatności, termin potwierdzenia) | +| **Mecz** (`/wydarzenia/...`) | Dołącz / dodaj gościa / kopiuj link / podział na drużyny / wynik / lista rezerwowa / zgłoszenie gracza. Organizator może wymagać **akceptacji** dołączeń | +| **Grupy** (`/grupy`) | Stałe ekipy graczy (sport, miasto). Zakładanie, **link z zaproszeniem** (`/g/kod`), członkowie, mecze grupy, okładka. Edycja przez założyciela | +| **Profile graczy** (`/gracze`, `/gracz/...`) | Publiczny profil gracza: awatar, statystyki (rozegrane mecze, frekwencja), znaczek „rzetelny gracz", historia | +| **Moje gry** (`/moje-gry`) | Mecze, które organizujesz lub na które się zapisałeś + historia | +| **Cykliczne** (`/cykliczne`) | Szablony powtarzalnych meczów (np. „każdy wtorek 18:00") z zapisami | +| **Turniej** (`/turniej`) | Rejestracja drużyn, składy, drabinka i terminarz meczów | +| **Rezerwacje** (`/rezerwacje`) | Twoje rezerwacje terminów (funkcja włączana flagą — patrz niżej) | +| **Profil** (`/profil`) | Imię, awatar, telefon (za zgodą), usunięcie konta | + +**Logowanie:** Google OAuth (przez Supabase). Bez logowania można przeglądać mapę i boiska; do tworzenia/dołączania trzeba się zalogować. + +--- + +## 2. Co może menedżer obiektu + +Właściciel/zarządca boiska (przypisany do obiektu) dostaje panel: + +| Strona | Co robi | +|---|---| +| **Moje obiekty** (`/obiekt`) | Lista zarządzanych boisk + dodawanie nowego | +| **Pulpit obiektu** (`/obiekt/...`) | Skróty do harmonogramu, cennika i rezerwacji | +| **Harmonogram / Cennik** | Godziny otwarcia, długość slotów, ceny wg dnia i pory | +| **Rezerwacje obiektu** | Zatwierdzanie / odrzucanie rezerwacji graczy | + +--- + +## 3. Co może admin + +Admin = pole `is_admin = true` w tabeli `profiles`. Nadajesz je w panelu użytkowników albo SQL-em w Supabase. + +| Strona | Co robi | +|---|---| +| **Użytkownicy** (`/admin/uzytkownicy`) | Lista kont, nadawanie/odbieranie roli admina, szukanie | +| **Analityka** (`/admin/analityka`) | Aktywni użytkownicy, retencja, log akcji (logowania, tworzenie/dołączanie do meczów i grup) | +| **Kontakt z obiektami** (`/admin/outreach`) | **CRM do pozyskiwania boisk** — najważniejszy panel admina (opis niżej) | +| **Rezerwacje obiektu** (`/admin/...`) | Zarządzanie rezerwacjami i konfiguracją systemu rezerwacji dowolnego boiska | + +### Panel „Kontakt z obiektami" (CRM) + +Tu prowadzimy rozmowy z obiektami, żeby podłączyć je do rezerwacji. Dla każdego boiska: + +- **Status w lejku:** nowy → do kontaktu → w toku → czeka na odpowiedź → zainteresowany → umówiony / odrzucony +- **Pełne dane kontaktowe** (telefon, e-mail, strona, operator, godziny, opis) — klik rozwija kartę +- **Przypisanie** — „Przejmij", żeby wziąć obiekt na siebie; widać, kto się nim zajmuje +- **Notatki, osoba kontaktowa, data oddzwonienia, ostatni kontakt** +- **Sekcja „AI znalazł"** — dane, które wyszukała automatyzacja (podsumowanie, link do rezerwacji) +- **Wykrywanie duplikatów** — jeśli ten sam telefon/e-mail jest w 3+ obiektach (zwykle błąd danych), pojawia się ostrzeżenie i przycisk „Wyczyść" +- **Filtry** (sport, kontakt, przypisanie, status, duplikaty) z licznikiem wyników + **eksport CSV** + +--- + +## 4. Skąd się biorą dane o boiskach + +Boisk jest ~1400. Dane uzupełniamy **automatycznie**, uruchamiając skrypty z zakładki **GitHub → Actions** (ręcznie, „Run workflow"). Kolejność: + +| # | Workflow (Actions) | Co dorzuca | Koszt | +|---|---|---|---| +| 1 | **Import boisk** (`scraper.py`) | Boiska z OpenStreetMap + Google (z odsiewaniem duplikatów po GPS) | darmowe | +| 2 | **Google Venue Enrichment** | Telefon, strona, godziny — z Google Places (po współrzędnych) | w ramach $200/mc Google | +| 3 | **AI Venue Enrichment** (Claude) | E-mail, operator, opis, sposób rezerwacji — wyszukiwarka Claude | ~grosze/obiekt | +| 4 | **Booking System Extractor** (Claude) | Czyta stronę WWW i wykrywa system rezerwacji (telefon/własny/zewnętrzny) | ~grosze | + +> Każdy job ma tryb **dry_run** (podgląd bez zapisu) — zawsze warto najpierw sprawdzić jakość na małym `limit`. + +Wyniki trafiają do tabel `fields` (dane boiska) i `field_outreach` (status kontaktu + AI). + +--- + +## 5. Stack i uruchomienie + +- **Frontend:** Next.js 14 (App Router), TypeScript, Tailwind CSS → hosting na **Vercel** +- **Dane/Auth:** **Supabase** (PostgreSQL, logowanie Google, RLS). Migracje w `supabase/migrations/` +- **Mapa:** Leaflet + OpenStreetMap (bez tokenu). Mapbox tylko do miniaturek zdjęć (opcjonalny) +- **Automatyzacja:** Python (`scraper/`) + API Google i Claude, odpalane w GitHub Actions + +```bash +# lokalnie +cp .env.example .env # uzupełnij klucze Supabase +cd frontend +npm install +npm run dev # http://localhost:3000 +npm test # testy jednostkowe (Vitest) +``` + +Struktura: `frontend/` (apka), `scraper/` (skrypty danych), `supabase/` (schema + migracje). + +--- + +## 6. Pomysły na rozwój + +- **Powiadomienia** — e-mail/push, gdy ktoś dołączy do meczu albo awansuje z rezerwy (dziś brak) +- **Agent kontaktowy** — automat, który wysyła maile do obiektów, zbiera odpowiedzi i podpowiada następny ruch w CRM (szkic gotowy, do uzgodnienia) +- **Wyszukiwarka** boisk po nazwie/dzielnicy na mapie +- **Płatności** — zbieranie składek za mecz (BLIK/Stripe) +- **Twardsze zabezpieczenia** (część reguł dostępu sprawdzana dziś po stronie przeglądarki — do domknięcia w RLS) + +--- + +*Pytania? Najszybciej ogarnąć kod zaczynając od `frontend/src/app` (strony) i `frontend/src/lib` (logika + zapytania do Supabase).* diff --git a/README.md b/README.md index 653d1d4..435549c 100644 --- a/README.md +++ b/README.md @@ -1,199 +1,103 @@ -# [Material Kit React Native](https://creativetimofficial.github.io/material-kit-react-native/docs/#) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social&logo=twitter)](https://twitter.com/home?status=Material%20Kit%20React%20Native,%20a%20cool%20Material%20Kit%20React%20Native%20App%20Template%20%E2%9D%A4%EF%B8%8F%20https%3A//bit.ly/2HObENt%20%23reactnative%20%23material%20%23design%20%23developers%20via%20%40CreativeTim) +# ⚽ Bojo — Boiska Poznań +> Znajdź boisko w Poznaniu, zorganizuj mecz i zbierz skład. Aplikacja webowa, logowanie przez Google. - ![version](https://img.shields.io/badge/version-1.9.0-blue.svg) [![GitHub issues open](https://img.shields.io/github/issues/creativetimofficial/material-kit-react-native.svg?style=flat)](https://github.com/creativetimofficial/material-kit-react-native/issues?q=is%3Aopen+is%3Aissue) [![GitHub issues closed](https://img.shields.io/github/issues-closed-raw/creativetimofficial/material-kit-react-native.svg?maxAge=2592000)](https://github.com/creativetimofficial/material-kit-react-native/issues?q=is%3Aissue+is%3Aclosed) +📖 **Nowy w projekcie? Zacznij od [PRZEWODNIK.md](./PRZEWODNIK.md)** — opis wszystkich funkcji (użytkownik + admin) w 5 minut. +--- -![Product Gif](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/material-kit-react-native/opt_mkrn_thumbnail.jpg) +## Architektura -Material Kit React Native is a fully coded app template built over [Galio.io](https://galio.io/?ref=creativetim), [React Native](https://facebook.github.io/react-native/?ref=creativetim) and [Expo](https://expo.io/?ref=creativetim) to allow you to create powerful and beautiful e-commerce mobile applications. We have redesigned all the usual components in Galio to make it look like Google's material design, minimalistic and easy to use. - -Start your development with a badass material UI Kit for React Native inspired by Material Design. If you like Google's Material Design, you will love this react native kit! It features a huge number of components and screens built to fit together and look amazing. - -### FULLY CODED COMPONENTS - -Material Kit React Native features over 200 variations of components like buttons, inputs, cards, navigations etc, giving you the freedom of choosing and combining. All components can take variations in colour, that you can easily modify inside our theme file. - -You will save a lot of time going from prototyping to full-functional code, because all elements are implemented. We wanted the design process to be seamless, so switching from image to the real page is very easy to do. - -### Components & Cards -Material Kit React Native comes packed with a large number of components and cards. Putting together a mobile app has never been easier than matching together different components. From the profile screen to a settings screen, you can easily customise and build your screens. We have created multiple options for you to put together and customise into pixel perfect screens. - -View [ all components/cards here](https://demos.creative-tim.com/material-kit-react-native/index.html#cards). - -### Example Screens -If you want to get inspiration or just show something directly to your clients, you can jump start your development with our pre-built example screens. From onboarding screens to profile or discover screens, you will be able to quickly set up the basic structure for your React Native mobile project. - -View [all screens here](https://demos.creative-tim.com/material-kit-react-native/index.html#screens). - - -Let us know your thoughts below. And good luck with development! - - -## Table of Contents - -* [Versions](#versions) -* [Demo](#demo) -* [Quick Start](#quick-start) -* [Documentation](#documentation) -* [File Structure](#file-structure) -* [OS Support](#os-support) -* [Resources](#resources) -* [Reporting Issues](#reporting-issues) -* [Technical Support or Questions](#technical-support-or-questions) -* [Licensing](#licensing) -* [Useful Links](#useful-links) - -## Versions - -[](https://www.creative-tim.com/product/material-kit)[](https://www.creative-tim.com/product/vue-material-kit)[](https://www.creative-tim.com/product/material-kit-react)[](https://www.creative-tim.com/product/material-kit-react-native)[](https://demos.creative-tim.com/material-kit-figma/presentation.html)[](https://themeisle.com/themes/hestia/?ref=creativetim)[](https://github.com/creativetimofficial/material-kit/tree/photoshop)[](https://github.com/creativetimofficial/material-kit/tree/sketch) - - - - - -| HTML | React | Vue | -| --- | --- | --- | -| [![Material Kit HTML](https://github.com/creativetimofficial/public-assets/blob/master/material-kit/material-kit.jpeg?raw=true)](https://www.creative-tim.com/product/material-kit) | [![Material Kit React](https://github.com/creativetimofficial/public-assets/blob/master/material-kit-react/material-kit-react.jpeg?raw=true)](https://www.creative-tim.com/product/material-kit-react) | [![Vue Material Kit](https://github.com/creativetimofficial/public-assets/blob/master/vue-material-kit/vue-material-kit.jpeg?raw=true)](https://www.creative-tim.com/product/vue-material-kit) - -| React Native | Figma | WordPress | -| --- | --- | --- | -| [![Material Kit React Native](https://github.com/creativetimofficial/public-assets/blob/master/material-kit-react-native/opt_mkrn_thumbnail.jpg?raw=true)](https://www.creative-tim.com/product/material-kit-react-native) | [![Material Kit Figma](https://github.com/creativetimofficial/public-assets/blob/master/material-kit-figma/material-kit-figma.jpg?raw=true)](https://demos.creative-tim.com/material-kit-figma/presentation.html) | [![Material Kit WordPress](https://github.com/creativetimofficial/public-assets/blob/master/material-kit-wordpress/opt_smd_thumbnail.jpg?raw=true)](https://themeisle.com/themes/hestia/?ref=creativetim) - -## Demo - -| Home Screen | Profile Screen | Chat Screen | Product Screen | -| --- | --- | --- | --- | -| [![Home Screen](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/material-kit-react-native/home-screen.png)](https://demos.creative-tim.com/material-kit-react-native/) | [![Profile Screen](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/material-kit-react-native/profile-screen.png)](https://demos.creative-tim.com/material-kit-react-native/) | [![Chat Screen](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/material-kit-react-native/chat-screen.png)](https://demos.creative-tim.com/material-kit-react-native/) | [![Product Screen](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/material-kit-react-native/product-screen.png)](https://demos.creative-tim.com/material-kit-react-native/) | - -- [Start page](https://demos.creative-tim.com/material-kit-react-native) -- [How to install our free demo](https://demos.creative-tim.com/material-kit-react-native/docs/#/install) - -[View more](https://demos.creative-tim.com/material-kit-react-native) - -## Quick start -- Try it out on Expo (Simulator for iOS or even your physical device if you have an Android) -- Buy from [Creative Tim](https://www.creative-tim.com/product/material-kit-pro-react-native) - - -## Documentation -The documentation for the Material Kit React Native is hosted at our [website](https://demos.creative-tim.com/material-kit-react-native/docs/). - - -## File Structure -Within the download you'll find the following directories and files: +Bez osobnego backendu — frontend rozmawia z Supabase bezpośrednio (chronione przez RLS). +Dane o boiskach uzupełniają skrypty Pythona uruchamiane ręcznie z GitHub Actions. ``` -material-kit-react-native/ -├── App.js -├── README.md -├── app.json -├── assets -├── babel.config.js -├── components -│   ├── Button.js -│   ├── Drawer.js -│   ├── Header.js -│   ├── Icon.js -│   ├── Product.js -│   ├── Select.js -│   ├── Switch.js -│   ├── Tabs.js -│   └── index.js -├── constants -│   ├── Images.js -│   ├── Theme.js -│   ├── index.js -│   ├── products.js -│   └── utils.js -├── navigation -│   ├── Menu.js -│   └── Screens.js -├── package-lock.json -├── package.json -├── screens -│   ├── Components.js -│   ├── Home.js -│   ├── Onboarding.js -│   ├── Pro.js -│   ├── Profile.js -│   └── Settings.js - +┌──────────────────┐ ┌──────────────────────┐ +│ Frontend │ REST │ Supabase │ +│ Next.js 14 │ ─────▶ │ PostgreSQL + Auth │ +│ (Vercel) │ │ (Google OAuth, RLS) │ +│ Leaflet + OSM │ └──────────▲───────────┘ +└──────────────────┘ │ service_role + │ + ┌─────────────┴────────────┐ + │ Scraper (GitHub Actions) │ + │ OSM + Google Places + │ + │ Claude (wzbogacanie) │ + └───────────────────────────┘ ``` +| Warstwa | Technologia | +|---|---| +| Frontend | Next.js 14 (App Router), TypeScript, Tailwind CSS | +| Dane / Auth | Supabase (PostgreSQL, Google OAuth, Row Level Security) | +| Mapa | Leaflet + OpenStreetMap (bez tokenu); Mapbox tylko do miniaturek | +| Hosting | Vercel | +| Dane boisk | Python (`scraper/`) + Google Places API + Claude, w GitHub Actions | -## OS Support - -At present, we officially aim to support the last two versions of the following operating systems: - -[](https://www.creative-tim.com/product/material-kit-pro-react-native)[](https://www.creative-tim.com/product/material-kit-pro-react-native) - - - -## Resources -- Demo: -- Download Page: -- Documentation: -- License Agreement: -- Support: -- Issues: [Github Issues Page](https://github.com/creativetimofficial/ct-material-kit-react-native/issues) -- [Material Kit](https://www.creative-tim.com/product/material-kit?ref=mkprn-readme) - For Front End Development -- [Buy our PRO version](https://www.creative-tim.com/product/material-kit-pro-react-native) -- **Dashboards:** +--- -| HTML | React | Vue | Angular | -| --- | --- | --- | --- | -| [![Material Dashboard HTML](https://github.com/creativetimofficial/public-assets/blob/master/material-dashboard-html/material-dashboard.jpeg?raw=true)](https://www.creative-tim.com/product/material-dashboard) | [![Material Dashboard React](https://github.com/creativetimofficial/public-assets/blob/master/material-dashboard-react/material-dashboard-react.jpeg?raw=true)](https://www.creative-tim.com/product/material-dashboard-react) | [![Vue Material Dashboard](https://github.com/creativetimofficial/public-assets/blob/master/vue-material-dashboard/vue-material-dashboard.jpeg?raw=true)](https://www.creative-tim.com/product/vue-material-dashboard) | [![ Material Dashboard Angular](https://github.com/creativetimofficial/public-assets/blob/master/material-dashboard-angular/material-dashboard-angular.jpg?raw=true)](https://www.creative-tim.com/product/material-dashboard-angular2) +## Uruchomienie lokalne -| HTML Dark | Vuetify | -| --- | --- | -| [![Material Dashboard Dark](https://github.com/creativetimofficial/public-assets/blob/master/material-dashboard-dark/material-dashboard-dark.jpg?raw=true)](https://www.creative-tim.com/product/material-dashboard-dark) | [![Material Dashboard Vuetify](https://github.com/creativetimofficial/public-assets/blob/master/material-dashboard-vuetify/material-dashboard-vuetify.jpg?raw=true)](https://www.creative-tim.com/product/vuetify-material-dashboard) +```bash +git clone && cd bojo-app +cp .env.example .env # uzupełnij klucze Supabase (patrz .env.example) +cd frontend +npm install +npm run dev # http://localhost:3000 +npm test # testy jednostkowe (Vitest) +npm run build # build produkcyjny +``` -## Reporting Issues - -We use GitHub Issues as the official bug tracker for the Material Kit React Native. Here are some advices for our users that want to report an issue: - -1. Make sure that you are using the latest version of the Material Kit React Native. -2. Providing us reproducible steps for the issue will shorten the time it takes for it to be fixed. -3. Some issues may be browser specific, so specifying in what browser you encountered the issue might help. - - -### Technical Support or Questions - -If you have questions or need help integrating the product please [contact us](https://www.creative-tim.com/contact-us) instead of opening an issue. - +**Wymagania:** Node.js 18+, konto Supabase. Python 3.11+ tylko jeśli pracujesz przy scraperze. -## Licensing +--- -- Copyright 2019 Creative Tim (https://www.creative-tim.com/) +## Baza danych -- Licensed under MIT (https://github.com/creativetimofficial/material-kit-react-native/blob/master/LICENSE) +Schema i migracje: `supabase/migrations/`. Wgrywasz je w Supabase → SQL Editor +(kolejno wg numeracji). Dane startowe: `supabase/seed.sql`. +Najważniejsze tabele: `fields` (boiska), `events` (mecze), `event_participants`, +`recurring_events` (cykliczne), `bookings` (rezerwacje), `field_outreach` (CRM kontaktu), +`profiles` (użytkownicy + flaga `is_admin`). +--- -## Useful Links +## Dane o boiskach (scraper) -- [Tutorials](https://www.youtube.com/channel/UCVyTG4sCw-rOvB9oHkzZD1w) -- [Affiliate Program](https://www.creative-tim.com/affiliates/new) (earn money) -- [Blog Creative Tim](http://blog.creative-tim.com/) -- [Free Products](https://www.creative-tim.com/bootstrap-themes/free) from Creative Tim -- [Premium Products](https://www.creative-tim.com/bootstrap-themes/premium) from Creative Tim -- [React Products](https://www.creative-tim.com/bootstrap-themes/react-themes) from Creative Tim -- [Angular Products](https://www.creative-tim.com/bootstrap-themes/angular-themes) from Creative Tim -- [VueJS Products](https://www.creative-tim.com/bootstrap-themes/vuejs-themes) from Creative Tim -- [More products](https://www.creative-tim.com/bootstrap-themes) from Creative Tim -- Check our Bundles [here](https://www.creative-tim.com/bundles?ref="mk-github-readme") -- [Buy our PRO version](https://www.creative-tim.com/product/material-kit-pro-react-native) +Uruchamiane ręcznie z **GitHub → Actions**. Kolejność i opis: patrz +[PRZEWODNIK.md, sekcja 4](./PRZEWODNIK.md#4-skąd-się-biorą-dane-o-boiskach). +Każdy workflow ma tryb `dry_run` (podgląd bez zapisu). +``` +scraper.py → import boisk (OSM + Google) +enrich_google.py → telefon / strona / godziny (Google Places, darmowe) +enrich.py → e-mail / operator / opis / rezerwacja (Claude web search) +enrich_booking.py → wykrycie systemu rezerwacji ze strony WWW (Claude) +``` -### Social Media +--- -Twitter: +## Struktura projektu -Facebook: +``` +bojo-app/ +├── frontend/ # Aplikacja Next.js (całość UI + logika) +│ └── src/ +│ ├── app/ # App Router — strony i trasy +│ ├── components/ # Komponenty React (mapa, layout, ui) +│ ├── lib/ # Klient Supabase, zapytania, walidacja +│ ├── config/ # Flagi funkcji +│ └── types/ # Typy TypeScript +├── scraper/ # Skrypty Pythona do danych o boiskach +├── supabase/migrations/ # Migracje SQL +├── .github/workflows/ # Importy/wzbogacanie danych (Actions) +└── PRZEWODNIK.md # Opis funkcji dla współpracowników +``` -Dribbble: +--- -Instagram: +## Licencja +MIT © 2026 Bojo diff --git a/STATUS.md b/STATUS.md new file mode 100644 index 0000000..3dddb87 --- /dev/null +++ b/STATUS.md @@ -0,0 +1,65 @@ +# Bojo — Status projektu + +*Aktualizacja: 4 czerwca 2026* + +> Pełny opis funkcji dla nowych osób: [PRZEWODNIK.md](./PRZEWODNIK.md). + +--- + +## Co aplikacja potrafi dziś + +- **Przeglądanie boisk** — mapa Poznania z pinezkami, lista boisk, podstrona każdego boiska z adresem, zdjęciem i dostępnymi sportami +- **Tworzenie wydarzeń** — zalogowany użytkownik ustawia sport, datę, godzinę, maksymalną liczbę graczy i widoczność (publiczne/prywatne) +- **Dołączanie do wydarzeń** — lista chętnych, lista rezerwowa (gdy komplet), możliwość opuszczenia meczu +- **Strona „Moje mecze"** — widok wydarzeń, które organizujesz lub na które się zapisałeś +- **Logowanie przez Google** — przez Supabase Auth +- **Widok mapy** (`/mapa`) — wszystkie boiska i planowane mecze na mapie + +--- + +## Co działa technicznie + +| Obszar | Stan | +|---|---| +| Strona główna | Działa — ikony sportów, liczniki meczów w ten weekend, sekcja „Jak to działa" | +| Podstrony boisk (`/boisko/orlik-rataje`) | Działa — adresy SEO-friendly, tytuł i opis dla Google, lista nadchodzących meczów, schemat JSON-LD dla wyszukiwarek | +| Kategorie sportów (`/boiska/pilka-nozna`) | Działa — lista boisk per sport, prawidłowy tytuł dla Google | +| Mapa boisk | Działa | +| Tworzenie/edycja wydarzeń | Działa | +| Dołączanie / lista rezerwowa | Działa | +| Robots.txt i Sitemap XML | Działa — Google będzie indeksować poprawnie | +| Testy automatyczne | 14 testów — tworzenie wydarzeń, dołączanie, lista rezerwowa, generowanie adresów URL | + +--- + +## Czego brakuje (priorytety) + +### Pilne +1. **Brak powiadomień** — organizator nie dostaje e-maila gdy ktoś dołączy; gracz nie wie, że został awansowany z rezerwy +2. **Brak obsługi płatności** — można zaznaczyć „zapłacił", ale prawdziwy przelew/BLIK nie jest podłączony +3. **Brak weryfikacji numeru telefonu** — można wpisać dowolne imię i dołączyć jako gość + +### Do przemyślenia +4. **Zdjęcia boisk** — na razie tylko dane z bazy; brak możliwości wgrania własnych fotek przez użytkownika +5. **Cykliczne mecze** — zakładka `/cykliczne` istnieje, ale nie ma jeszcze pełnej funkcjonalności +6. **Wyszukiwanie** — brak wyszukiwarki po nazwie boiska lub dzielnicy + +--- + +## Rzeczy, o które warto się martwić + +### Załatwione +- ~~Martwy kod backendu / pliki React Native~~ — usunięte (pozostałości po szablonie „Material Kit React Native": `app.json`, `babel.config.js`, `constants/`, `assets/` oraz nieużywany `docker-compose.yml`). + +### Średnia troska (warto rozwiązać) +- **Autoryzacja po stronie frontendu** — sprawdzenie, czy użytkownik jest organizatorem, odbywa się w przeglądarce, nie na serwerze. Oznacza to, że technicznie sprytny użytkownik mógłby edytować cudzy mecz. Wymaga zabezpieczenia w bazie danych (Row Level Security w Supabase). +- **Zmienne środowiskowe** — klucze Supabase są publiczne (NEXT_PUBLIC_*), co jest normalną praktyką dla tego rodzaju kluczy, ale trzeba pilnować, żeby prywatny klucz serwisowy nigdy nie trafił do kodu. + +### Brak poważnych problemów bezpieczeństwa +Nie znaleziono haseł ani sekretów w kodzie. Połączenie z bazą danych przez Supabase jest standardowe i bezpieczne. + +--- + +## Podsumowanie jednym zdaniem + +Aplikacja działa jako solidny prototyp — można przez nią planować mecze i zarządzać składem — ale brakuje jej powiadomień i zabezpieczeń po stronie serwera zanim trafi do szerszej publiczności. diff --git a/STRATEGIA.md b/STRATEGIA.md new file mode 100644 index 0000000..8281a9c --- /dev/null +++ b/STRATEGIA.md @@ -0,0 +1,177 @@ +# Bojo — Strategia i plan działania + +> Dokument roboczy na spotkanie z Janem. Stan na 2026-06-05. +> Kontekst: realny startup, monetyzacja później (najpierw użytkownicy), Jan = wspólnik biznesowy. + +--- + +## 1. Gdzie jesteśmy (stan na dziś) + +**Produkt działa i jest wdrożony.** Aplikacja webowa (Next.js + Supabase, hosting Vercel), spójny design, większość funkcji core gotowa: + +- Mapa boisk w Poznaniu (zaimportowane z OSM + wzbogacone AI: telefon, www, godziny, sposób rezerwacji) +- Organizacja meczów: tworzenie gry, zapisy, lista rezerwowa, goście bez konta +- Zaawansowane: drużyny (drag&drop, losowanie, kapitanowie), wyniki meczów, statystyki, płatności, potwierdzenia SMS +- Cykliczne gierki (stałe ekipy), przypomnienia, alerty o nowych grach w okolicy +- Logowanie Google + email (hasło / magic link / reset) +- Komentarze, profile publiczne graczy z reputacją, powtarzanie meczu, podział kosztów +- Panel admina: kontakt z obiektami (CRM), zarządzanie użytkownikami + +**Infrastruktura:** +- Frontend: Vercel | Baza + Auth: Supabase | Email: Resend (`noreply@bojo.app`) | SMS: SMSAPI.pl (główny) + Twilio (zapasowy) +- 26 migracji bazy, 4 workflowy GitHub Actions do wzbogacania danych o obiektach + +**Jedno wąskie gardło infrastrukturalne:** jest tylko **jedno środowisko** (prod). Każda zmiana idzie od razu na żywo. To pierwsza rzecz do naprawienia przy "realnym starcie". + +--- + +## 2. SMS — bramka i optymalizacja kosztów + +### Stan +- **SMSAPI.pl** = główny dostawca, **Twilio** = fallback. Limit 30 SMS/h na organizatora. + +### Analiza kosztów (orientacyjnie, PL) +| Dostawca | Koszt / SMS | Uwagi | +|----------|-------------|-------| +| **SMSAPI.pl** (standard, z nazwą nadawcy "Bojo") | ~0,06–0,09 zł | pakiety obniżają cenę; nazwa nadawcy wymaga rejestracji (darmowa) | +| **SMSAPI.pl ECO** (bez nazwy nadawcy) | ~0,04 zł | tańsze, ale wolniejsze i bez brandingu | +| **Twilio** (PL) | ~0,16–0,30 zł | **3-5× drożej**, wymaga rejestracji Alphanumeric Sender ID, rozliczenie w USD | + +### Rekomendacja +1. **Zostać przy SMSAPI jako głównym** — dla Polski jest bezkonkurencyjny cenowo. Twilio rozważyć do **usunięcia** (komplikuje kod i sekrety, a jest 3-5× droższy). Ewentualnie zostawić wyłączony jako "break glass". +2. **Zarejestrować nazwę nadawcy "Bojo"** w SMSAPI (darmowe) — SMS z "Bojo" zamiast losowego numeru = wygląda profesjonalnie i buduje markę. +3. **Najważniejsze — SMS to najdroższy kanał, więc minimalizować jego użycie.** Przesuwać komunikację na kanały darmowe/tanie: + - **Link sharing (WhatsApp/Messenger)** — 0 zł, już działa, to główny kanał zapraszania + - **Powiadomienia in-app** — 0 zł, już działa + - **Email (Resend)** — ~0 zł (patrz niżej), idealny do przypomnień + - **Push notifications (PWA)** — 0 zł, do zbudowania (Faza 1) — **to nasz przyszły główny darmowy kanał przypomnień** + - **SMS rezerwować TYLKO na:** potwierdzenia obecności od osób bez konta/appki oraz pilne "gramy dziś". To realnie tnie koszt SMS o ~80%. + +**Wniosek dla Jana:** koszt SMS przy rozsądnej strategii to grosze. Przy 100 aktywnych grach/mies. i SMS tylko na potwierdzenia → kilka–kilkanaście zł/mies. Nie jest to bariera. + +--- + +## 3. Email — przypomnienie konfiguracji + +- **Dostawca: Resend.** Nadawca: `noreply@bojo.app`. Szablony zaszyte w Edge Functions (zaproszenia, alerty o grach). +- **Koszt:** darmowy plan = **3000 maili/mies. (100/dzień)**. Płatny: $20/mies. za 50k maili. Na start z dużym zapasem — **0 zł**. +- **Do zrobienia:** zweryfikować domenę `bojo.app` w Resend (rekordy **SPF + DKIM** w DNS) — bez tego maile lecą do spamu lub są limitowane. To jednorazowe ~15 min. + +--- + +## 4. Rozdzielenie środowisk dev / prod + +To **priorytet #1 techniczny** przy "realnym starcie". Niski wysiłek, duża wartość — przestajemy testować na żywych użytkownikach. + +### Plan (koszt: 0 zł na start) +1. **Supabase: drugi projekt `bojo-dev`** (darmowy tier) jako baza dev/staging. Migracje i nowe funkcje testujemy najpierw tam. +2. **Vercel: Preview Deployments już działają** out-of-the-box — każdy branch/PR dostaje własny URL. Trzeba tylko ustawić **osobne zmienne środowiskowe**: + - `Production` → wskazuje na `bojo-prod` (Supabase produkcyjny) + - `Preview` → wskazuje na `bojo-dev` +3. **Workflow gitowy:** `feature branch → PR → preview deploy (na bojo-dev) → review → merge do master → prod`. (Dziś pushujemy prosto na master.) +4. **Sekrety Edge Functions** ustawić osobno per projekt Supabase. + +**Efekt:** bezpieczne testy, brak ryzyka zepsucia produkcji, możliwość pokazania Janowi/testerom wersji przed wdrożeniem. + +--- + +## 5. Dług techniczny do domknięcia (krótka lista) + +Z wcześniejszego zakresu zostało kilka rzeczy — szybkie do dokończenia: + +- [ ] **Zastosować migrację 026** na Supabase (komentarze + activity log) — *bez tego nowe funkcje nie działają na prodzie* +- [ ] **Zod — walidacja danych z bazy** (mappery `toEvent`, `toField` itd. — dziś rzutowanie `any` bez walidacji runtime) +- [ ] **Dokończyć logowanie błędów** — część `.catch(()=>{})` już zastąpiona, zostało ~kilka miejsc +- [ ] **Ujednolicić copy "Zapisz się" / "Dołącz do gry"** — wybrać jedną wersję w całej apce +- [ ] **Weryfikacja domeny w Resend** (SPF/DKIM) — patrz pkt 3 + +--- + +## 6. Roadmapa (fazowa) + +Priorytet: **wzrost i retencja** (monetyzacja dopiero przy trakcji). + +### Faza 0 — Fundamenty (najbliższe 1-2 tyg.) +- Rozdzielenie dev/prod +- Domknięcie długu technicznego (pkt 5) +- Weryfikacja domeny Resend, rejestracja nadawcy "Bojo" w SMSAPI +- **Cel: stabilna baza pod growth.** + +### Faza 1 — Akwizycja / viral (najbliższy miesiąc) +- **PWA + push notifications** — instalowalna apka na telefon + darmowy kanał przypomnień (zastępuje większość SMS) +- **Onboarding** — pierwsze wejście prowadzi do "stwórz lub znajdź grę" w < 1 min +- **Wzmocnienie pętli viralowej** — zapraszanie przez link już jest; dodać "udostępnij grę" wszędzie, podgląd OG ładny +- **Seed treści** — kilka realnych publicznych gier tygodniowo, żeby mapa nie była pusta (rola Jana: zebrać pierwsze ekipy) +- **SEO** — strony obiektów/sportów już są indeksowalne; rozwinąć pod frazy "boisko Poznań", "szukam do gry Poznań" +- **Cel: pierwszych 100 aktywnych użytkowników.** + +### Faza 2 — Retencja (2-3 miesiąc) +- Dopracowane cykliczne gierki / stałe ekipy (to wraca użytkownika co tydzień) +- Reputacja i profile graczy (zaczęte) — frekwencja, "niezawodny gracz" +- Powiadomienia push o stałych grach, przypomnienia +- **Cel: użytkownik wraca co tydzień bez naszej zachęty.** + +### Faza 3 — Monetyzacja (gdy jest trakcja) +- **Rezerwacje obiektów z prowizją** — `booking_enabled` już w kodzie; Jan domyka partnerstwa z obiektami (B2B) +- **Premium dla organizatorów** — darmowy core, płatne dodatki (SMS, statystyki, branding ekipy) +- **Cel: pierwszy przychód, walidacja modelu.** + +--- + +## 7. Plan współpracy z Janem + +Jan = **wspólnik biznesowy (nietechniczny)**. Podział kompetencji: + +### Jan (biznes / growth) +- **Outreach do obiektów** — jest gotowy panel CRM (`admin/outreach`) z danymi wzbogaconymi AI (telefony, maile, sposób rezerwacji). Domykanie partnerstw, włączanie `booking_enabled`. +- **Marketing / akwizycja** — grupy FB (piłka/siatka/kosz Poznań), uczelnie (AWF, PP, UAM), kluby amatorskie, orliki +- **Pierwsze ekipy** — osobiście zebrać pierwsze stałe gierki (seed) +- **Feedback od użytkowników** → backlog +- **Partnerstwa i sprzedaż B2B** (obiekty) w Fazie 3 + +### Franek (tech / produkt) +- Rozwój aplikacji, infrastruktura, decyzje produktowo-techniczne +- Roadmapa techniczna, jakość, bezpieczeństwo + +### Wspólnie +- Decyzje strategiczne i o monetyzacji +- **Cotygodniowy sync** + wspólny backlog (np. Notion / GitHub Projects) +- **KPI tygodniowe:** nowi użytkownicy, aktywne gry/tydzień, retencja (ilu wraca) + +### ⚠️ Do ustalenia na tym spotkaniu (ważne wcześnie, nie później) +1. **Własność / equity** — jak dzielimy udziały? Czy zakładamy spółkę, czy umowa partnerska? (lepiej ustalić teraz, gdy nie ma jeszcze wartości, niż przy pierwszym przychodzie) +2. **Zaangażowanie czasowe** — ile godzin/tydzień każdy realnie wkłada? +3. **Definicja sukcesu na 3 i 6 miesięcy** — co musi się wydarzyć, żebyśmy uznali, że "chwyciło"? +4. **Budżet** — czy wkładamy własne pieniądze (domena, ewentualnie płatne plany), ile, jak rozliczamy? + +--- + +## 8. Szata graficzna i UX + +### Stan obecny +Design jest **spójny i profesjonalny**: system kolorów (zielony `#15663E`, amber `#F5A623`), fonty (Inter + Bricolage Grotesque), tokeny w Tailwind. To solidna baza — nie wstyd pokazać. + +### Siostra (grafika) — gdzie da najwięcej wartości +Wchodzimy w growth, więc branding się opłaca. Konkretna prośba: +1. **Finalne logo + warianty** (dziś jest placeholder "B") — pozioma/kwadratowa/favicon +2. **Zestaw ilustracji "empty state"** (pusta lista gier, brak wyników itp.) — ociepla apkę +3. **Szablony social media** (post zapowiadający grę, plakat na uczelnię/orlik) — Jan będzie tego potrzebował do marketingu +4. **Dopracowany OG image** (podgląd linku) — jest podstawowy, można ładniej + +→ Najwięcej daje **logo + zestaw marketingowy**, bo bezpośrednio wspiera akwizycję. + +### UX — czy Claude wystarcza? +- Do **iteracji produktowych, flow, copy i implementacji** — tak, ogarniam to na bieżąco. +- **Świeże oko zewnętrznego UX-a** warto **po pierwszych użytkownikach** — na bazie realnego feedbacku (gdzie ludzie się gubią), nie teraz abstrakcyjnie. Wtedy 1-2 sesje testów z prawdziwymi użytkownikami dadzą więcej niż audyt UX bez danych. +- **Teraz nie jest to pilne.** Pilniejsze: branding od siostry + zebranie pierwszych użytkowników, żeby było na czym testować. + +--- + +## TL;DR na spotkanie + +1. **Produkt gotowy**, wdrożony, działa. Brakuje rozdzielenia dev/prod (priorytet techniczny #1, koszt 0 zł). +2. **Koszty komunikacji to grosze** — SMSAPI tani, email Resend darmowy, większość przypomnień przeniesiemy na darmowy push (PWA). +3. **Roadmapa:** najpierw fundamenty → akwizycja/viral → retencja → monetyzacja (rezerwacje + premium). +4. **Podział:** Jan = obiekty + marketing + pierwsze ekipy; Franek = tech + produkt. +5. **Ustalić dziś:** equity/własność, zaangażowanie, definicja sukcesu, budżet. +6. **Grafika:** poprosić siostrę o logo + zestaw marketingowy. UX zewnętrzny — później, po pierwszych użytkownikach. diff --git a/app.json b/app.json deleted file mode 100644 index 2253a8e..0000000 --- a/app.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "expo": { - "name": "rn-material-kit-free", - "description": "React Native - Material Kit Free", - "slug": "rn-material-kit-free", - "privacy": "unlisted", - "platforms": [ - "ios", - "android" - ], - "version": "1.8.0", - "orientation": "portrait", - "icon": "./assets/images/icon.png", - "splash": { - "image": "./assets/images/splash.png", - "resizeMode": "cover", - "backgroundColor": "#6C24AA" - }, - "updates": { - "fallbackToCacheTimeout": 0 - }, - "assetBundlePatterns": [ - "**/*" - ], - "ios": { - "supportsTablet": true - } - } -} diff --git a/assets/fonts/galioExtra.json b/assets/fonts/galioExtra.json deleted file mode 100644 index 22243fa..0000000 --- a/assets/fonts/galioExtra.json +++ /dev/null @@ -1 +0,0 @@ -{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M1014.627 969.373c12.497 12.497 12.497 32.758 0 45.255s-32.758 12.497-45.255 0l-192-192c-12.497-12.497-12.497-32.758 0-45.255s32.758-12.497 45.255 0l192 192z","M416 832c-229.75 0-416-186.25-416-416s186.25-416 416-416c229.75 0 416 186.25 416 416s-186.25 416-416 416zM416 768c194.404 0 352-157.596 352-352s-157.596-352-352-352c-194.404 0-352 157.596-352 352s157.596 352 352 352z"],"attrs":[{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["zoom-split"]},"attrs":[{},{}],"properties":{"order":5,"id":8,"name":"zoom-split","prevSize":32,"code":59648},"setIdx":0,"setId":1,"iconIdx":0},{"icon":{"paths":["M192 384h-160c-17.673 0-32-14.327-32-32v-256c0-17.673 14.327-32 32-32h323.2c15.218 0 28.332 10.718 31.36 25.632 12.114 59.656 64.566 102.528 125.44 102.528s113.326-42.872 125.44-102.528c3.028-14.914 16.142-25.632 31.36-25.632h323.2c17.673 0 32 14.327 32 32v256c0 17.673-14.327 32-32 32h-160v352c0 17.673-14.327 32-32 32-88.366 0-160 71.634-160 160 0 17.673-14.327 32-32 32h-192c-17.673 0-32-14.327-32-32 0-88.366-71.634-160-160-160-17.673 0-32-14.327-32-32v-352zM512 256.16c-82.331 0-154.393-52.282-181.089-128.16h-266.911v192h160c17.673 0 32 14.327 32 32v354.268c98.102 14.032 175.699 91.629 189.732 189.732h132.537c14.032-98.102 91.629-175.699 189.732-189.732v-354.268c0-17.673 14.327-32 32-32h160v-192h-266.911c-26.697 75.879-98.758 128.16-181.089 128.16z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["baby"]},"attrs":[{}],"properties":{"order":6,"id":7,"name":"baby","prevSize":32,"code":59649},"setIdx":0,"setId":1,"iconIdx":1},{"icon":{"paths":["M192 512c-106.057 0-192-85.943-192-192 0-5.438 1.386-10.787 4.027-15.541l160-288c5.644-10.159 16.352-16.459 27.973-16.459h640c11.621 0 22.329 6.301 27.973 16.459l160 288c2.641 4.754 4.027 10.102 4.027 15.541 0 106.057-85.943 192-192 192-66.792 0-125.606-34.086-160-85.815-34.394 51.729-93.208 85.815-160 85.815s-125.606-34.086-160-85.815c-34.394 51.729-93.208 85.815-160 85.815zM210.829 64l-146.591 263.865c4.061 67.046 59.693 120.135 127.763 120.135 70.711 0 128-57.289 128-128 0-42.667 64-42.667 64 0 0 70.711 57.289 128 128 128s128-57.289 128-128c0-42.667 64-42.667 64 0 0 70.711 57.289 128 128 128 68.070 0 123.702-53.090 127.763-120.135l-146.591-263.865h-602.342z","M832 960v-352c0-17.673 14.327-32 32-32s32 14.327 32 32v384c0 17.673-14.327 32-32 32h-704c-17.673 0-32-14.327-32-32v-384c0-17.673 14.327-32 32-32s32 14.327 32 32v352h640z","M448 768v224c0 17.673-14.327 32-32 32s-32-14.327-32-32v-256c0-17.673 14.327-32 32-32h192c17.673 0 32 14.327 32 32v256c0 17.673-14.327 32-32 32s-32-14.327-32-32v-224h-128z"],"attrs":[{},{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shop"]},"attrs":[{},{},{}],"properties":{"order":7,"id":6,"name":"shop","prevSize":32,"code":59650},"setIdx":0,"setId":1,"iconIdx":2},{"icon":{"paths":["M511.766 123.060l-119.701 242.501c-4.663 9.447-13.676 15.995-24.102 17.51l-267.577 38.882 193.636 188.73c7.545 7.354 10.988 17.949 9.208 28.334l-45.701 266.561 239.34-125.84c9.326-4.904 20.469-4.904 29.795 0l239.34 125.84-45.701-266.561c-1.78-10.384 1.663-20.98 9.208-28.334l193.636-188.73-267.577-38.882c-10.426-1.515-19.439-8.063-24.102-17.51l-119.701-242.501zM342.103 322.133l140.958-285.564c11.743-23.79 45.667-23.79 57.41 0l140.958 285.564 315.116 45.79c26.257 3.815 36.741 36.084 17.74 54.603l-228.036 222.259 53.817 313.902c4.483 26.15-22.965 46.091-46.449 33.743l-281.851-148.192-281.851 148.192c-23.484 12.347-50.932-7.593-46.449-33.743l53.817-313.902-228.036-222.259c-19.001-18.519-8.517-50.788 17.74-54.603l315.116-45.79z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["shape-star"]},"attrs":[{}],"properties":{"order":8,"id":5,"name":"shape-star","prevSize":32,"code":59651},"setIdx":0,"setId":1,"iconIdx":3},{"icon":{"paths":["M0 352c0-195.813 187.407-352 416-352s416 156.187 416 352c0 195.813-187.407 352-416 352-51.765 0-102.282-8.117-149.766-23.69l-151.032 113.289c-21.095 15.824-51.202 0.772-51.202-25.599v-228.337c-41.47-55.633-64-120.203-64-187.663zM128 703.995l113.47-85.114c8.702-6.527 20.123-8.187 30.322-4.407 45.157 16.736 93.942 25.526 144.207 25.526 195.564 0 352-130.376 352-288s-156.436-288-352-288c-195.564 0-352 130.376-352 288 0 56.258 19.917 110.249 57.028 156.828 4.514 5.666 6.972 12.696 6.972 19.94v175.227z","M608 928c-111.883 0-217.038-37.531-294.674-103.333-13.482-11.427-15.148-31.62-3.721-45.102s31.62-15.148 45.102-3.721c65.92 55.872 156.373 88.155 253.294 88.155 45.338 0 89.716-7.269 131.707-21.164 9.35-3.094 19.604-1.71 27.799 3.751l128.494 85.632v-179.451c0-7.244 2.458-14.274 6.972-19.94 37.128-46.601 57.028-100.53 57.028-156.828 0-50.198-15.748-98.555-45.449-141.469-10.058-14.532-6.43-34.466 8.102-44.524s34.466-6.43 44.524 8.102c36.959 53.402 56.823 114.397 56.823 177.891 0 67.501-22.516 132.018-64 187.664v228.336c0 25.556-28.48 40.801-49.746 26.629l-165.53-110.314c-43.915 12.946-89.892 19.685-136.724 19.685z"],"attrs":[{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["chat-33"]},"attrs":[{},{}],"properties":{"order":9,"id":4,"name":"chat-33","prevSize":32,"code":59652},"setIdx":0,"setId":1,"iconIdx":4},{"icon":{"paths":["M576 832c-141.385 0-256-114.615-256-256s114.615-256 256-256c141.385 0 256 114.615 256 256s-114.615 256-256 256zM576 768c106.039 0 192-85.961 192-192s-85.961-192-192-192c-106.039 0-192 85.961-192 192s85.961 192 192 192z","M160 384c-17.673 0-32-14.327-32-32s14.327-32 32-32h64c17.673 0 32 14.327 32 32s-14.327 32-32 32h-64z","M819.777 192h108.223c53.001 0 96 42.999 96 96v576c0 53.001-42.999 96-96 96h-832c-53.001 0-96-42.999-96-96v-576c0-53.001 42.999-96 96-96h236.223l55.155-110.311c5.421-10.841 16.501-17.689 28.622-17.689h320c12.121 0 23.201 6.848 28.622 17.689l55.155 110.311zM435.777 128l-55.155 110.311c-5.421 10.841-16.501 17.689-28.622 17.689h-256c-17.655 0-32 14.345-32 32v576c0 17.655 14.345 32 32 32h832c17.655 0 32-14.345 32-32v-576c0-17.655-14.345-32-32-32h-128c-12.121 0-23.201-6.848-28.622-17.689l-55.155-110.311h-280.446z"],"attrs":[{},{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["camera-18"]},"attrs":[{},{},{}],"properties":{"order":10,"id":3,"name":"camera-18","prevSize":32,"code":59653},"setIdx":0,"setId":1,"iconIdx":5},{"icon":{"paths":["M763.026 942.813l167.611-502.834h-855.608l167.611 502.834h520.385zM785.678 1005.668h-565.688c-13.527 0-25.537-8.656-29.814-21.489l-188.563-565.688c-6.783-20.35 8.364-41.365 29.814-41.365h942.813c21.451 0 36.598 21.015 29.814 41.365l-188.563 565.688c-4.278 12.833-16.287 21.489-29.814 21.489z","M248.099 296.899c-7.762 15.524-26.64 21.817-42.164 14.055s-21.817-26.64-14.055-42.164l125.708-251.417c7.762-15.524 26.64-21.817 42.164-14.055s21.817 26.64 14.055 42.164l-125.708 251.417z","M813.787 268.789c7.762 15.524 1.47 34.402-14.055 42.164s-34.402 1.47-42.164-14.055l-125.708-251.417c-7.762-15.524-1.47-34.402 14.055-42.164s34.402-1.47 42.164 14.055l125.708 251.417z"],"attrs":[{},{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["basket-simple"]},"attrs":[{},{},{}],"properties":{"order":11,"id":2,"name":"basket-simple","prevSize":32,"code":59654},"setIdx":0,"setId":1,"iconIdx":6},{"icon":{"paths":["M256 288c0 17.673-14.327 32-32 32s-32-14.327-32-32v-256c0-17.673 14.327-32 32-32s32 14.327 32 32v256z","M256 992c0 17.673-14.327 32-32 32s-32-14.327-32-32v-128c0-17.673 14.327-32 32-32s32 14.327 32 32v128z","M768 736c0-17.673 14.327-32 32-32s32 14.327 32 32v256c0 17.673-14.327 32-32 32s-32-14.327-32-32v-256z","M768 32c0-17.673 14.327-32 32-32s32 14.327 32 32v128c0 17.673-14.327 32-32 32s-32-14.327-32-32v-128z","M224 896c-123.712 0-224-100.288-224-224s100.288-224 224-224c123.712 0 224 100.288 224 224s-100.288 224-224 224zM224 832c88.366 0 160-71.634 160-160s-71.634-160-160-160c-88.366 0-160 71.634-160 160s71.634 160 160 160z","M800 576c-123.712 0-224-100.288-224-224s100.288-224 224-224c123.712 0 224 100.288 224 224s-100.288 224-224 224zM800 512c88.366 0 160-71.634 160-160s-71.634-160-160-160c-88.366 0-160 71.634-160 160s71.634 160 160 160z"],"attrs":[{},{},{},{},{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["preferences-circle-rotate"]},"attrs":[{},{},{},{},{},{}],"properties":{"order":12,"id":1,"name":"preferences-circle-rotate","prevSize":32,"code":59655},"setIdx":0,"setId":1,"iconIdx":7},{"icon":{"paths":["M512 256c-70.692 0-128 57.308-128 128v64c0 70.692 57.308 128 128 128s128-57.308 128-128v-64c0-70.692-57.308-128-128-128zM512 192c106.039 0 192 85.961 192 192v64c0 106.039-85.961 192-192 192s-192-85.961-192-192v-64c0-106.039 85.961-192 192-192z","M891.098 837.721c2.637 17.475-9.391 33.78-26.866 36.417s-33.78-9.391-36.417-26.866c-10.616-70.343-44.438-135.12-96.090-184.037-12.832-12.152-13.383-32.406-1.231-45.238s32.406-13.383 45.238-1.231c62.014 58.73 102.62 136.501 115.366 220.955z","M248.416 616.747c12.842-12.142 33.095-11.574 45.237 1.268s11.574 33.095-1.268 45.237c-51.728 48.907-85.614 113.709-96.265 184.095-2.644 17.474-18.953 29.496-36.428 26.852s-29.496-18.953-26.852-36.428c12.788-84.506 53.47-162.307 115.575-221.025z","M512 1024c-282.77 0-512-229.23-512-512s229.23-512 512-512c282.77 0 512 229.23 512 512s-229.23 512-512 512zM512 960c247.424 0 448-200.576 448-448s-200.576-448-448-448c-247.424 0-448 200.576-448 448s200.576 448 448 448z"],"attrs":[{},{},{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circle-10"]},"attrs":[{},{},{},{}],"properties":{"order":13,"id":0,"name":"circle-10","prevSize":32,"code":59656},"setIdx":0,"setId":1,"iconIdx":8}],"height":1024,"metadata":{"name":"galioExtra"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"galioExtra","majorVersion":1,"minorVersion":0},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false,"showSelector":false,"showMetrics":false,"showMetadata":false,"includeMetadata":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon","name":"icomoon"},"historySize":50,"showCodes":true,"gridSize":16,"showGrid":false,"showLiga":false}} \ No newline at end of file diff --git a/assets/fonts/galioExtra.ttf b/assets/fonts/galioExtra.ttf deleted file mode 100644 index f1119f5..0000000 Binary files a/assets/fonts/galioExtra.ttf and /dev/null differ diff --git a/assets/icons/baby.svg b/assets/icons/baby.svg deleted file mode 100644 index 1b40de6..0000000 --- a/assets/icons/baby.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - baby - Created with Sketch. - - - - - - \ No newline at end of file diff --git a/assets/icons/basket-simple.svg b/assets/icons/basket-simple.svg deleted file mode 100644 index a0116c4..0000000 --- a/assets/icons/basket-simple.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - basket-simple - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/assets/icons/camera-18.svg b/assets/icons/camera-18.svg deleted file mode 100644 index 0589580..0000000 --- a/assets/icons/camera-18.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - camera-18 - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/assets/icons/chat-33.svg b/assets/icons/chat-33.svg deleted file mode 100644 index 7189c35..0000000 --- a/assets/icons/chat-33.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - chat-33 - Created with Sketch. - - - - - - - \ No newline at end of file diff --git a/assets/icons/circle-10.svg b/assets/icons/circle-10.svg deleted file mode 100644 index fdd79e1..0000000 --- a/assets/icons/circle-10.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - circle-10 - Created with Sketch. - - - - - - - - - \ No newline at end of file diff --git a/assets/icons/preferences-circle-rotate.svg b/assets/icons/preferences-circle-rotate.svg deleted file mode 100644 index 621055d..0000000 --- a/assets/icons/preferences-circle-rotate.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - preferences-circle-rotate - Created with Sketch. - - - - - - - - - - - \ No newline at end of file diff --git a/assets/icons/shape-star.svg b/assets/icons/shape-star.svg deleted file mode 100644 index a066a84..0000000 --- a/assets/icons/shape-star.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - shape-star - Created with Sketch. - - - - - - \ No newline at end of file diff --git a/assets/icons/shop.svg b/assets/icons/shop.svg deleted file mode 100644 index a8214df..0000000 --- a/assets/icons/shop.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - shop - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/assets/icons/zoom-split.svg b/assets/icons/zoom-split.svg deleted file mode 100644 index b048660..0000000 --- a/assets/icons/zoom-split.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - zoom-split - Created with Sketch. - - - - - - - \ No newline at end of file diff --git a/assets/images/android.png b/assets/images/android.png deleted file mode 100644 index fec1051..0000000 Binary files a/assets/images/android.png and /dev/null differ diff --git a/assets/images/icon.png b/assets/images/icon.png deleted file mode 100644 index b587b90..0000000 Binary files a/assets/images/icon.png and /dev/null differ diff --git a/assets/images/ios.png b/assets/images/ios.png deleted file mode 100644 index e2863ef..0000000 Binary files a/assets/images/ios.png and /dev/null differ diff --git a/assets/images/splash.png b/assets/images/splash.png deleted file mode 100644 index f8560ad..0000000 Binary files a/assets/images/splash.png and /dev/null differ diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 2900afe..0000000 --- a/babel.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function(api) { - api.cache(true); - return { - presets: ['babel-preset-expo'], - }; -}; diff --git a/components/Button.js b/components/Button.js deleted file mode 100644 index 2b9a5a7..0000000 --- a/components/Button.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { StyleSheet } from 'react-native'; -import { LinearGradient } from 'expo-linear-gradient'; -import { Button, Text, theme } from 'galio-framework'; - -import materialTheme from '../constants/Theme'; - -export default class GaButton extends React.Component { - render() { - const { gradient, children, style, ...props } = this.props; - - if (gradient) { - return ( - - - - ); - } - - return ( - - ); - } -} - -const styles = StyleSheet.create({ - gradient: { - borderWidth: 0, - borderRadius: theme.SIZES.BASE * 2, - }, -}); \ No newline at end of file diff --git a/components/Drawer.js b/components/Drawer.js deleted file mode 100644 index 26b4306..0000000 --- a/components/Drawer.js +++ /dev/null @@ -1,198 +0,0 @@ -import React from "react"; -import { TouchableOpacity, StyleSheet } from "react-native"; -import { Block, Text, theme } from "galio-framework"; - -import Icon from "./Icon"; -import materialTheme from "../constants/Theme"; - -const proScreens = [ - "Woman", - "Man", - "Kids", - "New Collection", - "Sign In", - "Sign Up" -]; - -class DrawerItem extends React.Component { - renderIcon = () => { - const { title, focused } = this.props; - - switch (title) { - case "Home": - return ( - - ); - case "Woman": - return ( - - ); - case "Man": - return ( - - ); - case "Kids": - return ( - - ); - case "New Collection": - return ( - - ); - case "Profile": - return ( - - ); - case "Settings": - return ( - - ); - case "Components": - return ( - - ); - case "Sign In": - return ( - - ); - case "Sign Up": - return ( - - ); - default: - return null; - } - }; - - renderLabel = () => { - const { title } = this.props; - - if (proScreens.includes(title)) { - return ( - - - PRO - - - ); - } - - return null; - }; - - render() { - const { focused, title, navigation } = this.props; - const proScreen = proScreens.includes(title); - return ( - {navigation.navigate(title)}}> - - - {this.renderIcon()} - - - - {title} - - {this.renderLabel()} - - - - ); - } -} - -export default DrawerItem; - -const styles = StyleSheet.create({ - defaultStyle: { - paddingVertical: 16, - paddingHorizontal: 16 - }, - activeStyle: { - backgroundColor: materialTheme.COLORS.ACTIVE, - borderRadius: 4 - }, - shadow: { - shadowColor: theme.COLORS.BLACK, - shadowOffset: { - width: 0, - height: 2 - }, - shadowRadius: 8, - shadowOpacity: 0.2 - }, - pro: { - backgroundColor: materialTheme.COLORS.LABEL, - paddingHorizontal: 6, - marginLeft: 8, - borderRadius: 2, - height: 16, - width: 36 - } -}); diff --git a/components/Header.js b/components/Header.js deleted file mode 100644 index c060fc2..0000000 --- a/components/Header.js +++ /dev/null @@ -1,256 +0,0 @@ -import React from 'react'; -import { withNavigation } from '@react-navigation/compat'; -import { TouchableOpacity, StyleSheet, Platform, Dimensions } from 'react-native'; -import { Button, Block, NavBar, Input, Text, theme } from 'galio-framework'; - -import Icon from './Icon'; -import materialTheme from '../constants/Theme'; - -const { height, width } = Dimensions.get('window'); -const iPhoneX = () => Platform.OS === 'ios' && (height === 812 || width === 812 || height === 896 || width === 896); - -const ChatButton = ({isWhite, style, navigation}) => ( - navigation.navigate('Pro')}> - - - -); - -const BasketButton = ({isWhite, style, navigation}) => ( - navigation.navigate('Pro')}> - - - -); - -const SearchButton = ({isWhite, style, navigation}) => ( - navigation.navigate('Pro')}> - - -); - -class Header extends React.Component { - handleLeftPress = () => { - const { back, navigation } = this.props; - return (back ? navigation.goBack() : navigation.openDrawer()); - } - - renderRight = () => { - const { white, title, navigation } = this.props; - - if (title === 'Title') { - return [ - , - - ] - } - - switch (title) { - case 'Home': - return ([ - , - - ]); - case 'Deals': - return ([ - , - - ]); - case 'Categories': - return ([ - , - - ]); - case 'Category': - return ([ - , - - ]); - case 'Profile': - return ([ - , - - ]); - case 'Product': - return ([ - , - - ]); - case 'Search': - return ([ - , - - ]); - case 'Settings': - return ([ - , - - ]); - default: - break; - } - } - - renderSearch = () => { - const { navigation } = this.props; - return ( - navigation.navigate('Pro')} - iconContent={} - /> - ) - } - - renderTabs = () => { - const { navigation, tabTitleLeft, tabTitleRight } = this.props; - - return ( - - - - - ) - } - - renderHeader = () => { - const { search, tabs } = this.props; - if (search || tabs) { - return ( - - {search ? this.renderSearch() : null} - {tabs ? this.renderTabs() : null} - - ) - } - return null; - } - - render() { - const { back, title, white, transparent, navigation } = this.props; - // const { routeName } = navigation.state; - const noShadow = ["Search", "Categories", "Deals", "Pro", "Profile"].includes(title); - const headerStyles = [ - !noShadow ? styles.shadow : null, - transparent ? { backgroundColor: 'rgba(0,0,0,0)' } : null, - ]; - - return ( - - - {this.renderHeader()} - - ); - } -} - -export default withNavigation(Header); - -const styles = StyleSheet.create({ - button: { - padding: 12, - position: 'relative', - }, - title: { - width: '100%', - fontSize: 16, - fontWeight: 'bold', - }, - navbar: { - paddingVertical: 0, - paddingBottom: theme.SIZES.BASE * 1.5, - paddingTop: iPhoneX ? theme.SIZES.BASE * 4 : theme.SIZES.BASE, - zIndex: 5, - }, - shadow: { - backgroundColor: theme.COLORS.WHITE, - shadowColor: 'black', - shadowOffset: { width: 0, height: 2 }, - shadowRadius: 6, - shadowOpacity: 0.2, - elevation: 3, - }, - notify: { - backgroundColor: materialTheme.COLORS.LABEL, - borderRadius: 4, - height: theme.SIZES.BASE / 2, - width: theme.SIZES.BASE / 2, - position: 'absolute', - top: 8, - right: 8, - }, - header: { - backgroundColor: theme.COLORS.WHITE, - }, - divider: { - borderRightWidth: 0.3, - borderRightColor: theme.COLORS.MUTED, - }, - search: { - height: 48, - width: width - 32, - marginHorizontal: 16, - borderWidth: 1, - borderRadius: 3, - }, - tabs: { - marginBottom: 24, - marginTop: 10, - elevation: 4, - }, - tab: { - backgroundColor: theme.COLORS.TRANSPARENT, - width: width * 0.50, - borderRadius: 0, - borderWidth: 0, - height: 24, - elevation: 0, - }, - tabTitle: { - lineHeight: 19, - fontWeight: '300' - }, -}) \ No newline at end of file diff --git a/components/Icon.js b/components/Icon.js deleted file mode 100644 index c902e34..0000000 --- a/components/Icon.js +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import * as Font from 'expo-font'; -import { createIconSetFromIcoMoon } from '@expo/vector-icons'; -import { Icon } from 'galio-framework'; - -import GalioConfig from '../assets/fonts/galioExtra'; - -const GalioExtra = require('../assets/fonts/galioExtra.ttf'); -const IconGalioExtra = createIconSetFromIcoMoon(GalioConfig, 'GalioExtra'); - -export default class IconExtra extends React.Component { - state = { - fontLoaded: false, - } - - async componentDidMount() { - await Font.loadAsync({ GalioExtra: GalioExtra }); - this.setState({ fontLoaded: true }); - } - - render() { - const { name, family, ...rest } = this.props; - - if (name && family && this.state.fontLoaded) { - if (family === 'GalioExtra') { - return ; - } - return ; - } - - return null; - } -} diff --git a/components/Product.js b/components/Product.js deleted file mode 100644 index 532f0d0..0000000 --- a/components/Product.js +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { withNavigation } from '@react-navigation/compat'; -import { StyleSheet, Dimensions, Image, TouchableWithoutFeedback } from 'react-native'; -import { Block, Text, theme } from 'galio-framework'; - -import materialTheme from '../constants/Theme'; - -const { width } = Dimensions.get('screen'); - -class Product extends React.Component { - render() { - const { navigation, product, horizontal, full, style, priceColor, imageStyle } = this.props; - const imageStyles = [styles.image, full ? styles.fullImage : styles.horizontalImage, imageStyle]; - - return ( - - navigation.navigate('Pro', { product: product })}> - - - - - navigation.navigate('Pro', { product: product })}> - - {product.title} - ${product.price} - - - - ); - } -} - -export default withNavigation(Product); - -const styles = StyleSheet.create({ - product: { - backgroundColor: theme.COLORS.WHITE, - marginVertical: theme.SIZES.BASE, - borderWidth: 0, - minHeight: 114, - }, - productTitle: { - flex: 1, - flexWrap: 'wrap', - paddingBottom: 6, - }, - productDescription: { - padding: theme.SIZES.BASE / 2, - }, - imageContainer: { - elevation: 1, - }, - image: { - borderRadius: 3, - marginHorizontal: theme.SIZES.BASE / 2, - marginTop: -16, - }, - horizontalImage: { - height: 122, - width: 'auto', - }, - fullImage: { - height: 215, - width: width - theme.SIZES.BASE * 3, - }, - shadow: { - shadowColor: theme.COLORS.BLACK, - shadowOffset: { width: 0, height: 2 }, - shadowRadius: 4, - shadowOpacity: 0.1, - elevation: 2, - }, -}); \ No newline at end of file diff --git a/components/Select.js b/components/Select.js deleted file mode 100644 index c859e3f..0000000 --- a/components/Select.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import { StyleSheet } from 'react-native'; -import ModalDropdown from 'react-native-modal-dropdown'; -import { Block, Text, Icon,theme } from 'galio-framework'; - -export default class DropDown extends React.Component { - state = { - value: 1, - } - - handleOnSelect = (index, value) => { - const { onSelect } = this.props; - - this.setState({ value: value }); - onSelect && onSelect(index, value); - } - - render() { - const { onSelect, style, ...props } = this.props; - return ( - - - {this.state.value} - - - - ) - } -} - -const styles = StyleSheet.create({ - qty: { - width: 100, - backgroundColor: '#DCDCDC', - paddingHorizontal: 16, - paddingTop: 10, - paddingBottom:9.5, - borderRadius: 3, - shadowColor: "rgba(0, 0, 0, 0.1)", - shadowOffset: { width: 0, height: 2 }, - shadowRadius: 4, - shadowOpacity: 1, - }, - dropdown: { - marginTop: 8, - marginLeft: -16, - width: 100, - }, -}); diff --git a/components/Switch.js b/components/Switch.js deleted file mode 100644 index 6effca5..0000000 --- a/components/Switch.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { Switch, Platform } from 'react-native'; - -import materialTheme from '../constants/Theme'; - -export default class MkSwitch extends React.Component { - render() { - const { value, ...props } = this.props; - const thumbColor = Platform.OS === 'ios' ? null : - Platform.OS === 'android' && value ? materialTheme.COLORS.SWITCH_ON : materialTheme.COLORS.SWITCH_OFF; - - return ( - - ); - } -} \ No newline at end of file diff --git a/components/Tabs.js b/components/Tabs.js deleted file mode 100644 index 2ecd078..0000000 --- a/components/Tabs.js +++ /dev/null @@ -1,150 +0,0 @@ -import React from 'react'; -import { StyleSheet, Dimensions, FlatList, Animated } from 'react-native'; -import { Block, theme } from 'galio-framework'; - -const { width } = Dimensions.get('screen'); -import materialTheme from '../constants/Theme'; - -const defaultMenu = [ - { id: 'popular', title: 'Popular', }, - { id: 'beauty', title: 'Beauty', }, - { id: 'cars', title: 'Cars', }, - { id: 'motocycles', title: 'Motocycles', }, -]; - -export default class MenuHorizontal extends React.Component { - static defaultProps = { - data: defaultMenu, - initialIndex: null, - } - - state = { - active: null, - } - - componentDidMount() { - const { initialIndex } = this.props; - initialIndex && this.selectMenu(initialIndex); - } - - animatedValue = new Animated.Value(1); - - animate() { - this.animatedValue.setValue(0); - - Animated.timing(this.animatedValue, { - toValue: 1, - duration: 300, - // useNativeDriver: true, // color not supported - }).start() - } - - menuRef = React.createRef(); - - onScrollToIndexFailed = () => { - this.menuRef.current.scrollToIndex({ - index: 0, - viewPosition: 0.5 - }); - } - - selectMenu = (id) => { - this.setState({ active: id }); - - this.menuRef.current.scrollToIndex({ - index: this.props.data.findIndex(item => item.id === id), - viewPosition: 0.5 - }); - - this.animate(); - this.props.onChange && this.props.onChange(id); - } - - renderItem = (item) => { - const isActive = this.state.active === item.id; - - const textColor = this.animatedValue.interpolate({ - inputRange: [0, 1], - outputRange: [materialTheme.COLORS.MUTED, isActive ? materialTheme.COLORS.ACTIVE : materialTheme.COLORS.MUTED], - extrapolate: 'clamp', - }); - - const width = this.animatedValue.interpolate({ - inputRange: [0, 1], - outputRange: ['0%', isActive ? '100%' : '0%'], - extrapolate: 'clamp', - }); - - return ( - - this.selectMenu(item.id)}> - {item.title} - - - - ) - } - - renderMenu = () => { - const { data, ...props } = this.props; - - return ( - item.id} - showsHorizontalScrollIndicator={false} - onScrollToIndexFailed={this.onScrollToIndexFailed} - renderItem={({ item }) => this.renderItem(item)} - contentContainerStyle={styles.menu} - /> - ) - } - - render() { - return ( - - {this.renderMenu()} - - ) - } -} - -const styles = StyleSheet.create({ - container: { - width: width, - backgroundColor: theme.COLORS.WHITE, - zIndex: 2, - }, - shadow: { - shadowColor: theme.COLORS.BLACK, - shadowOffset: { width: 0, height: 2 }, - shadowRadius: 8, - shadowOpacity: 0.2, - elevation: 4, - }, - menu: { - paddingHorizontal: theme.SIZES.BASE * 2.5, - paddingTop: 8, - paddingBottom: 0, - }, - titleContainer: { - alignItems: 'center', - }, - menuTitle: { - fontWeight: '300', - fontSize: 16, - lineHeight: 28, - // paddingBottom: 8, - paddingHorizontal: 16, - color: materialTheme.COLORS.MUTED - }, -}); diff --git a/components/index.js b/components/index.js deleted file mode 100644 index e683466..0000000 --- a/components/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import Button from './Button'; -import Select from './Select'; -import Icon from './Icon'; -import Tabs from './Tabs'; -import Product from './Product'; -import Drawer from './Drawer'; -import Header from './Header'; -import Switch from './Switch'; - -export { - Button, - Select, - Icon, - Tabs, - Product, - Drawer, - Header, - Switch, -}; \ No newline at end of file diff --git a/constants/Images.js b/constants/Images.js deleted file mode 100644 index 3eded2a..0000000 --- a/constants/Images.js +++ /dev/null @@ -1,26 +0,0 @@ -const Onboarding = 'https://images.unsplash.com/photo-1505995433366-e12047f3f144?fit=crop&w=840&q=80'; -const Pro = 'https://images.unsplash.com/photo-1485796826113-174aa68fd81b?fit=crop&w=840&q=80'; -const Products = { - 'Accessories': 'https://source.unsplash.com//l1MCA0VyNrk/840x840', -}; - -const Profile = 'https://images.unsplash.com/photo-1512529920731-e8abaea917a5?fit=crop&w=840&q=80'; -const Avatar = 'https://images.unsplash.com/photo-1518725522904-4b3939358342?fit=crop&w=210&q=80'; - -const Viewed = [ - 'https://images.unsplash.com/photo-1508264443919-15a31e1d9c1a?fit=crop&w=240&q=80', - 'https://images.unsplash.com/photo-1497034825429-c343d7c6a68f?fit=crop&w=240&q=80', - 'https://images.unsplash.com/photo-1487376480913-24046456a727?fit=crop&w=240&q=80', - 'https://images.unsplash.com/photo-1494894194458-0174142560c0?fit=crop&w=240&q=80', - 'https://images.unsplash.com/photo-1500462918059-b1a0cb512f1d?fit=crop&w=240&q=80', - 'https://images.unsplash.com/photo-1542068829-1115f7259450?fit=crop&w=240&q=80', -]; - -export default { - Onboarding, - Pro, - Products, - Profile, - Viewed, - Avatar, -} \ No newline at end of file diff --git a/constants/Theme.js b/constants/Theme.js deleted file mode 100644 index ccb1320..0000000 --- a/constants/Theme.js +++ /dev/null @@ -1,27 +0,0 @@ -export default { - COLORS: { - DEFAULT: '#DCDCDC', - PRIMARY: '#9C26B0', - LABEL: '#FE2472', - INFO: '#00BCD4', - ERROR: '#F44336', - SUCCESS: '#4CAF50', - WARNING: '#FF9800', - MUTED: '#979797', - INPUT: '#DCDCDC', - ACTIVE: '#9C26B0', - BUTTON_COLOR: '#9C26B0', - PLACEHOLDER: '#9FA5AA', - SWITCH_ON: '#9C26B0', - SWITCH_OFF: '#D4D9DD', - GRADIENT_START: '#6B24AA', - GRADIENT_END: '#AC2688', - PRICE_COLOR: '#EAD5FB', - BORDER_COLOR: '#E7E7E7', - BLOCK: '#E7E7E7', - ICON: '#4A4A4A', - }, - SIZES: { - BLOCK_SHADOW_RADIUS: 2, - } -}; \ No newline at end of file diff --git a/constants/index.js b/constants/index.js deleted file mode 100644 index a3fce2d..0000000 --- a/constants/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import Images from './Images'; -import products from './products'; -import materialTheme from './Theme'; -import utils from './utils'; - -export { - Images, - products, - materialTheme, - utils, -} \ No newline at end of file diff --git a/constants/products.js b/constants/products.js deleted file mode 100644 index 3d1a710..0000000 --- a/constants/products.js +++ /dev/null @@ -1,29 +0,0 @@ -export default [ - { - title: 'Hardly Anything Takes More Coura...', - image: 'https://source.unsplash.com/dS2hi__ZZMk/840x840', - price: 180, - horizontal: true, - }, - { - title: 'Find the cheapest deals on our range...', - image: 'https://source.unsplash.com/tb6ulgGY5Zc/840x840', - price: 220, - }, - { - title: 'Adidas Tango Terry Jersey', - image: 'https://source.unsplash.com/YHbcum51JB0/840x840', - price: 40, - }, - { - title: 'Internet of Things (IoT) is Here to Stay', - image: 'https://source.unsplash.com/I7BSOoPa5hM/840x840', - price: 188, - horizontal: true, - }, - { - title: 'Coffee - A Drop of Happiness in a Cup', - image: 'https://source.unsplash.com/Ws4wd-vJ9M0/840x840', - price: 180, - }, -]; diff --git a/constants/utils.js b/constants/utils.js deleted file mode 100644 index 561e5cf..0000000 --- a/constants/utils.js +++ /dev/null @@ -1,6 +0,0 @@ -import { Platform, StatusBar } from 'react-native'; -import { theme } from 'galio-framework'; - -export const StatusHeight = StatusBar.currentHeight; -export const HeaderHeight = (theme.SIZES.BASE * 3.5 + (StatusHeight || 0)); -export const iPhoneX = () => Platform.OS === 'ios' && (height === 812 || width === 812); \ No newline at end of file diff --git a/design/home-before-after.html b/design/home-before-after.html new file mode 100644 index 0000000..79c1e69 --- /dev/null +++ b/design/home-before-after.html @@ -0,0 +1,196 @@ + + + + + +Bojo — strona główna: PRZED / PO + + + + + + + + + +
+

Bojo — strona główna

+

Porównanie warstwy wizualnej. Mapa zastąpiona placeholderem. Paleta robocza: zielony #15803d, limonka #84cc16, tekst #0f172a, tło #f8faf9.

+ +
+ + +
+
PRZED
+ + +
+
+

Następny mecz zaczyna się tutaj.

+

Boiska, mecze i gracze w Poznaniu i okolicach — w jednym miejscu.

+
+ + +
+
+
+ + +
+

Setki boisk. Poznań i okolice.

+

Kliknij boisko, żeby zobaczyć dostępność i aktywne gry.

+
+
[ Mapa ]
+
+
+ + +
+
+

Jak to działa?

+

Mniej organizowania, więcej grania.

+
+ +
+
+
+
📍
+ 1 +
+

Wybierz boisko i termin.

+
+

Setki boisk w Poznaniu — filtruj po sporcie.

+
+
+
+
+
👥
+ 2 +
+

Udostępnij link. Gracze potwierdzają jednym kliknięciem.

+
+

Zero rejestracji dla zaproszonych. Widzisz kto idzie.

+
+
+
+
+
+ 3 +
+

Ogłoś mecz publicznie — dograją się gracze z okolicy.

+
+
+
+
+
+
+ + +
+
PO
+ + +
+
+
+ + + Poznań i okolice + +

Następny mecz
zaczyna się tutaj.

+

Boiska, mecze i gracze w jednym miejscu. Zbierz skład bez przekopywania się przez grupy na Facebooku.

+
+ + +
+
+
+
+ + +
+
+

Setki boisk. Poznań i okolice.

+

Kliknij boisko, żeby zobaczyć dostępność i aktywne gry.

+
+
+
[ Mapa ]
+
+

Otwórz pełną mapę →

+
+ + +
+
+
+

Jak to działa?

+

Mniej organizowania, więcej grania.

+
+
+
+ +
📍1
+

Wybierz boisko i termin.

+

Setki boisk w Poznaniu — filtruj po sporcie.

+
+
+ +
👥2
+

Udostępnij link. Gracze potwierdzają jednym kliknięciem.

+

Zero rejestracji dla zaproszonych. Widzisz kto idzie.

+
+
+ +
3
+

Ogłoś mecz publicznie — dograją się gracze z okolicy.

+

Brakuje wam dwóch do składu? Ktoś z okolicy dołączy.

+
+
+
+
+
+ +
+
+ + + diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 0000000..521a9f7 --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..380dd01 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,21 @@ +FROM node:20-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +RUN apk add --no-cache libc6-compat +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm ci + +# Development server +FROM base AS dev +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +EXPOSE 3000 +ENV NODE_ENV=development +ENV NEXT_TELEMETRY_DISABLED=1 + +CMD ["npm", "run", "dev"] diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs new file mode 100644 index 0000000..b7fc0dd --- /dev/null +++ b/frontend/next.config.mjs @@ -0,0 +1,14 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: '*.supabase.co', + }, + ], + }, +}; + +export default nextConfig; diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..a2199f4 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,8167 @@ +{ + "name": "boiska-poznan-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "boiska-poznan-frontend", + "version": "0.1.0", + "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@supabase/supabase-js": "^2.43.4", + "@types/leaflet.markercluster": "^1.5.6", + "clsx": "^2.1.1", + "date-fns": "^3.6.0", + "leaflet": "^1.9.4", + "leaflet.markercluster": "^1.5.3", + "lucide-react": "^0.378.0", + "next": "14.2.3", + "next-themes": "^0.4.6", + "react": "^18", + "react-dom": "^18", + "react-leaflet": "^4.2.1" + }, + "devDependencies": { + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@types/leaflet": "^1.9.12", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "@vitejs/plugin-react": "^6.0.2", + "autoprefixer": "^10", + "eslint": "^8", + "eslint-config-next": "14.2.3", + "jsdom": "^29.1.1", + "postcss": "^8", + "tailwindcss": "^3.4.3", + "typescript": "^5", + "vitest": "^4.1.8" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "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/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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.2.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", + "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", + "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.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz", + "integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==", + "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.2.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.4", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz", + "integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==", + "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/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "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/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/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "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/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.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/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@next/env": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", + "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz", + "integrity": "sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "10.3.10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", + "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "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.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@react-leaflet/core": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", + "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "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/@rushstack/eslint-patch": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", + "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", + "dev": true, + "license": "MIT" + }, + "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.106.2", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.106.2.tgz", + "integrity": "sha512-VcAjUErkHkhC5Jaf+g/G1qbkQrFh8edaCdHa7pxJmHUjkWKjT7UnYCtPA89XV0N0GIYRkEqJZw5V62CtOxTmBQ==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.106.2", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.106.2.tgz", + "integrity": "sha512-oRnr0QrL8H+zTO1YyQ1QjiHZU/957jvubbxSJTUm2XLAgzoGGV9Tahfyd+uvLsBLRVmXLtpU3oyCjdQIvkGMOA==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/phoenix": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.2.tgz", + "integrity": "sha512-YSAGnmDAfuleFCVt3CeurQZAhxRfXWeZIIkwp7NhYzQ1UwW6ePSnzsFAiUm/mbCkfoCf70QQHKW/K6RKh52a4A==", + "license": "MIT" + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.106.2", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.106.2.tgz", + "integrity": "sha512-tDOzyPgp9pIRMR2x6C9+uDSJrnXSzxLtt3d7nC+Lrsy3jnJDHYfdQC/xcRyhJE/TOBJ0heSqRKR3UmejDjZxsw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.106.2", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.106.2.tgz", + "integrity": "sha512-LdRGT7DNhyZkPjubUv5bSdAZ0jSEX8wTHvx7htj7+K59TOZRvz4TuQK7tL2RWxyIZVeFMRluL04SzWS61rKnUA==", + "license": "MIT", + "dependencies": { + "@supabase/phoenix": "^0.4.2", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.106.2", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.106.2.tgz", + "integrity": "sha512-xgKCSYuev1YarV+iVqr+zlfgSyremnJtn8T0NCT8L4XmMv1CLtESc0Q6kNp8+mKWdX/8ND0nzm7OMKx08kwNAw==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.106.2", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.106.2.tgz", + "integrity": "sha512-2/RZ/1fmJx/MRSEDG2Xk8+J4JVk5clM9V0uSI6kUTrcS32KA89DtqI5RUOC9r6mzY3WBC9qexLjssIHjbLyVJA==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.106.2", + "@supabase/functions-js": "2.106.2", + "@supabase/postgrest-js": "2.106.2", + "@supabase/realtime-js": "2.106.2", + "@supabase/storage-js": "2.106.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "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.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "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/leaflet": { + "version": "1.9.21", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz", + "integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/leaflet.markercluster": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.5.6.tgz", + "integrity": "sha512-I7hZjO2+isVXGYWzKxBp8PsCzAYCJBc29qBdFpquOCkS7zFDqUsUvkEOyQHedsk/Cy5tocQzf+Ndorm5W9YKTQ==", + "license": "MIT", + "dependencies": { + "@types/leaflet": "^1.9" + } + }, + "node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.30", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.30.tgz", + "integrity": "sha512-3ek6mwJL5/VBewBcY4S66cqlCtK3qi4WIq37Z0m/NHw1hjhI7274Mx1qz/+ggSzyBCOEf7eHjBN6INjPAWYfYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "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/visitor-keys": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", + "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", + "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", + "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.8", + "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.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", + "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", + "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.8", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", + "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", + "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", + "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "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/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "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/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "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-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "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/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.12.0.tgz", + "integrity": "sha512-FTavr/7Ba0IptwGOPxnQvdyW2tAsdLBMTBXz7rKH6xJ2skpyxpBxyHkDdBs4lf69yRqYpkqCdfhnwS8YULGOmg==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz", + "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "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.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "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==", + "dev": true, + "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==", + "dev": true, + "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-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "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==", + "dev": true, + "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/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "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-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/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "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/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/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==", + "dev": true, + "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-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "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/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "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/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.364", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", + "integrity": "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", + "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "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", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-next": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.3.tgz", + "integrity": "sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "14.2.3", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "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.13.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.13.0.tgz", + "integrity": "sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==", + "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-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-import/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/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "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.0.0-canary-7118f5dd7-20230705", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", + "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", + "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" + } + }, + "node_modules/eslint-plugin-react/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/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/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/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "dev": true, + "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/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "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-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "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.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "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", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "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/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "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/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "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/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/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/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "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/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "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==", + "dev": 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.", + "dev": true, + "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==", + "dev": true, + "license": "ISC" + }, + "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/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-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-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "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-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==", + "dev": true, + "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==", + "dev": true, + "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-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==", + "dev": true, + "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-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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/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==", + "dev": true, + "license": "ISC" + }, + "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": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@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.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "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.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "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": "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/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/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, + "node_modules/leaflet.markercluster": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz", + "integrity": "sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA==", + "license": "MIT", + "peerDependencies": { + "leaflet": "^1.3.1" + } + }, + "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/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "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" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "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==", + "dev": true, + "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.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/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": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/lucide-react": { + "version": "0.378.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.378.0.tgz", + "integrity": "sha512-u6EPU8juLUk9ytRcyapkWI18epAv3RU+6+TC23ivjR0e+glWKBobFeSgRwOIJihzktILQuy6E0E80P2jVTDR5g==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "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/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "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/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "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/next": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", + "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", + "deprecated": "This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details.", + "license": "MIT", + "dependencies": { + "@next/env": "14.2.3", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.3", + "@next/swc-darwin-x64": "14.2.3", + "@next/swc-linux-arm64-gnu": "14.2.3", + "@next/swc-linux-arm64-musl": "14.2.3", + "@next/swc-linux-x64-gnu": "14.2.3", + "@next/swc-linux-x64-musl": "14.2.3", + "@next/swc-win32-arm64-msvc": "14.2.3", + "@next/swc-win32-ia32-msvc": "14.2.3", + "@next/swc-win32-x64-msvc": "14.2.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "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.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "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-exports-info/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "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==", + "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", + "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/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "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/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==", + "dev": true, + "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/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/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "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.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "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/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "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/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/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "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/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "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/react-leaflet": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", + "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^2.1.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": 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/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/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "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/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/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/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "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", + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "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": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "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/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.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "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/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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "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": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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/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/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "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/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "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-width-cjs/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "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==", + "dev": 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/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "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/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.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==", + "dev": true, + "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/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "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/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "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.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/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/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.4.2", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.2.tgz", + "integrity": "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.2" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.2.tgz", + "integrity": "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "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": "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/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "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==", + "dev": true, + "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/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-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "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==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "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": "7.27.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.0.tgz", + "integrity": "sha512-+t2Z/GwkZQDtu00813aP66ygViGtPHKhhoFZpQKpKrE+9jIgES+Zw+mFNaDWOVRKiuJjuqKHzD3B1sfGg8+ZOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "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==", + "dev": true, + "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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" + }, + "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.18", + "esbuild": "^0.27.0 || ^0.28.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/vitest": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", + "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "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.1.0", + "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.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", + "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/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "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/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/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/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": "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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "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.21", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.21.tgz", + "integrity": "sha512-zbRA8cVm6io/d5W8uIe2hblzN76/Wm3v/yiythQvr+dpBWeqhPSWIDNj4zOyHi4zKbMK6DN34Xsr9jPHJERAEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "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/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": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "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==", + "dev": true, + "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/wrap-ansi-cjs/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "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/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-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==", + "dev": true, + "license": "ISC" + }, + "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/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/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..643d941 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,47 @@ +{ + "name": "boiska-poznan-frontend", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@supabase/supabase-js": "^2.43.4", + "@types/leaflet.markercluster": "^1.5.6", + "clsx": "^2.1.1", + "date-fns": "^3.6.0", + "leaflet": "^1.9.4", + "leaflet.markercluster": "^1.5.3", + "lucide-react": "^0.378.0", + "next": "14.2.3", + "next-themes": "^0.4.6", + "react": "^18", + "react-dom": "^18", + "react-leaflet": "^4.2.1" + }, + "devDependencies": { + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@types/leaflet": "^1.9.12", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "@vitejs/plugin-react": "^6.0.2", + "autoprefixer": "^10", + "eslint": "^8", + "eslint-config-next": "14.2.3", + "jsdom": "^29.1.1", + "postcss": "^8", + "tailwindcss": "^3.4.3", + "typescript": "^5", + "vitest": "^4.1.8" + } +} diff --git a/frontend/postcss.config.mjs b/frontend/postcss.config.mjs new file mode 100644 index 0000000..b703d72 --- /dev/null +++ b/frontend/postcss.config.mjs @@ -0,0 +1,9 @@ +/** @type {import('postcss').ProcessOptions} */ +const config = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; + +export default config; diff --git a/frontend/public/mockups/mockup-1-lista-gier.png b/frontend/public/mockups/mockup-1-lista-gier.png new file mode 100644 index 0000000..6a9a125 Binary files /dev/null and b/frontend/public/mockups/mockup-1-lista-gier.png differ diff --git a/frontend/public/mockups/mockup-2-szczegoly-meczu.png b/frontend/public/mockups/mockup-2-szczegoly-meczu.png new file mode 100644 index 0000000..d6d2a80 Binary files /dev/null and b/frontend/public/mockups/mockup-2-szczegoly-meczu.png differ diff --git a/frontend/public/mockups/mockup-3-mapa-boisk.png b/frontend/public/mockups/mockup-3-mapa-boisk.png new file mode 100644 index 0000000..065601a Binary files /dev/null and b/frontend/public/mockups/mockup-3-mapa-boisk.png differ diff --git a/frontend/public/mockups/mockup-4-profil-gracza.png b/frontend/public/mockups/mockup-4-profil-gracza.png new file mode 100644 index 0000000..2c32fb4 Binary files /dev/null and b/frontend/public/mockups/mockup-4-profil-gracza.png differ diff --git a/frontend/public/mockups/screen-1-mecze.png b/frontend/public/mockups/screen-1-mecze.png new file mode 100644 index 0000000..9515b92 Binary files /dev/null and b/frontend/public/mockups/screen-1-mecze.png differ diff --git a/frontend/public/mockups/screen-2-moje.png b/frontend/public/mockups/screen-2-moje.png new file mode 100644 index 0000000..81bf653 Binary files /dev/null and b/frontend/public/mockups/screen-2-moje.png differ diff --git a/frontend/public/mockups/screen-2-nowe.png b/frontend/public/mockups/screen-2-nowe.png new file mode 100644 index 0000000..d8b408b Binary files /dev/null and b/frontend/public/mockups/screen-2-nowe.png differ diff --git a/frontend/public/mockups/screen-3-mapa.png b/frontend/public/mockups/screen-3-mapa.png new file mode 100644 index 0000000..d660ca2 Binary files /dev/null and b/frontend/public/mockups/screen-3-mapa.png differ diff --git a/frontend/public/mockups/screen-4-moje.png b/frontend/public/mockups/screen-4-moje.png new file mode 100644 index 0000000..896ce62 Binary files /dev/null and b/frontend/public/mockups/screen-4-moje.png differ diff --git a/frontend/public/mockups/screen-4-profil.png b/frontend/public/mockups/screen-4-profil.png new file mode 100644 index 0000000..81140ab Binary files /dev/null and b/frontend/public/mockups/screen-4-profil.png differ diff --git a/frontend/public/poznan-satellite.jpg b/frontend/public/poznan-satellite.jpg new file mode 100644 index 0000000..09a4eab Binary files /dev/null and b/frontend/public/poznan-satellite.jpg differ diff --git a/frontend/public/sports/beach-volleyball.svg b/frontend/public/sports/beach-volleyball.svg new file mode 100644 index 0000000..09210ab --- /dev/null +++ b/frontend/public/sports/beach-volleyball.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/scripts/seed-demo-events.mjs b/frontend/scripts/seed-demo-events.mjs new file mode 100644 index 0000000..a91f7e6 --- /dev/null +++ b/frontend/scripts/seed-demo-events.mjs @@ -0,0 +1,180 @@ +// Seed DEMO matches for Bojo — wipes all events, then creates a realistic set +// across dates/sports, each filled with the test players (+ named guests). +// +// Goal: show what the app looks like once it's busy, and have something to demo. +// +// ── Run it ─────────────────────────────────────────────────────────────────── +// 1) First create the test players (once): node scripts/seed-test-users.mjs +// 2) Then: +// cd frontend +// SUPABASE_URL="https://YOURPROJECT.supabase.co" \ +// SUPABASE_SERVICE_ROLE_KEY="eyJ...service_role..." \ +// node scripts/seed-demo-events.mjs +// +// ⚠ This DELETES every existing event (and their participants/comments via +// cascade). Only run it on a test/demo project. + +import { createClient } from '@supabase/supabase-js'; + +const SUPABASE_URL = process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL; +const SERVICE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY; + +if (!SUPABASE_URL || !SERVICE_KEY) { + console.error('\n✗ Set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY (Project Settings → API).\n'); + process.exit(1); +} + +const supabase = createClient(SUPABASE_URL, SERVICE_KEY, { + auth: { autoRefreshToken: false, persistSession: false }, +}); + +// ── helpers ────────────────────────────────────────────────────────────────── +const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; +const shuffle = (arr) => arr.map((v) => [Math.random(), v]).sort((a, b) => a[0] - b[0]).map(([, v]) => v); +const dateIn = (days) => { + const d = new Date(); + d.setDate(d.getDate() + days); + return d.toISOString().slice(0, 10); +}; + +// Named guests so full rosters look real (these have no account → show initials). +const GUEST_NAMES = [ + 'Tomasz Lis', 'Paweł Krawczyk', 'Adrian Mazur', 'Bartosz Kaczmarek', 'Damian Wróbel', + 'Sebastian Pawlak', 'Grzegorz Adamczyk', 'Marcin Dudek', 'Łukasz Sikora', 'Rafał Baran', + 'Krzysztof Michalski', 'Dawid Olszewski', 'Patryk Stępień', 'Hubert Górski', 'Oskar Witkowski', + 'Filip Rutkowski', 'Wojciech Zawadzki', 'Konrad Sadowski', 'Igor Jakubowski', 'Kamil Walczak', +]; + +// ── demo matches (Poznań venues as custom locations) ───────────────────────── +const EVENTS = [ + { title: 'Środowa piłka 7v7', sport: 'piłka nożna', max: 14, days: 1, time: '19:00', end: '21:00', cost: 1500, + field: 'Orlik Rataje', lat: 52.3889, lng: 16.9560, + desc: 'Regularna środowa gierka na sztucznej trawie. Gramy do dwóch straconych, rotacja składów. Poziom luźny — liczy się dobra zabawa i pełne składy. Buty turfy lub lanki.' }, + { title: 'Niedzielne 6v6 — hala', sport: 'piłka nożna', max: 12, days: 3, time: '11:00', end: '12:30', cost: 1800, + field: 'Hala Politechnika', lat: 52.4020, lng: 16.9490, + desc: 'Gramy w hali 2×30 min, równe składy losowane na miejscu. Obuwie na halę obowiązkowe (jasna podeszwa). Kameralna, stała ekipa — dobierzemy brakujących.' }, + { title: 'Piłka 7v7 — Wilda', sport: 'piłka nożna', max: 14, days: 2, time: '18:30', end: '20:00', cost: 0, + field: 'Orlik Wilda', lat: 52.3870, lng: 16.9200, + desc: 'Za darmo, Orlik miejski. Spokojne tempo, mile widziani początkujący. Zbieramy pełne 7 na 7 — wpadaj jak brakuje Ci gry.' }, + { title: 'Poranna piłka 7v7', sport: 'piłka nożna', max: 14, days: 5, time: '09:00', end: '10:30', cost: 1200, + field: 'Golęcin — kompleks boisk', lat: 52.4350, lng: 16.8950, + desc: 'Sobotni poranek, świeże nogi. Gramy szybkie mecze do gola, zmiana co 10 min. Po wszystkim kawa w klubie.' }, + { title: 'Wieczorne 6v6', sport: 'piłka nożna', max: 12, days: 7, time: '20:00', end: '21:30', cost: 1600, + field: 'Orlik Grunwald', lat: 52.3990, lng: 16.8800, + desc: 'Mecz pod światłami. Równe drużyny, gramy do 7 bramek lub do końca czasu. Stała grupa, szukamy kilku do kompletu.' }, + { title: 'Piłka 7v7 — Sołacz', sport: 'piłka nożna', max: 14, days: 9, time: '18:00', end: '19:30', cost: 1000, + field: 'Park Sołacki — boisko', lat: 52.4250, lng: 16.9050, + desc: 'Trawa naturalna, klimatyczne miejsce. Luźna gra dla każdego poziomu. Bramkarz mile widziany!' }, + + { title: 'Siatkówka plażowa 4v4', sport: 'siatkówka plażowa', max: 8, days: 2, time: '17:00', end: '19:00', cost: 800, + field: 'Malta — boisko piaszczyste', lat: 52.4030, lng: 16.9760, + desc: 'Piasek nad Maltą, dwa boiska. Gramy do dwóch wygranych setów, rotacja par. Poziom rekreacyjny, woda i dobry humor obowiązkowe.' }, + { title: 'Plażówka po pracy', sport: 'siatkówka plażowa', max: 8, days: 6, time: '18:30', end: '20:30', cost: 0, + field: 'Plaża Rataje', lat: 52.3850, lng: 16.9620, + desc: 'Luźne granie na piachu po pracy. Miksujemy składy, gramy krótkie sety. Za darmo — wpadaj uzupełnić ekipę.' }, + + { title: 'Streetball 3x3', sport: 'koszykówka', max: 6, days: 4, time: '19:00', end: '20:30', cost: 0, + field: 'Łęgi Dębińskie — boisko', lat: 52.3870, lng: 16.9080, + desc: 'Koszykówka uliczna do 21 punktów, winner stays. Jedna obręcz, szybkie zmiany. Za darmo, wpadaj na parkiet.' }, + { title: 'Kosz 5v5 — pełne boisko', sport: 'koszykówka', max: 10, days: 8, time: '20:00', end: '21:30', cost: 1400, + field: 'Hala Politechnika', lat: 52.4020, lng: 16.9490, + desc: 'Pełnowymiarowe granie 5 na 5 w hali. Gramy na czas z sędzią-amatorem. Średni poziom, dobierzemy brakujących do kompletu.' }, +]; + +async function run() { + // 1) Players to fill rosters with. + const { data: testUsers, error: uErr } = await supabase + .from('profiles') + .select('id, display_name') + .like('email', 'test%@example.com'); + if (uErr) { console.error('✗ Could not read test users:', uErr.message); process.exit(1); } + if (!testUsers || testUsers.length === 0) { + console.error('✗ No test users found. Run `node scripts/seed-test-users.mjs` first.'); + process.exit(1); + } + console.log(`Found ${testUsers.length} test players.`); + + // 2) Wipe existing events (cascades participants/comments/results). + const { error: dErr } = await supabase + .from('events') + .delete() + .neq('id', '00000000-0000-0000-0000-000000000000'); + if (dErr) { console.error('✗ Could not delete events:', dErr.message); process.exit(1); } + console.log('Deleted all existing events.'); + + // 3) Create the demo events, each filled. + let made = 0; + for (let i = 0; i < EVENTS.length; i++) { + const e = EVENTS[i]; + const organizer = testUsers[i % testUsers.length]; + + const { data: row, error: evErr } = await supabase + .from('events') + .insert({ + organizer_id: organizer.id, + organizer_name: organizer.display_name || 'Organizator', + sport: e.sport, + field_id: null, + field_name: e.field, + lat: e.lat, + lng: e.lng, + title: e.title, + description: e.desc, + event_date: dateIn(e.days), + event_time: e.time, + end_time: e.end, + max_players: e.max, + external_count: 0, + visibility: 'public', + team_mode: 'brak', + track_results: e.sport === 'piłka nożna', + cost_grosz: e.cost, + invite_only: false, + }) + .select('id') + .single(); + + if (evErr) { console.error(`✗ ${e.title}:`, evErr.message); continue; } + + // Fill: organizer + a few real players (with avatars), then named guests. + const target = Math.max(2, e.max - randInt(0, 2)); // mostly full, 0–2 free + const others = shuffle(testUsers.filter((u) => u.id !== organizer.id)); + const realCount = Math.min(target - 1, others.length, randInt(3, 8)); + + const rows = []; + // organizer first + rows.push({ user_id: organizer.id, name: organizer.display_name || 'Organizator', is_guest: false }); + for (let k = 0; k < realCount; k++) { + rows.push({ user_id: others[k].id, name: others[k].display_name || 'Gracz', is_guest: false }); + } + // guests fill the rest + const guests = shuffle(GUEST_NAMES); + let g = 0; + while (rows.length < target && g < guests.length) { + rows.push({ user_id: null, name: guests[g++], is_guest: true }); + } + + // One goalkeeper for football matches (≈70% of the time). + const gkIndex = e.sport === 'piłka nożna' && Math.random() < 0.7 ? randInt(0, rows.length - 1) : -1; + + const participantRows = rows.map((r, idx) => ({ + event_id: row.id, + user_id: r.user_id, + name: r.name, + is_guest: r.is_guest, + is_reserve: false, + is_goalkeeper: idx === gkIndex, + status: 'potwierdzony', + })); + + const { error: pErr } = await supabase.from('event_participants').insert(participantRows); + if (pErr) { console.error(` ⚠ participants for "${e.title}":`, pErr.message); } + + made++; + console.log(`✓ ${e.title.padEnd(28)} ${rows.length}/${e.max} graczy · ${dateIn(e.days)} ${e.time}`); + } + + console.log(`\nDone — created ${made} demo matches. Open the app to see a busy feed.\n`); +} + +run().catch((err) => { console.error('\nFatal:', err.message || err); process.exit(1); }); diff --git a/frontend/scripts/seed-test-users.mjs b/frontend/scripts/seed-test-users.mjs new file mode 100644 index 0000000..ebc6177 --- /dev/null +++ b/frontend/scripts/seed-test-users.mjs @@ -0,0 +1,124 @@ +// Seed test accounts for Bojo — realistic Polish names + avatar photos. +// +// Creates ~10 confirmed accounts you can log into straight away (no email +// confirmation needed). Safe to re-run: existing accounts are updated, not +// duplicated. +// +// ── How to run ────────────────────────────────────────────────────────────── +// cd frontend +// SUPABASE_URL="https://YOURPROJECT.supabase.co" \ +// SUPABASE_SERVICE_ROLE_KEY="eyJ...service_role..." \ +// node scripts/seed-test-users.mjs +// +// Get the service_role key in Supabase: Project Settings → API → service_role. +// NEVER commit it or expose it in the browser — it bypasses all security. +// +// Optional: TEST_PASSWORD="..." to override the default password. + +import { createClient } from '@supabase/supabase-js'; + +const SUPABASE_URL = + process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL; +const SERVICE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY; +const PASSWORD = process.env.TEST_PASSWORD || 'test1234'; + +if (!SUPABASE_URL || !SERVICE_KEY) { + console.error( + '\n✗ Missing env vars.\n' + + ' Set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY, e.g.:\n\n' + + ' SUPABASE_URL="https://xxx.supabase.co" \\\n' + + ' SUPABASE_SERVICE_ROLE_KEY="eyJ..." \\\n' + + ' node scripts/seed-test-users.mjs\n', + ); + process.exit(1); +} + +const supabase = createClient(SUPABASE_URL, SERVICE_KEY, { + auth: { autoRefreshToken: false, persistSession: false }, +}); + +// 10 test players — realistic Polish names + portrait photos (randomuser.me). +const PEOPLE = [ + { email: 'test1@example.com', name: 'Jakub Kowalski', avatar: 'https://randomuser.me/api/portraits/men/32.jpg' }, + { email: 'test2@example.com', name: 'Mateusz Nowak', avatar: 'https://randomuser.me/api/portraits/men/45.jpg' }, + { email: 'test3@example.com', name: 'Piotr Wiśniewski', avatar: 'https://randomuser.me/api/portraits/men/12.jpg' }, + { email: 'test4@example.com', name: 'Kacper Wójcik', avatar: 'https://randomuser.me/api/portraits/men/76.jpg' }, + { email: 'test5@example.com', name: 'Michał Kamiński', avatar: 'https://randomuser.me/api/portraits/men/8.jpg' }, + { email: 'test6@example.com', name: 'Zuzanna Lewandowska', avatar: 'https://randomuser.me/api/portraits/women/44.jpg' }, + { email: 'test7@example.com', name: 'Julia Zielińska', avatar: 'https://randomuser.me/api/portraits/women/68.jpg' }, + { email: 'test8@example.com', name: 'Maja Szymańska', avatar: 'https://randomuser.me/api/portraits/women/21.jpg' }, + { email: 'test9@example.com', name: 'Aleksandra Woźniak', avatar: 'https://randomuser.me/api/portraits/women/33.jpg' }, + { email: 'test10@example.com', name: 'Natalia Dąbrowska', avatar: 'https://randomuser.me/api/portraits/women/57.jpg' }, +]; + +/** Find an existing auth user by email (paginated listUsers). */ +async function findUserByEmail(email) { + for (let page = 1; page <= 10; page++) { + const { data, error } = await supabase.auth.admin.listUsers({ page, perPage: 200 }); + if (error) throw error; + const hit = data.users.find((u) => u.email?.toLowerCase() === email.toLowerCase()); + if (hit) return hit; + if (data.users.length < 200) break; + } + return null; +} + +async function upsertProfile(id, person) { + // The signup trigger creates a profile with display_name, but never sets the + // avatar — so write both here (service role bypasses RLS). + const { error } = await supabase.from('profiles').upsert({ + id, + email: person.email, + display_name: person.name, + avatar_url: person.avatar, + }); + if (error) console.warn(` ⚠ profile upsert failed: ${error.message}`); +} + +async function run() { + console.log(`\nSeeding ${PEOPLE.length} test accounts (password: "${PASSWORD}")\n`); + + for (const person of PEOPLE) { + const meta = { display_name: person.name, avatar_url: person.avatar }; + + const { data, error } = await supabase.auth.admin.createUser({ + email: person.email, + password: PASSWORD, + email_confirm: true, + user_metadata: meta, + }); + + let userId = data?.user?.id; + + if (error) { + // Most likely: already registered → update instead of failing. + const existing = await findUserByEmail(person.email); + if (!existing) { + console.error(`✗ ${person.email} — ${error.message}`); + continue; + } + userId = existing.id; + await supabase.auth.admin.updateUserById(userId, { + password: PASSWORD, + email_confirm: true, + user_metadata: meta, + }); + await upsertProfile(userId, person); + console.log(`↻ ${person.email.padEnd(20)} ${person.name} (updated)`); + continue; + } + + await upsertProfile(userId, person); + console.log(`✓ ${person.email.padEnd(20)} ${person.name}`); + } + + console.log( + `\nDone. Log in with any email above and password "${PASSWORD}".\n` + + 'Tip: use an incognito window per account to stay logged in as several at once.\n', + ); +} + +run().catch((e) => { + console.error('\nFatal:', e.message || e); + process.exit(1); +}); diff --git a/frontend/src/__tests__/events.test.ts b/frontend/src/__tests__/events.test.ts new file mode 100644 index 0000000..5ea8351 --- /dev/null +++ b/frontend/src/__tests__/events.test.ts @@ -0,0 +1,228 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +// --------------------------------------------------------------------------- +// Hoist mock variables so vi.mock factory can reference them +// --------------------------------------------------------------------------- +const { + mockInsert, mockSelect, mockSingle, mockDelete, mockUpdate, + mockEq, mockOrder, mockLimit, mockMaybeSingle, mockRpc, mockChain, +} = vi.hoisted(() => { + const mockInsert = vi.fn(); + const mockSelect = vi.fn(); + const mockSingle = vi.fn(); + const mockDelete = vi.fn().mockResolvedValue({ error: null }); + const mockUpdate = vi.fn().mockResolvedValue({ error: null }); + const mockEq = vi.fn(); + const mockOrder = vi.fn(); + const mockLimit = vi.fn(); + const mockMaybeSingle = vi.fn(); + // Rate-limit RPC — default to "allowed" so the guarded path runs. + const mockRpc = vi.fn().mockResolvedValue({ data: true, error: null }); + + const mockChain: Record> = { + insert: mockInsert, select: mockSelect, single: mockSingle, + delete: mockDelete, update: mockUpdate, eq: mockEq, + order: mockOrder, limit: mockLimit, maybeSingle: mockMaybeSingle, + }; + + // All methods return the chain so they can be chained further + Object.values(mockChain).forEach((fn) => { + if (fn !== mockDelete && fn !== mockUpdate) { + fn.mockReturnValue(mockChain); + } + }); + + return { + mockInsert, mockSelect, mockSingle, mockDelete, mockUpdate, + mockEq, mockOrder, mockLimit, mockMaybeSingle, mockRpc, mockChain, + }; +}); + +vi.mock('@/lib/supabase', () => ({ + supabase: { + from: vi.fn().mockReturnValue(mockChain), + rpc: mockRpc, + }, +})); + +import { createEvent, joinEvent, removeParticipant } from '@/lib/events'; + +beforeEach(() => { + vi.clearAllMocks(); + // Restore chain returns — insert chains too (events insert → .select().single()) + Object.values(mockChain).forEach((fn) => { + if (fn !== mockDelete && fn !== mockUpdate) { + (fn as ReturnType).mockReturnValue(mockChain); + } + }); + mockDelete.mockResolvedValue({ error: null }); + mockUpdate.mockResolvedValue({ error: null }); + mockRpc.mockResolvedValue({ data: true, error: null }); +}); + +// --------------------------------------------------------------------------- +// createEvent +// --------------------------------------------------------------------------- +describe('createEvent', () => { + it('inserts event and adds organizer as participant, returns id', async () => { + const fakeId = 'event-uuid-1234'; + mockSingle.mockResolvedValue({ data: { id: fakeId }, error: null }); + + const id = await createEvent( + { + sport: 'piłka nożna', + fieldName: 'Orlik Rataje', + date: '2026-07-01', + time: '18:00', + maxPlayers: 10, + visibility: 'private', + }, + 'organizer-uid', + 'Jan Kowalski', + ); + + expect(id).toBe(fakeId); + const { supabase } = await import('@/lib/supabase'); + expect(supabase.from).toHaveBeenCalledWith('events'); + expect(supabase.from).toHaveBeenCalledWith('event_participants'); + }); + + it('throws when Supabase returns an error', async () => { + mockSingle.mockResolvedValue({ data: null, error: { message: 'DB error' } }); + await expect( + createEvent({ sport: 'futsal', fieldName: 'X', date: '2026-01-01', time: '10:00', maxPlayers: 5, visibility: 'private' }, 'u', 'U'), + ).rejects.toThrow('DB error'); + }); +}); + +// --------------------------------------------------------------------------- +// joinEvent +// --------------------------------------------------------------------------- +describe('joinEvent', () => { + it('adds participant as non-reserve when slots available', async () => { + const { supabase } = await import('@/lib/supabase'); + + // joinEvent calls supabase.from() 3 times: + // 1. events → .select().eq().single() → { max_players: 10 } + // 2. event_participants → .select({count}).eq().eq().eq() → { count: 5 } + // 3. event_participants → .insert() → { error: null } + vi.mocked(supabase.from).mockImplementation((table: string) => { + if (table === 'events') { + return { + ...mockChain, + select: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnValue({ + single: vi.fn().mockResolvedValue({ data: { max_players: 10 }, error: null }), + }), + }), + } as unknown as ReturnType; + } + // event_participants count query chains three .eq() filters + // (event_id, is_reserve, pending_approval), then resolves. + const thirdEq = vi.fn().mockResolvedValue({ count: 5, error: null }); + const secondEq = vi.fn().mockReturnValue({ eq: thirdEq }); + const firstEq = vi.fn().mockReturnValue({ eq: secondEq }); + return { + ...mockChain, + select: vi.fn().mockReturnValue({ eq: firstEq }), + insert: vi.fn().mockResolvedValue({ error: null }), + } as unknown as ReturnType; + }); + + await expect(joinEvent('event-1', 'user-1', 'Test User')).resolves.toBeUndefined(); + }); + + it('throws when the rate limit is exceeded', async () => { + mockRpc.mockResolvedValue({ data: false, error: null }); + await expect(joinEvent('event-1', 'user-1', 'Test User')).rejects.toThrow(/Zbyt wiele/); + }); +}); + +// --------------------------------------------------------------------------- +// removeParticipant +// --------------------------------------------------------------------------- +describe('removeParticipant', () => { + it('deletes the participant row', async () => { + const { supabase } = await import('@/lib/supabase'); + + // removeParticipant calls supabase.from('event_participants') 3 times: + // 1. .select().eq().single() → fetch participant + // 2. .delete().eq() → delete row + // 3. .select().eq().eq().order().limit().maybeSingle() → find reserve (null) + let n = 0; + vi.mocked(supabase.from).mockImplementation(() => { + n++; + if (n === 1) return { + select: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnValue({ + single: vi.fn().mockResolvedValue({ data: { event_id: 'ev1', is_reserve: false }, error: null }), + }), + }), + } as unknown as ReturnType; + if (n === 2) return { + delete: vi.fn().mockReturnValue({ eq: vi.fn().mockResolvedValue({ error: null }) }), + } as unknown as ReturnType; + return { + select: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnValue({ + order: vi.fn().mockReturnValue({ + limit: vi.fn().mockReturnValue({ + maybeSingle: vi.fn().mockResolvedValue({ data: null, error: null }), + }), + }), + }), + }), + }), + } as unknown as ReturnType; + }); + + await expect(removeParticipant('participant-1')).resolves.toBeUndefined(); + }); + + it('promotes first reserve when a regular spot opens', async () => { + const { supabase } = await import('@/lib/supabase'); + + let deleteWasCalled = false; + let updateWasCalled = false; + let n = 0; + + vi.mocked(supabase.from).mockImplementation(() => { + n++; + if (n === 1) return { + select: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnValue({ + single: vi.fn().mockResolvedValue({ data: { event_id: 'ev1', is_reserve: false }, error: null }), + }), + }), + } as unknown as ReturnType; + if (n === 2) return { + delete: vi.fn().mockReturnValue({ + eq: vi.fn().mockImplementation(() => { deleteWasCalled = true; return Promise.resolve({ error: null }); }), + }), + } as unknown as ReturnType; + if (n === 3) return { + select: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnValue({ + eq: vi.fn().mockReturnValue({ + order: vi.fn().mockReturnValue({ + limit: vi.fn().mockReturnValue({ + maybeSingle: vi.fn().mockResolvedValue({ data: { id: 'reserve-1' }, error: null }), + }), + }), + }), + }), + }), + } as unknown as ReturnType; + return { + update: vi.fn().mockReturnValue({ + eq: vi.fn().mockImplementation(() => { updateWasCalled = true; return Promise.resolve({ error: null }); }), + }), + } as unknown as ReturnType; + }); + + await removeParticipant('participant-1'); + expect(deleteWasCalled).toBe(true); + expect(updateWasCalled).toBe(true); + }); +}); diff --git a/frontend/src/__tests__/slugify.test.ts b/frontend/src/__tests__/slugify.test.ts new file mode 100644 index 0000000..9c49422 --- /dev/null +++ b/frontend/src/__tests__/slugify.test.ts @@ -0,0 +1,49 @@ +import { describe, it, expect } from 'vitest'; +import { slugify, isUuid } from '@/lib/utils'; + +describe('slugify', () => { + it('converts basic name to slug', () => { + expect(slugify('Orlik Rataje')).toBe('orlik-rataje'); + }); + + it('strips Polish diacritics', () => { + expect(slugify('piłka nożna')).toBe('pilka-nozna'); + expect(slugify('siatkówka plażowa')).toBe('siatkowka-plazowa'); + expect(slugify('Hala Ząbkowska')).toBe('hala-zabkowska'); + }); + + it('handles ł correctly', () => { + expect(slugify('Łódź')).toBe('lodz'); + expect(slugify('piłka ręczna')).toBe('pilka-reczna'); + }); + + it('collapses multiple spaces and special chars into single dash', () => { + expect(slugify('ul. Dąbrowskiego 79A')).toBe('ul-dabrowskiego-79a'); + }); + + it('trims leading and trailing dashes', () => { + expect(slugify(' Boisko ')).toBe('boisko'); + }); + + it('is deterministic (same input → same output)', () => { + const name = 'Boisko Sportowe Reymonta'; + expect(slugify(name)).toBe(slugify(name)); + }); +}); + +describe('isUuid', () => { + it('recognises valid UUIDs', () => { + expect(isUuid('b1a2c3d4-0001-0001-0001-000000000001')).toBe(true); + expect(isUuid('550e8400-e29b-41d4-a716-446655440000')).toBe(true); + }); + + it('rejects slugs', () => { + expect(isUuid('orlik-rataje')).toBe(false); + expect(isUuid('pilka-nozna')).toBe(false); + }); + + it('rejects partial or malformed UUIDs', () => { + expect(isUuid('b1a2c3d4-0001')).toBe(false); + expect(isUuid('')).toBe(false); + }); +}); diff --git a/frontend/src/__tests__/validation.test.ts b/frontend/src/__tests__/validation.test.ts new file mode 100644 index 0000000..d82acee --- /dev/null +++ b/frontend/src/__tests__/validation.test.ts @@ -0,0 +1,123 @@ +import { describe, it, expect } from 'vitest'; +import { + sanitizeText, + validateName, + sanitizeDescription, + sanitizeAddress, + validatePhone, + normalizePhone, +} from '@/lib/validation'; + +describe('sanitizeText', () => { + it('strips HTML tags', () => { + expect(sanitizeText('Orlik')).toBe('Orlik'); + expect(sanitizeText('Boisko')).toBe('alert(1)Boisko'); + expect(sanitizeText('')).toBe(''); + }); + + it('strips ASCII control characters', () => { + expect(sanitizeText('Orlik\x00\x07Rataje')).toBe('OrlikRataje'); + }); + + it('trims surrounding whitespace', () => { + expect(sanitizeText(' Boisko ')).toBe('Boisko'); + }); + + it('clamps to the given max length', () => { + expect(sanitizeText('abcdefghij', 5)).toBe('abcde'); + }); + + it('keeps Polish diacritics intact', () => { + expect(sanitizeText('Piłka nożna ążśćź')).toBe('Piłka nożna ążśćź'); + }); + + it('returns empty string for tag-only input', () => { + expect(sanitizeText('
')).toBe(''); + }); +}); + +describe('validateName', () => { + it('returns the sanitized name for valid input', () => { + expect(validateName(' Orlik Rataje ')).toBe('Orlik Rataje'); + expect(validateName('Hala')).toBe('Hala'); + }); + + it('throws when the name is empty or whitespace only', () => { + expect(() => validateName('')).toThrow('Nazwa jest wymagana.'); + expect(() => validateName(' ')).toThrow('Nazwa jest wymagana.'); + }); + + it('throws when only HTML tags are supplied', () => { + expect(() => validateName('
')).toThrow(/wymagana/); + }); + + it('uses a custom label in the error message', () => { + expect(() => validateName('', 'Tytuł')).toThrow('Tytuł jest wymagana.'); + }); + + it('truncates to the max length rather than throwing', () => { + const longName = 'a'.repeat(200); + expect(validateName(longName, 'Nazwa', 100)).toHaveLength(100); + }); +}); + +describe('sanitizeDescription', () => { + it('allows up to 1000 characters', () => { + expect(sanitizeDescription('x'.repeat(1500))).toHaveLength(1000); + }); + + it('never throws on empty input', () => { + expect(sanitizeDescription('')).toBe(''); + }); +}); + +describe('sanitizeAddress', () => { + it('allows up to 300 characters', () => { + expect(sanitizeAddress('x'.repeat(400))).toHaveLength(300); + }); + + it('strips tags from an address', () => { + expect(sanitizeAddress('ul. Dąbrowskiego 79')).toBe('ul. Dąbrowskiego 79'); + }); +}); + +describe('validatePhone', () => { + it('accepts 9 bare digits', () => { + expect(validatePhone('501234567')).toBe(true); + }); + + it('accepts +48 prefix', () => { + expect(validatePhone('+48501234567')).toBe(true); + }); + + it('ignores spaces and dashes', () => { + expect(validatePhone('501 234 567')).toBe(true); + expect(validatePhone('501-234-567')).toBe(true); + expect(validatePhone('+48 501-234-567')).toBe(true); + }); + + it('rejects too short / too long numbers', () => { + expect(validatePhone('12345')).toBe(false); + expect(validatePhone('5012345678')).toBe(false); + }); + + it('rejects non-digit content', () => { + expect(validatePhone('abcdefghi')).toBe(false); + expect(validatePhone('')).toBe(false); + }); +}); + +describe('normalizePhone', () => { + it('adds +48 to a bare 9-digit number', () => { + expect(normalizePhone('501234567')).toBe('+48501234567'); + }); + + it('keeps an existing +48 prefix', () => { + expect(normalizePhone('+48501234567')).toBe('+48501234567'); + }); + + it('strips spaces and dashes before normalizing', () => { + expect(normalizePhone('501 234 567')).toBe('+48501234567'); + expect(normalizePhone('+48 501-234-567')).toBe('+48501234567'); + }); +}); diff --git a/frontend/src/app/admin/[fieldId]/page.tsx b/frontend/src/app/admin/[fieldId]/page.tsx new file mode 100644 index 0000000..1bca024 --- /dev/null +++ b/frontend/src/app/admin/[fieldId]/page.tsx @@ -0,0 +1,454 @@ +'use client'; + +import { useState, useEffect, useCallback } from 'react'; +import { useParams } from 'next/navigation'; +import Link from 'next/link'; +import { ArrowLeft, Lock, CalendarDays } from 'lucide-react'; +import Header from '@/components/layout/Header'; +import Button from '@/components/ui/Button'; +import { useAuth } from '@/lib/auth'; +import { useAdmin } from '@/lib/admin'; +import { getField, updateFieldBookingSettings } from '@/lib/api'; +import { getFieldBookings, updateBookingStatus } from '@/lib/bookings'; +import type { Field, Booking, BookingType } from '@/types'; + +type BookingWithField = Booking & { fieldName: string }; + +type Tab = 'rezerwacje' | 'ustawienia'; + +const inputCls = + 'border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500'; + +function todayString(): string { + return new Date().toISOString().slice(0, 10); +} + +function StatusBadge({ status }: { status: Booking['status'] }) { + const map: Record = { + pending: { label: 'Oczekujące', cls: 'bg-amber-100 text-amber-700' }, + confirmed: { label: 'Potwierdzone', cls: 'bg-green-100 text-green-700' }, + cancelled: { label: 'Anulowane', cls: 'bg-slate-100 text-slate-500' }, + }; + const { label, cls } = map[status]; + return ( + + {label} + + ); +} + +export default function AdminFieldPage() { + const { fieldId } = useParams<{ fieldId: string }>(); + const { user, loading: authLoading } = useAuth(); + const isAdmin = useAdmin(); + + const [pageLoading, setPageLoading] = useState(true); + const [notAllowed, setNotAllowed] = useState(false); + const [field, setField] = useState(null); + const [tab, setTab] = useState('rezerwacje'); + + // --- Rezerwacje tab --- + const [selectedDate, setSelectedDate] = useState(todayString()); + const [allBookings, setAllBookings] = useState([]); + const [bookingsLoading, setBookingsLoading] = useState(false); + const [busy, setBusy] = useState>(new Set()); + + // --- Ustawienia tab --- + const [bookingType, setBookingType] = useState('none'); + const [bookingUrl, setBookingUrl] = useState(''); + const [bookingEnabled, setBookingEnabled] = useState(false); + const [saving, setSaving] = useState(false); + const [saveSuccess, setSaveSuccess] = useState(false); + const [saveError, setSaveError] = useState(null); + + const loadBookings = useCallback(async () => { + setBookingsLoading(true); + try { + const data = await getFieldBookings(fieldId); + setAllBookings(data); + } finally { + setBookingsLoading(false); + } + }, [fieldId]); + + useEffect(() => { + if (authLoading) return; + if (!user) { + setNotAllowed(true); + setPageLoading(false); + return; + } + + getField(fieldId) + .then((f) => { + if (f.managerId !== user.id && !isAdmin) { + setNotAllowed(true); + return; + } + setField(f); + setBookingType(f.bookingType); + setBookingUrl(f.bookingUrl ?? ''); + setBookingEnabled(f.bookingEnabled); + }) + .catch(() => setNotAllowed(true)) + .finally(() => setPageLoading(false)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [authLoading, user, isAdmin]); + + useEffect(() => { + if (!field) return; + loadBookings(); + }, [field, loadBookings]); + + const bookingsForDate = allBookings.filter((b) => b.date === selectedDate); + + const handleStatus = async (bookingId: string, status: 'confirmed' | 'cancelled') => { + setBusy((prev) => new Set(prev).add(bookingId)); + try { + await updateBookingStatus(bookingId, status); + await loadBookings(); + } catch { + // silently ignore + } finally { + setBusy((prev) => { + const next = new Set(prev); + next.delete(bookingId); + return next; + }); + } + }; + + const handleSaveSettings = async (e: React.FormEvent) => { + e.preventDefault(); + setSaving(true); + setSaveSuccess(false); + setSaveError(null); + try { + await updateFieldBookingSettings( + fieldId, + bookingType, + bookingType === 'external' ? bookingUrl.trim() || undefined : undefined, + bookingEnabled, + ); + setSaveSuccess(true); + } catch (err) { + setSaveError(err instanceof Error ? err.message : 'Nie udało się zapisać ustawień'); + } finally { + setSaving(false); + } + }; + + // --- Loading skeleton --- + if (authLoading || pageLoading) { + return ( +
+
+
+
+
+ {[1, 2, 3].map((i) => ( +
+ ))} +
+
+
+ ); + } + + // --- Not logged in --- + if (!user) { + return ( +
+
+
+
+ +

Musisz być zalogowany

+

Zaloguj się, aby uzyskać dostęp do panelu.

+ + Wróć do mapy + +
+
+
+ ); + } + + // --- Access denied --- + if (notAllowed) { + return ( +
+
+
+
+ +

Brak dostępu

+

+ Ta strona jest dostępna tylko dla menedżera obiektu lub administratorów. +

+ + Wróć do obiektów + +
+
+
+ ); + } + + return ( +
+
+
+ {/* Header row */} +
+ + + +
+

+ {field?.name ?? 'Panel rezerwacji'} +

+

Panel zarządzania obiektem

+
+
+ + {/* Tabs */} +
+ {(['rezerwacje', 'ustawienia'] as Tab[]).map((t) => ( + + ))} +
+ + {/* === Tab: Rezerwacje === */} + {tab === 'rezerwacje' && ( +
+ {/* Date picker */} +
+ + setSelectedDate(e.target.value)} + className={inputCls} + /> +
+ + {/* Bookings list */} + {bookingsLoading ? ( +
+ {[1, 2, 3].map((i) => ( +
+ ))} +
+ ) : bookingsForDate.length === 0 ? ( +
+ +

Brak rezerwacji na ten dzień

+
+ ) : ( +
+ {bookingsForDate.map((booking, idx) => { + const isBusy = busy.has(booking.id); + return ( +
+
+
+ {/* Time + status */} +
+ + {booking.startTime.slice(0, 5)}–{booking.endTime.slice(0, 5)} + + +
+ {/* Player name */} +

{booking.userName}

+ {/* Phone */} + {booking.phone && ( +

{booking.phone}

+ )} + {/* Players count + sport */} +

+ {booking.playersCount} os. + {booking.sport ? ` · ${booking.sport}` : ''} +

+
+ + {/* Action buttons */} + {booking.status !== 'cancelled' && ( +
+ {booking.status === 'pending' && ( + + )} + +
+ )} +
+
+ ); + })} +
+ )} +
+ )} + + {/* === Tab: Ustawienia === */} + {tab === 'ustawienia' && ( +
+
+

Typ rezerwacji

+ + {/* Radio: none */} + + + {/* Radio: internal */} + + + {/* Radio: external */} + + + {/* bookingUrl input — shown only for external */} + {bookingType === 'external' && ( +
+ + setBookingUrl(e.target.value)} + placeholder="https://..." + className={`${inputCls} w-full`} + maxLength={500} + /> +
+ )} +
+ + {/* booking_enabled override */} +
+

Widoczność rezerwacji

+ +
+ + {/* Feedback */} + {saveSuccess && ( +
+ Ustawienia zostały zapisane. +
+ )} + {saveError && ( +
+ {saveError} +
+ )} + + +
+ )} +
+
+ ); +} diff --git a/frontend/src/app/admin/analityka/page.tsx b/frontend/src/app/admin/analityka/page.tsx new file mode 100644 index 0000000..06a1de3 --- /dev/null +++ b/frontend/src/app/admin/analityka/page.tsx @@ -0,0 +1,293 @@ +'use client'; + +import { useState, useEffect, useCallback, useMemo } from 'react'; +import Link from 'next/link'; +import { + Lock, BarChart3, Users, LogIn, CalendarPlus, UserPlus, + UsersRound, RefreshCw, TrendingUp, +} from 'lucide-react'; +import Header from '@/components/layout/Header'; +import { useAuth } from '@/lib/auth'; +import { supabase } from '@/lib/supabase'; +import type { AnalyticsEvent } from '@/lib/analytics'; + +interface Row { + id: string; + user_id: string | null; + user_email: string | null; + event_type: string; + path: string | null; + metadata: Record | null; + created_at: string; +} + +const TYPE_LABELS: Record = { + login: 'Logowanie', + event_created: 'Utworzył mecz', + event_joined: 'Dołączył do meczu', + group_created: 'Utworzył grupę', + group_joined: 'Dołączył do grupy', +}; + +const DAY = 24 * 60 * 60 * 1000; + +/** Local YYYY-MM-DD key for day-bucketing. */ +function dayKey(d: Date): string { + return d.toLocaleDateString('sv-SE'); // ISO-like in local tz +} + +export default function AnalyticsAdminPage() { + const { user, loading: authLoading } = useAuth(); + const [adminState, setAdminState] = useState<'checking' | 'yes' | 'no'>('checking'); + const [rows, setRows] = useState([]); + const [loading, setLoading] = useState(true); + + // --- Admin check --- + useEffect(() => { + if (authLoading) return; + if (!user) { setAdminState('no'); return; } + supabase.from('profiles').select('is_admin').eq('id', user.id).maybeSingle() + .then(({ data }) => setAdminState(data?.is_admin ? 'yes' : 'no'), () => setAdminState('no')); + }, [authLoading, user]); + + // --- Load last 30 days --- + const load = useCallback(async () => { + setLoading(true); + const since = new Date(Date.now() - 30 * DAY).toISOString(); + const { data } = await supabase + .from('analytics_events') + .select('*') + .gte('created_at', since) + .order('created_at', { ascending: false }) + .limit(10000); + setRows((data as Row[]) ?? []); + setLoading(false); + }, []); + + useEffect(() => { if (adminState === 'yes') load(); }, [adminState, load]); + + // --- Derived metrics --- + const stats = useMemo(() => { + const now = Date.now(); + const since1 = now - DAY; + const since7 = now - 7 * DAY; + + const within = (r: Row, from: number) => new Date(r.created_at).getTime() >= from; + const distinctUsers = (rs: Row[]) => + new Set(rs.filter((r) => r.user_id).map((r) => r.user_id)).size; + + const today = rows.filter((r) => within(r, since1)); + const week = rows.filter((r) => within(r, since7)); + + const countType = (rs: Row[], t: string) => rs.filter((r) => r.event_type === t).length; + + // Returning users: distinct users active on ≥2 different days in the window + const daysByUser = new Map>(); + for (const r of rows) { + if (!r.user_id) continue; + const set = daysByUser.get(r.user_id) ?? new Set(); + set.add(dayKey(new Date(r.created_at))); + daysByUser.set(r.user_id, set); + } + let returning = 0; + daysByUser.forEach((days) => { if (days.size >= 2) returning += 1; }); + const totalUsers30 = daysByUser.size; + + // Daily activity (distinct active users) for last 14 days + const daily: { day: string; users: number; events: number }[] = []; + for (let i = 13; i >= 0; i--) { + const d = new Date(now - i * DAY); + const key = dayKey(d); + const dayRows = rows.filter((r) => dayKey(new Date(r.created_at)) === key); + daily.push({ + day: key.slice(5), // MM-DD + users: new Set(dayRows.filter((r) => r.user_id).map((r) => r.user_id)).size, + events: dayRows.length, + }); + } + + return { + activeToday: distinctUsers(today), + active7: distinctUsers(week), + active30: totalUsers30, + loginsToday: countType(today, 'login'), + logins7: countType(week, 'login'), + eventsCreated7: countType(week, 'event_created'), + eventsJoined7: countType(week, 'event_joined'), + groupsCreated7: countType(week, 'group_created'), + groupsJoined7: countType(week, 'group_joined'), + returning, + totalUsers30, + retentionPct: totalUsers30 > 0 ? Math.round((returning / totalUsers30) * 100) : 0, + daily, + }; + }, [rows]); + + const maxDaily = Math.max(1, ...stats.daily.map((d) => d.events)); + + // ---- Guards ---- + if (authLoading || adminState === 'checking') { + return ( +
+
+
+
+
+ {[1, 2, 3, 4].map((i) =>
)} +
+
+
+ ); + } + + if (adminState === 'no') { + return ( +
+
+
+
+ +

Brak dostępu

+

+ Analityka jest dostępna tylko dla administratorów. +

+ Wróć na stronę główną +
+
+
+ ); + } + + return ( +
+
+
+
+
+

+ Analityka +

+

+ {loading ? 'Ładowanie…' : `Ostatnie 30 dni · ${rows.length} zdarzeń`} +

+
+ +
+ + {/* Active users */} +
+ + + +
+ + {/* Retention highlight */} +
+
+ +

Retencja (30 dni)

+
+

+ {stats.retentionPct}% + + {stats.returning} z {stats.totalUsers30} użytkowników wróciło w innym dniu + +

+

+ To kluczowy miernik — ilu graczy wraca po pierwszej wizycie. Tę liczbę chcemy ruszyć w górę. +

+
+ + {/* Action counts (7d) */} +
+ + + + + +
+ + {/* Daily activity chart */} +
+

Aktywność dzienna (14 dni)

+
+ {stats.daily.map((d) => ( +
+
+
+
+ {d.day} +
+ ))} +
+
+ + {/* Raw log */} +
+

Ostatnie zdarzenia

+ {loading ? ( +
+ {[1, 2, 3, 4, 5].map((i) =>
)} +
+ ) : rows.length === 0 ? ( +

Brak zdarzeń. Pojawią się gdy użytkownicy zaczną korzystać z aplikacji.

+ ) : ( +
    + {rows.slice(0, 100).map((r) => ( +
  • + + {TYPE_LABELS[r.event_type] ?? r.event_type} + + + {r.user_email ?? anonim} + + {formatWhen(r.created_at)} +
  • + ))} +
+ )} +
+
+
+ ); +} + +function StatCard({ + icon: Icon, label, value, sub, accent = false, +}: { + icon: React.ComponentType<{ className?: string }>; + label: string; + value: number; + sub?: string; + accent?: boolean; +}) { + return ( +
+
+ + {label} +
+

{value}

+ {sub &&

{sub}

} +
+ ); +} + +function formatWhen(iso: string): string { + const d = new Date(iso); + const diff = Date.now() - d.getTime(); + if (diff < 60_000) return 'teraz'; + if (diff < 3_600_000) return `${Math.floor(diff / 60_000)} min temu`; + if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)} h temu`; + return d.toLocaleDateString('pl-PL', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' }); +} diff --git a/frontend/src/app/admin/boisko/[id]/page.tsx b/frontend/src/app/admin/boisko/[id]/page.tsx new file mode 100644 index 0000000..6c5a4f2 --- /dev/null +++ b/frontend/src/app/admin/boisko/[id]/page.tsx @@ -0,0 +1,428 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { ArrowLeft, Lock, Eye, EyeOff, Trash2 } from 'lucide-react'; +import Link from 'next/link'; +import Header from '@/components/layout/Header'; +import Button from '@/components/ui/Button'; +import { useAuth } from '@/lib/auth'; +import { useAdmin } from '@/lib/admin'; +import { updateField } from '@/lib/api'; +import { supabase } from '@/lib/supabase'; + +const SPORTS = [ + 'piłka nożna', + 'futsal', + 'koszykówka', + 'siatkówka', + 'siatkówka plażowa', + 'piłka ręczna', +] as const; + +const SURFACES = [ + { value: '', label: '— nieznana —' }, + { value: 'grass', label: 'Trawa naturalna' }, + { value: 'artificial', label: 'Trawa sztuczna' }, + { value: 'concrete', label: 'Beton / asfalt' }, + { value: 'sand', label: 'Piasek' }, + { value: 'indoor', label: 'Hala / parkiet' }, +] as const; + +const inputCls = + 'w-full border border-slate-300 rounded-lg px-3 py-2 text-sm text-slate-800 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent'; + +export default function AdminVenueEditorPage() { + const { id } = useParams<{ id: string }>(); + const router = useRouter(); + const { user, loading: authLoading } = useAuth(); + const isAdmin = useAdmin(); + + const [pageLoading, setPageLoading] = useState(true); + const [notAllowed, setNotAllowed] = useState(false); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(null); + + const [name, setName] = useState(''); + const [address, setAddress] = useState(''); + const [sport, setSport] = useState([]); + const [available, setAvailable] = useState(true); + const [surface, setSurface] = useState(''); + const [isIndoor, setIsIndoor] = useState(false); + const [phone, setPhone] = useState(''); + const [website, setWebsite] = useState(''); + const [contactVisible, setContactVisible] = useState(false); + const [mapVisibility, setMapVisibility] = useState<'public' | 'hidden' | 'organizer_only'>('organizer_only'); + const [visibilityBusy, setVisibilityBusy] = useState(false); + const [deleteBusy, setDeleteBusy] = useState(false); + const [deleteConfirm, setDeleteConfirm] = useState(false); + + useEffect(() => { + if (authLoading) return; + if (!user) { setNotAllowed(true); setPageLoading(false); return; } + }, [authLoading, user]); + + useEffect(() => { + if (authLoading) return; + if (!user) return; + + (async () => { + const { data: f, error } = await supabase + .from('fields') + .select('*') + .eq('id', id) + .single(); + if (error || !f) { + setNotAllowed(true); + } else { + setName(f.name); + setAddress(f.address); + setSport(f.sport ?? []); + setAvailable(f.available); + setSurface(f.surface ?? ''); + setIsIndoor(f.is_indoor ?? false); + setPhone(f.phone ?? ''); + setWebsite(f.website ?? ''); + setContactVisible(f.contact_visible ?? false); + setMapVisibility(f.map_visibility ?? 'organizer_only'); + } + setPageLoading(false); + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [authLoading, user]); + + const toggleSport = (s: string) => { + setSport((prev) => + prev.includes(s) ? prev.filter((x) => x !== s) : [...prev, s], + ); + }; + + const handleToggleVisibility = async () => { + if (!isAdmin) return; + const next = mapVisibility === 'public' ? 'hidden' : 'public'; + const nextModeration = next === 'public' ? 'approved' : 'hidden'; + setVisibilityBusy(true); + try { + await supabase.from('fields').update({ + map_visibility: next, + moderation_status: nextModeration, + }).eq('id', id); + setMapVisibility(next); + } finally { setVisibilityBusy(false); } + }; + + const handleDelete = async () => { + if (!isAdmin || !deleteConfirm) return; + setDeleteBusy(true); + try { + await supabase.from('fields').delete().eq('id', id); + router.push('/admin/moderacja'); + } finally { setDeleteBusy(false); } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!isAdmin) return; + setSubmitting(true); + setError(null); + try { + await updateField(id, { + name, + address, + sport, + available, + surface, + isIndoor, + phone: phone.trim() || undefined, + website: website.trim() || undefined, + contactVisible, + }); + router.push('/mapa'); + } catch (err) { + setError(err instanceof Error ? err.message : 'Nie udało się zapisać zmian'); + setSubmitting(false); + } + }; + + if (pageLoading || authLoading) { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+ ); + } + + if (notAllowed || !isAdmin) { + return ( +
+
+
+
+ +

Brak dostępu

+

Ta strona jest dostępna tylko dla administratorów.

+ + Wróć do mapy + +
+
+
+ ); + } + + return ( +
+
+
+
+ + + +

Edytuj boisko

+
+ +
+
+
+ + setName(e.target.value)} + className={inputCls} + required + maxLength={120} + /> +
+ +
+ + setAddress(e.target.value)} + className={inputCls} + required + maxLength={200} + /> +
+ +
+ +
+ {SPORTS.map((s) => ( + + ))} +
+
+ +
+ + +
+ +
+ +
+ +
+ +
+
+ +
+
+

Dane kontaktowe

+

+ Telefon i e-mail ze scrapera OSM są domyślnie ukryte. Zaznacz poniżej, żeby udostępnić je użytkownikom. +

+ +
+
+ + setPhone(e.target.value)} + placeholder="+48 123 456 789" + className={inputCls} + maxLength={30} + /> +
+ +
+ + setWebsite(e.target.value)} + placeholder="https://" + className={inputCls} + maxLength={200} + /> +
+ + +
+
+
+ + {error && ( +
+ {error} +
+ )} + +
+

Panel rezerwacji

+ + → Panel rezerwacji + +
+ + {/* ── Admin: widoczność + usuwanie ── */} + {isAdmin && ( +
+

Widoczność na mapie

+ +
+
+ {mapVisibility === 'public' + ? + : } +
+

+ {mapVisibility === 'public' ? 'Widoczny publicznie' : 'Ukryty'} +

+

+ {mapVisibility === 'public' + ? 'Obiekt pojawia się na mapie dla wszystkich' + : 'Obiekt nie jest widoczny na mapie'} +

+
+
+ +
+ +
+

Strefa niebezpieczna

+ {!deleteConfirm ? ( + + ) : ( +
+

Na pewno? Tej operacji nie można cofnąć.

+
+ + +
+
+ )} +
+
+ )} + +
+ + + + +
+
+
+
+ ); +} diff --git a/frontend/src/app/admin/moderacja/page.tsx b/frontend/src/app/admin/moderacja/page.tsx new file mode 100644 index 0000000..3c41e3a --- /dev/null +++ b/frontend/src/app/admin/moderacja/page.tsx @@ -0,0 +1,496 @@ +'use client'; + +import { useState, useEffect, useCallback, useMemo } from 'react'; +import Link from 'next/link'; +import { + Lock, Search, Trash2, EyeOff, Check, Satellite, RefreshCw, MapPin, ChevronDown, ChevronRight, +} from 'lucide-react'; +import Header from '@/components/layout/Header'; +import { useAuth } from '@/lib/auth'; +import { supabase } from '@/lib/supabase'; +import { sportEmoji } from '@/lib/sports'; + +// --------------------------------------------------------------------------- +// Types +// --------------------------------------------------------------------------- + +type ModerationStatus = 'pending' | 'approved' | 'hidden'; + +interface VenueRow { + id: string; + name: string; + address: string; + sport: string[]; + lat: number; + lng: number; + photo_url: string | null; + photo_reference: string | null; + photo_source: string | null; + map_visibility: string; + moderation_status: ModerationStatus | null; +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function satelliteUrl(lat: number, lng: number): string { + const token = process.env.NEXT_PUBLIC_MAPBOX_TOKEN; + if (!token) return ''; + return `https://api.mapbox.com/styles/v1/mapbox/satellite-streets-v12/static/${lng},${lat},17,0/400x240@2x?access_token=${token}`; +} + +function bestPhotoSrc(v: VenueRow): string { + if (v.photo_reference) return `/api/venue-photo?ref=${encodeURIComponent(v.photo_reference)}&w=400`; + if (v.photo_url) return v.photo_url; + return satelliteUrl(v.lat, v.lng); +} + +function photoLabel(v: VenueRow): string { + if (v.photo_reference) return 'Google'; + if (v.photo_url) { + if (v.photo_source === 'wikimedia') return 'Wikimedia'; + if (v.photo_source === 'satellite') return 'Satelita (scraper)'; + return 'URL'; + } + return 'Satelita'; +} + +const STATUS_META: Record = { + pending: { label: 'Do sprawdzenia', cls: 'bg-amber-100 text-amber-700' }, + approved: { label: 'Zatwierdzone', cls: 'bg-green-100 text-green-700' }, + hidden: { label: 'Ukryte', cls: 'bg-red-100 text-red-700' }, +}; + +// --------------------------------------------------------------------------- +// Toast +// --------------------------------------------------------------------------- +interface Toast { id: number; msg: string; ok: boolean } +let _tid = 0; + +function useToasts() { + const [toasts, setToasts] = useState([]); + const add = useCallback((msg: string, ok = true) => { + const id = ++_tid; + setToasts((t) => [...t, { id, msg, ok }]); + setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 3500); + }, []); + return { toasts, add }; +} + +// --------------------------------------------------------------------------- +// VenueCard +// --------------------------------------------------------------------------- + +function VenueCard({ + venue, onUpdate, onDelete, onSkip, isSkipped, +}: { + venue: VenueRow; + onUpdate: (id: string, patch: Partial) => Promise; + onDelete: (id: string) => Promise; + onSkip?: () => void; + isSkipped?: boolean; +}) { + const [busy, setBusy] = useState(false); + const [imgErr, setImgErr] = useState(false); + const status = venue.moderation_status ?? 'pending'; + const smeta = STATUS_META[status] ?? STATUS_META.pending; + + const hasExternalPhoto = !!(venue.photo_reference || venue.photo_url); + const photoSrc = imgErr ? satelliteUrl(venue.lat, venue.lng) : bestPhotoSrc(venue); + + async function act(patch: Partial) { + setBusy(true); + await onUpdate(venue.id, patch); + setBusy(false); + } + + const btnBase = 'inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold transition-colors disabled:opacity-40'; + const btnGhost = `${btnBase} border border-slate-200 bg-white text-slate-600 hover:border-slate-400`; + const btnGreen = `${btnBase} bg-green-600 text-white hover:bg-green-700`; + const btnAmber = `${btnBase} bg-amber-500 text-white hover:bg-amber-600`; + const btnRed = `${btnBase} bg-red-500 text-white hover:bg-red-600`; + const btnSkip = `${btnBase} border border-slate-200 bg-white text-slate-500 hover:bg-slate-50`; + + return ( +
+ {/* Photo */} +
+ {photoSrc && ( + // eslint-disable-next-line @next/next/no-img-element + setImgErr(true)} + /> + )} + + {imgErr ? 'Satelita' : photoLabel(venue)} + + + {smeta.label} + + {isSkipped && ( + + ← Pominięty + + )} +
+ + {/* Info */} +
+

+ {venue.name} +

+

+ {venue.address} +

+
+ {venue.sport.map((s) => ( + + {sportEmoji(s)} {s} + + ))} +
+ + {/* Photo controls */} +
+ {hasExternalPhoto ? ( + + ) : ( + + Satelita + + )} +
+ + {/* Status + action controls */} +
+ {status !== 'approved' && ( + + )} + {status !== 'hidden' && ( + + )} + {status !== 'pending' && ( + + )} + {onSkip && !isSkipped && ( + + )} + +
+
+
+ ); +} + +// --------------------------------------------------------------------------- +// Page +// --------------------------------------------------------------------------- + +const TABS: { key: ModerationStatus | 'all'; label: string }[] = [ + { key: 'all', label: 'Wszystkie' }, + { key: 'pending', label: 'Do sprawdzenia' }, + { key: 'approved', label: 'Zatwierdzone' }, + { key: 'hidden', label: 'Ukryte' }, +]; + +export default function ModeracjaPage() { + const { user, loading: authLoading } = useAuth(); + const [adminState, setAdminState] = useState<'checking' | 'yes' | 'no'>('checking'); + + useEffect(() => { + if (authLoading) return; + if (!user) { setAdminState('no'); return; } + supabase + .from('profiles').select('is_admin').eq('id', user.id).maybeSingle() + .then( + ({ data }) => setAdminState(data?.is_admin ? 'yes' : 'no'), + () => setAdminState('no'), + ); + }, [authLoading, user]); + + const [venues, setVenues] = useState([]); + const [loading, setLoading] = useState(true); + const [tab, setTab] = useState('pending'); + const [search, setSearch] = useState(''); + const { toasts, add: addToast } = useToasts(); + + // Skip set — session-only, resets on reload + const [skipped, setSkipped] = useState>(new Set()); + const [skippedOpen, setSkippedOpen] = useState(false); + + // Session progress tracking — record how many were pending when we started + const [sessionTotal, setSessionTotal] = useState(null); + const [sessionDone, setSessionDone] = useState(0); + + const load = useCallback(async () => { + setLoading(true); + const { data, error } = await supabase + .from('fields') + .select('id,name,address,sport,lat,lng,photo_url,photo_reference,photo_source,map_visibility,moderation_status') + .order('name'); + if (!error && data) setVenues(data as VenueRow[]); + setLoading(false); + }, []); + + useEffect(() => { + if (adminState === 'yes') load(); + }, [adminState, load]); + + // Set session total once data is loaded (only once) + useEffect(() => { + if (!loading && sessionTotal === null && venues.length > 0) { + const pending = venues.filter((v) => (v.moderation_status ?? 'pending') === 'pending').length; + setSessionTotal(pending); + } + }, [loading, venues, sessionTotal]); + + const filtered = useMemo(() => { + let list = venues; + if (tab !== 'all') list = list.filter((v) => (v.moderation_status ?? 'pending') === tab); + if (search.trim()) { + const q = search.toLowerCase(); + list = list.filter((v) => v.name.toLowerCase().includes(q) || v.address?.toLowerCase().includes(q)); + } + return list; + }, [venues, tab, search]); + + // Separate active (non-skipped) from skipped within filtered + const activeVenues = useMemo(() => filtered.filter((v) => !skipped.has(v.id)), [filtered, skipped]); + const skippedVenues = useMemo(() => filtered.filter((v) => skipped.has(v.id)), [filtered, skipped]); + + const counts = useMemo(() => ({ + pending: venues.filter((v) => (v.moderation_status ?? 'pending') === 'pending').length, + approved: venues.filter((v) => v.moderation_status === 'approved').length, + hidden: venues.filter((v) => v.moderation_status === 'hidden').length, + }), [venues]); + + const handleSkip = useCallback((id: string) => { + setSkipped((prev) => new Set([...Array.from(prev), id])); + }, []); + + const handleUpdate = useCallback(async (id: string, patch: Partial) => { + const venue = venues.find((v) => v.id === id); + const wasPending = (venue?.moderation_status ?? 'pending') === 'pending'; + const becomesDecided = patch.moderation_status === 'approved' || patch.moderation_status === 'hidden'; + + const { error } = await supabase.from('fields').update(patch).eq('id', id); + if (error) { addToast(`Błąd: ${error.message}`, false); return; } + setVenues((prev) => prev.map((v) => v.id === id ? { ...v, ...patch } : v)); + addToast('Zapisano'); + + if (wasPending && becomesDecided) { + setSessionDone((n) => n + 1); + } + }, [venues, addToast]); + + const handleDelete = useCallback(async (id: string) => { + const { error } = await supabase.from('fields').delete().eq('id', id); + if (error) { addToast(`Błąd: ${error.message}`, false); return; } + setVenues((prev) => prev.filter((v) => v.id !== id)); + setSkipped((prev) => { const n = new Set(prev); n.delete(id); return n; }); + addToast('Usunięto obiekt'); + }, [addToast]); + + // --- Auth guards --- + if (authLoading || adminState === 'checking') { + return ( +
+
+
Ładowanie…
+
+ ); + } + if (adminState === 'no') { + return ( +
+
+
+ +

Brak dostępu

+
+
+ ); + } + + const progressPct = sessionTotal ? Math.round((sessionDone / sessionTotal) * 100) : 0; + + return ( +
+
+ + {/* Toasts */} +
+ {toasts.map((t) => ( +
+ {t.msg} +
+ ))} +
+ +
+ {/* Page header */} +
+
+

Moderacja boisk

+

+ {counts.pending} do sprawdzenia · {counts.approved} zatwierdzone · {counts.hidden} ukryte +

+
+ ← Admin +
+ + {/* Session progress bar */} + {sessionTotal !== null && sessionTotal > 0 && ( +
+
+
+ Postęp sesji + + {sessionDone} / {sessionTotal} przetworzonych + {skipped.size > 0 && ( + · {skipped.size} pominiętych + )} + +
+
+
+
+
+
+ )} + + {/* Filters */} +
+
+ {TABS.map(({ key, label }) => { + const cnt = key === 'all' ? venues.length : counts[key as ModerationStatus] ?? 0; + return ( + + ); + })} +
+
+ + setSearch(e.target.value)} + placeholder="Szukaj po nazwie lub adresie…" + className="w-full border border-slate-200 rounded-xl pl-9 pr-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500 bg-white" + /> +
+ {/* Result count */} + + {activeVenues.length} obiektów + {filtered.length !== venues.length && ` / ${venues.length} łącznie`} + +
+ + {/* Active grid */} + {loading ? ( +
+ {Array.from({ length: 10 }).map((_, i) => ( +
+ ))} +
+ ) : activeVenues.length === 0 && skippedVenues.length === 0 ? ( +
Brak wyników
+ ) : activeVenues.length === 0 && skippedVenues.length > 0 ? ( +
+

Wszystkie obiekty pominięte

+

Wróć do pominiętych poniżej, żeby je przetworzyć.

+
+ ) : ( +
+ {activeVenues.map((v) => ( + handleSkip(v.id)} + /> + ))} +
+ )} + + {/* Skipped section */} + {skippedVenues.length > 0 && ( +
+ + + {skippedOpen && ( +
+ {skippedVenues.map((v) => ( + + ))} +
+ )} +
+ )} +
+
+ ); +} diff --git a/frontend/src/app/admin/outreach/page.tsx b/frontend/src/app/admin/outreach/page.tsx new file mode 100644 index 0000000..22da67a --- /dev/null +++ b/frontend/src/app/admin/outreach/page.tsx @@ -0,0 +1,916 @@ +'use client'; + +import { useState, useEffect, useMemo, useCallback } from 'react'; +import Link from 'next/link'; +import { + Lock, Search, Phone, Mail, Globe, Download, Star, ChevronDown, + UserCheck, RotateCcw, Check, Sparkles, X, Building2, Clock, ExternalLink, MapPin, + AlertTriangle, Trash2, Eye, EyeOff, +} from 'lucide-react'; +import Header from '@/components/layout/Header'; +import Button from '@/components/ui/Button'; +import { useAuth, displayName } from '@/lib/auth'; +import { supabase } from '@/lib/supabase'; +import { getFields } from '@/lib/api'; +import type { MapVisibility } from '@/types'; +import { + getOutreachMap, saveOutreach, + STATUS_META, STATUS_ORDER, BOOKING_SYSTEM_META, BOOKING_SYSTEM_ORDER, + type Outreach, type OutreachStatus, type BookingSystem, type OutreachPatch, +} from '@/lib/outreach'; +import type { Field } from '@/types'; +import { + OUTREACH_DATA_FILTERS, fieldHasData, type DataKey, +} from '@/lib/fieldFilters'; + +const inputCls = + 'border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500'; + +function defaultOutreach(fieldId: string): Outreach { + return { fieldId, status: 'nowy', bookingSystem: 'nieznany', priority: 0 }; +} + +function todayISO(): string { + return new Date().toISOString().slice(0, 10); +} + +function formatPl(iso?: string): string { + if (!iso) return '—'; + const d = new Date(iso); + if (Number.isNaN(d.getTime())) return '—'; + return d.toLocaleDateString('pl-PL', { day: 'numeric', month: 'short', year: 'numeric' }); +} + +function siteHost(url: string): string { + try { return new URL(url).hostname.replace(/^www\./, ''); } catch { return url.slice(0, 40); } +} + +// --------------------------------------------------------------------------- +// Map visibility — shared metadata + control +// --------------------------------------------------------------------------- +const VIS_OPTIONS: { value: MapVisibility; icon: React.ElementType; label: string; short: string; cls: string }[] = [ + { value: 'public', icon: Eye, label: 'Na mapie', short: 'Mapa', cls: 'bg-green-50 text-green-700 border-green-200' }, + { value: 'organizer_only', icon: EyeOff, label: 'Tylko org.', short: 'Org.', cls: 'bg-amber-50 text-amber-700 border-amber-200' }, + { value: 'hidden', icon: X, label: 'Ukryty', short: 'Ukryty', cls: 'bg-red-50 text-red-700 border-red-200' }, +]; + +function VisibilityControl({ value, onChange, busy, compact }: { + value: MapVisibility; + onChange: (v: MapVisibility) => void; + busy?: boolean; + compact?: boolean; +}) { + return ( +
e.stopPropagation()} + > + {VIS_OPTIONS.map(({ value: v, icon: Icon, label, short, cls }) => ( + + ))} +
+ ); +} + +// --------------------------------------------------------------------------- +// Toast +// --------------------------------------------------------------------------- +interface Toast { id: number; message: string; type: 'success' | 'error' } +let toastCounter = 0; + +// --------------------------------------------------------------------------- +// CSV export +// --------------------------------------------------------------------------- +function csvCell(v: string | number | undefined | null): string { + const s = String(v ?? ''); + return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s; +} + +function exportCsv(rows: { field: Field; o: Outreach }[]) { + const headers = [ + 'Nazwa', 'Dzielnica', 'Adres', 'Kod', 'Sporty', 'Telefon', 'E-mail', 'WWW', 'Operator', + 'Status', 'System rezerwacji', 'Przypisany', 'Osoba kontaktowa', + 'Ostatni kontakt', 'Followup', 'Notatki', 'AI', 'Ostatnia zmiana', + ]; + const lines = rows.map(({ field: f, o }) => [ + f.name, f.district, f.address, f.postcode, f.sport.join(' / '), + f.phone, f.email, f.website, f.operator, + STATUS_META[o.status].label, BOOKING_SYSTEM_META[o.bookingSystem], + o.assignedName, o.contactPerson, + o.lastContactedAt ? formatPl(o.lastContactedAt) : '', + o.nextFollowupAt ? formatPl(o.nextFollowupAt) : '', + o.notes, o.aiSummary, o.updatedByName, + ].map(csvCell).join(',')); + + const csv = '' + [headers.join(','), ...lines].join('\n'); + const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `obiekty-outreach-${todayISO()}.csv`; + a.click(); + URL.revokeObjectURL(url); +} + +// =========================================================================== +export default function OutreachPanel() { + const { user, loading: authLoading } = useAuth(); + const [adminState, setAdminState] = useState<'checking' | 'yes' | 'no'>('checking'); + + const [fields, setFields] = useState([]); + const [outreach, setOutreach] = useState>(new Map()); + const [loading, setLoading] = useState(true); + + const [toasts, setToasts] = useState([]); + const addToast = useCallback((message: string, type: 'success' | 'error' = 'success') => { + const id = ++toastCounter; + setToasts((p) => [...p, { id, message, type }]); + setTimeout(() => setToasts((p) => p.filter((t) => t.id !== id)), 3500); + }, []); + + // --- Filters --- + const [search, setSearch] = useState(''); + const [fStatus, setFStatus] = useState<'all' | OutreachStatus>('all'); + const [fSport, setFSport] = useState('all'); + const [fDistrict, setFDistrict] = useState('all'); + const [fAssign, setFAssign] = useState<'all' | 'mine' | 'unassigned'>('all'); + const [fVis, setFVis] = useState<'all' | MapVisibility>('all'); + const [fData, setFData] = useState([]); + const toggleData = useCallback((k: DataKey) => { + setFData((prev) => (prev.includes(k) ? prev.filter((x) => x !== k) : [...prev, k])); + }, []); + const [fHideDone, setFHideDone] = useState(false); + const [fDuplicates, setFDuplicates] = useState(false); + const [expanded, setExpanded] = useState(null); + + // --- Admin check (own, to avoid access-denied flash) --- + useEffect(() => { + if (authLoading) return; + if (!user) { setAdminState('no'); return; } + supabase.from('profiles').select('is_admin').eq('id', user.id).maybeSingle() + .then(({ data }) => setAdminState(data?.is_admin ? 'yes' : 'no'), () => setAdminState('no')); + }, [authLoading, user]); + + // --- Load data --- + useEffect(() => { + if (adminState !== 'yes') return; + let cancelled = false; + (async () => { + setLoading(true); + try { + const [fieldsRes, oMap] = await Promise.all([ + getFields({ limit: 5000 }), + getOutreachMap(), + ]); + if (cancelled) return; + setFields(fieldsRes.fields); + setOutreach(oMap); + } catch (e) { + if (!cancelled) addToast(e instanceof Error ? e.message : 'Błąd ładowania', 'error'); + } finally { + if (!cancelled) setLoading(false); + } + })(); + return () => { cancelled = true; }; + }, [adminState, addToast]); + + const getO = useCallback( + (fieldId: string): Outreach => outreach.get(fieldId) ?? defaultOutreach(fieldId), + [outreach], + ); + + // --- Persist a patch --- + const persist = useCallback(async (fieldId: string, patch: OutreachPatch) => { + if (!user) return; + // optimistic + setOutreach((prev) => { + const next = new Map(prev); + next.set(fieldId, { ...(prev.get(fieldId) ?? defaultOutreach(fieldId)), ...patch } as Outreach); + return next; + }); + try { + const saved = await saveOutreach(fieldId, patch, { id: user.id, name: displayName(user) }); + setOutreach((prev) => { const n = new Map(prev); n.set(fieldId, saved); return n; }); + } catch (e) { + addToast(e instanceof Error ? e.message : 'Nie udało się zapisać', 'error'); + } + }, [user, addToast]); + + // --- Suspicious contacts: phone/email shared by 3+ fields --- + const suspiciousMap = useMemo(() => { + const phoneCnt = new Map(); + const emailCnt = new Map(); + fields.forEach((f) => { + if (f.phone) phoneCnt.set(f.phone, (phoneCnt.get(f.phone) ?? 0) + 1); + if (f.email) emailCnt.set(f.email, (emailCnt.get(f.email) ?? 0) + 1); + }); + const result = new Map(); + fields.forEach((f) => { + const pc = f.phone ? (phoneCnt.get(f.phone) ?? 1) : 1; + const ec = f.email ? (emailCnt.get(f.email) ?? 1) : 1; + if (pc >= 3 || ec >= 3) result.set(f.id, { phoneCount: pc, emailCount: ec }); + }); + return result; + }, [fields]); + + // --- Toggle map_visibility on a field --- + const changeVisibility = useCallback(async (fieldId: string, v: MapVisibility) => { + const { error } = await supabase + .from('fields') + .update({ map_visibility: v }) + .eq('id', fieldId); + if (error) { addToast(error.message, 'error'); return; } + setFields((prev) => prev.map((f) => f.id === fieldId ? { ...f, mapVisibility: v } : f)); + addToast(v === 'public' ? 'Obiekt widoczny na mapie' : 'Obiekt ukryty z mapy'); + }, [addToast]); + + // --- Clear AI-enriched contact data from fields table --- + const clearContact = useCallback(async (fieldId: string) => { + const { error } = await supabase + .from('fields') + .update({ phone: null, email: null, website: null }) + .eq('id', fieldId); + if (error) { addToast(error.message, 'error'); return; } + setFields((prev) => prev.map((f) => + f.id === fieldId ? { ...f, phone: undefined, email: undefined, website: undefined } : f, + )); + addToast('Dane kontaktowe wyczyszczone'); + }, [addToast]); + + // --- Sports for filter dropdown --- + const sportOptions = useMemo(() => { + const s = new Set(); + fields.forEach((f) => f.sport.forEach((x) => s.add(x))); + return Array.from(s).sort(); + }, [fields]); + + // --- Districts for filter dropdown --- + const districtOptions = useMemo(() => { + const s = new Set(); + fields.forEach((f) => { if (f.district) s.add(f.district); }); + return Array.from(s).sort((a, b) => a.localeCompare(b, 'pl')); + }, [fields]); + + // --- Filtered + sorted rows --- + const rows = useMemo(() => { + const q = search.trim().toLowerCase(); + const list = fields + .map((f) => ({ field: f, o: getO(f.id) })) + .filter(({ field: f, o }) => { + if (q && !f.name.toLowerCase().includes(q) && !f.address.toLowerCase().includes(q)) return false; + if (fStatus !== 'all' && o.status !== fStatus) return false; + if (fSport !== 'all' && !f.sport.includes(fSport)) return false; + if (fDistrict !== 'all' && f.district !== fDistrict) return false; + if (fAssign === 'mine' && o.assignedTo !== user?.id) return false; + if (fAssign === 'unassigned' && o.assignedTo) return false; + if (fVis !== 'all' && f.mapVisibility !== fVis) return false; + // Multi-select data requirements (AND). 'booking' also looks at the + // outreach record, since the team logs the reservation system there. + for (const k of fData) { + const ok = k === 'booking' + ? (o.bookingSystem !== 'nieznany' && o.bookingSystem !== 'brak') || fieldHasData(f, 'booking') + : fieldHasData(f, k); + if (!ok) return false; + } + if (fHideDone && (o.status === 'umowiony' || o.status === 'odrzucony')) return false; + if (fDuplicates && !suspiciousMap.has(f.id)) return false; + return true; + }); + // sort: high priority first, then has-contact-info, then name + list.sort((a, b) => { + if (b.o.priority !== a.o.priority) return b.o.priority - a.o.priority; + const ca = a.field.phone || a.field.email || a.field.website ? 1 : 0; + const cb = b.field.phone || b.field.email || b.field.website ? 1 : 0; + if (cb !== ca) return cb - ca; + return a.field.name.localeCompare(b.field.name, 'pl'); + }); + return list; + }, [fields, getO, search, fStatus, fSport, fDistrict, fAssign, fVis, fData, fHideDone, fDuplicates, suspiciousMap, user?.id]); + + const filtersActive = !!search || fStatus !== 'all' || fSport !== 'all' || fDistrict !== 'all' || fAssign !== 'all' || fVis !== 'all' || fData.length > 0 || fHideDone || fDuplicates; + + // --- Pipeline stats --- + const stats = useMemo(() => { + const counts = Object.fromEntries(STATUS_ORDER.map((s) => [s, 0])) as Record; + fields.forEach((f) => { counts[getO(f.id).status]++; }); + return counts; + }, [fields, getO]); + + // ---- Guards ---- + if (authLoading || adminState === 'checking') { + return ( +
+
+
+
+
+ {[1, 2, 3, 4, 5].map((i) =>
)} +
+
+
+ ); + } + + if (adminState === 'no') { + return ( +
+
+
+
+ +

Brak dostępu

+

Panel kontaktu z obiektami jest dostępny tylko dla administratorów.

+ Wróć na stronę główną +
+
+
+ ); + } + + return ( +
+
+
+ {/* Title row */} +
+
+

Kontakt z obiektami

+

+ {loading ? 'Ładowanie…' : `${fields.length} obiektów łącznie`} +

+
+ +
+ + {/* Pipeline stats */} +
+ {STATUS_ORDER.map((s) => ( + + ))} +
+ + {/* Filters */} +
+
+ + setSearch(e.target.value)} + placeholder="Szukaj po nazwie lub adresie…" + className={`${inputCls} w-full pl-9`} + /> +
+ + {districtOptions.length > 0 && ( + + )} + + + + + {/* Count badge */} + {!loading && ( + + {rows.length}{filtersActive ? ` / ${fields.length}` : ''} obiektów + + )} + + {/* Multi-select: which data the venue has (AND) */} +
+ Ma dane: + {OUTREACH_DATA_FILTERS.map(({ key, label }) => { + const active = fData.includes(key); + return ( + + ); + })} + {fData.length > 0 && ( + + )} +
+
+ + {/* Table */} + {loading ? ( +
+ {[1, 2, 3, 4, 5, 6].map((i) =>
)} +
+ ) : rows.length === 0 ? ( +
+

Brak obiektów spełniających filtry

+
+ ) : ( +
+ + + + + + + + + + + + + + {rows.map(({ field: f, o }) => ( + setExpanded((cur) => (cur === f.id ? null : f.id))} + onPatch={(patch) => persist(f.id, patch)} + currentUser={user ? { id: user.id, name: displayName(user) } : null} + onToast={addToast} + suspicious={suspiciousMap.get(f.id)} + onClearContact={() => clearContact(f.id)} + onVisibilityChange={(v) => changeVisibility(f.id, v)} + /> + ))} + +
ObiektMapaKontaktStatusSystemPrzypisany
+
+ )} +
+ + {/* Toasts */} +
+ {toasts.map((t) => ( +
+ {t.message} +
+ ))} +
+
+ ); +} + +// =========================================================================== +// Row + expandable editor +// =========================================================================== +interface RowProps { + field: Field; + o: Outreach; + isExpanded: boolean; + onToggle: () => void; + onPatch: (patch: OutreachPatch) => void; + currentUser: { id: string; name: string } | null; + onToast: (m: string, t?: 'success' | 'error') => void; + suspicious?: { phoneCount: number; emailCount: number }; + onClearContact: () => Promise; + onVisibilityChange: (v: MapVisibility) => Promise; +} + +function OutreachRow({ field: f, o, isExpanded, onToggle, onPatch, currentUser, onToast, suspicious, onClearContact, onVisibilityChange }: RowProps) { + // Local draft for free-text fields (saved explicitly) + const [notes, setNotes] = useState(o.notes ?? ''); + const [contactPerson, setContactPerson] = useState(o.contactPerson ?? ''); + const [followup, setFollowup] = useState(o.nextFollowupAt ?? ''); + const [assignName, setAssignName] = useState(o.assignedName ?? ''); + const [savingDraft, setSavingDraft] = useState(false); + const [clearingContact, setClearingContact] = useState(false); + const [togglingVis, setTogglingVis] = useState(false); + + // keep drafts in sync if outreach changes underneath + useEffect(() => { setNotes(o.notes ?? ''); }, [o.notes]); + useEffect(() => { setContactPerson(o.contactPerson ?? ''); }, [o.contactPerson]); + useEffect(() => { setFollowup(o.nextFollowupAt ?? ''); }, [o.nextFollowupAt]); + useEffect(() => { setAssignName(o.assignedName ?? ''); }, [o.assignedName]); + + const dirty = + notes !== (o.notes ?? '') || + contactPerson !== (o.contactPerson ?? '') || + followup !== (o.nextFollowupAt ?? '') || + assignName !== (o.assignedName ?? ''); + + const saveDraft = async () => { + setSavingDraft(true); + const patch: OutreachPatch = { notes, contactPerson, nextFollowupAt: followup }; + if (assignName !== (o.assignedName ?? '')) { + patch.assignedName = assignName || null; + if (!assignName) patch.assignedTo = null; + } + await onPatch(patch); + setSavingDraft(false); + onToast('Zapisano'); + }; + + const claim = () => { + if (!currentUser) return; + onPatch({ assignedTo: currentUser.id, assignedName: currentUser.name }); + }; + const release = () => onPatch({ assignedTo: null, assignedName: null }); + + const markContactedToday = () => onPatch({ lastContactedAt: new Date().toISOString() }); + + const isMyRow = o.assignedTo === currentUser?.id; + const isSomeoneElsesRow = !!o.assignedTo && !isMyRow; + + return ( + <> + + {/* Obiekt */} + +
+ +
+
+

{f.name}

+
+

+ {f.district && {f.district} · } + {f.address} +

+ {f.sport.length > 0 && ( +

{f.sport.join(' · ')}

+ )} +
+
+ + + {/* Mapa — szybki przełącznik widoczności */} + + { + setTogglingVis(true); + await onVisibilityChange(v); + setTogglingVis(false); + }} + /> + + + {/* Kontakt — pełny tekst */} + +
+ {suspicious && ( + + + {Math.max(suspicious.phoneCount, suspicious.emailCount)}× ten sam kontakt + + )} + {f.phone ? ( + e.stopPropagation()}> + {f.phone} + + ) : null} + {f.email ? ( + e.stopPropagation()}> + {f.email} + + ) : null} + {f.website ? ( + e.stopPropagation()}> + {siteHost(f.website)} + + ) : null} + {!f.phone && !f.email && !f.website && ( + brak kontaktu + )} +
+ + + {/* Status */} + +
+ + +
+ + + {/* System rezerwacji */} + + + + + {/* Przypisany */} + + {o.assignedName ? ( +
+ + {o.assignedName} + + {isMyRow ? ( + + ) : ( + + )} +
+ ) : ( + + )} + + + {/* Expand chevron */} + + + + + + {/* Expanded editor */} + {isExpanded && ( + + + + {/* Dane obiektu */} +
+ {/* Header: name + address + link */} +
+
+

{f.name}

+

+ + {f.address}{f.postcode ? `, ${f.postcode}` : ''} +

+ {f.sport.length > 0 && ( +

{f.sport.join(' · ')}

+ )} +
+
+ { + setTogglingVis(true); + await onVisibilityChange(v); + setTogglingVis(false); + }} + /> + e.stopPropagation()} + className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-primary-700 bg-primary-50 hover:bg-primary-100 transition-colors" + > + Otwórz + +
+
+
+ {f.phone && ( + + {f.phone} + + )} + {f.email && ( + + {f.email} + + )} + {f.website && ( + + {f.website} + + )} + {f.operator && ( + + {f.operator} + + )} + {f.openingHours && ( + + + {f.openingHours} + + )} + {f.description && ( +

{f.description}

+ )} +
+ {suspicious && ( +
+

+ + {f.phone && suspicious.phoneCount >= 3 && <>Telefon {f.phone} używany w {suspicious.phoneCount} obiektach. } + {f.email && suspicious.emailCount >= 3 && <>E-mail {f.email} używany w {suspicious.emailCount} obiektach. } + Prawdopodobnie błędne dane z AI. +

+ +
+ )} +
+ + {/* AI enrichment */} + {(o.aiSummary || o.bookingUrl) && ( +
+ +
+

+ AI znalazł{o.aiEnrichedAt ? ` · ${formatPl(o.aiEnrichedAt)}` : ''} + {o.bookingProvider && · {o.bookingProvider}} +

+ {o.aiSummary && ( +

{o.aiSummary}

+ )} + {o.bookingUrl && ( + + + {o.bookingUrl} + + )} +
+
+ )} + +
+ {/* Notes */} +
+ +