From 546d2c503f66ff5345a607f79aac045615d5d003 Mon Sep 17 00:00:00 2001 From: vsecoder Date: Fri, 17 Apr 2026 14:37:00 +0300 Subject: [PATCH] chore(release): clean up repo for v0.0.1 release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Excluded from release bundle: - CONTEXT.md, CHANGELOG.md (agent/project working notes) - client-app/ (React Native messenger — tracked separately) - contracts/hello_go/ (unused standalone example) Kept contracts/counter/ and contracts/name_registry/ as vm-test fixtures (referenced by vm/vm_test.go; NOT production contracts). Docs refactor: - docs/README.md — new top-level index with cross-references - docs/quickstart.md — rewrite around single-node as primary path - docs/node/README.md — full rewrite, all CLI flags, schema table - docs/api/README.md — add /api/well-known-version, /api/update-check - docs/contracts/README.md — split native (Go) vs WASM (user-deployable) - docs/update-system.md — new, full 5-layer update system design - README.md — link into docs/, drop CHANGELOG/client-app references Build-time version system (inherited from earlier commits this branch): - node --version / client --version with ldflags-injected metadata - /api/well-known-version with {build, protocol_version, features[]} - Peer-version gossip on dchain/version/v1 - /api/update-check against Gitea release API - deploy/single/update.sh with semver guard + 15-min systemd jitter --- .gitignore | 5 + CHANGELOG.md | 181 - CONTEXT.md | 229 - README.md | 320 +- client-app/.gitignore | 0 client-app/README.md | 93 - client-app/app.json | 36 - client-app/app/(app)/_layout.tsx | 127 - client-app/app/(app)/chats/[id].tsx | 413 - client-app/app/(app)/chats/_layout.tsx | 7 - client-app/app/(app)/chats/index.tsx | 337 - client-app/app/(app)/new-contact.tsx | 332 - client-app/app/(app)/requests.tsx | 236 - client-app/app/(app)/settings.tsx | 732 -- client-app/app/(app)/wallet.tsx | 596 -- client-app/app/(auth)/create.tsx | 82 - client-app/app/(auth)/created.tsx | 118 - client-app/app/(auth)/import.tsx | 301 - client-app/app/_layout.tsx | 40 - client-app/app/index.tsx | 220 - client-app/babel.config.js | 12 - client-app/components/ui/Avatar.tsx | 37 - client-app/components/ui/Badge.tsx | 24 - client-app/components/ui/Button.tsx | 76 - client-app/components/ui/Card.tsx | 16 - client-app/components/ui/Input.tsx | 34 - client-app/components/ui/Separator.tsx | 7 - client-app/components/ui/index.ts | 6 - client-app/global.css | 3 - client-app/hooks/useBalance.ts | 94 - client-app/hooks/useContacts.ts | 80 - client-app/hooks/useMessages.ts | 123 - client-app/hooks/useWellKnownContracts.ts | 61 - client-app/lib/api.ts | 701 -- client-app/lib/crypto.ts | 156 - client-app/lib/storage.ts | 101 - client-app/lib/store.ts | 103 - client-app/lib/types.ts | 86 - client-app/lib/utils.ts | 35 - client-app/lib/ws.ts | 401 - client-app/metro.config.js | 6 - client-app/nativewind-env.d.ts | 1 - client-app/package-lock.json | 10317 -------------------- client-app/package.json | 51 - client-app/tailwind.config.js | 28 - client-app/tsconfig.json | 9 - contracts/hello_go/hello_go_abi.json | 38 - contracts/hello_go/main.go | 104 - docs/README.md | 34 + docs/api/README.md | 81 +- docs/contracts/README.md | 96 +- docs/development/README.md | 15 +- docs/node/README.md | 301 +- docs/quickstart.md | 240 +- docs/update-system.md | 201 + 55 files changed, 702 insertions(+), 17381 deletions(-) delete mode 100644 CHANGELOG.md delete mode 100644 CONTEXT.md delete mode 100644 client-app/.gitignore delete mode 100644 client-app/README.md delete mode 100644 client-app/app.json delete mode 100644 client-app/app/(app)/_layout.tsx delete mode 100644 client-app/app/(app)/chats/[id].tsx delete mode 100644 client-app/app/(app)/chats/_layout.tsx delete mode 100644 client-app/app/(app)/chats/index.tsx delete mode 100644 client-app/app/(app)/new-contact.tsx delete mode 100644 client-app/app/(app)/requests.tsx delete mode 100644 client-app/app/(app)/settings.tsx delete mode 100644 client-app/app/(app)/wallet.tsx delete mode 100644 client-app/app/(auth)/create.tsx delete mode 100644 client-app/app/(auth)/created.tsx delete mode 100644 client-app/app/(auth)/import.tsx delete mode 100644 client-app/app/_layout.tsx delete mode 100644 client-app/app/index.tsx delete mode 100644 client-app/babel.config.js delete mode 100644 client-app/components/ui/Avatar.tsx delete mode 100644 client-app/components/ui/Badge.tsx delete mode 100644 client-app/components/ui/Button.tsx delete mode 100644 client-app/components/ui/Card.tsx delete mode 100644 client-app/components/ui/Input.tsx delete mode 100644 client-app/components/ui/Separator.tsx delete mode 100644 client-app/components/ui/index.ts delete mode 100644 client-app/global.css delete mode 100644 client-app/hooks/useBalance.ts delete mode 100644 client-app/hooks/useContacts.ts delete mode 100644 client-app/hooks/useMessages.ts delete mode 100644 client-app/hooks/useWellKnownContracts.ts delete mode 100644 client-app/lib/api.ts delete mode 100644 client-app/lib/crypto.ts delete mode 100644 client-app/lib/storage.ts delete mode 100644 client-app/lib/store.ts delete mode 100644 client-app/lib/types.ts delete mode 100644 client-app/lib/utils.ts delete mode 100644 client-app/lib/ws.ts delete mode 100644 client-app/metro.config.js delete mode 100644 client-app/nativewind-env.d.ts delete mode 100644 client-app/package-lock.json delete mode 100644 client-app/package.json delete mode 100644 client-app/tailwind.config.js delete mode 100644 client-app/tsconfig.json delete mode 100644 contracts/hello_go/hello_go_abi.json delete mode 100644 contracts/hello_go/main.go create mode 100644 docs/README.md create mode 100644 docs/update-system.md diff --git a/.gitignore b/.gitignore index 8e54f72..9d0821e 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,8 @@ Thumbs.db # Claude Code / agent local state .claude/ + +# Not part of the release bundle — tracked separately +CONTEXT.md +CHANGELOG.md +client-app/ diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 4542c2a..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,181 +0,0 @@ -# DChain CHANGELOG - -Consolidated record of what landed. Replaces the now-deleted -`REFACTOR_PLAN.md`, `NODE_ONBOARDING.md`, and `ROADMAP.md` — every numbered -item there is either shipped (listed below) or explicitly deferred. - ---- - -## Production-ready stack (shipped) - -### Consensus & chain -- **PBFT multi-sig validator admission**. `ADD_VALIDATOR` requires - ⌈2/3⌉ cosigs from the current set + candidate must have ≥ `MinValidatorStake` - (1 T) locked via STAKE. Same gate on forced `REMOVE_VALIDATOR`; self- - removal stays unilateral. -- **Equivocation slashing**. `SLASH` tx with `reason=equivocation` carries - both conflicting PREPARE/COMMIT messages as evidence; `ValidateEquivocation` - verifies on-chain — any node can report, no trust. Offender's stake is - burned and they're evicted from the set. -- **Liveness tracking**. PBFT records per-validator last-seen seqNum; - `LivenessReport()` + `MissedBlocks()` surface stalemates. Exposed via - `dchain_max_missed_blocks` Prometheus gauge. -- **Fair mempool**. Per-sender FIFO queues drained round-robin into - proposals; one spammer can't starve others. -- **Block-reward fix**. Synthetic BLOCK_REWARD transactions use `From=""` - so self-validators don't appear to pay themselves in history. - -### Storage & stability -- **Re-entrant-deadlock fix**. Dedicated `configMu` and `nativeMu` separate - from `c.mu` — applyTx can safely read config/native registry while - `AddBlock` holds the write lock. -- **BadgerDB tuning**. `WithValueLogFileSize(64 MiB)` + `WithNumVersionsToKeep(1)` - + background `StartValueLogGC` every 5 minutes + one-shot `CompactNow()` - at startup. Reclaims gigabytes from upgraded nodes automatically. -- **`TipIndex()`** — lock-free reads so `/api/blocks` and `/api/txs/recent` - never hang even when `AddBlock` is stuck. -- **Chronological tx index** (`txchron::`). `RecentTxs` - runs in O(limit) instead of O(empty blocks) — important when tps is low. -- **WASM VM timeout + `WithCloseOnContextDone`**. Any contract call aborts - at 30 s hard cap; gas metering evasion can no longer freeze the chain. - -### Native contracts -- **`native:username_registry`** (v2.1.0). Replaces WASM registry — 100× - faster, no VM failure surface. `register(name)` requires exact - `tx.Amount = 10 000 µT` (burned, visible in history). Min length 4, - lowercase `a-z 0-9 _ -`, first char letter, reserved words blacklist. -- **Native dispatcher** in `applyTx` → checks `native:*` IDs first, falls - through to WASM VM otherwise. ABI JSON + contract metadata surfaced via - `/api/contracts/:id` with `"native": true` flag. -- **Well-known auto-discovery**. `/api/well-known-contracts` returns - canonical contract IDs indexed by ABI name; native always wins. Client - auto-syncs on connect. - -### WebSocket gateway (push-based UX) -- **`GET /api/ws`** — persistent bidirectional JSON-framed connection. -- **Topics**: `blocks`, `tx`, `addr:`, `inbox:`, - `contract_log`, `contract:`, `typing:`. -- **`auth` op**. Client signs server-issued nonce with Ed25519; hub binds - connection to pubkey so scoped subscriptions (`addr:*`, `inbox:*`, - `typing:*`) are accepted only for owned identities. -- **`submit_tx` op**. Low-latency tx submission with correlated - `submit_ack` frame; removes the HTTP round-trip. Client falls back to - POST `/api/tx` automatically if WS is down. -- **Typing indicators**. Ephemeral `typing` op, authenticated, scoped to - recipient. Mobile client shows "печатает…" in chat header. -- **Per-connection quotas**. Max 10 connections / IP, 32 subs / connection. - Bounded outbox drops oldest on overflow with `{event:"lag"}` notice. -- **Fanout mirrors SSE**. `eventBus` dispatches to SSE + WS + future - consumers from one emit site. - -### Relay mailbox -- **Push notifications**. `Mailbox.SetOnStore` hook → `wsHub.EmitInbox(...)` - on every fresh envelope. Client's `useMessages` subscribes instead of - polling every 3 s. -- **Relay TTL**. `REGISTER_RELAY` and HEARTBEAT (from registered relays) - refresh a `relayhb:` timestamp; `/api/relays` filters anything - older than 2 hours. Stale relays are delisted automatically. - -### Node onboarding -- **`--join `** — multi-seed bootstrap. Tries each URL in - order, persists the live list to `/seeds.json` on first success so - subsequent restarts don't need the CLI flag. -- **`/api/network-info`** — one-shot payload (chain_id, genesis_hash, - validators, peers, contracts, stats) for joiners. -- **`/api/peers`** — live libp2p peer list with multiaddrs. -- **Genesis-hash verification**. A node with expected hash aborts if its - local block 0 doesn't match (protection against forged seeds). Override - with `--allow-genesis-mismatch` for migrations. -- **Gap-fill on gossip**. Blocks with `b.Index > tip+1` trigger - `SyncFromPeerFull` to the gossiping peer (rate-limited 1 per peer per - minute). Nodes recover from brief outages without restart. - -### API surface & security -- **Rate limiter** (`node/api_guards.go`). Per-IP token bucket on - `/api/tx` and `/v2/chain/transactions`: 10 tx/s, burst 20. -- **Request-size cap**. `/api/tx` body ≤ 64 KiB. -- **Timestamp validation**. ±1 h window on submit, refuses clock-skewed - or replayed txs. -- **Humanised errors in client**. `humanizeTxError` translates 429 / - 400+timestamp / 400+signature / network-failure into Russian user- - facing text. - -### Observability & ops -- **Prometheus `/metrics`**. Zero-dep in-tree implementation (`node/metrics.go`) - with counters (blocks, txs, submit accepted/rejected), gauges - (ws connections, peer count, max missed blocks), histogram (block - commit seconds). -- **Load test**. `cmd/loadtest` — N concurrent WS clients with auth + - scoped subs + TRANSFER at rate. Validates chain advances, reject rate, - ws-drop count. Smoke at 20 clients × 15 s → 136 accepted / 0 rejected. -- **Structured logging**. `--log-format=text|json` flag. JSON mode routes - both `slog.*` and legacy `log.Printf` through one JSON handler for - Loki/ELK ingestion. -- **Observer mode**. `--observer` (env `DCHAIN_OBSERVER`) disables PBFT - producer + heartbeat + auto-relay-register; node still gossips and - serves HTTP/WS. For horizontally-scaling read-only API frontends. - -### Deployment -- **`deploy/single/`** — one-node production bundle: - - Same `Dockerfile.slim` as the cluster variant. - - Compose stack: 1 node + Caddy + optional Prometheus/Grafana. - - Supports three operator-chosen access modes: - - Public (no token) — anyone can read + submit. - - Public reads, token-gated writes (`DCHAIN_API_TOKEN` set) — - reads stay open, submit tx requires `Authorization: Bearer`. - - Fully private (`DCHAIN_API_TOKEN` + `DCHAIN_API_PRIVATE`) — - every endpoint requires the token. - - Runbook covers three scenarios: genesis node, joiner, private. -- **`deploy/prod/`** — 3-validator cluster for federations/consortiums. -- **Access-token middleware** in `node/api_guards.go`: - - `withWriteTokenGuard` gates POST /api/tx and WS submit_tx. - - `withReadTokenGuard` gates reads when `--api-private` is set. - - WS upgrade applies the same check; `submit_tx` ops on a - non-authenticated connection are rejected with `submit_ack` - rejected. -- **All CLI flags accept `DCHAIN_*` env fallbacks** for Docker-driven - configuration, including the new `DCHAIN_API_TOKEN` / - `DCHAIN_API_PRIVATE`. - -### Client (React Native / Expo) -- WebSocket module `lib/ws.ts` with reconnect, auto-resubscribe, - auto-auth on reconnect. -- `useBalance`, `useContacts`, `useMessages` — all push-based with HTTP - polling fallback after 15 s disconnect. -- `useWellKnownContracts` — auto-syncs `settings.contractId` with node's - canonical registry. -- Safe-area-aware layout throughout. Tab bar no longer hides under home - indicator on iPhone. -- Username purchase UI with live validation (min 4, first letter, charset). -- Transaction detail sheet with system-tx handling (BLOCK_REWARD shows - "Сеть" as counterpart, not validator's self-pay). - ---- - -## Deliberately deferred - -- **Split `blockchain/chain.go`** into `state/`, `applytx/`, `mempool/`, - `index/`, `events/` subpackages. A ~2.5k-line single-file refactor is - high risk; to be attempted after the chain has been running in prod - long enough that regressions there would be caught fast. -- **Full `p2p/` rewrite with typed event channel.** The libp2p integration - works; event-bus was added at the node layer instead (see `node/events.go`). -- **Full mempool admission pricing** (gas-priced priority queues). - Current fair round-robin works within spam-proofing needs. - ---- - -## Compatibility notes - -- BadgerDB tuning is compatible with databases created by previous - versions; the first run reclaims old value-log space via `CompactNow()`. -- `AddValidatorPayload` / `RemoveValidatorPayload` gained a `cosigs` - field; older payloads without it still parse (default empty), but will - fail the ⌈2/3⌉ threshold on chains with >1 validator. -- `BLOCK_REWARD` transactions changed from `From=validator` to `From=""`. - Old indexed records keep their previous `From`; new ones use the new - shape. Explorer/client handle both. -- Registration fee for usernames moved from internal `ctx.Debit` to - `tx.Amount`. The WASM username_registry is superseded by - `native:username_registry`; well-known endpoint returns the native - version as canonical. diff --git a/CONTEXT.md b/CONTEXT.md deleted file mode 100644 index 7e4c178..0000000 --- a/CONTEXT.md +++ /dev/null @@ -1,229 +0,0 @@ -# Decentralized Messenger — Project Context for Claude Code - -> Этот файл содержит полный контекст проекта из чата. Передай его в Claude Code командой: -> `claude --context CONTEXT.md` или просто открой в проекте и Claude Code подхватит его автоматически через CLAUDE.md - ---- - -## Суть проекта - -Полностью децентрализованный мессенджер с функциональностью уровня Telegram/ВКонтакте. -- Никакого центрального сервера -- Блокчейн как регулятор сети (не транспорт) -- Кастомный протокол с маскировкой трафика -- E2E шифрование верифицированное через блокчейн - ---- - -## Архитектура: четыре слоя - -### Слой 1 — Идентичность (L1 блокчейн) -Хранит только редкие важные события: -- Регистрация keypair (Ed25519) — раз в жизни -- Создание канала/чата — владелец и метаданные -- Изменение прав участников -- Открытие/закрытие платёжных state channels - -**Тела сообщений НИКОГДА не попадают в блокчейн.** - -### Слой 2 — Транспорт (relay-ноды) -- DHT маршрутизация (как BitTorrent) — нет центрального роутера -- Onion routing — каждый узел видит только следующий хоп -- Маскировка трафика — имитация HTTPS/QUIC или BitTorrent uTP -- Офлайн-буфер — зашифрованные конверты TTL 30 дней -- Proof of Relay — криптодоказательство честной доставки - -### Слой 3 — Хранение -- IPFS / Arweave — медиафайлы (content-addressed) -- Relay-кэш — горячая история, последние N сообщений -- Локальная зашифрованная БД (SQLite + NaCl) на устройстве -- Блокчейн — только хэши событий - -### Слой 4 — Приложение -Личные сообщения, группы, каналы, звонки (WebRTC P2P), сторис, посты, боты - ---- - -## Блокчейн: детали - -### Консенсус — PBFT (Tendermint-style) -- Финальность: 1–3 секунды -- Три фазы: Pre-prepare → Prepare → Commit -- Кворум: 2/3 валидаторов -- Валидаторы = операторы крупных relay-нод - -### Структура блока (Go) -```go -type Block struct { - Index uint64 - Timestamp time.Time - Transactions []*Transaction - PrevHash []byte - Hash []byte // SHA-256 - ValidatorSig []byte // Ed25519 - Validator string // pub_key валидатора -} - -type Transaction struct { - ID string - Type EventType // REGISTER_KEY | CREATE_CHANNEL | ADD_MEMBER | OPEN_PAY_CHAN ... - From string // pub_key отправителя - To string // pub_key получателя (если есть) - Payload []byte // json с данными события - Signature []byte // Ed25519 подпись From - Timestamp time.Time -} -``` - -### Хранение блоков -- Light nodes для мобильных клиентов (только заголовки) -- Sharding для валидаторов (каждый хранит свой шард) - ---- - -## Формат сообщений и постов - -### Личное сообщение — конверт -```go -type Envelope struct { - To []byte // pub_key получателя (для маршрутизации relay) - Nonce []byte // 24 случайных байта (anti-replay) - Ciphertext []byte // NaCl box: зашифровано pub_bob + priv_alice - SentAt int64 // unix timestamp (внутри шифра, снаружи не видно) -} -``` -Relay видит только `To`. Всё остальное — непрозрачный blob. - -Поток доставки: -1. Alice шифрует конверт pub_key Боба -2. Отправляет на свою relay-ноду (P2P) -3. Relay ищет Bob по DHT и доставляет (<50мс если онлайн) -4. Если офлайн — хранит конверт TTL 30 дней -5. Bob расшифровывает своим priv_key - -### Пост в канале -```go -type Post struct { - ChannelID string - SeqNum uint64 // монотонно растёт — клиент знает что пропустил - ContentHash []byte // sha256 тела = IPFS CID - AuthorSig []byte // подпись канала - Timestamp int64 - // Тело поста хранится в IPFS по ContentHash, не здесь -} -``` - -Поток публикации: -1. Автор создаёт Post, подписывает, загружает тело в IPFS -2. Relay-нода анонсирует через gossip-протокол: "в канале X пост #N" -3. Волна расходится по DHT к подписчикам -4. Клиент проверяет: sig автора → pub_key из блокчейна → sha256(тело) == ContentHash -5. Тело подгружается из IPFS лениво (lazy loading) - -Офлайн-синхронизация через seq_num: клиент хранит последний прочитанный номер, -при подключении запрашивает пропущенные у relay. - ---- - -## Экономика - -### Три механизма -1. **State Channels** — микроплатежи без газа на каждое действие (как Lightning Network) -2. **Proof of Relay** — нода зарабатывает токены за доказанную доставку сообщений -3. **Delegated Staking** — делегировать токены ноде оператора без своего сервера - -### Источники токенов -- Стартовый грант при регистрации -- PoW при создании keypair (CPU-барьер против Sybil) -- Лёгкая нода на телефоне (relay для соседей пока на зарядке) - ---- - -## Безопасность - -### E2E шифрование -- Signal Protocol (Double Ratchet) или Noise Protocol -- Sender Keys для групп — один симметричный ключ на группу -- Блокчейн решает проблему TOFU (верификация pub_key) - -### Защита метаданных -- Onion routing — relay не знает реального отправителя -- Sealed sender — сервер видит только получателя -- Маскировка трафика — QUIC / obfs4 / domain fronting - -### Sybil-защита -- PoW при регистрации -- Социальный граф — новый аккаунт без контактов имеет ограниченные права - ---- - -## Технический стек - -| Компонент | Библиотека | Причина | -|-----------|-----------|---------| -| Блокчейн | Cosmos SDK / Tendermint | Лучший PBFT на Go | -| P2P сеть | go-libp2p | Используется IPFS и Ethereum | -| БД блоков | BadgerDB | Go-native key-value | -| Криптография | crypto/ed25519 (stdlib) | В стандартной библиотеке | -| E2E шифрование | golang.org/x/crypto/nacl | NaCl/box | -| gRPC API | google.golang.org/grpc | Стандарт для Go | -| Relay протокол | кастомный поверх QUIC | Контроль маскировки | - ---- - -## Структура репозитория (планируемая) - -``` -/ -├── blockchain/ -│ ├── block.go # структура блока, хэширование, валидация -│ ├── chain.go # хранилище, state machine -│ └── types.go # Transaction, EventType и т.д. -├── consensus/ -│ └── pbft.go # PBFT: Pre-prepare → Prepare → Commit -├── identity/ -│ └── identity.go # keypair Ed25519, подпись, верификация -├── relay/ -│ ├── node.go # relay-нода: маршрутизация конвертов -│ ├── dht.go # DHT для discovery нод -│ └── buffer.go # офлайн-буфер с TTL -├── messaging/ -│ ├── envelope.go # личные сообщения (NaCl box) -│ └── channel.go # посты в каналах (IPFS + gossip) -├── crypto/ -│ ├── nacl.go # обёртки над NaCl box/secretbox -│ └── shamir.go # Shamir's Secret Sharing для recovery ключей -└── cmd/ - ├── node/ # relay-нода (сервер) - └── client/ # CLI клиент для тестирования -``` - ---- - -## Уже написанный код (в outputs/) - -- `blockchain/block.go` — Block, Transaction, GenesisBlock, ComputeHash, Validate -- `blockchain/chain.go` — Chain, AddBlock, applyTx, state (identities, channels) -- `consensus/consensus.go` — Node, HandleMessage, PBFT фазы, broadcast -- `identity/identity.go` — Generate, RegisterTx, SignMessage, VerifyMessage -- `main.go` — пример запуска, simulateBlockProduction, simulateMessageFlow - ---- - -## Следующие приоритеты для разработки - -1. Заменить in-memory map в chain.go на BadgerDB -2. Добавить go-libp2p для P2P между нодами -3. Реализовать DHT для discovery и маршрутизации -4. Написать relay/node.go с буфером конвертов -5. Написать messaging/envelope.go с NaCl шифрованием -6. View-change протокол в PBFT (смена лидера при падении) - ---- - -## Аналоги для изучения -- **Nostr** — минималистичный протокол, Lightning для relay -- **Tendermint** — лучший PBFT на Go, изучить view-change -- **go-libp2p** — P2P стек -- **Status.im** — мессенджер на Ethereum, токен SNT -- **lnd** — Lightning Network на Go (state channels) diff --git a/README.md b/README.md index 0977509..6f6271d 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,91 @@ # DChain Блокчейн-стек для децентрализованного мессенджера: + - **PBFT** консенсус с multi-sig validator governance и equivocation slashing - **Native Go контракты** рядом с WASM (wazero) — нулевая задержка для - системных сервисов типа username registry + системных сервисов типа `username_registry` - **WebSocket push API** — клиент не опрашивает, все события прилетают на соединение - **E2E-шифрованный relay mailbox** на libp2p gossipsub с TTL live-detection +- **Система обновлений:** build-time версия → `/api/well-known-version`, + peer-version gossip, `/api/update-check` против Gitea releases, + `update.sh` с semver guard - **Prometheus `/metrics`**, Caddy auto-HTTPS, observer mode, load-test -- React Native / Expo мессенджер (`client-app/`) ## Содержание -- [Быстрый старт (dev)](#быстрый-старт-dev) +- [Быстрый старт](#быстрый-старт) - [Продакшен деплой](#продакшен-деплой) -- [Клиент](#клиент) - [Архитектура](#архитектура) -- [Контракты](#контракты) - [REST / WebSocket API](#rest--websocket-api) - [CLI](#cli) - [Мониторинг](#мониторинг) - [Тесты](#тесты) -- [История изменений](#история-изменений) +- [Документация](#документация) --- -## Быстрый старт (dev) +## Быстрый старт -3 валидатора в docker-compose с замоканной «интернет» топологией: +Одна нода в Docker, HTTP API на `localhost:8080`, Explorer UI и Swagger +открыты: ```bash -docker compose up --build -d -open http://localhost:8081 # Explorer главной ноды -curl -s http://localhost:8081/api/netstats # синхронность ноды -``` +# 1. Собираем prod-образ +docker build -t dchain-node-slim -f deploy/prod/Dockerfile.slim . -После поднятия нативный `username_registry` уже доступен — отдельный -`--profile deploy` больше не нужен. Системные контракты регистрируются -в Go-коде при запуске (см. `blockchain/native.go`). - -## Продакшен деплой - -Два варианта, по масштабу: - -### 🔸 Single-node (`deploy/single/`) - -**Рекомендуется для личного/первого узла.** Один узел + Caddy TLS + опциональный Prometheus. - -```bash -cd deploy/single - -# 1. Ключ ноды (один раз, сохранить в безопасном месте) -docker build -t dchain-node-slim -f ../prod/Dockerfile.slim ../.. +# 2. Ключ ноды (один раз) mkdir -p keys docker run --rm --entrypoint /usr/local/bin/client \ -v "$PWD/keys:/out" dchain-node-slim \ keygen --out /out/node.json -# 2. Конфиг -cp node.env.example node.env && $EDITOR node.env -# минимум: DCHAIN_ANNOUNCE=, DOMAIN=, ACME_EMAIL= - -# 3. Вверх -docker compose up -d +# 3. Запуск (genesis-валидатор) +docker run -d --name dchain --restart unless-stopped \ + -p 4001:4001 -p 8080:8080 \ + -v dchain_data:/data \ + -v "$PWD/keys:/keys:ro" \ + -e DCHAIN_GENESIS=true \ + -e DCHAIN_ANNOUNCE=/ip4/127.0.0.1/tcp/4001 \ + dchain-node-slim \ + --db=/data/chain --mailbox-db=/data/mailbox --key=/keys/node.json \ + --relay-key=/data/relay.json --listen=/ip4/0.0.0.0/tcp/4001 --stats-addr=:8080 # 4. Проверка -curl -s https://$DOMAIN/api/netstats -curl -s https://$DOMAIN/api/well-known-version +open http://localhost:8080/ # Explorer +open http://localhost:8080/swagger # Swagger UI +curl -s http://localhost:8080/api/well-known-version | jq . ``` +3-node dev-кластер (для тестов PBFT кворума, slashing, federation): `docker compose up --build -d` — см. [`docs/quickstart.md`](docs/quickstart.md). + +## Продакшен деплой + +Два варианта, по масштабу. + +### 🔸 Single-node (`deploy/single/`) + +**Рекомендуется для личного/первого узла.** Один узел + Caddy TLS + +опциональный Prometheus. Полный runbook с 6 сценариями (публичная с UI, +headless, полностью приватная, genesis, joiner, auto-update) — в +[`deploy/single/README.md`](deploy/single/README.md). + #### Модели доступа | Режим | `DCHAIN_API_TOKEN` | `DCHAIN_API_PRIVATE` | Поведение | |-------|:------------------:|:--------------------:|-----------| | Public (default) | не задан | — | Все могут читать и писать | -| Public reads / token writes | задан | `false` | Читать — любой; submit tx — только с токеном | +| Token writes | задан | `false` | Читать — любой; submit tx — только с токеном | | Fully private | задан | `true` | Всё требует `Authorization: Bearer ` | #### UI / Swagger — включать или нет? -| Нужно | `DCHAIN_DISABLE_UI` | `DCHAIN_DISABLE_SWAGGER` | Где что открыто | -|-------|:-------------------:|:------------------------:|-----------------| -| Публичная с эксплорером + живой docs | не задано | не задано | `/` Explorer, `/swagger` UI, `/api/*`, `/metrics` | -| Headless API-нода с открытой OpenAPI | `true` | не задано | `/swagger` UI, `/api/*`, `/metrics` (Explorer выкл.) | -| Личная hardened | `true` | `true` | только `/api/*` + `/metrics` | +| Нужно | `DCHAIN_DISABLE_UI` | `DCHAIN_DISABLE_SWAGGER` | Открыто | +|-------|:-------------------:|:------------------------:|---------| +| Публичная с эксплорером + docs | не задано | не задано | `/` Explorer, `/swagger`, `/api/*`, `/metrics` | +| Headless API-нода с OpenAPI | `true` | не задано | `/swagger`, `/api/*`, `/metrics` | +| Личная hardened | `true` | `true` | `/api/*` + `/metrics` | Флаги читаются и из CLI (`--disable-ui`, `--disable-swagger`), и из env. `/api/*` JSON-поверхность регистрируется всегда — отключить её можно @@ -92,7 +94,7 @@ curl -s https://$DOMAIN/api/well-known-version #### Auto-update ```ini -# node.env — когда проект у вас в Gitea +# node.env — когда проект в Gitea DCHAIN_UPDATE_SOURCE_URL=https://gitea.example.com/api/v1/repos/OWNER/REPO/releases/latest UPDATE_ALLOW_MAJOR=false ``` @@ -104,175 +106,102 @@ sudo systemctl daemon-reload sudo systemctl enable --now dchain-update.timer ``` -Скрипт `deploy/single/update.sh` читает `/api/update-check`, делает -semver-guarded `git checkout tag`, ребилдит образ с injected версией, -smoke-test `node --version`, recreate, health poll. - -Подробные сценарии (первая нода, joiner, приватная, headless) + -полный API reference + systemd-интеграция + troubleshooting — в -**[`deploy/single/README.md`](deploy/single/README.md)**. +Подробно: [`docs/update-system.md`](docs/update-system.md) + [`deploy/UPDATE_STRATEGY.md`](deploy/UPDATE_STRATEGY.md). ### 🔹 Multi-validator (`deploy/prod/`) -3 validator'а в PBFT-кворуме, Caddy с ip_hash для WS-стикинесса и -least-conn для REST. Для федераций / консорциумов — см. -`deploy/prod/README.md`. - -## Клиент - -```bash -cd client-app -npm install -npx expo start # Expo Dev Tools — iOS / Android / Web -``` - -React Native + Expo + NativeWind. Ключевые экраны: - -| Экран | Описание | -|-------|----------| -| Welcome / Create Account | Генерация Ed25519 + X25519 keypair | -| Chat List | Диалоги + real-time via WS (`inbox:`) | -| Chat | E2E NaCl, typing-индикатор, day-separators | -| Contact Requests | Входящие запросы (push через `addr:`) | -| Add Contact | Поиск по `@username` (native registry) или hex | -| Wallet | Баланс, история (push), кошельковые action buttons | -| Settings | Нода, `@username` покупка, экспорт ключа | - -Клиент подсоединяется к одной ноде через HTTP + WebSocket: -- **WS для всего real-time** (balance, inbox, contacts, tx commit). -- **HTTP fallback** через 15 s после обрыва, автоматически. -- **Auto-discovery** канонического `username_registry` через - `/api/well-known-contracts`. +3 validator'а в PBFT-кворуме, Caddy с `ip_hash` для WS-стикинесса и +`least-conn` для REST. Для федераций / консорциумов — см. +[`deploy/prod/README.md`](deploy/prod/README.md) и +[`docs/node/multi-server.md`](docs/node/multi-server.md). ## Архитектура ``` ┌────────────┐ libp2p ┌────────────┐ libp2p ┌────────────┐ -│ node1 │◄─────pubsub──►│ node2 │◄─────pubsub──►│ node3 │ +│ node-A │◄─────pubsub──►│ node-B │◄─────pubsub──►│ node-C │ │ validator │ │ validator │ │ validator │ │ + relay │ │ + relay │ │ + relay │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ │ │ │ HTTPS / wss (via Caddy) │ │ ▼ ▼ ▼ - mobile / web / CLI clients (load-balanced ip_hash for WS) + mobile / web / CLI clients (ip_hash для WS, least-conn для REST) ``` -Слои (`blockchain/`): -- `chain.go` — блочная машина (applyTx, AddBlock, BadgerDB) -- `native.go` — системные Go-контракты (интерфейс + registry) -- `native_username.go` — реализация username_registry -- `equivocation.go` — проверка evidence для SLASH -- `types.go` — транзакции, payloads +Четыре слоя: network → chain → transport → app. Детали — в +[`docs/architecture.md`](docs/architecture.md). -Сети (`p2p/`): -- gossipsub topics `dchain/tx/v1`, `dchain/blocks/v1` -- стрим-протокол `/dchain/sync/1.0.0` для catch-up -- mDNS + DHT для peer discovery +Основные пакеты: -Консенсус (`consensus/pbft.go`): -- Pre-prepare → Prepare → Commit, 2/3 quorum -- liveness tracking, equivocation detection (`recordVote`, `TakeEvidence`) -- per-sender FIFO мемпул + round-robin drain до `MaxTxsPerBlock` - -Node service layer (`node/`): -- HTTP API + SSE + **WebSocket hub** с auth и topic-based fanout -- Prometheus `/metrics` (zero-dep) -- event bus (`events.go`) — SSE + WS + future consumers с одного emit'а -- rate-limiter + body-size cap на tx submit - -Relay (`relay/`): -- encrypted envelope routing через gossipsub -- BadgerDB mailbox для offline-получателей (TTL 7 дней) -- `SetOnStore` hook push'ит новые envelopes в WS - -## Контракты - -**Системные (native Go)** — зарегистрированы при запуске ноды: - -| ID | Назначение | ABI | -|----|-----------|-----| -| `native:username_registry` | `@username` → адрес | `register`, `resolve`, `lookup`, `transfer`, `release` | - -**Пользовательские (WASM)** — деплоятся через `DEPLOY_CONTRACT` tx. -TinyGo SDK + host functions (get_state, transfer, get_caller, log, -call_contract, …). Ручной деплой из `scripts/deploy_contracts.sh` или -CLI `client deploy-contract --wasm ...`. - -Обзор ABI / примеры — `docs/contracts/` (отдельные файлы на auction, -escrow, governance, username_registry). +| Путь | Роль | +|------|------| +| `blockchain/` | Блочная машина, applyTx, native-контракты, schema-migrations | +| `consensus/` | PBFT (Pre-prepare → Prepare → Commit), мемпул, equivocation | +| `p2p/` | libp2p host, gossipsub, sync протокол, peer-version gossip | +| `node/` | HTTP + WS API, SSE, metrics, access control | +| `node/version/` | Build-time version metadata (ldflags-инжектимый) | +| `vm/` | wazero runtime для WASM-контрактов + gas model | +| `relay/` | E2E mailbox с NaCl-envelopes | +| `identity/` | Ed25519 + X25519 keypair, tx signing | +| `economy/` | Fee model, rewards | +| `wallet/` | Optional payout wallet (отдельный ключ) | ## REST / WebSocket API -### Chain +Обзор (полный reference — [`docs/api/README.md`](docs/api/README.md)): +### Chain + stats | Endpoint | Описание | |----------|----------| -| `GET /api/netstats` | total_blocks, tx_count, supply, peer count | -| `GET /api/blocks?limit=N` | Последние блоки | -| `GET /api/block/{index}` | Конкретный блок | -| `GET /api/tx/{id}` | Транзакция по ID | -| `GET /api/txs/recent?limit=N` | Последние tx (O(limit)) | -| `GET /api/address/{pubkey}` | Баланс + история tx | -| `GET /api/validators` | Активный validator set | -| `GET /api/network-info` | One-shot bootstrap payload (chain_id, genesis, peers, validators, contracts) | -| `GET /api/peers` | Живые libp2p peer'ы | -| `GET /api/well-known-contracts` | Канонические контракты (native + WASM) | -| `GET /api/contracts/{id}` | Метаданные контракта, `"native": true/false` | -| `GET /api/contracts/{id}/state/{key}` | Прямое чтение state | -| `GET /api/relays` | Список relay-нод (filtered TTL) | +| `GET /api/netstats` | height, total_txs, supply, validator_count | +| `GET /api/network-info` | chain_id, genesis, peers, validators, contracts | +| `GET /api/blocks?limit=N` / `/api/block/{index}` | Блоки | +| `GET /api/tx/{id}` / `/api/txs/recent?limit=N` | Транзакции | +| `GET /api/address/{pub_or_addr}` | Баланс + история | +| `GET /api/validators` | Validator set | +| `GET /api/peers` | Connected peers + их версии | | `POST /api/tx` | Submit signed tx (rate-limited, size-capped) | +### Discovery + update +| Endpoint | Описание | +|----------|----------| +| `GET /api/well-known-version` | `{node_version, build{}, protocol_version, features[], chain_id}` | +| `GET /api/well-known-contracts` | `{name → contract_id}` map | +| `GET /api/update-check` | Diff с Gitea release (см. [`docs/update-system.md`](docs/update-system.md)) | + ### Real-time +- `GET /api/ws` — **WebSocket** (recommended). Ops: `auth`, `subscribe`, + `unsubscribe`, `submit_tx`, `typing`, `ping`. Push: `block`, `tx`, + `inbox`, `typing`, `submit_ack`. +- `GET /api/events` — SSE legacy one-way. -- `GET /api/events` — Server-Sent Events (classic, 1-way) -- `GET /api/ws` — **WebSocket** (bidirectional, recommended) +Scoped WS-топики (`addr:`, `inbox:`, `typing:`) требуют auth через +Ed25519-nonce; публичные (`blocks`, `tx`, `contract_log`) — без. -WS protocol — см. `node/ws.go`: - -```json -{ "op": "auth", "pubkey": "...", "sig": "..." } -{ "op": "subscribe", "topic": "addr:..." | "inbox:..." | "typing:..." | "blocks" | "tx" } -{ "op": "unsubscribe", "topic": "..." } -{ "op": "submit_tx", "tx": {...}, "id": "client-req-id" } -{ "op": "typing", "to": "" } -{ "op": "ping" } -``` - -Server push: - -```json -{ "event": "hello", "chain_id": "...", "auth_nonce": "..." } -{ "event": "block", "data": {...} } -{ "event": "tx", "data": {...} } -{ "event": "inbox", "data": { id, recipient_pub, sender_pub, sent_at } } -{ "event": "typing", "data": { from, to } } -{ "event": "submit_ack", "id": "...", "status": "accepted|rejected", "reason": "..." } -``` - -Scoped топики (`addr:`, `inbox:`, `typing:`) требуют auth. Без auth -доступны только публичные (`blocks`, `tx`, `contract_log`). +### Docs / UI +- `GET /swagger` — **Swagger UI** (рендерится через swagger-ui-dist). +- `GET /swagger/openapi.json` — сырая OpenAPI 3.0 спека. +- `GET /` — block-explorer HTML (выключается `DCHAIN_DISABLE_UI=true`). ## CLI ```bash -client keygen --out key.json -client balance --key key.json --node URL -client transfer --key key.json --to --amount <µT> --node URL -client call-contract --key key.json --contract native:username_registry \ - --method register --args '["alice"]' --amount 10000 \ - --node URL -client add-validator --key key.json --target --cosigs pub:sig,pub:sig -client admit-sign --key validator.json --target -client remove-validator --key key.json --target -# ... полный список — `client` без аргументов +client keygen --out key.json +client --version # → dchain-client vX.Y.Z (commit=... date=...) +client balance --key key.json --node URL +client transfer --key key.json --to --amount <µT> --node URL +client call-contract --key key.json --contract native:username_registry \ + --method register --arg alice --amount 10000 \ + --node URL +client add-validator --key key.json --target --cosigs pub:sig,pub:sig +client admit-sign --key validator.json --target +client deploy-contract --key key.json --wasm ./my.wasm --abi ./my_abi.json --node URL +# полный список — `client` без аргументов, или в docs/cli/README.md ``` -Node flags (все читают `DCHAIN_*` env fallbacks): -- `--db`, `--listen`, `--announce`, `--peers`, `--validators` -- `--join http://seed1:8080,http://seed2:8080` (multi-seed, persisted) -- `--genesis`, `--observer`, `--register-relay` -- `--log-format text|json`, `--allow-genesis-mismatch` +Все флаги `node` — в [`docs/node/README.md`](docs/node/README.md), либо +`node --help`. ## Мониторинг @@ -289,22 +218,39 @@ dchain_max_missed_blocks # worst validator liveness gap dchain_block_commit_seconds # histogram of AddBlock time ``` -Grafana dashboard + provisioning — `deploy/prod/grafana/` (create as -needed; Prometheus data source is auto-wired in compose profile -`monitor`). +Grafana + Prometheus поднимаются вместе с нодой через +`docker compose --profile monitor up -d` (см. `deploy/single/docker-compose.yml`). ## Тесты ```bash -go test ./... # blockchain + consensus + relay + identity + vm +# Unit + integration +go test ./... # blockchain + consensus + identity + relay + vm +go vet ./... # static checks + +# End-to-end load (3-node dev cluster должен быть поднят): +docker compose up --build -d go run ./cmd/loadtest \ --node http://localhost:8081 \ --funder testdata/node1.json \ - --clients 50 --duration 60s # end-to-end WS + submit_tx + native contract path + --clients 50 --duration 60s ``` -## История изменений +## Документация -Подробный список того, что сделано за последнюю итерацию (стабилизация -чейна, governance, WS gateway, observability, native contracts, -deployment) — `CHANGELOG.md`. +Полный справочник в `docs/` — см. [`docs/README.md`](docs/README.md) как +оглавление. Ключевые документы: + +| Документ | О чём | +|----------|-------| +| [`docs/quickstart.md`](docs/quickstart.md) | Три пути: локально, single-node prod, федерация | +| [`docs/architecture.md`](docs/architecture.md) | 4 слоя, consensus, storage, gas model | +| [`docs/update-system.md`](docs/update-system.md) | Versioning + update-check + semver guard | +| [`docs/api/README.md`](docs/api/README.md) | REST + WebSocket reference | +| [`docs/cli/README.md`](docs/cli/README.md) | CLI команды и флаги | +| [`docs/node/README.md`](docs/node/README.md) | Запуск ноды (native + Docker) | +| [`docs/contracts/README.md`](docs/contracts/README.md) | Системные + WASM контракты | +| [`docs/development/README.md`](docs/development/README.md) | SDK для своих контрактов | +| [`deploy/single/README.md`](deploy/single/README.md) | Single-node operator runbook | +| [`deploy/prod/README.md`](deploy/prod/README.md) | Multi-validator federation | +| [`deploy/UPDATE_STRATEGY.md`](deploy/UPDATE_STRATEGY.md) | Forward-compat design (4 слоя) | diff --git a/client-app/.gitignore b/client-app/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/client-app/README.md b/client-app/README.md deleted file mode 100644 index 856ba71..0000000 --- a/client-app/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# DChain Messenger — React Native Client - -E2E-encrypted mobile/desktop messenger built on the DChain blockchain stack. - -**Stack:** React Native · Expo · NativeWind (Tailwind) · TweetNaCl · Zustand - -## Quick Start - -```bash -cd client-app -npm install -npx expo start # opens Expo Dev Tools -# Press 'i' for iOS simulator, 'a' for Android, 'w' for web -``` - -## Requirements - -- Node.js 18+ -- [Expo Go](https://expo.dev/client) on your phone (for Expo tunnel), or iOS/Android emulator -- A running DChain node (see root README for `docker compose up --build -d`) - -## Project Structure - -``` -client-app/ -├── app/ -│ ├── _layout.tsx # Root layout — loads keys, sets up nav -│ ├── index.tsx # Welcome / onboarding -│ ├── (auth)/ -│ │ ├── create.tsx # Generate new Ed25519 + X25519 keys -│ │ ├── created.tsx # Key created — export reminder -│ │ └── import.tsx # Import existing key.json -│ └── (app)/ -│ ├── _layout.tsx # Tab bar — Chats · Wallet · Settings -│ ├── chats/ -│ │ ├── index.tsx # Chat list with contacts -│ │ └── [id].tsx # Individual chat with E2E encryption -│ ├── requests.tsx # Incoming contact requests -│ ├── new-contact.tsx # Add contact by @username or address -│ ├── wallet.tsx # Balance + TX history + send -│ └── settings.tsx # Node URL, key export, profile -├── components/ui/ # shadcn-style components (Button, Card, Input…) -├── hooks/ -│ ├── useMessages.ts # Poll relay inbox, decrypt messages -│ ├── useBalance.ts # Poll token balance -│ └── useContacts.ts # Load contacts + poll contact requests -└── lib/ - ├── api.ts # REST client for all DChain endpoints - ├── crypto.ts # NaCl box encrypt/decrypt, Ed25519 sign - ├── storage.ts # SecureStore (keys) + AsyncStorage (data) - ├── store.ts # Zustand global state - ├── types.ts # TypeScript interfaces - └── utils.ts # cn(), formatAmount(), relativeTime() -``` - -## Cryptography - -| Operation | Algorithm | Library | -|-----------|-----------|---------| -| Transaction signing | Ed25519 | TweetNaCl `sign` | -| Key exchange | X25519 (Curve25519) | TweetNaCl `box` | -| Message encryption | NaCl box (XSalsa20-Poly1305) | TweetNaCl `box` | -| Key storage | Device secure enclave | expo-secure-store | - -Messages are encrypted as: -``` -Envelope { - sender_pub: // sender's public key - recipient_pub: // recipient's public key - nonce: <24-byte hex> // random per message - ciphertext: // NaCl box(plaintext, nonce, sender_priv, recipient_pub) -} -``` - -## Connect to your node - -1. Start the DChain node: `docker compose up --build -d` -2. Open the app → Settings → Node URL → `http://YOUR_IP:8081` -3. If using Expo Go on physical device: your PC and phone must be on the same network, or use `npx expo start --tunnel` - -## Key File Format - -The `key.json` exported/imported by the app: -```json -{ - "pub_key": "26018d40...", // Ed25519 public key (64 hex chars) - "priv_key": "...", // Ed25519 private key (128 hex chars) - "x25519_pub": "...", // X25519 public key (64 hex chars) - "x25519_priv": "..." // X25519 private key (64 hex chars) -} -``` - -This is the same format as the Go node's `--key` flag. diff --git a/client-app/app.json b/client-app/app.json deleted file mode 100644 index e76123c..0000000 --- a/client-app/app.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "expo": { - "name": "DChain Messenger", - "slug": "dchain-messenger", - "version": "1.0.0", - "orientation": "portrait", - "userInterfaceStyle": "dark", - "backgroundColor": "#0d1117", - "ios": { - "supportsTablet": false, - "bundleIdentifier": "com.dchain.messenger" - }, - "android": { - "package": "com.dchain.messenger", - "softwareKeyboardLayoutMode": "pan" - }, - "web": { - "bundler": "metro", - "output": "static" - }, - "plugins": [ - "expo-router", - "expo-secure-store", - [ - "expo-camera", - { - "cameraPermission": "Allow DChain to scan QR codes for node configuration." - } - ] - ], - "experiments": { - "typedRoutes": false - }, - "scheme": "dchain" - } -} diff --git a/client-app/app/(app)/_layout.tsx b/client-app/app/(app)/_layout.tsx deleted file mode 100644 index 627d7c2..0000000 --- a/client-app/app/(app)/_layout.tsx +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Main app tab layout. - * Redirects to welcome if no key found. - */ - -import React, { useEffect } from 'react'; -import { Tabs, router } from 'expo-router'; -import { Ionicons } from '@expo/vector-icons'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useStore } from '@/lib/store'; -import { useBalance } from '@/hooks/useBalance'; -import { useContacts } from '@/hooks/useContacts'; -import { useWellKnownContracts } from '@/hooks/useWellKnownContracts'; -import { getWSClient } from '@/lib/ws'; - -const C_ACCENT = '#7db5ff'; -const C_MUTED = '#98a7c2'; -const C_BG = '#111a2b'; -const C_BORDER = '#1c2840'; - -export default function AppLayout() { - const keyFile = useStore(s => s.keyFile); - const requests = useStore(s => s.requests); - const insets = useSafeAreaInsets(); - - useBalance(); - useContacts(); - useWellKnownContracts(); // auto-discover canonical system contracts from node - - // Arm the WS client with this user's Ed25519 keypair. The client signs the - // server's auth nonce on every (re)connect so scoped subscriptions - // (addr:, inbox:) are accepted. Without this the - // server would still accept global topic subs but reject scoped ones. - useEffect(() => { - const ws = getWSClient(); - if (keyFile) { - ws.setAuthCreds({ pubKey: keyFile.pub_key, privKey: keyFile.priv_key }); - } else { - ws.setAuthCreds(null); - } - }, [keyFile]); - - useEffect(() => { - if (keyFile === null) { - const t = setTimeout(() => { - if (!useStore.getState().keyFile) router.replace('/'); - }, 300); - return () => clearTimeout(t); - } - }, [keyFile]); - - // Tab bar layout math: - // icon (22) + gap (4) + label (~13) = ~39px of content - // We add a 12px visual margin above, and pad the bottom by the larger of - // the platform safe-area inset or 10px so the bar never sits flush on the - // home indicator. - const BAR_CONTENT_HEIGHT = 52; - const bottomPad = Math.max(insets.bottom, 10); - - return ( - - ( - - ), - tabBarBadge: requests.length > 0 ? requests.length : undefined, - tabBarBadgeStyle: { backgroundColor: C_ACCENT, fontSize: 10 }, - }} - /> - ( - - ), - }} - /> - ( - - ), - }} - /> - {/* Non-tab screens — hidden from tab bar */} - - - - ); -} diff --git a/client-app/app/(app)/chats/[id].tsx b/client-app/app/(app)/chats/[id].tsx deleted file mode 100644 index 62d862d..0000000 --- a/client-app/app/(app)/chats/[id].tsx +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Chat view — DChain messenger. - * Safe-area aware header/input, smooth scroll, proper E2E indicators, - * responsive send button with press feedback. - */ - -import React, { useState, useRef, useEffect, useCallback } from 'react'; -import { - View, Text, FlatList, TextInput, TouchableOpacity, Pressable, - KeyboardAvoidingView, Platform, ActivityIndicator, Alert, -} from 'react-native'; -import { router, useLocalSearchParams } from 'expo-router'; -import { Ionicons } from '@expo/vector-icons'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useStore } from '@/lib/store'; -import { useMessages } from '@/hooks/useMessages'; -import { encryptMessage } from '@/lib/crypto'; -import { sendEnvelope } from '@/lib/api'; -import { getWSClient } from '@/lib/ws'; -import { appendMessage, loadMessages } from '@/lib/storage'; -import { formatTime, randomId } from '@/lib/utils'; -import { Avatar } from '@/components/ui/Avatar'; -import type { Message } from '@/lib/types'; - -// ─── Design tokens ──────────────────────────────────────────────────────────── -const C = { - bg: '#0b1220', - surface: '#111a2b', - surface2:'#162035', - surface3:'#1a2640', - line: '#1c2840', - text: '#e6edf9', - muted: '#98a7c2', - accent: '#7db5ff', - ok: '#41c98a', - warn: '#f0b35a', - err: '#ff7a87', -} as const; - -// ─── Helpers ────────────────────────────────────────────────────────────────── - -function shortAddr(a: string, n = 5): string { - if (!a) return '—'; - return a.length <= n * 2 + 1 ? a : `${a.slice(0, n)}…${a.slice(-n)}`; -} - -/** Group messages by calendar day for day-separator labels. */ -function dateBucket(ts: number): string { - const d = new Date(ts * 1000); - const now = new Date(); - const yday = new Date(); yday.setDate(now.getDate() - 1); - const same = (a: Date, b: Date) => - a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); - if (same(d, now)) return 'Сегодня'; - if (same(d, yday)) return 'Вчера'; - return d.toLocaleDateString('ru', { day: 'numeric', month: 'long' }); -} - -// A row in the FlatList: either a message or a date separator we inject. -type Row = - | { kind: 'msg'; msg: Message } - | { kind: 'sep'; id: string; label: string }; - -function buildRows(msgs: Message[]): Row[] { - const rows: Row[] = []; - let lastBucket = ''; - for (const m of msgs) { - const b = dateBucket(m.timestamp); - if (b !== lastBucket) { - rows.push({ kind: 'sep', id: `sep_${b}_${m.id}`, label: b }); - lastBucket = b; - } - rows.push({ kind: 'msg', msg: m }); - } - return rows; -} - -// ─── Screen ─────────────────────────────────────────────────────────────────── - -export default function ChatScreen() { - const { id: contactAddress } = useLocalSearchParams<{ id: string }>(); - const keyFile = useStore(s => s.keyFile); - const contacts = useStore(s => s.contacts); - const messages = useStore(s => s.messages); - const setMsgs = useStore(s => s.setMessages); - const appendMsg = useStore(s => s.appendMessage); - const insets = useSafeAreaInsets(); - - const contact = contacts.find(c => c.address === contactAddress); - const chatMsgs = messages[contactAddress ?? ''] ?? []; - const listRef = useRef(null); - - const [text, setText] = useState(''); - const [sending, setSending] = useState(false); - const [peerTyping, setPeerTyping] = useState(false); - - // Poll relay inbox for messages from this contact - useMessages(contact?.x25519Pub ?? ''); - - // Subscribe to typing indicators sent to us. Clears after 3 seconds of - // silence so the "typing…" bubble disappears naturally when the peer - // stops. sendTyping on our side happens per-keystroke (throttled below). - useEffect(() => { - if (!keyFile?.x25519_pub) return; - const ws = getWSClient(); - let clearTimer: ReturnType | null = null; - - const off = ws.subscribe('typing:' + keyFile.x25519_pub, (frame) => { - if (frame.event !== 'typing') return; - const d = frame.data as { from?: string } | undefined; - // Only show typing for the contact currently open in this view. - if (!contact?.x25519Pub || d?.from !== contact.x25519Pub) return; - setPeerTyping(true); - if (clearTimer) clearTimeout(clearTimer); - clearTimer = setTimeout(() => setPeerTyping(false), 3_000); - }); - - return () => { - off(); - if (clearTimer) clearTimeout(clearTimer); - }; - }, [keyFile?.x25519_pub, contact?.x25519Pub]); - - // Throttled sendTyping: fire on every keystroke but no more than every 2s. - const lastSentTyping = useRef(0); - const handleTextChange = useCallback((t: string) => { - setText(t); - if (!contact?.x25519Pub || !t.trim()) return; - const now = Date.now(); - if (now - lastSentTyping.current < 2_000) return; - lastSentTyping.current = now; - getWSClient().sendTyping(contact.x25519Pub); - }, [contact?.x25519Pub]); - - // Load cached messages on mount - useEffect(() => { - if (contactAddress) { - loadMessages(contactAddress).then(cached => { - setMsgs(contactAddress, cached as Message[]); - }); - } - }, [contactAddress]); - - const displayName = contact?.username - ? `@${contact.username}` - : contact?.alias ?? shortAddr(contactAddress ?? '', 6); - - const canSend = !!text.trim() && !sending && !!contact?.x25519Pub; - - const send = useCallback(async () => { - if (!text.trim() || !keyFile || !contact) return; - if (!contact.x25519Pub) { - Alert.alert( - 'Ключ ещё не опубликован', - 'Контакт пока не опубликовал ключ шифрования. Попробуйте позже.', - ); - return; - } - setSending(true); - try { - const { nonce, ciphertext } = encryptMessage( - text.trim(), - keyFile.x25519_priv, - contact.x25519Pub, - ); - await sendEnvelope({ - sender_pub: keyFile.x25519_pub, - recipient_pub: contact.x25519Pub, - nonce, - ciphertext, - }); - const msg: Message = { - id: randomId(), - from: keyFile.x25519_pub, - text: text.trim(), - timestamp: Math.floor(Date.now() / 1000), - mine: true, - }; - appendMsg(contact.address, msg); - await appendMessage(contact.address, msg); - setText(''); - setTimeout(() => listRef.current?.scrollToEnd({ animated: true }), 50); - } catch (e: any) { - Alert.alert('Ошибка отправки', e.message); - } finally { - setSending(false); - } - }, [text, keyFile, contact]); - - const rows = buildRows(chatMsgs); - - const renderRow = ({ item }: { item: Row }) => { - if (item.kind === 'sep') { - return ( - - - {item.label} - - - ); - } - const m = item.msg; - return ( - - {!m.mine && ( - - - - )} - - - {m.text} - - - - {formatTime(m.timestamp)} - - {m.mine && ( - - )} - - - - ); - }; - - return ( - - {/* ── Header ── */} - - router.back()} - activeOpacity={0.6} - style={{ padding: 8, marginRight: 4 }} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} - > - - - - - - {contact?.x25519Pub && ( - - )} - - - - - {displayName} - - - {peerTyping ? ( - <> - - печатает… - - ) : contact?.x25519Pub ? ( - <> - - E2E шифрование - - ) : ( - <> - - Ожидание ключа шифрования - - )} - - - - - - - - - {/* ── Messages ── */} - r.kind === 'sep' ? r.id : r.msg.id} - renderItem={renderRow} - contentContainerStyle={{ paddingTop: 14, paddingBottom: 10, flexGrow: 1 }} - onContentSizeChange={() => listRef.current?.scrollToEnd({ animated: false })} - ListEmptyComponent={() => ( - - - - - - Начните разговор - - - Сообщения зашифрованы end-to-end.{'\n'}Только вы и {displayName} их прочитаете. - - - )} - showsVerticalScrollIndicator={false} - /> - - {/* ── Input bar ── */} - - - - - - ({ - width: 42, height: 42, borderRadius: 21, - backgroundColor: canSend ? C.accent : C.surface2, - alignItems: 'center', justifyContent: 'center', - flexShrink: 0, - opacity: pressed && canSend ? 0.7 : 1, - transform: [{ scale: pressed && canSend ? 0.95 : 1 }], - })} - > - {sending - ? - : - } - - - - ); -} diff --git a/client-app/app/(app)/chats/_layout.tsx b/client-app/app/(app)/chats/_layout.tsx deleted file mode 100644 index 9e80355..0000000 --- a/client-app/app/(app)/chats/_layout.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { Stack } from 'expo-router'; - -export default function ChatsLayout() { - return ( - - ); -} diff --git a/client-app/app/(app)/chats/index.tsx b/client-app/app/(app)/chats/index.tsx deleted file mode 100644 index 54c74b7..0000000 --- a/client-app/app/(app)/chats/index.tsx +++ /dev/null @@ -1,337 +0,0 @@ -/** - * Chat list — DChain messenger. - * Safe-area aware, Ionicons, polished empty states, responsive press feedback. - */ - -import React, { useCallback, useMemo, useEffect, useState } from 'react'; -import { View, Text, FlatList, Pressable, TouchableOpacity } from 'react-native'; -import { router } from 'expo-router'; -import { Ionicons } from '@expo/vector-icons'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useStore } from '@/lib/store'; -import { getWSClient } from '@/lib/ws'; -import { Avatar } from '@/components/ui/Avatar'; -import { formatTime, formatAmount } from '@/lib/utils'; -import type { Contact } from '@/lib/types'; - -const MIN_FEE = 5000; - -// ─── Design tokens ──────────────────────────────────────────────────────────── -const C = { - bg: '#0b1220', - surface: '#111a2b', - surface2: '#162035', - surface3: '#1a2640', - line: '#1c2840', - text: '#e6edf9', - muted: '#98a7c2', - accent: '#7db5ff', - ok: '#41c98a', - warn: '#f0b35a', - err: '#ff7a87', -} as const; - -// ─── Helpers ────────────────────────────────────────────────────────────────── - -/** Truncate a message preview without breaking words too awkwardly. */ -function previewText(s: string, max = 60): string { - if (s.length <= max) return s; - return s.slice(0, max).trimEnd() + '…'; -} - -/** Short address helper matching the rest of the app. */ -function shortAddr(a: string, n = 6): string { - if (!a) return '—'; - return a.length <= n * 2 + 1 ? a : `${a.slice(0, n)}…${a.slice(-n)}`; -} - -export default function ChatsScreen() { - const contacts = useStore(s => s.contacts); - const messages = useStore(s => s.messages); - const requests = useStore(s => s.requests); - const balance = useStore(s => s.balance); - const keyFile = useStore(s => s.keyFile); - const insets = useSafeAreaInsets(); - - // Real-time transport indicator (green dot when WS is live, yellow when - // using HTTP polling fallback). - const [wsLive, setWsLive] = useState(false); - useEffect(() => { - const ws = getWSClient(); - setWsLive(ws.isConnected()); - return ws.onConnectionChange(ok => setWsLive(ok)); - }, []); - - const hasBalance = balance >= MIN_FEE; - - const displayName = (c: Contact) => - c.username ? `@${c.username}` : c.alias ?? shortAddr(c.address, 5); - - const lastMsg = (c: Contact) => { - const msgs = messages[c.address]; - return msgs?.length ? msgs[msgs.length - 1] : null; - }; - - // Sort contacts: most recent activity first. - const sortedContacts = useMemo(() => { - const withTime = contacts.map(c => { - const last = lastMsg(c); - return { - contact: c, - sortKey: last ? last.timestamp : (c.addedAt / 1000), - }; - }); - return withTime - .sort((a, b) => b.sortKey - a.sortKey) - .map(x => x.contact); - }, [contacts, messages]); - - const renderItem = useCallback(({ item: c, index }: { item: Contact; index: number }) => { - const last = lastMsg(c); - const name = displayName(c); - const hasKey = !!c.x25519Pub; - - return ( - router.push(`/(app)/chats/${c.address}`)} - android_ripple={{ color: C.surface2 }} - style={({ pressed }) => ({ - flexDirection: 'row', alignItems: 'center', - paddingHorizontal: 14, paddingVertical: 12, - borderTopWidth: index === 0 ? 0 : 1, borderTopColor: C.line, - backgroundColor: pressed ? C.surface2 : 'transparent', - })} - > - {/* Avatar with E2E status pip */} - - - {hasKey && ( - - - - )} - - - {/* Text column */} - - - - {name} - - {last && ( - - {formatTime(last.timestamp)} - - )} - - - {last?.mine && ( - - )} - - {last ? previewText(last.text) : (hasKey ? 'Напишите первое сообщение' : 'Ожидание публикации ключа…')} - - - - - ); - }, [messages, lastMsg]); - - return ( - - - {/* ── Header ── */} - - - - Сообщения - - {contacts.length > 0 && ( - - - {contacts.length} {contacts.length === 1 ? 'контакт' : contacts.length < 5 ? 'контакта' : 'контактов'} - {' · '} - E2E - {' · '} - - - - {wsLive ? 'live' : 'polling'} - - - )} - - - - {/* Incoming requests chip */} - {requests.length > 0 && ( - router.push('/(app)/requests')} - activeOpacity={0.7} - style={{ - flexDirection: 'row', alignItems: 'center', gap: 6, - backgroundColor: 'rgba(125,181,255,0.14)', borderRadius: 999, - paddingHorizontal: 12, paddingVertical: 7, - borderWidth: 1, borderColor: 'rgba(125,181,255,0.25)', - }} - > - - - {requests.length} - - - )} - - {/* Add contact button */} - hasBalance ? router.push('/(app)/new-contact') : router.push('/(app)/wallet')} - activeOpacity={0.7} - style={{ - width: 38, height: 38, borderRadius: 19, - backgroundColor: hasBalance ? C.accent : C.surface2, - alignItems: 'center', justifyContent: 'center', - }} - > - - - - - - {/* ── No balance gate (no contacts) ── */} - {!hasBalance && contacts.length === 0 && ( - - - - - - - Пополните баланс - - - - Отправка запроса контакта стоит{' '} - {formatAmount(MIN_FEE)} - {' '}— антиспам-сбор идёт напрямую получателю. - - router.push('/(app)/wallet')} - activeOpacity={0.7} - style={{ - flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, - paddingVertical: 11, borderRadius: 10, - backgroundColor: C.accent, - }} - > - - Перейти в кошелёк - - - )} - - {/* ── Low balance warning (has contacts) ── */} - {!hasBalance && contacts.length > 0 && ( - router.push('/(app)/wallet')} - activeOpacity={0.7} - style={{ - flexDirection: 'row', alignItems: 'center', gap: 10, - marginHorizontal: 16, marginTop: 12, - backgroundColor: 'rgba(255,122,135,0.08)', - borderRadius: 10, paddingHorizontal: 12, paddingVertical: 10, - borderWidth: 1, borderColor: 'rgba(255,122,135,0.18)', - }} - > - - - Недостаточно токенов для добавления контакта - - - - )} - - {/* ── Empty state (has balance, no contacts) ── */} - {contacts.length === 0 && hasBalance && ( - - - - - - Нет диалогов - - - Добавьте контакт по адресу или{' '} - @username - {' '}для начала зашифрованной переписки. - - router.push('/(app)/new-contact')} - activeOpacity={0.7} - style={{ - flexDirection: 'row', alignItems: 'center', gap: 8, - paddingHorizontal: 22, paddingVertical: 12, borderRadius: 12, - backgroundColor: C.accent, - }} - > - - Добавить контакт - - - )} - - {/* ── Chat list ── */} - {contacts.length > 0 && ( - c.address} - renderItem={renderItem} - contentContainerStyle={{ paddingBottom: 20 }} - showsVerticalScrollIndicator={false} - /> - )} - - ); -} diff --git a/client-app/app/(app)/new-contact.tsx b/client-app/app/(app)/new-contact.tsx deleted file mode 100644 index 1effbe5..0000000 --- a/client-app/app/(app)/new-contact.tsx +++ /dev/null @@ -1,332 +0,0 @@ -/** - * Add New Contact — DChain explorer design style. - * Sends CONTACT_REQUEST on-chain with correct amount/fee fields. - */ - -import React, { useState } from 'react'; -import { - View, Text, ScrollView, Alert, TouchableOpacity, TextInput, -} from 'react-native'; -import { router } from 'expo-router'; -import { Ionicons } from '@expo/vector-icons'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useStore } from '@/lib/store'; -import { getIdentity, buildContactRequestTx, submitTx } from '@/lib/api'; -import { shortAddr } from '@/lib/crypto'; -import { formatAmount } from '@/lib/utils'; -import { Avatar } from '@/components/ui/Avatar'; - -const C = { - bg: '#0b1220', - surface: '#111a2b', - surface2:'#162035', - line: '#1c2840', - text: '#e6edf9', - muted: '#98a7c2', - accent: '#7db5ff', - ok: '#41c98a', - warn: '#f0b35a', - err: '#ff7a87', -} as const; - -const MIN_CONTACT_FEE = 5000; - -const FEE_OPTIONS = [ - { label: '5 000 µT', value: 5_000, note: 'минимум' }, - { label: '10 000 µT', value: 10_000, note: 'стандарт' }, - { label: '50 000 µT', value: 50_000, note: 'приоритет' }, -]; - -interface Resolved { - address: string; - nickname?: string; - x25519?: string; -} - -export default function NewContactScreen() { - const keyFile = useStore(s => s.keyFile); - const settings = useStore(s => s.settings); - const balance = useStore(s => s.balance); - const insets = useSafeAreaInsets(); - - const [query, setQuery] = useState(''); - const [intro, setIntro] = useState(''); - const [fee, setFee] = useState(MIN_CONTACT_FEE); - const [resolved, setResolved] = useState(null); - const [searching, setSearching] = useState(false); - const [sending, setSending] = useState(false); - const [error, setError] = useState(null); - - async function search() { - const q = query.trim(); - if (!q) return; - setSearching(true); - setResolved(null); - setError(null); - try { - let address = q; - - // @username lookup via registry contract - if (q.startsWith('@') || (!q.match(/^[0-9a-f]{64}$/i) && !q.startsWith('DC'))) { - const name = q.replace('@', ''); - const { resolveUsername } = await import('@/lib/api'); - const addr = await resolveUsername(settings.contractId, name); - if (!addr) { - setError(`@${name} не зарегистрирован в сети.`); - return; - } - address = addr; - } - - // Fetch identity to get nickname and x25519 key - const identity = await getIdentity(address); - setResolved({ - address: identity?.pub_key ?? address, - nickname: identity?.nickname || undefined, - x25519: identity?.x25519_pub || undefined, - }); - } catch (e: any) { - setError(e.message); - } finally { - setSearching(false); - } - } - - async function sendRequest() { - if (!resolved || !keyFile) return; - if (balance < fee + 1000) { - Alert.alert('Недостаточно средств', `Нужно ${formatAmount(fee + 1000)} (fee + комиссия сети).`); - return; - } - setSending(true); - setError(null); - try { - const tx = buildContactRequestTx({ - from: keyFile.pub_key, - to: resolved.address, - contactFee: fee, - intro: intro.trim() || undefined, - privKey: keyFile.priv_key, - }); - await submitTx(tx); - Alert.alert( - 'Запрос отправлен', - `Контакту ${resolved.nickname ? '@' + resolved.nickname : shortAddr(resolved.address)} отправлен запрос.`, - [{ text: 'OK', onPress: () => router.back() }], - ); - } catch (e: any) { - setError(e.message); - } finally { - setSending(false); - } - } - - const displayName = resolved - ? (resolved.nickname ? `@${resolved.nickname}` : shortAddr(resolved.address)) - : null; - - return ( - - {/* Header */} - - router.back()} - activeOpacity={0.6} - style={{ padding: 8 }} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} - > - - - Добавить контакт - - - {/* Search */} - - Адрес или @username - - - - { setQuery(t); setError(null); }} - onSubmitEditing={search} - placeholder="@username или 64-символьный hex" - placeholderTextColor={C.muted} - autoCapitalize="none" - autoCorrect={false} - style={{ color: C.text, fontSize: 14, height: 44 }} - /> - - - - {searching ? '…' : 'Найти'} - - - - - {/* Error */} - {error && ( - - ⚠ {error} - - )} - - {/* Resolved contact card */} - {resolved && ( - - - - {resolved.nickname && ( - - @{resolved.nickname} - - )} - - {resolved.address} - - {resolved.x25519 && ( - - ✓ Зашифрованные сообщения поддерживаются - - )} - - - Найден - - - )} - - {/* Intro + fee (shown after search succeeds) */} - {resolved && ( - <> - {/* Intro */} - - Сообщение (необязательно) - - - - - {intro.length}/280 - - - - {/* Fee selector */} - - Антиспам-сбор (отправляется контакту) - - - {FEE_OPTIONS.map(opt => ( - setFee(opt.value)} - style={{ - flex: 1, borderRadius: 10, paddingVertical: 10, alignItems: 'center', - backgroundColor: fee === opt.value - ? 'rgba(125,181,255,0.15)' : C.surface, - borderWidth: fee === opt.value ? 1 : 0, - borderColor: fee === opt.value ? C.accent : 'transparent', - }} - > - - {opt.label} - - {opt.note} - - ))} - - - {/* Balance info */} - - Ваш баланс: - = fee + 1000 ? C.text : C.err, fontSize: 13, fontWeight: '600' }}> - {formatAmount(balance)} - - - Итого: {formatAmount(fee + 1000)} - - - - {/* Send button */} - - - {sending ? 'Отправка…' : 'Отправить запрос'} - - - - )} - - {/* Hint when no search yet */} - {!resolved && !error && ( - - - Введите @username или вставьте 64-символьный hex-адрес пользователя DChain. - - - )} - - ); -} diff --git a/client-app/app/(app)/requests.tsx b/client-app/app/(app)/requests.tsx deleted file mode 100644 index 75e76bc..0000000 --- a/client-app/app/(app)/requests.tsx +++ /dev/null @@ -1,236 +0,0 @@ -/** - * Contact requests screen — DChain explorer design style. - */ - -import React, { useState } from 'react'; -import { View, Text, FlatList, Alert, TouchableOpacity } from 'react-native'; -import { router } from 'expo-router'; -import { Ionicons } from '@expo/vector-icons'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useStore } from '@/lib/store'; -import { buildAcceptContactTx, submitTx, getIdentity } from '@/lib/api'; -import { saveContact } from '@/lib/storage'; -import { shortAddr } from '@/lib/crypto'; -import { relativeTime } from '@/lib/utils'; -import { Avatar } from '@/components/ui/Avatar'; -import type { ContactRequest } from '@/lib/types'; - -const C = { - bg: '#0b1220', - surface: '#111a2b', - surface2:'#162035', - line: '#1c2840', - text: '#e6edf9', - muted: '#98a7c2', - accent: '#7db5ff', - ok: '#41c98a', - warn: '#f0b35a', - err: '#ff7a87', -} as const; - -export default function RequestsScreen() { - const keyFile = useStore(s => s.keyFile); - const requests = useStore(s => s.requests); - const setRequests = useStore(s => s.setRequests); - const upsertContact = useStore(s => s.upsertContact); - const insets = useSafeAreaInsets(); - - const [accepting, setAccepting] = useState(null); - - async function accept(req: ContactRequest) { - if (!keyFile) return; - setAccepting(req.txHash); - try { - // Fetch requester's identity to get their x25519 key for messaging - const identity = await getIdentity(req.from); - const x25519Pub = identity?.x25519_pub ?? ''; - - const tx = buildAcceptContactTx({ - from: keyFile.pub_key, - to: req.from, - privKey: keyFile.priv_key, - }); - await submitTx(tx); - - // Save contact with x25519 key (empty if they haven't registered one) - const contact = { - address: req.from, - x25519Pub, - username: req.username, - addedAt: Date.now(), - }; - upsertContact(contact); - await saveContact(contact); - - setRequests(requests.filter(r => r.txHash !== req.txHash)); - Alert.alert('Принято', `${req.username ? '@' + req.username : shortAddr(req.from)} добавлен в контакты.`); - } catch (e: any) { - Alert.alert('Ошибка', e.message); - } finally { - setAccepting(null); - } - } - - function decline(req: ContactRequest) { - Alert.alert( - 'Отклонить запрос', - `Отклонить запрос от ${req.username ? '@' + req.username : shortAddr(req.from)}?`, - [ - { text: 'Отмена', style: 'cancel' }, - { - text: 'Отклонить', - style: 'destructive', - onPress: () => setRequests(requests.filter(r => r.txHash !== req.txHash)), - }, - ], - ); - } - - return ( - - {/* Header */} - - router.back()} - activeOpacity={0.6} - style={{ padding: 8 }} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} - > - - - - Запросы контактов - - {requests.length > 0 && ( - - - {requests.length} - - - )} - - - {requests.length === 0 ? ( - - - - - - Нет входящих запросов - - - Когда кто-то пришлёт вам запрос в контакты, он появится здесь. - - - ) : ( - r.txHash} - contentContainerStyle={{ padding: 14, paddingBottom: 24, gap: 12 }} - renderItem={({ item: req }) => ( - accept(req)} - onDecline={() => decline(req)} - /> - )} - /> - )} - - ); -} - -function RequestCard({ - req, isAccepting, onAccept, onDecline, -}: { - req: ContactRequest; - isAccepting: boolean; - onAccept: () => void; - onDecline: () => void; -}) { - const displayName = req.username ? `@${req.username}` : shortAddr(req.from); - - return ( - - {/* Sender info */} - - - - - {displayName} - - - {req.from} - - - {relativeTime(req.timestamp)} - - - {/* Intro message */} - {!!req.intro && ( - - Приветствие - {req.intro} - - )} - - {/* Divider */} - - - {/* Actions */} - - - - - {isAccepting ? 'Принятие…' : 'Принять'} - - - - - Отклонить - - - - ); -} diff --git a/client-app/app/(app)/settings.tsx b/client-app/app/(app)/settings.tsx deleted file mode 100644 index 6c7fd52..0000000 --- a/client-app/app/(app)/settings.tsx +++ /dev/null @@ -1,732 +0,0 @@ -/** - * Settings screen — DChain explorer style, inline styles, Russian locale. - */ - -import React, { useState, useEffect } from 'react'; -import { - View, Text, ScrollView, TextInput, Alert, TouchableOpacity, -} from 'react-native'; -import * as Clipboard from 'expo-clipboard'; -import * as FileSystem from 'expo-file-system'; -import * as Sharing from 'expo-sharing'; -import { router } from 'expo-router'; -import { Ionicons } from '@expo/vector-icons'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useStore } from '@/lib/store'; -import { saveSettings, deleteKeyFile } from '@/lib/storage'; -import { - setNodeUrl, getNetStats, resolveUsername, reverseResolve, - buildCallContractTx, submitTx, - USERNAME_REGISTRATION_FEE, MIN_USERNAME_LENGTH, MAX_USERNAME_LENGTH, - humanizeTxError, -} from '@/lib/api'; -import { shortAddr } from '@/lib/crypto'; -import { formatAmount } from '@/lib/utils'; -import { Avatar } from '@/components/ui/Avatar'; - -// ─── Design tokens ──────────────────────────────────────────────────────────── -const C = { - bg: '#0b1220', - surface: '#111a2b', - surface2:'#162035', - surface3:'#1a2640', - line: '#1c2840', - text: '#e6edf9', - muted: '#98a7c2', - accent: '#7db5ff', - ok: '#41c98a', - warn: '#f0b35a', - err: '#ff7a87', -} as const; - -// ─── Reusable sub-components ───────────────────────────────────────────────── - -function SectionLabel({ children }: { children: string }) { - return ( - - {children} - - ); -} - -function Card({ children, style }: { children: React.ReactNode; style?: object }) { - return ( - - {children} - - ); -} - -function CardRow({ - icon, label, value, first, -}: { - icon: keyof typeof Ionicons.glyphMap; - label: string; - value?: string; - first?: boolean; -}) { - return ( - - - - - - {label} - {value !== undefined && ( - - {value} - - )} - - - ); -} - -function FieldInput({ - label, value, onChangeText, placeholder, keyboardType, autoCapitalize, autoCorrect, -}: { - label: string; - value: string; - onChangeText: (v: string) => void; - placeholder?: string; - keyboardType?: any; - autoCapitalize?: any; - autoCorrect?: boolean; -}) { - return ( - - {label} - - - - - ); -} - -// ─── Screen ─────────────────────────────────────────────────────────────────── - -export default function SettingsScreen() { - const keyFile = useStore(s => s.keyFile); - const settings = useStore(s => s.settings); - const setSettings = useStore(s => s.setSettings); - const username = useStore(s => s.username); - const setUsername = useStore(s => s.setUsername); - const setKeyFile = useStore(s => s.setKeyFile); - const balance = useStore(s => s.balance); - - const [nodeUrl, setNodeUrlLocal] = useState(settings.nodeUrl); - const [contractId, setContractId] = useState(settings.contractId); - const [nodeStatus, setNodeStatus] = useState<'checking' | 'ok' | 'error'>('checking'); - const [peerCount, setPeerCount] = useState(null); - const [blockCount, setBlockCount] = useState(null); - const [copied, setCopied] = useState(false); - const [showAdvanced, setShowAdvanced] = useState(false); - const insets = useSafeAreaInsets(); - - // Username registration state - const [nameInput, setNameInput] = useState(''); - const [registering, setRegistering] = useState(false); - const [nameError, setNameError] = useState(null); - - // Sanitize: lowercase, only a-z 0-9 _ -, max 32 (contract limit) - function onNameInputChange(v: string) { - const cleaned = v.toLowerCase().replace(/[^a-z0-9_\-]/g, '').slice(0, MAX_USERNAME_LENGTH); - setNameInput(cleaned); - setNameError(null); - } - - async function registerUsername() { - if (!keyFile) return; - const name = nameInput.trim(); - // Mirror blockchain/native_username.go validateName so the UI gives - // immediate feedback without a round trip to the chain. - if (name.length < MIN_USERNAME_LENGTH) { - setNameError(`Минимум ${MIN_USERNAME_LENGTH} символа`); - return; - } - if (!/^[a-z]/.test(name)) { - setNameError('Имя должно начинаться с буквы a-z'); - return; - } - if (!settings.contractId) { - setNameError('Не задан ID контракта реестра в настройках ноды.'); - return; - } - const fee = USERNAME_REGISTRATION_FEE; - // Reserve for: registry fee (burned) + MIN_CALL_FEE (to validator) - // + small gas headroom (native contract is cheap, but gas is pre-charged). - const GAS_HEADROOM = 2_000; - const total = fee + 1000 + GAS_HEADROOM; - if (balance < total) { - setNameError(`Нужно ${formatAmount(total)} (с запасом на газ), доступно ${formatAmount(balance)}.`); - return; - } - // Check if name is already taken - try { - const existing = await resolveUsername(settings.contractId, name); - if (existing) { - setNameError(`@${name} уже зарегистрировано.`); - return; - } - } catch { - // ignore — lookup failure is OK, contract will reject on duplicate - } - - Alert.alert( - 'Купить @' + name + '?', - `Стоимость: ${formatAmount(fee)} + комиссия ${formatAmount(1000)}.\nИмя привязывается к вашему адресу навсегда (до release).`, - [ - { text: 'Отмена', style: 'cancel' }, - { - text: 'Купить', onPress: async () => { - setRegistering(true); - setNameError(null); - try { - const tx = buildCallContractTx({ - from: keyFile.pub_key, - contractId: settings.contractId, - method: 'register', - args: [name], - // Attach the registration fee in tx.amount — the contract - // requires exactly this much and burns it. Visible in the - // explorer so the user sees the real cost. - amount: USERNAME_REGISTRATION_FEE, - privKey: keyFile.priv_key, - }); - await submitTx(tx); - Alert.alert( - 'Отправлено', - `Транзакция покупки @${name} принята. Имя появится в профиле через несколько секунд.`, - ); - setNameInput(''); - // Poll every 2s for up to 20s until the address ↔ name binding is visible. - let attempts = 0; - const iv = setInterval(async () => { - attempts++; - const got = keyFile - ? await reverseResolve(settings.contractId, keyFile.pub_key) - : null; - if (got) { - setUsername(got); - clearInterval(iv); - } else if (attempts >= 10) { - clearInterval(iv); - } - }, 2000); - } catch (e: any) { - setNameError(humanizeTxError(e)); - } finally { - setRegistering(false); - } - }, - }, - ], - ); - } - - // Flat fee — same for every name that passes validation. - const nameFee = USERNAME_REGISTRATION_FEE; - const nameIsValid = nameInput.length >= MIN_USERNAME_LENGTH && /^[a-z]/.test(nameInput); - - useEffect(() => { checkNode(); }, []); - - // Pick up auto-discovered contract IDs (useWellKnownContracts updates the - // store; reflect it into the local TextInput state so the UI stays consistent). - useEffect(() => { - setContractId(settings.contractId); - }, [settings.contractId]); - - // When the registry contract becomes known (either via manual save or - // auto-discovery), look up the user's registered username reactively. - // Sets username unconditionally — a null result CLEARS the cached name, - // which matters when the user switches nodes / chains: a name on the - // previous chain should no longer show when connected to a chain where - // the same pubkey isn't registered. - useEffect(() => { - if (!settings.contractId || !keyFile) { - setUsername(null); - return; - } - (async () => { - const name = await reverseResolve(settings.contractId, keyFile.pub_key); - setUsername(name); - })(); - }, [settings.contractId, keyFile, setUsername]); - - async function checkNode() { - setNodeStatus('checking'); - try { - const stats = await getNetStats(); - setNodeStatus('ok'); - setPeerCount(stats.peer_count); - setBlockCount(stats.total_blocks); - if (settings.contractId && keyFile) { - // Address → username: must use reverseResolve, not resolveUsername - // (resolveUsername goes username → address). - const name = await reverseResolve(settings.contractId, keyFile.pub_key); - if (name) setUsername(name); - } - } catch { - setNodeStatus('error'); - } - } - - async function saveNode() { - const url = nodeUrl.trim().replace(/\/$/, ''); - setNodeUrl(url); - const next = { nodeUrl: url, contractId: contractId.trim() }; - setSettings(next); - await saveSettings(next); - Alert.alert('Сохранено', 'Настройки ноды обновлены.'); - checkNode(); - } - - async function exportKey() { - if (!keyFile) return; - try { - const json = JSON.stringify(keyFile, null, 2); - const path = FileSystem.cacheDirectory + 'dchain_key.json'; - await FileSystem.writeAsStringAsync(path, json); - if (await Sharing.isAvailableAsync()) { - await Sharing.shareAsync(path, { - mimeType: 'application/json', - dialogTitle: 'Экспорт ключа DChain', - }); - } - } catch (e: any) { - Alert.alert('Ошибка экспорта', e.message); - } - } - - async function copyAddress() { - if (!keyFile) return; - await Clipboard.setStringAsync(keyFile.pub_key); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - } - - function logout() { - Alert.alert( - 'Удалить аккаунт', - 'Ключ будет удалён с устройства. Убедитесь, что у вас есть резервная копия!', - [ - { text: 'Отмена', style: 'cancel' }, - { - text: 'Удалить', - style: 'destructive', - onPress: async () => { - await deleteKeyFile(); - setKeyFile(null); - router.replace('/'); - }, - }, - ], - ); - } - - const statusColor = nodeStatus === 'ok' ? C.ok : nodeStatus === 'error' ? C.err : C.warn; - const statusLabel = nodeStatus === 'ok' ? 'Подключена' : nodeStatus === 'error' ? 'Недоступна' : 'Проверка…'; - - return ( - - - Настройки - - - {/* ── Профиль ── */} - Профиль - - {/* Avatar row */} - - - - {username ? ( - @{username} - ) : ( - Имя не зарегистрировано - )} - - {keyFile ? shortAddr(keyFile.pub_key, 10) : '—'} - - - - - {/* Copy address */} - - - - - {copied ? 'Скопировано' : 'Скопировать адрес'} - - - - - - {/* ── Имя пользователя ── */} - Имя пользователя - - {username ? ( - // Already registered - - - - - - @{username} - Привязано к вашему адресу - - - Активно - - - ) : ( - // Register new - <> - - - Купить никнейм - - - Короткие имена дороже. Оплата идёт в казну контракта реестра. - - - - {/* Input */} - - - @ - - {nameInput.length > 0 && ( - {nameInput.length} - )} - - {nameError && ( - ⚠ {nameError} - )} - - - {/* Fee breakdown + rules */} - - - {/* Primary cost line */} - - - Плата за ник (сгорает) - - {formatAmount(nameFee)} - - - - - Комиссия сети (валидатору) - - {formatAmount(1000)} - - - - {/* Total */} - - Итого - - {formatAmount(nameFee + 1000)} - - - - {/* Rules */} - - Минимум {MIN_USERNAME_LENGTH} символа, только{' '} - a-z 0-9 _ - - , первый символ — буква. - - - - - {/* Register button */} - - - - - {registering ? 'Покупка…' : 'Купить никнейм'} - - - {!settings.contractId && ( - - Укажите ID контракта реестра в настройках ноды ниже - - )} - - - )} - - - {/* ── Нода ── */} - Нода - - {/* Connection status */} - - - - - - Подключение - {statusLabel} - - {nodeStatus === 'ok' && ( - - {peerCount !== null && ( - - {peerCount} пиров - - )} - {blockCount !== null && ( - - {blockCount.toLocaleString()} блоков - - )} - - )} - - - {/* Node URL input */} - - - - - {/* Registry contract — auto-detected from node; manual override under advanced */} - - - - - КОНТРАКТ РЕЕСТРА ИМЁН - - - {settings.contractId ? 'Авто-обнаружен' : 'Не найден'} - - - - {settings.contractId || '—'} - - setShowAdvanced(v => !v)} - style={{ marginTop: 8 }} - > - - {showAdvanced ? '▾ Скрыть ручной ввод' : '▸ Указать вручную (не требуется)'} - - - {showAdvanced && ( - - - - Оставьте пустым — клиент запросит канонический контракт у ноды. - - - )} - - - {/* Save button */} - - - - Сохранить и переподключиться - - - - - {/* ── Безопасность ── */} - Безопасность - - - - - - - Экспорт ключа - Сохранить приватный ключ как key.json - - - Экспорт - - - - - {/* ── Опасная зона ── */} - Опасная зона - - - - - Удалить аккаунт - - - Удаляет ключ с устройства. Онлайн-идентичность сохраняется, но доступ будет потерян без резервной копии. - - - - Удалить с устройства - - - - - ); -} diff --git a/client-app/app/(app)/wallet.tsx b/client-app/app/(app)/wallet.tsx deleted file mode 100644 index 829869e..0000000 --- a/client-app/app/(app)/wallet.tsx +++ /dev/null @@ -1,596 +0,0 @@ -/** - * Wallet screen — DChain explorer style. - * Balance block inspired by Tinkoff/Gravity UI reference. - * Icons: Ionicons from @expo/vector-icons. - */ - -import React, { useState, useCallback, useEffect } from 'react'; -import { - View, Text, ScrollView, Modal, TouchableOpacity, - Alert, RefreshControl, -} from 'react-native'; -import * as Clipboard from 'expo-clipboard'; -import { Ionicons } from '@expo/vector-icons'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useStore } from '@/lib/store'; -import { useBalance } from '@/hooks/useBalance'; -import { buildTransferTx, submitTx, getTxHistory, getBalance } from '@/lib/api'; -import { shortAddr } from '@/lib/crypto'; -import { formatAmount, relativeTime } from '@/lib/utils'; -import { Input } from '@/components/ui/Input'; -import type { TxRecord } from '@/lib/types'; - -// ─── Design tokens ──────────────────────────────────────────────────────────── -const C = { - bg: '#0b1220', - surface: '#111a2b', - surface2:'#162035', - surface3:'#1a2640', - line: '#1c2840', - text: '#e6edf9', - muted: '#98a7c2', - accent: '#7db5ff', - ok: '#41c98a', - warn: '#f0b35a', - err: '#ff7a87', -} as const; - -// ─── TX metadata ────────────────────────────────────────────────────────────── - -const TX_META: Record = { - TRANSFER: { label: 'Перевод', icon: 'paper-plane-outline', color: C.accent }, - CONTACT_REQUEST: { label: 'Запрос контакта', icon: 'person-add-outline', color: C.ok }, - ACCEPT_CONTACT: { label: 'Принят контакт', icon: 'person-outline', color: C.ok }, - BLOCK_CONTACT: { label: 'Блокировка', icon: 'ban-outline', color: C.err }, - DEPLOY_CONTRACT: { label: 'Деплой контракта',icon: 'document-text-outline', color: C.warn }, - CALL_CONTRACT: { label: 'Вызов контракта', icon: 'flash-outline', color: C.warn }, - STAKE: { label: 'Стейкинг', icon: 'lock-closed-outline', color: C.accent}, - UNSTAKE: { label: 'Вывод стейка', icon: 'lock-open-outline', color: C.muted }, - REGISTER_KEY: { label: 'Регистрация', icon: 'key-outline', color: C.muted }, - BLOCK_REWARD: { label: 'Награда', icon: 'diamond-outline', color: C.ok }, -}; - -function txMeta(type: string) { - return TX_META[type] ?? { label: type.replace(/_/g, ' '), icon: 'ellipse-outline' as any, color: C.muted }; -} - -// ─── Component ──────────────────────────────────────────────────────────────── - -export default function WalletScreen() { - const keyFile = useStore(s => s.keyFile); - const balance = useStore(s => s.balance); - const setBalance = useStore(s => s.setBalance); - const insets = useSafeAreaInsets(); - - useBalance(); - - const [txHistory, setTxHistory] = useState([]); - const [refreshing, setRefreshing] = useState(false); - const [showSend, setShowSend] = useState(false); - const [selectedTx, setSelectedTx] = useState(null); - const [toAddress, setToAddress] = useState(''); - const [amount, setAmount] = useState(''); - const [fee, setFee] = useState('1000'); - const [sending, setSending] = useState(false); - const [copied, setCopied] = useState(false); - - const load = useCallback(async () => { - if (!keyFile) return; - setRefreshing(true); - try { - const [hist, bal] = await Promise.all([ - getTxHistory(keyFile.pub_key), - getBalance(keyFile.pub_key), - ]); - setTxHistory(hist); - setBalance(bal); - } catch {} - setRefreshing(false); - }, [keyFile]); - - useEffect(() => { load(); }, [load]); - - const copyAddress = async () => { - if (!keyFile) return; - await Clipboard.setStringAsync(keyFile.pub_key); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - const send = async () => { - if (!keyFile) return; - const amt = parseInt(amount); - const f = parseInt(fee); - if (!toAddress.trim() || isNaN(amt) || amt <= 0) { - Alert.alert('Неверные данные', 'Введите корректный адрес и сумму.'); - return; - } - if (amt + f > balance) { - Alert.alert('Недостаточно средств', `Нужно ${formatAmount(amt + f)}, доступно ${formatAmount(balance)}.`); - return; - } - setSending(true); - try { - const tx = buildTransferTx({ from: keyFile.pub_key, to: toAddress.trim(), amount: amt, fee: f, privKey: keyFile.priv_key }); - await submitTx(tx); - setShowSend(false); - setToAddress(''); - setAmount(''); - Alert.alert('Отправлено', 'Транзакция принята нодой.'); - setTimeout(load, 1500); - } catch (e: any) { - Alert.alert('Ошибка', e.message); - } finally { - setSending(false); - } - }; - - return ( - - } - contentContainerStyle={{ paddingBottom: 32 }} - > - {/* ── Balance hero ── */} - setShowSend(true)} - onReceive={copyAddress} - onRefresh={load} - onCopy={copyAddress} - /> - - {/* ── Transaction list ── */} - - - История транзакций - - - {txHistory.length === 0 ? ( - - - Нет транзакций - Потяните вниз, чтобы обновить - - ) : ( - - {/* Table header */} - - - ТИП - - - АДРЕС - - - СУММА - - - - {txHistory.map((tx, i) => ( - setSelectedTx(tx)} - /> - ))} - - )} - - - - {/* Send modal */} - setShowSend(false)}> - setShowSend(false)} - /> - - - {/* TX Detail modal */} - setSelectedTx(null)}> - {selectedTx && ( - setSelectedTx(null)} /> - )} - - - ); -} - -// ─── Balance Hero ───────────────────────────────────────────────────────────── - -function BalanceHero({ - balance, address, copied, topInset, onSend, onReceive, onRefresh, onCopy, -}: { - balance: number; address: string; copied: boolean; - topInset: number; - onSend: () => void; onReceive: () => void; - onRefresh: () => void; onCopy: () => void; -}) { - return ( - - {/* Label */} - - Баланс - - - {/* Main balance */} - - {formatAmount(balance)} - - - {/* µT sub-label */} - - {(balance ?? 0).toLocaleString()} µT - - - {/* Address chip */} - - - - {copied ? 'Скопировано' : (address ? shortAddr(address, 8) : '—')} - - - - {/* Action buttons */} - - - - - - - ); -} - -function ActionButton({ - icon, label, color, onPress, -}: { - icon: keyof typeof Ionicons.glyphMap; label: string; color: string; onPress: () => void; -}) { - return ( - - - - - {label} - - ); -} - -// ─── Transaction Row ────────────────────────────────────────────────────────── -// Column widths must match the header above: -// col1 (type+icon): 140 col2 (address): flex:1 col3 (amount): 84 - -// tx types where "from" is always the owner but it's income, not a send -const RECEIVED_TYPES = new Set(['BLOCK_REWARD', 'STAKE_REWARD']); - -function TxRow({ - tx, myPubKey, isLast, onPress, -}: { - tx: TxRecord; myPubKey: string; isLast: boolean; onPress: () => void; -}) { - const meta = txMeta(tx.type); - const isSynthetic = !tx.from; // block reward / mint - const isSent = !isSynthetic && !RECEIVED_TYPES.has(tx.type) && tx.from === myPubKey; - const amt = tx.amount ?? 0; - const amtText = amt === 0 ? '' : `${isSent ? '−' : '+'}${formatAmount(amt)}`; - const amtColor = isSent ? C.err : C.ok; - - // Counterpart label: for synthetic (empty from) rewards → "Сеть", - // otherwise show the short address of the other side. - const counterpart = isSynthetic - ? 'Сеть' - : isSent - ? (tx.to ? shortAddr(tx.to, 6) : '—') - : shortAddr(tx.from, 6); - - return ( - - {/* Col 1 — icon + type label, fixed 140px */} - - - - - - {meta.label} - - - - {/* Col 2 — address, flex */} - - - {isSent ? `→ ${counterpart}` : `← ${counterpart}`} - - - - {/* Col 3 — amount + time, fixed 84px, right-aligned */} - - {!!amtText && ( - - {amtText} - - )} - - {relativeTime(tx.timestamp)} - - - - ); -} - -// ─── Send Sheet ─────────────────────────────────────────────────────────────── - -function SendSheet({ - balance, toAddress, setToAddress, amount, setAmount, fee, setFee, sending, onSend, onClose, -}: { - balance: number; - toAddress: string; setToAddress: (v: string) => void; - amount: string; setAmount: (v: string) => void; - fee: string; setFee: (v: string) => void; - sending: boolean; onSend: () => void; onClose: () => void; -}) { - return ( - - - Отправить - - - - - - - - Доступно: - {formatAmount(balance)} - - - - - - - - {!sending && } - - {sending ? 'Отправка…' : 'Подтвердить'} - - - - ); -} - -// ─── TX Detail Sheet ────────────────────────────────────────────────────────── - -function TxDetailSheet({ - tx, myPubKey, onClose, -}: { - tx: TxRecord; myPubKey: string; onClose: () => void; -}) { - const [copiedHash, setCopiedHash] = useState(false); - const meta = txMeta(tx.type); - const isSent = tx.from === myPubKey; - const amtValue = tx.amount ?? 0; - - const copyHash = async () => { - await Clipboard.setStringAsync(tx.hash); - setCopiedHash(true); - setTimeout(() => setCopiedHash(false), 2000); - }; - - const amtColor = amtValue === 0 ? C.muted : isSent ? C.err : C.ok; - const amtSign = amtValue === 0 ? '' : isSent ? '−' : '+'; - - return ( - - {/* Handle */} - - - - - - {/* Header */} - - Транзакция - - - - - - {/* Hero */} - - - - - 0 ? 8 : 0 }}> - {meta.label} - - {amtValue > 0 && ( - - {amtSign}{formatAmount(amtValue)} - - )} - - - {tx.status === 'confirmed' ? '✓ Подтверждена' : '⏳ В обработке'} - - - - - {/* Details table */} - - - {tx.amount !== undefined && ( - - )} - - {tx.timestamp > 0 && ( - - )} - - - {/* Addresses */} - - {/* "From" for synthetic txs (empty tx.from) reads "Сеть" rather than an empty row. */} - - {tx.to && ( - - )} - - - {/* TX Hash */} - - - - - - TX ID / Hash - - - - {tx.hash} - - - - - - - - {copiedHash ? 'Скопировано' : 'Копировать хеш'} - - - - - ); -} - -// ─── Detail Row ─────────────────────────────────────────────────────────────── - -function DetailRow({ - icon, label, value, mono, truncate, first, -}: { - icon: keyof typeof Ionicons.glyphMap; - label: string; value: string; - mono?: boolean; truncate?: boolean; first?: boolean; -}) { - return ( - - - {label} - - {value} - - - ); -} diff --git a/client-app/app/(auth)/create.tsx b/client-app/app/(auth)/create.tsx deleted file mode 100644 index b21712c..0000000 --- a/client-app/app/(auth)/create.tsx +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Create Account screen. - * Generates a new Ed25519 + X25519 keypair and saves it securely. - */ - -import React, { useState } from 'react'; -import { View, Text, ScrollView, Alert } from 'react-native'; -import { router } from 'expo-router'; -import { generateKeyFile } from '@/lib/crypto'; -import { saveKeyFile } from '@/lib/storage'; -import { useStore } from '@/lib/store'; -import { Button } from '@/components/ui/Button'; -import { Input } from '@/components/ui/Input'; -import { Card } from '@/components/ui/Card'; - -export default function CreateAccountScreen() { - const setKeyFile = useStore(s => s.setKeyFile); - const [loading, setLoading] = useState(false); - - async function handleCreate() { - setLoading(true); - try { - const kf = generateKeyFile(); - await saveKeyFile(kf); - setKeyFile(kf); - router.replace('/(auth)/created'); - } catch (e: any) { - Alert.alert('Error', e.message); - } finally { - setLoading(false); - } - } - - return ( - - {/* Header */} - - - Create Account - - A new identity will be generated on your device. - Your private key never leaves this app. - - - {/* Info cards */} - - - - - - - - ⚠ Important - - After creation, export and backup your key file. - If you lose it there is no recovery — the blockchain has no password reset. - - - - - - ); -} - -function InfoRow({ icon, label, desc }: { icon: string; label: string; desc: string }) { - return ( - - {icon} - - {label} - {desc} - - - ); -} diff --git a/client-app/app/(auth)/created.tsx b/client-app/app/(auth)/created.tsx deleted file mode 100644 index 241d63c..0000000 --- a/client-app/app/(auth)/created.tsx +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Account Created confirmation screen. - * Shows address, pubkeys, and export options. - */ - -import React, { useState } from 'react'; -import { View, Text, ScrollView, Alert, Share } from 'react-native'; -import { router } from 'expo-router'; -import * as Clipboard from 'expo-clipboard'; -import * as FileSystem from 'expo-file-system'; -import * as Sharing from 'expo-sharing'; -import { useStore } from '@/lib/store'; -import { shortAddr } from '@/lib/crypto'; -import { Button } from '@/components/ui/Button'; -import { Card } from '@/components/ui/Card'; -import { Separator } from '@/components/ui/Separator'; - -export default function AccountCreatedScreen() { - const keyFile = useStore(s => s.keyFile); - const [copied, setCopied] = useState(null); - - if (!keyFile) { - router.replace('/'); - return null; - } - - async function copy(value: string, label: string) { - await Clipboard.setStringAsync(value); - setCopied(label); - setTimeout(() => setCopied(null), 2000); - } - - async function exportKey() { - try { - const json = JSON.stringify(keyFile, null, 2); - const path = FileSystem.cacheDirectory + 'dchain_key.json'; - await FileSystem.writeAsStringAsync(path, json); - if (await Sharing.isAvailableAsync()) { - await Sharing.shareAsync(path, { - mimeType: 'application/json', - dialogTitle: 'Save your DChain key file', - }); - } else { - Alert.alert('Export', 'Sharing not available on this device.'); - } - } catch (e: any) { - Alert.alert('Export failed', e.message); - } - } - - return ( - - {/* Success header */} - - - - - Account Created! - - Your keys have been generated and stored securely. - - - - {/* Address card */} - - - Your Address (Ed25519) - - - {keyFile.pub_key} - - - - - {/* X25519 key */} - - - Encryption Key (X25519) - - - {keyFile.x25519_pub} - - - - - {/* Export warning */} - - 🔐 Backup your key file - - Export dchain_key.json and store it safely. - This file contains your private keys — keep it secret. - - - - - - - ); -} diff --git a/client-app/app/(auth)/import.tsx b/client-app/app/(auth)/import.tsx deleted file mode 100644 index ca814f1..0000000 --- a/client-app/app/(auth)/import.tsx +++ /dev/null @@ -1,301 +0,0 @@ -/** - * Import Existing Key screen. - * Two methods: - * 1. Paste JSON directly into a text field - * 2. Pick key.json file via document picker - */ - -import React, { useState } from 'react'; -import { - View, Text, ScrollView, TextInput, - TouchableOpacity, Alert, Pressable, -} from 'react-native'; -import { router } from 'expo-router'; -import * as DocumentPicker from 'expo-document-picker'; -import * as Clipboard from 'expo-clipboard'; -import { saveKeyFile } from '@/lib/storage'; -import { useStore } from '@/lib/store'; -import { Button } from '@/components/ui/Button'; -import type { KeyFile } from '@/lib/types'; - -type Tab = 'paste' | 'file'; - -const REQUIRED_FIELDS: (keyof KeyFile)[] = ['pub_key', 'priv_key', 'x25519_pub', 'x25519_priv']; - -function validateKeyFile(raw: string): KeyFile { - let parsed: any; - try { - parsed = JSON.parse(raw.trim()); - } catch { - throw new Error('Invalid JSON — check that you copied the full key file contents.'); - } - for (const field of REQUIRED_FIELDS) { - if (!parsed[field] || typeof parsed[field] !== 'string') { - throw new Error(`Missing or invalid field: "${field}"`); - } - if (!/^[0-9a-f]+$/i.test(parsed[field])) { - throw new Error(`Field "${field}" must be a hex string.`); - } - } - return parsed as KeyFile; -} - -export default function ImportKeyScreen() { - const setKeyFile = useStore(s => s.setKeyFile); - - const [tab, setTab] = useState('paste'); - const [jsonText, setJsonText] = useState(''); - const [fileName, setFileName] = useState(null); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - // ── Shared: save validated key and navigate ────────────────────────────── - async function applyKey(kf: KeyFile) { - setLoading(true); - setError(null); - try { - await saveKeyFile(kf); - setKeyFile(kf); - router.replace('/(app)/chats'); - } catch (e: any) { - setError(e.message); - } finally { - setLoading(false); - } - } - - // ── Method 1: paste JSON ───────────────────────────────────────────────── - async function handlePasteImport() { - setError(null); - const text = jsonText.trim(); - if (!text) { - // Try reading clipboard if field is empty - const clip = await Clipboard.getStringAsync(); - if (clip) setJsonText(clip); - return; - } - try { - const kf = validateKeyFile(text); - await applyKey(kf); - } catch (e: any) { - setError(e.message); - } - } - - // ── Method 2: pick file ────────────────────────────────────────────────── - async function pickFile() { - setError(null); - try { - const result = await DocumentPicker.getDocumentAsync({ - type: ['application/json', 'text/plain', '*/*'], - copyToCacheDirectory: true, - }); - if (result.canceled) return; - - const asset = result.assets[0]; - setFileName(asset.name); - - // Use fetch() — readAsStringAsync is deprecated in newer expo-file-system - const response = await fetch(asset.uri); - const raw = await response.text(); - - const kf = validateKeyFile(raw); - await applyKey(kf); - } catch (e: any) { - setError(e.message); - } - } - - const tabStyle = (t: Tab) => ({ - flex: 1 as const, - paddingVertical: 10, - alignItems: 'center' as const, - borderBottomWidth: 2, - borderBottomColor: tab === t ? '#2563eb' : 'transparent', - }); - - const tabTextStyle = (t: Tab) => ({ - fontSize: 14, - fontWeight: '600' as const, - color: tab === t ? '#fff' : '#8b949e', - }); - - return ( - - {/* Back */} - router.back()} style={{ marginBottom: 24, alignSelf: 'flex-start' }}> - ← Back - - - - Import Key - - - Restore your account from an existing{' '} - key.json. - - - {/* Tabs */} - - setTab('paste')}> - 📋 Paste JSON - - setTab('file')}> - 📁 Open File - - - - {/* ── Paste tab ── */} - {tab === 'paste' && ( - - - Key JSON - - - - { setJsonText(t); setError(null); }} - placeholder={'{\n "pub_key": "...",\n "priv_key": "...",\n "x25519_pub": "...",\n "x25519_priv": "..."\n}'} - placeholderTextColor="#8b949e" - multiline - numberOfLines={8} - autoCapitalize="none" - autoCorrect={false} - style={{ - color: '#fff', - fontFamily: 'monospace', - fontSize: 12, - lineHeight: 18, - minHeight: 160, - textAlignVertical: 'top', - }} - /> - - - {/* Paste from clipboard shortcut */} - {!jsonText && ( - { - const clip = await Clipboard.getStringAsync(); - if (clip) { setJsonText(clip); setError(null); } - else Alert.alert('Clipboard empty', 'Copy your key JSON first.'); - }} - style={{ - flexDirection: 'row', alignItems: 'center', gap: 8, - padding: 12, backgroundColor: '#161b22', - borderWidth: 1, borderColor: '#30363d', borderRadius: 12, - }} - > - 📋 - Paste from clipboard - - )} - - {error && ( - - ⚠ {error} - - )} - - - - )} - - {/* ── File tab ── */} - {tab === 'file' && ( - - - 📂 - - {fileName ?? 'Choose key.json'} - - - Tap to browse files - - - - {fileName && ( - - 📄 - - {fileName} - - - )} - - {error && ( - - ⚠ {error} - - )} - - {loading && ( - - Validating key… - - )} - - )} - - {/* Format hint */} - - - Expected format - - - {`{\n "pub_key": "<64 hex chars>",\n "priv_key": "<128 hex chars>",\n "x25519_pub": "<64 hex chars>",\n "x25519_priv": "<64 hex chars>"\n}`} - - - - ); -} diff --git a/client-app/app/_layout.tsx b/client-app/app/_layout.tsx deleted file mode 100644 index 8b70cc4..0000000 --- a/client-app/app/_layout.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import '../global.css'; - -import React, { useEffect } from 'react'; -import { Stack } from 'expo-router'; -import { StatusBar } from 'expo-status-bar'; -import { View } from 'react-native'; -import { SafeAreaProvider } from 'react-native-safe-area-context'; -import { loadKeyFile, loadSettings } from '@/lib/storage'; -import { setNodeUrl } from '@/lib/api'; -import { useStore } from '@/lib/store'; - -export default function RootLayout() { - const setKeyFile = useStore(s => s.setKeyFile); - const setSettings = useStore(s => s.setSettings); - - // Bootstrap: load key + settings from storage - useEffect(() => { - (async () => { - const [kf, settings] = await Promise.all([loadKeyFile(), loadSettings()]); - if (kf) setKeyFile(kf); - setSettings(settings); - setNodeUrl(settings.nodeUrl); - })(); - }, []); - - return ( - - - - - - - ); -} diff --git a/client-app/app/index.tsx b/client-app/app/index.tsx deleted file mode 100644 index 8a3d41c..0000000 --- a/client-app/app/index.tsx +++ /dev/null @@ -1,220 +0,0 @@ -/** - * Welcome / landing screen. - * - Node URL input with live ping + QR scanner - * - Create / Import account buttons - * Redirects to (app)/chats if key already loaded. - */ - -import React, { useEffect, useState, useCallback } from 'react'; -import { - View, Text, TextInput, Pressable, - ScrollView, Alert, ActivityIndicator, -} from 'react-native'; -import { router } from 'expo-router'; -import { CameraView, useCameraPermissions } from 'expo-camera'; -import { useStore } from '@/lib/store'; -import { saveSettings } from '@/lib/storage'; -import { setNodeUrl, getNetStats } from '@/lib/api'; -import { Button } from '@/components/ui/Button'; - -export default function WelcomeScreen() { - const keyFile = useStore(s => s.keyFile); - const settings = useStore(s => s.settings); - const setSettings = useStore(s => s.setSettings); - - const [nodeInput, setNodeInput] = useState(''); - const [scanning, setScanning] = useState(false); - const [checking, setChecking] = useState(false); - const [nodeOk, setNodeOk] = useState(null); - - const [permission, requestPermission] = useCameraPermissions(); - - useEffect(() => { - if (keyFile) router.replace('/(app)/chats'); - }, [keyFile]); - - useEffect(() => { - setNodeInput(settings.nodeUrl); - }, [settings.nodeUrl]); - - const applyNode = useCallback(async (url: string) => { - const clean = url.trim().replace(/\/$/, ''); - if (!clean) return; - setChecking(true); - setNodeOk(null); - setNodeUrl(clean); - try { - await getNetStats(); - setNodeOk(true); - const next = { ...settings, nodeUrl: clean }; - setSettings(next); - await saveSettings(next); - } catch { - setNodeOk(false); - } finally { - setChecking(false); - } - }, [settings, setSettings]); - - const onQrScanned = useCallback(({ data }: { data: string }) => { - setScanning(false); - let url = data.trim(); - try { const p = JSON.parse(url); if (p.nodeUrl) url = p.nodeUrl; } catch {} - setNodeInput(url); - applyNode(url); - }, [applyNode]); - - const openScanner = async () => { - if (!permission?.granted) { - const { granted } = await requestPermission(); - if (!granted) { - Alert.alert('Camera permission required', 'Allow camera access to scan QR codes.'); - return; - } - } - setScanning(true); - }; - - // ── QR Scanner overlay ─────────────────────────────────────────────────── - if (scanning) { - return ( - - - - - - Point at a DChain node QR code - - - setScanning(false)} - style={{ - position: 'absolute', top: 56, left: 16, - backgroundColor: 'rgba(0,0,0,0.6)', borderRadius: 20, - paddingHorizontal: 16, paddingVertical: 8, - }} - > - ✕ Cancel - - - ); - } - - // ── Main screen ────────────────────────────────────────────────────────── - const statusColor = nodeOk === true ? '#3fb950' : nodeOk === false ? '#f85149' : '#8b949e'; - - return ( - - {/* Logo ─ takes remaining space above, centered */} - - - - - - DChain - - - Decentralised E2E-encrypted messenger.{'\n'}Your keys. Your messages. - - - - {/* Bottom section ─ node input + buttons */} - - - {/* Node URL label */} - - Node URL - - - {/* Input row */} - - - {/* Status dot */} - - - { setNodeInput(t); setNodeOk(null); }} - onEndEditing={() => applyNode(nodeInput)} - onSubmitEditing={() => applyNode(nodeInput)} - placeholder="http://192.168.1.10:8081" - placeholderTextColor="#8b949e" - autoCapitalize="none" - autoCorrect={false} - keyboardType="url" - returnKeyType="done" - style={{ flex: 1, color: '#fff', fontSize: 14, paddingVertical: 14 }} - /> - - {checking - ? - : nodeOk === true - ? - : nodeOk === false - ? - : null - } - - - {/* QR button */} - ({ - width: 48, alignItems: 'center', justifyContent: 'center', - backgroundColor: '#21262d', borderWidth: 1, borderColor: '#30363d', - borderRadius: 12, opacity: pressed ? 0.7 : 1, - })} - > - - - - - {/* Status text */} - {nodeOk === true && ( - - ✓ Node connected - - )} - {nodeOk === false && ( - - ✗ Cannot reach node — check URL and that the node is running - - )} - - {/* Buttons */} - - - - - - - ); -} diff --git a/client-app/babel.config.js b/client-app/babel.config.js deleted file mode 100644 index d08d04a..0000000 --- a/client-app/babel.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = function (api) { - api.cache(true); - return { - presets: [ - ['babel-preset-expo', { jsxImportSource: 'nativewind' }], - 'nativewind/babel', - ], - plugins: [ - 'react-native-reanimated/plugin', // must be last - ], - }; -}; diff --git a/client-app/components/ui/Avatar.tsx b/client-app/components/ui/Avatar.tsx deleted file mode 100644 index 8151675..0000000 --- a/client-app/components/ui/Avatar.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { View, Text } from 'react-native'; -import { cn } from '@/lib/utils'; - -/** Deterministic color from a string */ -function colorFor(str: string): string { - const colors = [ - 'bg-blue-600', 'bg-purple-600', 'bg-green-600', - 'bg-pink-600', 'bg-orange-600', 'bg-teal-600', - 'bg-red-600', 'bg-indigo-600', 'bg-cyan-600', - ]; - let h = 0; - for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) >>> 0; - return colors[h % colors.length]; -} - -interface AvatarProps { - name?: string; - size?: 'sm' | 'md' | 'lg'; - className?: string; -} - -const sizeMap = { - sm: { outer: 'w-8 h-8', text: 'text-sm' }, - md: { outer: 'w-10 h-10', text: 'text-base' }, - lg: { outer: 'w-14 h-14', text: 'text-xl' }, -}; - -export function Avatar({ name = '?', size = 'md', className }: AvatarProps) { - const initials = name.slice(0, 2).toUpperCase(); - const { outer, text } = sizeMap[size]; - return ( - - {initials} - - ); -} diff --git a/client-app/components/ui/Badge.tsx b/client-app/components/ui/Badge.tsx deleted file mode 100644 index aab9fa7..0000000 --- a/client-app/components/ui/Badge.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { View, Text } from 'react-native'; -import { cn } from '@/lib/utils'; - -interface BadgeProps { - label: string | number; - variant?: 'default' | 'success' | 'destructive' | 'muted'; - className?: string; -} - -const variantMap = { - default: 'bg-primary', - success: 'bg-success', - destructive: 'bg-destructive', - muted: 'bg-surfaceHigh border border-border', -}; - -export function Badge({ label, variant = 'default', className }: BadgeProps) { - return ( - - {label} - - ); -} diff --git a/client-app/components/ui/Button.tsx b/client-app/components/ui/Button.tsx deleted file mode 100644 index 040b1c4..0000000 --- a/client-app/components/ui/Button.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import { Pressable, Text, ActivityIndicator } from 'react-native'; -import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from '@/lib/utils'; - -const buttonVariants = cva( - 'flex-row items-center justify-center rounded-xl px-5 py-3 active:opacity-80', - { - variants: { - variant: { - default: 'bg-primary', - secondary: 'bg-surfaceHigh border border-border', - destructive: 'bg-destructive', - ghost: 'bg-transparent', - outline: 'bg-transparent border border-primary', - }, - size: { - sm: 'px-3 py-2', - md: 'px-5 py-3', - lg: 'px-6 py-4', - icon: 'p-2', - }, - }, - defaultVariants: { - variant: 'default', - size: 'md', - }, - }, -); - -const textVariants = cva('font-semibold text-center', { - variants: { - variant: { - default: 'text-white', - secondary: 'text-white', - destructive: 'text-white', - ghost: 'text-primary', - outline: 'text-primary', - }, - size: { - sm: 'text-sm', - md: 'text-base', - lg: 'text-lg', - icon: 'text-base', - }, - }, - defaultVariants: { - variant: 'default', - size: 'md', - }, -}); - -interface ButtonProps extends VariantProps { - onPress?: () => void; - disabled?: boolean; - loading?: boolean; - children: React.ReactNode; - className?: string; -} - -export function Button({ - variant, size, onPress, disabled, loading, children, className, -}: ButtonProps) { - return ( - - {loading - ? - : {children} - } - - ); -} diff --git a/client-app/components/ui/Card.tsx b/client-app/components/ui/Card.tsx deleted file mode 100644 index 01fe288..0000000 --- a/client-app/components/ui/Card.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { View } from 'react-native'; -import { cn } from '@/lib/utils'; - -interface CardProps { - children: React.ReactNode; - className?: string; -} - -export function Card({ children, className }: CardProps) { - return ( - - {children} - - ); -} diff --git a/client-app/components/ui/Input.tsx b/client-app/components/ui/Input.tsx deleted file mode 100644 index 7a09b39..0000000 --- a/client-app/components/ui/Input.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { forwardRef } from 'react'; -import { TextInput, View, Text, type TextInputProps } from 'react-native'; -import { cn } from '@/lib/utils'; - -interface InputProps extends TextInputProps { - label?: string; - error?: string; - className?: string; -} - -export const Input = forwardRef( - ({ label, error, className, ...props }, ref) => ( - - {label && ( - {label} - )} - - {error && ( - {error} - )} - - ), -); - -Input.displayName = 'Input'; diff --git a/client-app/components/ui/Separator.tsx b/client-app/components/ui/Separator.tsx deleted file mode 100644 index 29edd95..0000000 --- a/client-app/components/ui/Separator.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; -import { View } from 'react-native'; -import { cn } from '@/lib/utils'; - -export function Separator({ className }: { className?: string }) { - return ; -} diff --git a/client-app/components/ui/index.ts b/client-app/components/ui/index.ts deleted file mode 100644 index 3af05b2..0000000 --- a/client-app/components/ui/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { Button } from './Button'; -export { Card } from './Card'; -export { Input } from './Input'; -export { Avatar } from './Avatar'; -export { Badge } from './Badge'; -export { Separator } from './Separator'; diff --git a/client-app/global.css b/client-app/global.css deleted file mode 100644 index b5c61c9..0000000 --- a/client-app/global.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/client-app/hooks/useBalance.ts b/client-app/hooks/useBalance.ts deleted file mode 100644 index 54d5dac..0000000 --- a/client-app/hooks/useBalance.ts +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Balance hook — uses the WebSocket gateway to receive instant updates when - * a tx involving the current address is committed, with HTTP polling as a - * graceful fallback for old nodes that don't expose /api/ws. - * - * Flow: - * 1. On mount: immediate HTTP fetch so the UI has a non-zero balance ASAP - * 2. Subscribe to `addr:` on the WS hub - * 3. On every `tx` event, re-fetch balance (cheap — one Badger read server-side) - * 4. If WS disconnects for >15s, fall back to 10-second polling until it reconnects - */ - -import { useEffect, useCallback, useRef } from 'react'; -import { getBalance } from '@/lib/api'; -import { getWSClient } from '@/lib/ws'; -import { useStore } from '@/lib/store'; - -const FALLBACK_POLL_INTERVAL = 10_000; // HTTP poll when WS is down -const WS_GRACE_BEFORE_POLLING = 15_000; // don't start polling immediately on disconnect - -export function useBalance() { - const keyFile = useStore(s => s.keyFile); - const setBalance = useStore(s => s.setBalance); - - const refresh = useCallback(async () => { - if (!keyFile) return; - try { - const bal = await getBalance(keyFile.pub_key); - setBalance(bal); - } catch { - // transient — next call will retry - } - }, [keyFile, setBalance]); - - // --- fallback polling management --- - const pollTimerRef = useRef | null>(null); - const disconnectSinceRef = useRef(null); - const disconnectTORef = useRef | null>(null); - - const startPolling = useCallback(() => { - if (pollTimerRef.current) return; - console.log('[useBalance] WS down for grace period — starting HTTP poll'); - refresh(); - pollTimerRef.current = setInterval(refresh, FALLBACK_POLL_INTERVAL); - }, [refresh]); - - const stopPolling = useCallback(() => { - if (pollTimerRef.current) { - clearInterval(pollTimerRef.current); - pollTimerRef.current = null; - } - if (disconnectTORef.current) { - clearTimeout(disconnectTORef.current); - disconnectTORef.current = null; - } - disconnectSinceRef.current = null; - }, []); - - useEffect(() => { - if (!keyFile) return; - const ws = getWSClient(); - - // Immediate HTTP fetch so the UI is not empty while the WS hello arrives. - refresh(); - - // Refresh balance whenever a tx for our address is committed. - const offTx = ws.subscribe('addr:' + keyFile.pub_key, (frame) => { - if (frame.event === 'tx') { - refresh(); - } - }); - - // Manage fallback polling based on WS connection state. - const offConn = ws.onConnectionChange((ok) => { - if (ok) { - stopPolling(); - refresh(); // catch up anything we missed while disconnected - } else if (disconnectTORef.current === null) { - disconnectSinceRef.current = Date.now(); - disconnectTORef.current = setTimeout(startPolling, WS_GRACE_BEFORE_POLLING); - } - }); - - ws.connect(); - - return () => { - offTx(); - offConn(); - stopPolling(); - }; - }, [keyFile, refresh, startPolling, stopPolling]); - - return { refresh }; -} diff --git a/client-app/hooks/useContacts.ts b/client-app/hooks/useContacts.ts deleted file mode 100644 index cf8f9e9..0000000 --- a/client-app/hooks/useContacts.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Contacts + inbound request tracking. - * - * - Loads cached contacts from local storage on boot. - * - Subscribes to the address WS topic so a new CONTACT_REQUEST pulls the - * relay contact list immediately (sub-second UX). - * - Keeps a 30 s polling fallback for nodes without WS or while disconnected. - */ - -import { useEffect, useCallback } from 'react'; -import { fetchContactRequests } from '@/lib/api'; -import { getWSClient } from '@/lib/ws'; -import { loadContacts } from '@/lib/storage'; -import { useStore } from '@/lib/store'; - -const FALLBACK_POLL_INTERVAL = 30_000; - -export function useContacts() { - const keyFile = useStore(s => s.keyFile); - const setContacts = useStore(s => s.setContacts); - const setRequests = useStore(s => s.setRequests); - const contacts = useStore(s => s.contacts); - - // Load cached contacts from local storage once - useEffect(() => { - loadContacts().then(setContacts); - }, [setContacts]); - - const pollRequests = useCallback(async () => { - if (!keyFile) return; - try { - const raw = await fetchContactRequests(keyFile.pub_key); - - // Filter out already-accepted contacts - const contactAddresses = new Set(contacts.map(c => c.address)); - - const requests = raw - .filter(r => r.status === 'pending' && !contactAddresses.has(r.requester_pub)) - .map(r => ({ - from: r.requester_pub, - // x25519Pub will be fetched from identity when user taps Accept - x25519Pub: '', - intro: r.intro ?? '', - timestamp: r.created_at, - txHash: r.tx_id, - })); - - setRequests(requests); - } catch { - // Ignore transient network errors - } - }, [keyFile, contacts, setRequests]); - - useEffect(() => { - if (!keyFile) return; - const ws = getWSClient(); - - // Initial load + low-frequency fallback poll (covers missed WS events, - // works even when the node has no WS endpoint). - pollRequests(); - const interval = setInterval(pollRequests, FALLBACK_POLL_INTERVAL); - - // Immediate refresh when a CONTACT_REQUEST / ACCEPT_CONTACT tx addressed - // to us lands on-chain. WS fan-out already filters to our address topic. - const off = ws.subscribe('addr:' + keyFile.pub_key, (frame) => { - if (frame.event === 'tx') { - const d = frame.data as { tx_type?: string } | undefined; - if (d?.tx_type === 'CONTACT_REQUEST' || d?.tx_type === 'ACCEPT_CONTACT') { - pollRequests(); - } - } - }); - ws.connect(); - - return () => { - clearInterval(interval); - off(); - }; - }, [keyFile, pollRequests]); -} diff --git a/client-app/hooks/useMessages.ts b/client-app/hooks/useMessages.ts deleted file mode 100644 index b6ec46e..0000000 --- a/client-app/hooks/useMessages.ts +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Subscribe to the relay inbox via WebSocket and decrypt incoming envelopes - * for the active chat. Falls back to 30-second polling whenever the WS is - * not connected — preserves correctness on older nodes or flaky networks. - * - * Flow: - * 1. On mount: one HTTP fetch so we have whatever is already in the inbox - * 2. Subscribe to topic `inbox:` — the node pushes a summary - * for each fresh envelope as soon as mailbox.Store() succeeds - * 3. On each push, pull the full envelope list (cheap — bounded by - * MailboxPerRecipientCap) and decrypt anything we haven't seen yet - * 4. If WS disconnects for > 15 seconds, start a 30 s HTTP poll until it - * reconnects - */ - -import { useEffect, useCallback, useRef } from 'react'; -import { fetchInbox } from '@/lib/api'; -import { getWSClient } from '@/lib/ws'; -import { decryptMessage } from '@/lib/crypto'; -import { appendMessage } from '@/lib/storage'; -import { useStore } from '@/lib/store'; - -const FALLBACK_POLL_INTERVAL = 30_000; // HTTP poll when WS is down -const WS_GRACE_BEFORE_POLLING = 15_000; // don't start polling immediately on disconnect - -export function useMessages(contactX25519: string) { - const keyFile = useStore(s => s.keyFile); - const appendMsg = useStore(s => s.appendMessage); - - const pullAndDecrypt = useCallback(async () => { - if (!keyFile || !contactX25519) return; - try { - const envelopes = await fetchInbox(keyFile.x25519_pub); - for (const env of envelopes) { - // Only process messages from this contact - if (env.sender_pub !== contactX25519) continue; - - const text = decryptMessage( - env.ciphertext, - env.nonce, - env.sender_pub, - keyFile.x25519_priv, - ); - if (!text) continue; - - const msg = { - id: `${env.sender_pub}_${env.timestamp}_${env.nonce.slice(0, 8)}`, - from: env.sender_pub, - text, - timestamp: env.timestamp, - mine: false, - }; - appendMsg(contactX25519, msg); - await appendMessage(contactX25519, msg); - } - } catch (e) { - // Don't surface inbox errors aggressively — next event or poll retries - console.warn('[useMessages] pull error:', e); - } - }, [keyFile, contactX25519, appendMsg]); - - // ── Fallback polling state ──────────────────────────────────────────── - const pollTimerRef = useRef | null>(null); - const disconnectTORef = useRef | null>(null); - - const startPolling = useCallback(() => { - if (pollTimerRef.current) return; - console.log('[useMessages] WS down — starting HTTP poll fallback'); - pullAndDecrypt(); - pollTimerRef.current = setInterval(pullAndDecrypt, FALLBACK_POLL_INTERVAL); - }, [pullAndDecrypt]); - - const stopPolling = useCallback(() => { - if (pollTimerRef.current) { - clearInterval(pollTimerRef.current); - pollTimerRef.current = null; - } - if (disconnectTORef.current) { - clearTimeout(disconnectTORef.current); - disconnectTORef.current = null; - } - }, []); - - useEffect(() => { - if (!keyFile || !contactX25519) return; - - const ws = getWSClient(); - - // Initial fetch — populate whatever landed before we mounted. - pullAndDecrypt(); - - // Subscribe to our x25519 inbox — the node emits on mailbox.Store. - // Topic filter: only envelopes for ME; we then filter by sender inside - // the handler so we only render messages in THIS chat. - const offInbox = ws.subscribe('inbox:' + keyFile.x25519_pub, (frame) => { - if (frame.event !== 'inbox') return; - const d = frame.data as { sender_pub?: string } | undefined; - // Optimisation: if the envelope is from a different peer, skip the - // whole refetch — we'd just drop it in the sender filter below anyway. - if (d?.sender_pub && d.sender_pub !== contactX25519) return; - pullAndDecrypt(); - }); - - // Manage fallback polling based on WS connection state. - const offConn = ws.onConnectionChange((ok) => { - if (ok) { - stopPolling(); - // Catch up anything we missed while disconnected. - pullAndDecrypt(); - } else if (disconnectTORef.current === null) { - disconnectTORef.current = setTimeout(startPolling, WS_GRACE_BEFORE_POLLING); - } - }); - - ws.connect(); - - return () => { - offInbox(); - offConn(); - stopPolling(); - }; - }, [keyFile, contactX25519, pullAndDecrypt, startPolling, stopPolling]); -} diff --git a/client-app/hooks/useWellKnownContracts.ts b/client-app/hooks/useWellKnownContracts.ts deleted file mode 100644 index 5e97b22..0000000 --- a/client-app/hooks/useWellKnownContracts.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Auto-discover canonical system contracts from the node so the user doesn't - * have to paste contract IDs into settings by hand. - * - * Flow: - * 1. On app boot (and whenever nodeUrl changes), call GET /api/well-known-contracts - * 2. If the node advertises a `username_registry` and the user has not - * manually set `settings.contractId`, auto-populate it and persist. - * 3. A user-supplied contractId is never overwritten — so power users can - * still pin a non-canonical deployment from settings. - */ - -import { useEffect } from 'react'; -import { fetchWellKnownContracts } from '@/lib/api'; -import { saveSettings } from '@/lib/storage'; -import { useStore } from '@/lib/store'; - -export function useWellKnownContracts() { - const nodeUrl = useStore(s => s.settings.nodeUrl); - const contractId = useStore(s => s.settings.contractId); - const settings = useStore(s => s.settings); - const setSettings = useStore(s => s.setSettings); - - useEffect(() => { - let cancelled = false; - - async function run() { - if (!nodeUrl) return; - const res = await fetchWellKnownContracts(); - if (cancelled || !res) return; - - const registry = res.contracts['username_registry']; - if (!registry) return; - - // Always keep the stored contractId in sync with what the node reports - // as canonical. If the user resets their chain or we migrate from a - // WASM contract to the native one, the stale contract_id cached in - // local storage would otherwise keep the client trying to call a - // contract that no longer exists on this chain. - // - // To still support intentional overrides: the UI's "advanced" section - // allows pasting a specific ID — and since that also writes to - // settings.contractId, the loop converges back to whatever the node - // says after a short delay. Operators who want a hard override should - // either run a patched node or pin the value with a wrapper config - // outside the app. - if (registry.contract_id !== contractId) { - const next = { ...settings, contractId: registry.contract_id }; - setSettings({ contractId: registry.contract_id }); - await saveSettings(next); - console.log('[well-known] synced username_registry =', registry.contract_id, - '(was:', contractId || '', ')'); - } - } - - run(); - return () => { cancelled = true; }; - // Re-run when the node URL changes (user switched networks) or when - // contractId is cleared. - }, [nodeUrl, contractId]); // eslint-disable-line react-hooks/exhaustive-deps -} diff --git a/client-app/lib/api.ts b/client-app/lib/api.ts deleted file mode 100644 index ca254e0..0000000 --- a/client-app/lib/api.ts +++ /dev/null @@ -1,701 +0,0 @@ -/** - * DChain REST API client. - * All requests go to the configured node URL (e.g. http://192.168.1.10:8081). - */ - -import type { Envelope, TxRecord, NetStats, Contact } from './types'; - -// ─── Base ───────────────────────────────────────────────────────────────────── - -let _nodeUrl = 'http://localhost:8081'; - -/** - * Listeners invoked AFTER _nodeUrl changes. The WS client registers here so - * that switching nodes in Settings tears down the old socket and re-dials - * the new one (without this, a user who pointed their app at node A would - * keep receiving A's events forever after flipping to B). - */ -const nodeUrlListeners = new Set<(url: string) => void>(); - -export function setNodeUrl(url: string) { - const normalised = url.replace(/\/$/, ''); - if (_nodeUrl === normalised) return; - _nodeUrl = normalised; - for (const fn of nodeUrlListeners) { - try { fn(_nodeUrl); } catch { /* ignore — listeners are best-effort */ } - } -} - -export function getNodeUrl(): string { - return _nodeUrl; -} - -/** Register a callback for node-URL changes. Returns an unsubscribe fn. */ -export function onNodeUrlChange(fn: (url: string) => void): () => void { - nodeUrlListeners.add(fn); - return () => { nodeUrlListeners.delete(fn); }; -} - -async function get(path: string): Promise { - const res = await fetch(`${_nodeUrl}${path}`); - if (!res.ok) throw new Error(`GET ${path} → ${res.status}`); - return res.json() as Promise; -} - -/** - * Enhanced error reporter for POST failures. The node's `jsonErr` writes - * `{"error": "..."}` as the response body; we parse that out so the UI layer - * can show a meaningful message instead of a raw status code. - * - * Rate-limit and timestamp-skew rejections produce specific strings the UI - * can translate to user-friendly Russian via matcher functions below. - */ -async function post(path: string, body: unknown): Promise { - const res = await fetch(`${_nodeUrl}${path}`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }); - if (!res.ok) { - const text = await res.text(); - // Try to extract {"error":"..."} payload for a cleaner message. - let detail = text; - try { - const parsed = JSON.parse(text); - if (parsed?.error) detail = parsed.error; - } catch { /* keep raw text */ } - // Include HTTP status so `humanizeTxError` can branch on 429/400/etc. - throw new Error(`${res.status}: ${detail}`); - } - return res.json() as Promise; -} - -/** - * Turn a submission error from `post()` / `submitTx()` into a user-facing - * Russian message with actionable hints. Preserves the raw detail at the end - * so advanced users can still copy the original for support. - */ -export function humanizeTxError(e: unknown): string { - const raw = e instanceof Error ? e.message : String(e); - if (raw.startsWith('429')) { - return 'Слишком много запросов к ноде. Подождите пару секунд и попробуйте снова.'; - } - if (raw.startsWith('400') && raw.includes('timestamp')) { - return 'Часы устройства не синхронизированы с нодой. Проверьте время на телефоне (±1 час).'; - } - if (raw.startsWith('400') && raw.includes('signature')) { - return 'Подпись транзакции невалидна. Попробуйте ещё раз; если не помогает — вероятна несовместимость версий клиента и ноды.'; - } - if (raw.startsWith('400')) { - return `Нода отклонила транзакцию: ${raw.replace(/^400:\s*/, '')}`; - } - if (raw.startsWith('5')) { - return `Ошибка ноды (${raw}). Попробуйте позже.`; - } - // Network-level - if (raw.toLowerCase().includes('network request failed')) { - return 'Нет связи с нодой. Проверьте URL в настройках и доступность сервера.'; - } - return raw; -} - -// ─── Chain API ──────────────────────────────────────────────────────────────── - -export async function getNetStats(): Promise { - return get('/api/netstats'); -} - -interface AddrResponse { - balance_ut: number; - balance: string; - transactions: Array<{ - id: string; - type: string; - from: string; - to?: string; - amount_ut: number; - fee_ut: number; - time: string; // ISO-8601 e.g. "2025-01-01T12:00:00Z" - block_index: number; - }>; - tx_count: number; - has_more: boolean; -} - -export async function getBalance(pubkey: string): Promise { - const data = await get(`/api/address/${pubkey}`); - return data.balance_ut ?? 0; -} - -/** - * Transaction as sent to /api/tx — maps 1-to-1 to blockchain.Transaction JSON. - * Key facts: - * - `payload` is base64-encoded JSON bytes (Go []byte → base64 in JSON) - * - `signature` is base64-encoded Ed25519 sig (Go []byte → base64 in JSON) - * - `timestamp` is RFC3339 string (Go time.Time → string in JSON) - * - There is NO nonce field; dedup is by `id` - */ -export interface RawTx { - id: string; // "tx-" or sha256-based - type: string; // "TRANSFER", "CONTACT_REQUEST", etc. - from: string; // hex Ed25519 pub key - to: string; // hex Ed25519 pub key (empty string if N/A) - amount: number; // µT (uint64) - fee: number; // µT (uint64) - memo?: string; // optional - payload: string; // base64(json.Marshal(TypeSpecificPayload)) - signature: string; // base64(ed25519.Sign(canonical_bytes, priv)) - timestamp: string; // RFC3339 e.g. "2025-01-01T12:00:00Z" -} - -export async function submitTx(tx: RawTx): Promise<{ id: string; status: string }> { - console.log('[submitTx] →', { - id: tx.id, - type: tx.type, - from: tx.from.slice(0, 12) + '…', - to: tx.to ? tx.to.slice(0, 12) + '…' : '', - amount: tx.amount, - fee: tx.fee, - timestamp: tx.timestamp, - transport: 'auto', - }); - - // Try the WebSocket path first: no HTTP round-trip, and we get a proper - // submit_ack correlated back to our tx id. Falls through to HTTP if WS is - // unavailable (old node, disconnected, timeout, etc.) so legacy setups - // keep working. - try { - // Lazy import avoids a circular dep with lib/ws.ts (which itself - // imports getNodeUrl from this module). - const { getWSClient } = await import('./ws'); - const ws = getWSClient(); - if (ws.isConnected()) { - try { - const res = await ws.submitTx(tx); - console.log('[submitTx] ← accepted via WS', res); - return { id: res.id || tx.id, status: 'accepted' }; - } catch (e) { - console.warn('[submitTx] WS path failed, falling back to HTTP:', e); - } - } - } catch { /* circular import edge case — ignore and use HTTP */ } - - try { - const res = await post<{ id: string; status: string }>('/api/tx', tx); - console.log('[submitTx] ← accepted via HTTP', res); - return res; - } catch (e) { - console.warn('[submitTx] ← rejected', e); - throw e; - } -} - -export async function getTxHistory(pubkey: string, limit = 50): Promise { - const data = await get(`/api/address/${pubkey}?limit=${limit}`); - return (data.transactions ?? []).map(tx => ({ - hash: tx.id, - type: tx.type, - from: tx.from, - to: tx.to, - amount: tx.amount_ut, - fee: tx.fee_ut, - // Convert ISO-8601 string → unix seconds - timestamp: tx.time ? Math.floor(new Date(tx.time).getTime() / 1000) : 0, - status: 'confirmed' as const, - })); -} - -// ─── Relay API ──────────────────────────────────────────────────────────────── - -export interface SendEnvelopeReq { - sender_pub: string; - recipient_pub: string; - nonce: string; - ciphertext: string; -} - -export async function sendEnvelope(env: SendEnvelopeReq): Promise<{ ok: boolean }> { - return post<{ ok: boolean }>('/api/relay/send', env); -} - -export async function fetchInbox(x25519PubHex: string): Promise { - return get(`/api/relay/inbox?pub=${x25519PubHex}`); -} - -// ─── Contact requests (on-chain) ───────────────────────────────────────────── - -/** - * Maps blockchain.ContactInfo returned by GET /api/relay/contacts?pub=... - * The response shape is { pub, count, contacts: ContactInfo[] }. - */ -export interface ContactRequestRaw { - requester_pub: string; // Ed25519 pubkey of requester - requester_addr: string; // DChain address (DC…) - status: string; // "pending" | "accepted" | "blocked" - intro: string; // plaintext intro message (may be empty) - fee_ut: number; // anti-spam fee paid in µT - tx_id: string; // transaction ID - created_at: number; // unix seconds -} - -export async function fetchContactRequests(edPubHex: string): Promise { - const data = await get<{ contacts: ContactRequestRaw[] }>(`/api/relay/contacts?pub=${edPubHex}`); - return data.contacts ?? []; -} - -// ─── Identity API ───────────────────────────────────────────────────────────── - -export interface IdentityInfo { - pub_key: string; - address: string; - x25519_pub: string; // hex Curve25519 key; empty string if not published - nickname: string; - registered: boolean; -} - -/** Fetch identity info for any pubkey or DC address. Returns null on 404. */ -export async function getIdentity(pubkeyOrAddr: string): Promise { - try { - return await get(`/api/identity/${pubkeyOrAddr}`); - } catch { - return null; - } -} - -// ─── Contract API ───────────────────────────────────────────────────────────── - -/** - * Response shape from GET /api/contracts/{id}/state/{key}. - * The node handler (node/api_contract.go:handleContractState) returns either: - * { value_b64: null, value_hex: null, ... } when the key is missing - * or - * { value_b64: "...", value_hex: "...", value_u64?: 0 } when the key exists. - */ -interface ContractStateResponse { - contract_id: string; - key: string; - value_b64: string | null; - value_hex: string | null; - value_u64?: number; -} - -/** - * Decode a hex string (lowercase/uppercase) back to the original string value - * it represents. The username registry contract stores values as plain ASCII - * bytes (pubkey hex strings / username strings), so `value_hex` on the wire - * is the hex-encoding of UTF-8 bytes. We hex-decode to bytes, then interpret - * those bytes as UTF-8. - */ -function hexToUtf8(hex: string): string { - if (hex.length % 2 !== 0) return ''; - const bytes = new Uint8Array(hex.length / 2); - for (let i = 0; i < hex.length; i += 2) { - bytes[i / 2] = parseInt(hex.substr(i, 2), 16); - } - // TextDecoder is available in Hermes / RN's JS runtime. - try { - return new TextDecoder('utf-8').decode(bytes); - } catch { - // Fallback for environments without TextDecoder. - let s = ''; - for (const b of bytes) s += String.fromCharCode(b); - return s; - } -} - -/** username → address (hex pubkey). Returns null if unregistered. */ -export async function resolveUsername(contractId: string, username: string): Promise { - try { - const data = await get(`/api/contracts/${contractId}/state/name:${username}`); - if (!data.value_hex) return null; - const decoded = hexToUtf8(data.value_hex).trim(); - return decoded || null; - } catch { - return null; - } -} - -/** address (hex pubkey) → username. Returns null if this address hasn't registered a name. */ -export async function reverseResolve(contractId: string, address: string): Promise { - try { - const data = await get(`/api/contracts/${contractId}/state/addr:${address}`); - if (!data.value_hex) return null; - const decoded = hexToUtf8(data.value_hex).trim(); - return decoded || null; - } catch { - return null; - } -} - -// ─── Well-known contracts ───────────────────────────────────────────────────── - -/** - * Per-entry shape returned by GET /api/well-known-contracts. - * Matches node/api_well_known.go:WellKnownContract. - */ -export interface WellKnownContract { - contract_id: string; - name: string; - version?: string; - deployed_at: number; -} - -/** - * Response from GET /api/well-known-contracts. - * `contracts` is keyed by ABI name (e.g. "username_registry"). - */ -export interface WellKnownResponse { - count: number; - contracts: Record; -} - -/** - * Fetch the node's view of canonical system contracts so the client doesn't - * have to force the user to paste contract IDs into settings. - * - * The node returns the earliest-deployed contract per ABI name; this means - * every peer in the same chain reports the same mapping. - * - * Returns `null` on failure (old node, network hiccup, endpoint missing). - */ -export async function fetchWellKnownContracts(): Promise { - try { - return await get('/api/well-known-contracts'); - } catch { - return null; - } -} - -// ─── Node version / update-check ───────────────────────────────────────────── -// -// The three calls below let the client: -// 1. fetchNodeVersion() — see what tag/commit/features the connected node -// exposes. Used on first boot + on every chain-switch so we can warn if -// a required feature is missing. -// 2. checkNodeVersion(required) — thin wrapper that returns {supported, -// missing} by diffing a client-expected feature list against the node's. -// 3. fetchUpdateCheck() — ask the node whether its operator has a newer -// release available from their configured release source (Gitea). For -// messenger UX this is purely informational ("the node you're on is N -// versions behind"), never used to update the node automatically. - -/** The shape returned by GET /api/well-known-version. */ -export interface NodeVersionInfo { - node_version: string; - protocol_version: number; - features: string[]; - chain_id?: string; - build?: { - tag: string; - commit: string; - date: string; - dirty: string; - }; -} - -/** Client-expected protocol version. Bumped only when wire-protocol breaks. */ -export const CLIENT_PROTOCOL_VERSION = 1; - -/** - * Minimum feature set this client build relies on. A node missing any of - * these is considered "unsupported" — caller should surface an upgrade - * prompt to the user instead of silently failing on the first feature call. - */ -export const CLIENT_REQUIRED_FEATURES = [ - 'chain_id', - 'identity_registry', - 'onboarding_api', - 'relay_mailbox', - 'ws_submit_tx', -]; - -/** GET /api/well-known-version. Returns null on failure (old node, network hiccup). */ -export async function fetchNodeVersion(): Promise { - try { - return await get('/api/well-known-version'); - } catch { - return null; - } -} - -/** - * Check whether the connected node supports this client's required features - * and protocol version. Returns a decision blob the UI can render directly. - * - * { supported: true } → everything fine - * { supported: false, reason: "...", ... } → show update prompt - * { supported: null, reason: "unreachable" } → couldn't reach the endpoint, - * likely old node — assume OK - * but warn quietly. - */ -export async function checkNodeVersion( - required: string[] = CLIENT_REQUIRED_FEATURES, -): Promise<{ - supported: boolean | null; - reason?: string; - missing?: string[]; - info?: NodeVersionInfo; -}> { - const info = await fetchNodeVersion(); - if (!info) { - return { supported: null, reason: 'unreachable' }; - } - if (info.protocol_version !== CLIENT_PROTOCOL_VERSION) { - return { - supported: false, - reason: `protocol v${info.protocol_version} but client expects v${CLIENT_PROTOCOL_VERSION}`, - info, - }; - } - const have = new Set(info.features || []); - const missing = required.filter((f) => !have.has(f)); - if (missing.length > 0) { - return { - supported: false, - reason: `node missing features: ${missing.join(', ')}`, - missing, - info, - }; - } - return { supported: true, info }; -} - -/** The shape returned by GET /api/update-check. */ -export interface UpdateCheckResponse { - current: { tag: string; commit: string; date: string; dirty: string }; - latest?: { tag: string; commit?: string; url?: string; published_at?: string }; - update_available: boolean; - checked_at: string; - source?: string; -} - -/** - * GET /api/update-check. Returns null when: - * - the node operator hasn't configured DCHAIN_UPDATE_SOURCE_URL (503), - * - upstream Gitea call failed (502), - * - request errored out. - * All three are non-fatal for the client; the UI just doesn't render the - * "update available" banner. - */ -export async function fetchUpdateCheck(): Promise { - try { - return await get('/api/update-check'); - } catch { - return null; - } -} - -// ─── Transaction builder helpers ───────────────────────────────────────────── - -import { signBase64, bytesToBase64 } from './crypto'; - -/** Minimum blockchain tx fee paid to the block validator (matches blockchain.MinFee = 1000 µT). */ -const MIN_TX_FEE = 1000; - -const _encoder = new TextEncoder(); - -/** RFC3339 timestamp with second precision — matches Go time.Time JSON output. */ -function rfc3339Now(): string { - const d = new Date(); - d.setMilliseconds(0); - // toISOString() gives "2025-01-01T12:00:00.000Z" → replace ".000Z" with "Z" - return d.toISOString().replace('.000Z', 'Z'); -} - -/** Unique transaction ID (nanoseconds-like using Date.now + random). */ -function newTxID(): string { - return `tx-${Date.now()}${Math.floor(Math.random() * 1_000_000)}`; -} - -/** - * Canonical bytes for signing — must match identity.txSignBytes in Go exactly. - * - * Go struct field order: id, type, from, to, amount, fee, payload, timestamp. - * JS JSON.stringify preserves insertion order, so we rely on that here. - */ -function txCanonicalBytes(tx: { - id: string; type: string; from: string; to: string; - amount: number; fee: number; payload: string; timestamp: string; -}): Uint8Array { - const s = JSON.stringify({ - id: tx.id, - type: tx.type, - from: tx.from, - to: tx.to, - amount: tx.amount, - fee: tx.fee, - payload: tx.payload, - timestamp: tx.timestamp, - }); - return _encoder.encode(s); -} - -/** Encode a JS string (UTF-8) to base64. */ -function strToBase64(s: string): string { - return bytesToBase64(_encoder.encode(s)); -} - -export function buildTransferTx(params: { - from: string; - to: string; - amount: number; - fee: number; - privKey: string; - memo?: string; -}): RawTx { - const id = newTxID(); - const timestamp = rfc3339Now(); - const payloadObj = params.memo ? { memo: params.memo } : {}; - const payload = strToBase64(JSON.stringify(payloadObj)); - - const canonical = txCanonicalBytes({ - id, type: 'TRANSFER', from: params.from, to: params.to, - amount: params.amount, fee: params.fee, payload, timestamp, - }); - - return { - id, type: 'TRANSFER', from: params.from, to: params.to, - amount: params.amount, fee: params.fee, - memo: params.memo, - payload, timestamp, - signature: signBase64(canonical, params.privKey), - }; -} - -/** - * CONTACT_REQUEST transaction. - * - * blockchain.Transaction fields: - * Amount = contactFee — anti-spam fee, paid directly to recipient (>= 5000 µT) - * Fee = MIN_TX_FEE — blockchain tx fee to the block validator (1000 µT) - * Payload = ContactRequestPayload { intro? } as base64 JSON bytes - */ -export function buildContactRequestTx(params: { - from: string; // sender Ed25519 pubkey - to: string; // recipient Ed25519 pubkey - contactFee: number; // anti-spam amount paid to recipient (>= 5000 µT) - intro?: string; // optional plaintext intro message (≤ 280 chars) - privKey: string; -}): RawTx { - const id = newTxID(); - const timestamp = rfc3339Now(); - // Payload matches ContactRequestPayload{Intro: "..."} in Go - const payloadObj = params.intro ? { intro: params.intro } : {}; - const payload = strToBase64(JSON.stringify(payloadObj)); - - const canonical = txCanonicalBytes({ - id, type: 'CONTACT_REQUEST', from: params.from, to: params.to, - amount: params.contactFee, fee: MIN_TX_FEE, payload, timestamp, - }); - - return { - id, type: 'CONTACT_REQUEST', from: params.from, to: params.to, - amount: params.contactFee, fee: MIN_TX_FEE, payload, timestamp, - signature: signBase64(canonical, params.privKey), - }; -} - -/** - * ACCEPT_CONTACT transaction. - * AcceptContactPayload is an empty struct in Go — no fields needed. - */ -export function buildAcceptContactTx(params: { - from: string; // acceptor Ed25519 pubkey (us — the recipient of the request) - to: string; // requester Ed25519 pubkey - privKey: string; -}): RawTx { - const id = newTxID(); - const timestamp = rfc3339Now(); - const payload = strToBase64(JSON.stringify({})); // AcceptContactPayload{} - - const canonical = txCanonicalBytes({ - id, type: 'ACCEPT_CONTACT', from: params.from, to: params.to, - amount: 0, fee: MIN_TX_FEE, payload, timestamp, - }); - - return { - id, type: 'ACCEPT_CONTACT', from: params.from, to: params.to, - amount: 0, fee: MIN_TX_FEE, payload, timestamp, - signature: signBase64(canonical, params.privKey), - }; -} - -// ─── Contract call ──────────────────────────────────────────────────────────── - -/** Minimum base fee for CALL_CONTRACT (matches blockchain.MinCallFee). */ -const MIN_CALL_FEE = 1000; - -/** - * CALL_CONTRACT transaction. - * - * Payload shape (CallContractPayload): - * { contract_id, method, args_json?, gas_limit } - * - * `amount` is the payment attached to the call and made available to the - * contract as `tx.Amount`. Whether it's collected depends on the contract - * — e.g. username_registry.register requires exactly 10_000 µT. Contracts - * that don't need payment should be called with `amount: 0` (default). - * - * The on-chain tx envelope carries `amount` openly, so the explorer shows - * the exact cost of a call rather than hiding it in a contract-internal - * debit — this was the UX motivation for this field. - * - * `fee` is the NETWORK fee paid to the block validator (not the contract). - * `gas` costs are additional and billed at the live gas price. - */ -export function buildCallContractTx(params: { - from: string; - contractId: string; - method: string; - args?: unknown[]; // JSON-serializable arguments - amount?: number; // µT attached to the call (default 0) - gasLimit?: number; // default 1_000_000 - privKey: string; -}): RawTx { - const id = newTxID(); - const timestamp = rfc3339Now(); - const amount = params.amount ?? 0; - - const argsJson = params.args && params.args.length > 0 - ? JSON.stringify(params.args) - : ''; - - const payloadObj = { - contract_id: params.contractId, - method: params.method, - args_json: argsJson, - gas_limit: params.gasLimit ?? 1_000_000, - }; - const payload = strToBase64(JSON.stringify(payloadObj)); - - const canonical = txCanonicalBytes({ - id, type: 'CALL_CONTRACT', from: params.from, to: '', - amount, fee: MIN_CALL_FEE, payload, timestamp, - }); - - return { - id, type: 'CALL_CONTRACT', from: params.from, to: '', - amount, fee: MIN_CALL_FEE, payload, timestamp, - signature: signBase64(canonical, params.privKey), - }; -} - -/** - * Flat registration fee for a username, in µT. - * - * The native username_registry charges a single flat fee (10 000 µT = 0.01 T) - * per register() call regardless of name length, replacing the earlier - * length-based formula. Flat pricing is easier to communicate and the - * 4-char minimum (enforced both in the client UI and the on-chain contract) - * already removes the squatting pressure that tiered pricing mitigated. - */ -export const USERNAME_REGISTRATION_FEE = 10_000; - -/** Minimum/maximum allowed username length. Match blockchain/native_username.go. */ -export const MIN_USERNAME_LENGTH = 4; -export const MAX_USERNAME_LENGTH = 32; - -/** @deprecated Kept for backward compatibility; always returns the flat fee. */ -export function usernameRegistrationFee(_name: string): number { - return USERNAME_REGISTRATION_FEE; -} diff --git a/client-app/lib/crypto.ts b/client-app/lib/crypto.ts deleted file mode 100644 index 8f649f5..0000000 --- a/client-app/lib/crypto.ts +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Cryptographic operations for DChain messenger. - * - * Ed25519 — transaction signing (via TweetNaCl sign) - * X25519 — Diffie-Hellman key exchange for NaCl box - * NaCl box — authenticated encryption for relay messages - */ - -import nacl from 'tweetnacl'; -import { decodeUTF8, encodeUTF8 } from 'tweetnacl-util'; -import { getRandomBytes } from 'expo-crypto'; -import type { KeyFile } from './types'; - -// ─── PRNG ───────────────────────────────────────────────────────────────────── -// TweetNaCl looks for window.crypto which doesn't exist in React Native/Hermes. -// Wire nacl to expo-crypto which uses the platform's secure RNG natively. -nacl.setPRNG((output: Uint8Array, length: number) => { - const bytes = getRandomBytes(length); - for (let i = 0; i < length; i++) output[i] = bytes[i]; -}); - -// ─── Helpers ────────────────────────────────────────────────────────────────── - -export function hexToBytes(hex: string): Uint8Array { - if (hex.length % 2 !== 0) throw new Error('odd hex length'); - const bytes = new Uint8Array(hex.length / 2); - for (let i = 0; i < bytes.length; i++) { - bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16); - } - return bytes; -} - -export function bytesToHex(bytes: Uint8Array): string { - return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); -} - -// ─── Key generation ─────────────────────────────────────────────────────────── - -/** - * Generate a new identity: Ed25519 signing keys + X25519 encryption keys. - * Returns a KeyFile compatible with the Go node format. - */ -export function generateKeyFile(): KeyFile { - // Ed25519 for signing / blockchain identity - const signKP = nacl.sign.keyPair(); - - // X25519 for NaCl box encryption - // nacl.box.keyPair() returns Curve25519 keys - const boxKP = nacl.box.keyPair(); - - return { - pub_key: bytesToHex(signKP.publicKey), - priv_key: bytesToHex(signKP.secretKey), - x25519_pub: bytesToHex(boxKP.publicKey), - x25519_priv: bytesToHex(boxKP.secretKey), - }; -} - -// ─── NaCl box encryption ────────────────────────────────────────────────────── - -/** - * Encrypt a plaintext message using NaCl box. - * Sender uses their X25519 secret key + recipient's X25519 public key. - * Returns { nonce, ciphertext } as hex strings. - */ -export function encryptMessage( - plaintext: string, - senderSecretHex: string, - recipientPubHex: string, -): { nonce: string; ciphertext: string } { - const nonce = nacl.randomBytes(nacl.box.nonceLength); - const message = decodeUTF8(plaintext); - const secretKey = hexToBytes(senderSecretHex); - const publicKey = hexToBytes(recipientPubHex); - - const box = nacl.box(message, nonce, publicKey, secretKey); - return { - nonce: bytesToHex(nonce), - ciphertext: bytesToHex(box), - }; -} - -/** - * Decrypt a NaCl box. - * Recipient uses their X25519 secret key + sender's X25519 public key. - */ -export function decryptMessage( - ciphertextHex: string, - nonceHex: string, - senderPubHex: string, - recipientSecHex: string, -): string | null { - try { - const ciphertext = hexToBytes(ciphertextHex); - const nonce = hexToBytes(nonceHex); - const senderPub = hexToBytes(senderPubHex); - const secretKey = hexToBytes(recipientSecHex); - - const plain = nacl.box.open(ciphertext, nonce, senderPub, secretKey); - if (!plain) return null; - return encodeUTF8(plain); - } catch { - return null; - } -} - -// ─── Ed25519 signing ────────────────────────────────────────────────────────── - -/** - * Sign arbitrary data with the Ed25519 private key. - * Returns signature as hex. - */ -export function sign(data: Uint8Array, privKeyHex: string): string { - const secretKey = hexToBytes(privKeyHex); - const sig = nacl.sign.detached(data, secretKey); - return bytesToHex(sig); -} - -/** - * Sign arbitrary data with the Ed25519 private key. - * Returns signature as base64 — this is the format the Go blockchain node - * expects ([]byte fields are base64 in JSON). - */ -export function signBase64(data: Uint8Array, privKeyHex: string): string { - const secretKey = hexToBytes(privKeyHex); - const sig = nacl.sign.detached(data, secretKey); - return bytesToBase64(sig); -} - -/** Encode bytes as base64. Works on Hermes (btoa is available since RN 0.71). */ -export function bytesToBase64(bytes: Uint8Array): string { - let binary = ''; - for (let i = 0; i < bytes.length; i++) { - binary += String.fromCharCode(bytes[i]); - } - return btoa(binary); -} - -/** - * Verify an Ed25519 signature. - */ -export function verify(data: Uint8Array, sigHex: string, pubKeyHex: string): boolean { - try { - return nacl.sign.detached.verify(data, hexToBytes(sigHex), hexToBytes(pubKeyHex)); - } catch { - return false; - } -} - -// ─── Address helpers ────────────────────────────────────────────────────────── - -/** Truncate a long hex address for display: 8...8 */ -export function shortAddr(hex: string, chars = 8): string { - if (hex.length <= chars * 2 + 3) return hex; - return `${hex.slice(0, chars)}…${hex.slice(-chars)}`; -} diff --git a/client-app/lib/storage.ts b/client-app/lib/storage.ts deleted file mode 100644 index 1f2e94a..0000000 --- a/client-app/lib/storage.ts +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Persistent storage for keys and app settings. - * On mobile: expo-secure-store for key material, AsyncStorage for settings. - * On web: falls back to localStorage (dev only). - */ - -import * as SecureStore from 'expo-secure-store'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import type { KeyFile, Contact, NodeSettings } from './types'; - -// ─── Keys ───────────────────────────────────────────────────────────────────── - -const KEYFILE_KEY = 'dchain_keyfile'; -const CONTACTS_KEY = 'dchain_contacts'; -const SETTINGS_KEY = 'dchain_settings'; -const CHATS_KEY = 'dchain_chats'; - -/** Save the key file in secure storage (encrypted on device). */ -export async function saveKeyFile(kf: KeyFile): Promise { - await SecureStore.setItemAsync(KEYFILE_KEY, JSON.stringify(kf)); -} - -/** Load key file. Returns null if not set. */ -export async function loadKeyFile(): Promise { - const raw = await SecureStore.getItemAsync(KEYFILE_KEY); - if (!raw) return null; - return JSON.parse(raw) as KeyFile; -} - -/** Delete key file (logout / factory reset). */ -export async function deleteKeyFile(): Promise { - await SecureStore.deleteItemAsync(KEYFILE_KEY); -} - -// ─── Node settings ───────────────────────────────────────────────────────────── - -const DEFAULT_SETTINGS: NodeSettings = { - nodeUrl: 'http://localhost:8081', - contractId: '', -}; - -export async function loadSettings(): Promise { - const raw = await AsyncStorage.getItem(SETTINGS_KEY); - if (!raw) return DEFAULT_SETTINGS; - return { ...DEFAULT_SETTINGS, ...JSON.parse(raw) }; -} - -export async function saveSettings(s: Partial): Promise { - const current = await loadSettings(); - await AsyncStorage.setItem(SETTINGS_KEY, JSON.stringify({ ...current, ...s })); -} - -// ─── Contacts ───────────────────────────────────────────────────────────────── - -export async function loadContacts(): Promise { - const raw = await AsyncStorage.getItem(CONTACTS_KEY); - if (!raw) return []; - return JSON.parse(raw) as Contact[]; -} - -export async function saveContact(c: Contact): Promise { - const contacts = await loadContacts(); - const idx = contacts.findIndex(x => x.address === c.address); - if (idx >= 0) contacts[idx] = c; - else contacts.push(c); - await AsyncStorage.setItem(CONTACTS_KEY, JSON.stringify(contacts)); -} - -export async function deleteContact(address: string): Promise { - const contacts = await loadContacts(); - await AsyncStorage.setItem( - CONTACTS_KEY, - JSON.stringify(contacts.filter(c => c.address !== address)), - ); -} - -// ─── Message cache (per-chat local store) ──────────────────────────────────── - -export interface CachedMessage { - id: string; - from: string; - text: string; - timestamp: number; - mine: boolean; -} - -export async function loadMessages(chatId: string): Promise { - const raw = await AsyncStorage.getItem(`${CHATS_KEY}_${chatId}`); - if (!raw) return []; - return JSON.parse(raw) as CachedMessage[]; -} - -export async function appendMessage(chatId: string, msg: CachedMessage): Promise { - const msgs = await loadMessages(chatId); - // Deduplicate by id - if (msgs.find(m => m.id === msg.id)) return; - msgs.push(msg); - // Keep last 500 messages per chat - const trimmed = msgs.slice(-500); - await AsyncStorage.setItem(`${CHATS_KEY}_${chatId}`, JSON.stringify(trimmed)); -} diff --git a/client-app/lib/store.ts b/client-app/lib/store.ts deleted file mode 100644 index 7e4a763..0000000 --- a/client-app/lib/store.ts +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Global app state via Zustand. - * Keeps runtime state; persistent data lives in storage.ts. - */ - -import { create } from 'zustand'; -import type { KeyFile, Contact, Chat, Message, ContactRequest, NodeSettings } from './types'; - -interface AppState { - // Identity - keyFile: KeyFile | null; - username: string | null; - setKeyFile: (kf: KeyFile | null) => void; - setUsername: (u: string | null) => void; - - // Node settings - settings: NodeSettings; - setSettings: (s: Partial) => void; - - // Contacts - contacts: Contact[]; - setContacts: (contacts: Contact[]) => void; - upsertContact: (c: Contact) => void; - - // Chats (derived from contacts + messages) - chats: Chat[]; - setChats: (chats: Chat[]) => void; - - // Active chat messages - messages: Record; // key: contactAddress - setMessages: (chatId: string, msgs: Message[]) => void; - appendMessage: (chatId: string, msg: Message) => void; - - // Contact requests (pending) - requests: ContactRequest[]; - setRequests: (reqs: ContactRequest[]) => void; - - // Balance - balance: number; - setBalance: (b: number) => void; - - // Loading / error states - loading: boolean; - setLoading: (v: boolean) => void; - error: string | null; - setError: (e: string | null) => void; - - // Nonce cache (to avoid refetching) - nonce: number; - setNonce: (n: number) => void; -} - -export const useStore = create((set, get) => ({ - keyFile: null, - username: null, - setKeyFile: (kf) => set({ keyFile: kf }), - setUsername: (u) => set({ username: u }), - - settings: { - nodeUrl: 'http://localhost:8081', - contractId: '', - }, - setSettings: (s) => set(state => ({ settings: { ...state.settings, ...s } })), - - contacts: [], - setContacts: (contacts) => set({ contacts }), - upsertContact: (c) => set(state => { - const idx = state.contacts.findIndex(x => x.address === c.address); - if (idx >= 0) { - const updated = [...state.contacts]; - updated[idx] = c; - return { contacts: updated }; - } - return { contacts: [...state.contacts, c] }; - }), - - chats: [], - setChats: (chats) => set({ chats }), - - messages: {}, - setMessages: (chatId, msgs) => set(state => ({ - messages: { ...state.messages, [chatId]: msgs }, - })), - appendMessage: (chatId, msg) => set(state => { - const current = state.messages[chatId] ?? []; - if (current.find(m => m.id === msg.id)) return {}; - return { messages: { ...state.messages, [chatId]: [...current, msg] } }; - }), - - requests: [], - setRequests: (reqs) => set({ requests: reqs }), - - balance: 0, - setBalance: (b) => set({ balance: b }), - - loading: false, - setLoading: (v) => set({ loading: v }), - error: null, - setError: (e) => set({ error: e }), - - nonce: 0, - setNonce: (n) => set({ nonce: n }), -})); diff --git a/client-app/lib/types.ts b/client-app/lib/types.ts deleted file mode 100644 index 0c1181a..0000000 --- a/client-app/lib/types.ts +++ /dev/null @@ -1,86 +0,0 @@ -// ─── Key material ──────────────────────────────────────────────────────────── - -export interface KeyFile { - pub_key: string; // hex Ed25519 public key (32 bytes) - priv_key: string; // hex Ed25519 private key (64 bytes) - x25519_pub: string; // hex X25519 public key (32 bytes) - x25519_priv: string; // hex X25519 private key (32 bytes) -} - -// ─── Contact ───────────────────────────────────────────────────────────────── - -export interface Contact { - address: string; // Ed25519 pubkey hex — blockchain address - x25519Pub: string; // X25519 pubkey hex — encryption key - username?: string; // @name from registry contract - alias?: string; // local nickname - addedAt: number; // unix ms -} - -// ─── Messages ───────────────────────────────────────────────────────────────── - -export interface Envelope { - sender_pub: string; // X25519 hex - recipient_pub: string; // X25519 hex - nonce: string; // hex 24 bytes - ciphertext: string; // hex NaCl box - timestamp: number; // unix seconds -} - -export interface Message { - id: string; - from: string; // X25519 pubkey of sender - text: string; - timestamp: number; - mine: boolean; -} - -// ─── Chat ──────────────────────────────────────────────────────────────────── - -export interface Chat { - contactAddress: string; // Ed25519 pubkey hex - contactX25519: string; // X25519 pubkey hex - username?: string; - alias?: string; - lastMessage?: string; - lastTime?: number; - unread: number; -} - -// ─── Contact request ───────────────────────────────────────────────────────── - -export interface ContactRequest { - from: string; // Ed25519 pubkey hex - x25519Pub: string; // X25519 pubkey hex; empty until fetched from identity - username?: string; - intro: string; // plaintext intro (stored on-chain) - timestamp: number; - txHash: string; -} - -// ─── Transaction ───────────────────────────────────────────────────────────── - -export interface TxRecord { - hash: string; - type: string; - from: string; - to?: string; - amount?: number; - fee: number; - timestamp: number; - status: 'confirmed' | 'pending'; -} - -// ─── Node info ─────────────────────────────────────────────────────────────── - -export interface NetStats { - total_blocks: number; - total_txs: number; - peer_count: number; - chain_id: string; -} - -export interface NodeSettings { - nodeUrl: string; - contractId: string; // username_registry contract -} diff --git a/client-app/lib/utils.ts b/client-app/lib/utils.ts deleted file mode 100644 index 19f61f5..0000000 --- a/client-app/lib/utils.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { clsx, type ClassValue } from 'clsx'; -import { twMerge } from 'tailwind-merge'; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} - -/** Format µT amount to human-readable string */ -export function formatAmount(microTokens: number | undefined | null): string { - if (microTokens == null) return '—'; - if (microTokens >= 1_000_000) return `${(microTokens / 1_000_000).toFixed(2)} T`; - if (microTokens >= 1_000) return `${(microTokens / 1_000).toFixed(1)} mT`; - return `${microTokens} µT`; -} - -/** Format unix seconds to relative time */ -export function relativeTime(unixSeconds: number | undefined | null): string { - if (!unixSeconds) return ''; - const diff = Date.now() / 1000 - unixSeconds; - if (diff < 60) return 'just now'; - if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; - if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; - return new Date(unixSeconds * 1000).toLocaleDateString(); -} - -/** Format unix seconds to HH:MM */ -export function formatTime(unixSeconds: number | undefined | null): string { - if (!unixSeconds) return ''; - return new Date(unixSeconds * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); -} - -/** Generate a random nonce string */ -export function randomId(): string { - return Math.random().toString(36).slice(2) + Date.now().toString(36); -} diff --git a/client-app/lib/ws.ts b/client-app/lib/ws.ts deleted file mode 100644 index 6683ac9..0000000 --- a/client-app/lib/ws.ts +++ /dev/null @@ -1,401 +0,0 @@ -/** - * DChain WebSocket client — replaces balance / inbox / contacts polling with - * server-push. Matches `node/ws.go` exactly. - * - * Usage: - * const ws = getWSClient(); - * ws.connect(); // idempotent - * const off = ws.subscribe('addr:ab12…', ev => { ... }); - * // later: - * off(); // unsubscribe + stop handler - * ws.disconnect(); - * - * Features: - * - Auto-reconnect with exponential backoff (1s → 30s cap). - * - Re-subscribes all topics after a reconnect. - * - `hello` frame exposes chain_id + tip_height for connection state UI. - * - Degrades silently if the endpoint returns 501 (old node without WS). - */ - -import { getNodeUrl, onNodeUrlChange } from './api'; -import { sign } from './crypto'; - -export type WSEventName = - | 'hello' - | 'block' - | 'tx' - | 'contract_log' - | 'inbox' - | 'typing' - | 'pong' - | 'error' - | 'subscribed' - | 'submit_ack' - | 'lag'; - -export interface WSFrame { - event: WSEventName; - data?: unknown; - topic?: string; - msg?: string; - chain_id?: string; - tip_height?: number; - /** Server-issued nonce in the hello frame; client signs it for auth. */ - auth_nonce?: string; - // submit_ack fields - id?: string; - status?: 'accepted' | 'rejected'; - reason?: string; -} - -type Handler = (frame: WSFrame) => void; - -class WSClient { - private ws: WebSocket | null = null; - private url: string | null = null; - private reconnectMs: number = 1000; - private closing: boolean = false; - - /** topic → set of handlers interested in frames for this topic */ - private handlers: Map> = new Map(); - /** topics we want the server to push — replayed on every reconnect */ - private wantedTopics: Set = new Set(); - - private connectionListeners: Set<(ok: boolean, err?: string) => void> = new Set(); - private helloInfo: { chainId?: string; tipHeight?: number; authNonce?: string } = {}; - - /** - * Credentials used for auto-auth on every (re)connect. The signer runs on - * each hello frame so scoped subscriptions (addr:*, inbox:*) are accepted. - * Without these, subscribe requests to scoped topics get rejected by the - * server; global topics (blocks, tx, …) still work unauthenticated. - */ - private authCreds: { pubKey: string; privKey: string } | null = null; - - /** Current connection state (read-only for UI). */ - isConnected(): boolean { - return this.ws?.readyState === WebSocket.OPEN; - } - getHelloInfo(): { chainId?: string; tipHeight?: number } { - return this.helloInfo; - } - - /** Subscribe to a connection-state listener — fires on connect/disconnect. */ - onConnectionChange(cb: (ok: boolean, err?: string) => void): () => void { - this.connectionListeners.add(cb); - return () => this.connectionListeners.delete(cb) as unknown as void; - } - private fireConnectionChange(ok: boolean, err?: string) { - for (const cb of this.connectionListeners) { - try { cb(ok, err); } catch { /* noop */ } - } - } - - /** - * Register the Ed25519 keypair used for auto-auth. The signer runs on each - * (re)connect against the server-issued nonce so the connection is bound - * to this identity. Pass null to disable auth (only global topics will - * work — useful for observers). - */ - setAuthCreds(creds: { pubKey: string; privKey: string } | null): void { - this.authCreds = creds; - // If we're already connected, kick off auth immediately. - if (creds && this.isConnected() && this.helloInfo.authNonce) { - this.sendAuth(this.helloInfo.authNonce); - } - } - - /** Idempotent connect. Call once on app boot. */ - connect(): void { - const base = getNodeUrl(); - const newURL = base.replace(/^http/, 'ws') + '/api/ws'; - if (this.ws) { - const state = this.ws.readyState; - // Already pointing at this URL and connected / connecting — nothing to do. - if (this.url === newURL && (state === WebSocket.OPEN || state === WebSocket.CONNECTING)) { - return; - } - // URL changed (operator flipped nodes in settings) — tear down and - // re-dial. Existing subscriptions live in wantedTopics and will be - // replayed after the new onopen fires. - if (this.url !== newURL && (state === WebSocket.OPEN || state === WebSocket.CONNECTING)) { - try { this.ws.close(); } catch { /* noop */ } - this.ws = null; - } - } - this.closing = false; - this.url = newURL; - try { - this.ws = new WebSocket(this.url); - } catch (e: any) { - this.fireConnectionChange(false, e?.message ?? 'ws construct failed'); - this.scheduleReconnect(); - return; - } - - this.ws.onopen = () => { - this.reconnectMs = 1000; // reset backoff - this.fireConnectionChange(true); - // Replay all wanted subscriptions. - for (const topic of this.wantedTopics) { - this.sendRaw({ op: 'subscribe', topic }); - } - }; - - this.ws.onmessage = (ev) => { - let frame: WSFrame; - try { - frame = JSON.parse(typeof ev.data === 'string' ? ev.data : ''); - } catch { - return; - } - if (frame.event === 'hello') { - this.helloInfo = { - chainId: frame.chain_id, - tipHeight: frame.tip_height, - authNonce: frame.auth_nonce, - }; - // Auto-authenticate if credentials are set. The server binds this - // connection to the signed pubkey so scoped subscriptions (addr:*, - // inbox:*) get through. On reconnect a new nonce is issued, so the - // auth dance repeats transparently. - if (this.authCreds && frame.auth_nonce) { - this.sendAuth(frame.auth_nonce); - } - } - // Dispatch to all handlers for any topic that could match this frame. - // We use a simple predicate: look at the frame to decide which topics it - // was fanned out to, then fire every matching handler. - for (const topic of this.topicsForFrame(frame)) { - const set = this.handlers.get(topic); - if (!set) continue; - for (const h of set) { - try { h(frame); } catch (e) { console.warn('[ws] handler error', e); } - } - } - }; - - this.ws.onerror = (e: any) => { - this.fireConnectionChange(false, 'ws error'); - }; - - this.ws.onclose = () => { - this.ws = null; - this.fireConnectionChange(false); - if (!this.closing) this.scheduleReconnect(); - }; - } - - disconnect(): void { - this.closing = true; - if (this.ws) { - try { this.ws.close(); } catch { /* noop */ } - this.ws = null; - } - } - - /** - * Subscribe to a topic. Returns an `off()` function that unsubscribes AND - * removes the handler. If multiple callers subscribe to the same topic, - * the server is only notified on the first and last caller. - */ - subscribe(topic: string, handler: Handler): () => void { - let set = this.handlers.get(topic); - if (!set) { - set = new Set(); - this.handlers.set(topic, set); - } - set.add(handler); - - // Notify server only on the first handler for this topic. - if (!this.wantedTopics.has(topic)) { - this.wantedTopics.add(topic); - if (this.isConnected()) { - this.sendRaw({ op: 'subscribe', topic }); - } else { - this.connect(); // lazy-connect on first subscribe - } - } - - return () => { - const s = this.handlers.get(topic); - if (!s) return; - s.delete(handler); - if (s.size === 0) { - this.handlers.delete(topic); - this.wantedTopics.delete(topic); - if (this.isConnected()) { - this.sendRaw({ op: 'unsubscribe', topic }); - } - } - }; - } - - /** Force a keepalive ping. Useful for debugging. */ - ping(): void { - this.sendRaw({ op: 'ping' }); - } - - /** - * Send a typing indicator to another user. Recipient is their X25519 pubkey - * (the one used for inbox encryption). Ephemeral — no ack, no retry; just - * fire and forget. Call on each keystroke but throttle to once per 2-3s - * at the caller side so we don't flood the WS with frames. - */ - sendTyping(recipientX25519: string): void { - if (!this.isConnected()) return; - try { - this.ws!.send(JSON.stringify({ op: 'typing', to: recipientX25519 })); - } catch { /* best-effort */ } - } - - /** - * Submit a signed transaction over the WebSocket and resolve once the - * server returns a `submit_ack`. Saves the HTTP round-trip on every tx - * and gives the UI immediate accept/reject feedback. - * - * Rejects if: - * - WS is not connected (caller should fall back to HTTP) - * - Server returns `status: "rejected"` — `reason` is surfaced as error msg - * - No ack within `timeoutMs` (default 10 s) - */ - submitTx(tx: unknown, timeoutMs = 10_000): Promise<{ id: string }> { - if (!this.isConnected()) { - return Promise.reject(new Error('WS not connected')); - } - const reqId = 's_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8); - - return new Promise((resolve, reject) => { - const off = this.subscribe('$system', (frame) => { - if (frame.event !== 'submit_ack' || frame.id !== reqId) return; - off(); - clearTimeout(timer); - if (frame.status === 'accepted') { - // `msg` carries the server-confirmed tx id. - resolve({ id: typeof frame.msg === 'string' ? frame.msg : '' }); - } else { - reject(new Error(frame.reason || 'submit_tx rejected')); - } - }); - const timer = setTimeout(() => { - off(); - reject(new Error('submit_tx timeout (' + timeoutMs + 'ms)')); - }, timeoutMs); - - try { - this.ws!.send(JSON.stringify({ op: 'submit_tx', tx, id: reqId })); - } catch (e: any) { - off(); - clearTimeout(timer); - reject(new Error('WS send failed: ' + (e?.message ?? 'unknown'))); - } - }); - } - - // ── internals ─────────────────────────────────────────────────────────── - - private scheduleReconnect(): void { - if (this.closing) return; - const delay = Math.min(this.reconnectMs, 30_000); - this.reconnectMs = Math.min(this.reconnectMs * 2, 30_000); - setTimeout(() => { - if (!this.closing) this.connect(); - }, delay); - } - - private sendRaw(cmd: { op: string; topic?: string }): void { - if (!this.isConnected()) return; - try { this.ws!.send(JSON.stringify(cmd)); } catch { /* noop */ } - } - - /** - * Sign the server nonce with our Ed25519 private key and send the `auth` - * op. The server binds this connection to `authCreds.pubKey`; subsequent - * subscribe requests to `addr:` / `inbox:` are accepted. - */ - private sendAuth(nonce: string): void { - if (!this.authCreds || !this.isConnected()) return; - try { - const bytes = new TextEncoder().encode(nonce); - const sig = sign(bytes, this.authCreds.privKey); - this.ws!.send(JSON.stringify({ - op: 'auth', - pubkey: this.authCreds.pubKey, - sig, - })); - } catch (e) { - console.warn('[ws] auth send failed:', e); - } - } - - /** - * Given an incoming frame, enumerate every topic that handlers could have - * subscribed to and still be interested. This mirrors the fan-out logic in - * node/ws.go:EmitBlock / EmitTx / EmitContractLog. - */ - private topicsForFrame(frame: WSFrame): string[] { - switch (frame.event) { - case 'block': - return ['blocks']; - case 'tx': { - const d = frame.data as { from?: string; to?: string } | undefined; - const topics = ['tx']; - if (d?.from) topics.push('addr:' + d.from); - if (d?.to && d.to !== d.from) topics.push('addr:' + d.to); - return topics; - } - case 'contract_log': { - const d = frame.data as { contract_id?: string } | undefined; - const topics = ['contract_log']; - if (d?.contract_id) topics.push('contract:' + d.contract_id); - return topics; - } - case 'inbox': { - // Node fans inbox events to `inbox` + `inbox:`; - // we mirror that here so both firehose listeners and address-scoped - // subscribers see the event. - const d = frame.data as { recipient_pub?: string } | undefined; - const topics = ['inbox']; - if (d?.recipient_pub) topics.push('inbox:' + d.recipient_pub); - return topics; - } - case 'typing': { - // Server fans to `typing:` only (the recipient). - const d = frame.data as { to?: string } | undefined; - return d?.to ? ['typing:' + d.to] : []; - } - // Control-plane events — no topic fan-out; use a pseudo-topic so UI - // can listen for them via subscribe('$system', ...). - case 'hello': - case 'pong': - case 'error': - case 'subscribed': - case 'submit_ack': - case 'lag': - return ['$system']; - default: - return []; - } - } -} - -let _singleton: WSClient | null = null; - -/** - * Return the app-wide WebSocket client. Safe to call from any component; - * `.connect()` is idempotent. - * - * On first creation we register a node-URL listener so flipping the node - * in Settings tears down the existing socket and dials the new one — the - * user's active subscriptions (addr:*, inbox:*) replay automatically. - */ -export function getWSClient(): WSClient { - if (!_singleton) { - _singleton = new WSClient(); - onNodeUrlChange(() => { - // Fire and forget — connect() is idempotent and handles stale URLs. - _singleton!.connect(); - }); - } - return _singleton; -} diff --git a/client-app/metro.config.js b/client-app/metro.config.js deleted file mode 100644 index f3321ba..0000000 --- a/client-app/metro.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const { getDefaultConfig } = require('expo/metro-config'); -const { withNativeWind } = require('nativewind/metro'); - -const config = getDefaultConfig(__dirname); - -module.exports = withNativeWind(config, { input: './global.css' }); diff --git a/client-app/nativewind-env.d.ts b/client-app/nativewind-env.d.ts deleted file mode 100644 index a13e313..0000000 --- a/client-app/nativewind-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/client-app/package-lock.json b/client-app/package-lock.json deleted file mode 100644 index 0e78df7..0000000 --- a/client-app/package-lock.json +++ /dev/null @@ -1,10317 +0,0 @@ -{ - "name": "dchain-messenger", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "dchain-messenger", - "version": "1.0.0", - "dependencies": { - "@react-native-async-storage/async-storage": "2.2.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "expo": "~54.0.0", - "expo-asset": "~12.0.12", - "expo-camera": "~16.1.6", - "expo-clipboard": "~8.0.8", - "expo-constants": "~18.0.13", - "expo-crypto": "~14.1.4", - "expo-document-picker": "~14.0.8", - "expo-file-system": "~19.0.21", - "expo-font": "~14.0.11", - "expo-linking": "~8.0.11", - "expo-router": "~6.0.23", - "expo-secure-store": "~15.0.8", - "expo-sharing": "~14.0.8", - "expo-splash-screen": "~31.0.13", - "expo-status-bar": "~3.0.9", - "expo-web-browser": "~15.0.10", - "nativewind": "^4.1.23", - "react": "19.1.0", - "react-native": "0.81.5", - "react-native-reanimated": "~3.17.0", - "react-native-safe-area-context": "~5.6.0", - "react-native-screens": "~4.16.0", - "react-native-worklets": "~0.8.1", - "tailwind-merge": "^2.6.0", - "tailwindcss": "^3.4.17", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1", - "zustand": "^5.0.3" - }, - "devDependencies": { - "@babel/core": "^7.25.2", - "@types/react": "~19.1.0", - "babel-preset-expo": "~13.0.0", - "typescript": "^5.3.3" - } - }, - "node_modules/@0no-co/graphql.web": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz", - "integrity": "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==", - "license": "MIT", - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - }, - "peerDependenciesMeta": { - "graphql": { - "optional": true - } - } - }, - "node_modules/@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==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", - "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", - "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "regexpu-core": "^6.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", - "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "debug": "^4.4.3", - "lodash.debounce": "^4.0.8", - "resolve": "^1.22.11" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", - "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", - "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz", - "integrity": "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-syntax-decorators": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-default-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.27.1.tgz", - "integrity": "sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz", - "integrity": "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-default-from": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.28.6.tgz", - "integrity": "sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz", - "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", - "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", - "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", - "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", - "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", - "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", - "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz", - "integrity": "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-flow": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", - "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", - "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", - "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", - "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", - "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", - "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-syntax-jsx": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", - "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", - "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "babel-plugin-polyfill-corejs2": "^0.4.14", - "babel-plugin-polyfill-corejs3": "^0.13.0", - "babel-plugin-polyfill-regenerator": "^0.6.5", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", - "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", - "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", - "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.28.0", - "@babel/plugin-transform-react-jsx": "^7.27.1", - "@babel/plugin-transform-react-jsx-development": "^7.27.1", - "@babel/plugin-transform-react-pure-annotations": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", - "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse--for-generate-function-map": { - "name": "@babel/traverse", - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@expo/cli": { - "version": "54.0.23", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.23.tgz", - "integrity": "sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g==", - "license": "MIT", - "dependencies": { - "@0no-co/graphql.web": "^1.0.8", - "@expo/code-signing-certificates": "^0.0.6", - "@expo/config": "~12.0.13", - "@expo/config-plugins": "~54.0.4", - "@expo/devcert": "^1.2.1", - "@expo/env": "~2.0.8", - "@expo/image-utils": "^0.8.8", - "@expo/json-file": "^10.0.8", - "@expo/metro": "~54.2.0", - "@expo/metro-config": "~54.0.14", - "@expo/osascript": "^2.3.8", - "@expo/package-manager": "^1.9.10", - "@expo/plist": "^0.4.8", - "@expo/prebuild-config": "^54.0.8", - "@expo/schema-utils": "^0.1.8", - "@expo/spawn-async": "^1.7.2", - "@expo/ws-tunnel": "^1.0.1", - "@expo/xcpretty": "^4.3.0", - "@react-native/dev-middleware": "0.81.5", - "@urql/core": "^5.0.6", - "@urql/exchange-retry": "^1.3.0", - "accepts": "^1.3.8", - "arg": "^5.0.2", - "better-opn": "~3.0.2", - "bplist-creator": "0.1.0", - "bplist-parser": "^0.3.1", - "chalk": "^4.0.0", - "ci-info": "^3.3.0", - "compression": "^1.7.4", - "connect": "^3.7.0", - "debug": "^4.3.4", - "env-editor": "^0.4.1", - "expo-server": "^1.0.5", - "freeport-async": "^2.0.0", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "lan-network": "^0.1.6", - "minimatch": "^9.0.0", - "node-forge": "^1.3.3", - "npm-package-arg": "^11.0.0", - "ora": "^3.4.0", - "picomatch": "^3.0.1", - "pretty-bytes": "^5.6.0", - "pretty-format": "^29.7.0", - "progress": "^2.0.3", - "prompts": "^2.3.2", - "qrcode-terminal": "0.11.0", - "require-from-string": "^2.0.2", - "requireg": "^0.2.2", - "resolve": "^1.22.2", - "resolve-from": "^5.0.0", - "resolve.exports": "^2.0.3", - "semver": "^7.6.0", - "send": "^0.19.0", - "slugify": "^1.3.4", - "source-map-support": "~0.5.21", - "stacktrace-parser": "^0.1.10", - "structured-headers": "^0.4.1", - "tar": "^7.5.2", - "terminal-link": "^2.1.1", - "undici": "^6.18.2", - "wrap-ansi": "^7.0.0", - "ws": "^8.12.1" - }, - "bin": { - "expo-internal": "build/bin/cli" - }, - "peerDependencies": { - "expo": "*", - "expo-router": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "expo-router": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/@expo/cli/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/cli/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/cli/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/cli/node_modules/glob/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/cli/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/code-signing-certificates": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz", - "integrity": "sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==", - "license": "MIT", - "dependencies": { - "node-forge": "^1.3.3" - } - }, - "node_modules/@expo/config": { - "version": "12.0.13", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.13.tgz", - "integrity": "sha512-Cu52arBa4vSaupIWsF0h7F/Cg//N374nYb7HAxV0I4KceKA7x2UXpYaHOL7EEYYvp7tZdThBjvGpVmr8ScIvaQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "~7.10.4", - "@expo/config-plugins": "~54.0.4", - "@expo/config-types": "^54.0.10", - "@expo/json-file": "^10.0.8", - "deepmerge": "^4.3.1", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "require-from-string": "^2.0.2", - "resolve-from": "^5.0.0", - "resolve-workspace-root": "^2.0.0", - "semver": "^7.6.0", - "slugify": "^1.3.4", - "sucrase": "~3.35.1" - } - }, - "node_modules/@expo/config-plugins": { - "version": "54.0.4", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.4.tgz", - "integrity": "sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q==", - "license": "MIT", - "dependencies": { - "@expo/config-types": "^54.0.10", - "@expo/json-file": "~10.0.8", - "@expo/plist": "^0.4.8", - "@expo/sdk-runtime-versions": "^1.0.0", - "chalk": "^4.1.2", - "debug": "^4.3.5", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.5.4", - "slash": "^3.0.0", - "slugify": "^1.6.6", - "xcode": "^3.0.1", - "xml2js": "0.6.0" - } - }, - "node_modules/@expo/config-plugins/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/config-plugins/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/config-plugins/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/config-plugins/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/config-plugins/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/config-types": { - "version": "54.0.10", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.10.tgz", - "integrity": "sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA==", - "license": "MIT" - }, - "node_modules/@expo/config/node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@expo/config/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/config/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/config/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/config/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/config/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/devcert": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.2.1.tgz", - "integrity": "sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==", - "license": "MIT", - "dependencies": { - "@expo/sudo-prompt": "^9.3.1", - "debug": "^3.1.0" - } - }, - "node_modules/@expo/devcert/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/@expo/devtools": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@expo/devtools/-/devtools-0.1.8.tgz", - "integrity": "sha512-SVLxbuanDjJPgc0sy3EfXUMLb/tXzp6XIHkhtPVmTWJAp+FOr6+5SeiCfJrCzZFet0Ifyke2vX3sFcKwEvCXwQ==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/@expo/env": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.0.11.tgz", - "integrity": "sha512-xV+ps6YCW7XIPVUwFVCRN2nox09dnRwy8uIjwHWTODu0zFw4kp4omnVkl0OOjuu2XOe7tdgAHxikrkJt9xB/7Q==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "debug": "^4.3.4", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", - "getenv": "^2.0.0" - } - }, - "node_modules/@expo/fingerprint": { - "version": "0.15.4", - "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.15.4.tgz", - "integrity": "sha512-eYlxcrGdR2/j2M6pEDXo9zU9KXXF1vhP+V+Tl+lyY+bU8lnzrN6c637mz6Ye3em2ANy8hhUR03Raf8VsT9Ogng==", - "license": "MIT", - "dependencies": { - "@expo/spawn-async": "^1.7.2", - "arg": "^5.0.2", - "chalk": "^4.1.2", - "debug": "^4.3.4", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "ignore": "^5.3.1", - "minimatch": "^9.0.0", - "p-limit": "^3.1.0", - "resolve-from": "^5.0.0", - "semver": "^7.6.0" - }, - "bin": { - "fingerprint": "bin/cli.js" - } - }, - "node_modules/@expo/fingerprint/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/fingerprint/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/fingerprint/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/fingerprint/node_modules/glob/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/fingerprint/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/image-utils": { - "version": "0.8.13", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.8.13.tgz", - "integrity": "sha512-1I//yBQeTY6p0u1ihqGNDAr35EbSG8uFEupFrIF0jd++h9EWH33521yZJU1yE+mwGlzCb61g3ehu78siMhXBlA==", - "license": "MIT", - "dependencies": { - "@expo/require-utils": "^55.0.4", - "@expo/spawn-async": "^1.7.2", - "chalk": "^4.0.0", - "getenv": "^2.0.0", - "jimp-compact": "0.16.1", - "parse-png": "^2.1.0", - "semver": "^7.6.0" - } - }, - "node_modules/@expo/image-utils/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/json-file": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.13.tgz", - "integrity": "sha512-pX/XjQn7tgNw6zuuV2ikmegmwe/S7uiwhrs2wXrANMkq7ozrA+JcZwgW9Q/8WZgciBzfAhNp5hnackHcrmapQA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "json5": "^2.2.3" - } - }, - "node_modules/@expo/metro": { - "version": "54.2.0", - "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-54.2.0.tgz", - "integrity": "sha512-h68TNZPGsk6swMmLm9nRSnE2UXm48rWwgcbtAHVMikXvbxdS41NDHHeqg1rcQ9AbznDRp6SQVC2MVpDnsRKU1w==", - "license": "MIT", - "dependencies": { - "metro": "0.83.3", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-config": "0.83.3", - "metro-core": "0.83.3", - "metro-file-map": "0.83.3", - "metro-minify-terser": "0.83.3", - "metro-resolver": "0.83.3", - "metro-runtime": "0.83.3", - "metro-source-map": "0.83.3", - "metro-symbolicate": "0.83.3", - "metro-transform-plugins": "0.83.3", - "metro-transform-worker": "0.83.3" - } - }, - "node_modules/@expo/metro-config": { - "version": "54.0.14", - "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-54.0.14.tgz", - "integrity": "sha512-hxpLyDfOR4L23tJ9W1IbJJsG7k4lv2sotohBm/kTYyiG+pe1SYCAWsRmgk+H42o/wWf/HQjE5k45S5TomGLxNA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.20.0", - "@babel/generator": "^7.20.5", - "@expo/config": "~12.0.13", - "@expo/env": "~2.0.8", - "@expo/json-file": "~10.0.8", - "@expo/metro": "~54.2.0", - "@expo/spawn-async": "^1.7.2", - "browserslist": "^4.25.0", - "chalk": "^4.1.0", - "debug": "^4.3.2", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", - "getenv": "^2.0.0", - "glob": "^13.0.0", - "hermes-parser": "^0.29.1", - "jsc-safe-url": "^0.2.4", - "lightningcss": "^1.30.1", - "minimatch": "^9.0.0", - "postcss": "~8.4.32", - "resolve-from": "^5.0.0" - }, - "peerDependencies": { - "expo": "*" - }, - "peerDependenciesMeta": { - "expo": { - "optional": true - } - } - }, - "node_modules/@expo/metro-config/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/metro-config/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@expo/metro-config/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/metro-config/node_modules/glob/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/metro-config/node_modules/hermes-estree": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", - "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", - "license": "MIT" - }, - "node_modules/@expo/metro-config/node_modules/hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", - "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", - "license": "MIT", - "dependencies": { - "hermes-estree": "0.29.1" - } - }, - "node_modules/@expo/metro-runtime": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-6.1.2.tgz", - "integrity": "sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g==", - "license": "MIT", - "dependencies": { - "anser": "^1.4.9", - "pretty-format": "^29.7.0", - "stacktrace-parser": "^0.1.10", - "whatwg-fetch": "^3.0.0" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-dom": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/@expo/osascript": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.4.2.tgz", - "integrity": "sha512-/XP7PSYF2hzOZzqfjgkoWtllyeTN8dW3aM4P6YgKcmmPikKL5FdoyQhti4eh6RK5a5VrUXJTOlTNIpIHsfB5Iw==", - "license": "MIT", - "dependencies": { - "@expo/spawn-async": "^1.7.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@expo/package-manager": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.10.4.tgz", - "integrity": "sha512-y9Mr4Kmpk4abAVZrNNPCdzOZr8nLLyi18p1SXr0RCVA8IfzqZX/eY4H+50a0HTmXqIsPZrQdcdb4I3ekMS9GvQ==", - "license": "MIT", - "dependencies": { - "@expo/json-file": "^10.0.13", - "@expo/spawn-async": "^1.7.2", - "chalk": "^4.0.0", - "npm-package-arg": "^11.0.0", - "ora": "^3.4.0", - "resolve-workspace-root": "^2.0.0" - } - }, - "node_modules/@expo/plist": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.8.tgz", - "integrity": "sha512-pfNtErGGzzRwHP+5+RqswzPDKkZrx+Cli0mzjQaus1ZWFsog5ibL+nVT3NcporW51o8ggnt7x813vtRbPiyOrQ==", - "license": "MIT", - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.2.3", - "xmlbuilder": "^15.1.1" - } - }, - "node_modules/@expo/prebuild-config": { - "version": "54.0.8", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-54.0.8.tgz", - "integrity": "sha512-EA7N4dloty2t5Rde+HP0IEE+nkAQiu4A/+QGZGT9mFnZ5KKjPPkqSyYcRvP5bhQE10D+tvz6X0ngZpulbMdbsg==", - "license": "MIT", - "dependencies": { - "@expo/config": "~12.0.13", - "@expo/config-plugins": "~54.0.4", - "@expo/config-types": "^54.0.10", - "@expo/image-utils": "^0.8.8", - "@expo/json-file": "^10.0.8", - "@react-native/normalize-colors": "0.81.5", - "debug": "^4.3.1", - "resolve-from": "^5.0.0", - "semver": "^7.6.0", - "xml2js": "0.6.0" - }, - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/@expo/prebuild-config/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/require-utils": { - "version": "55.0.4", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.4.tgz", - "integrity": "sha512-JAANvXqV7MOysWeVWgaiDzikoyDjJWOV/ulOW60Zb3kXJfrx2oZOtGtDXDFKD1mXuahQgoM5QOjuZhF7gFRNjA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "@babel/core": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^5.0.0-0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@expo/schema-utils": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-0.1.8.tgz", - "integrity": "sha512-9I6ZqvnAvKKDiO+ZF8BpQQFYWXOJvTAL5L/227RUbWG1OVZDInFifzCBiqAZ3b67NRfeAgpgvbA7rejsqhY62A==", - "license": "MIT" - }, - "node_modules/@expo/sdk-runtime-versions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz", - "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==", - "license": "MIT" - }, - "node_modules/@expo/spawn-async": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", - "integrity": "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@expo/sudo-prompt": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@expo/sudo-prompt/-/sudo-prompt-9.3.2.tgz", - "integrity": "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==", - "license": "MIT" - }, - "node_modules/@expo/vector-icons": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-15.1.1.tgz", - "integrity": "sha512-Iu2VkcoI5vygbtYngm7jb4ifxElNVXQYdDrYkT7UCEIiKLeWnQY0wf2ZhHZ+Wro6Sc5TaumpKUOqDRpLi5rkvw==", - "license": "MIT", - "peerDependencies": { - "expo-font": ">=14.0.4", - "react": "*", - "react-native": "*" - } - }, - "node_modules/@expo/ws-tunnel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.6.tgz", - "integrity": "sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==", - "license": "MIT" - }, - "node_modules/@expo/xcpretty": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.4.3.tgz", - "integrity": "sha512-wC562eD3gS6vO2tWHToFhlFnmHKfKHgF1oyvojeSkLK/ZYop1bMU+7cOMiF9Sq70CzcsLy/EMRy/uRc76QmNRw==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/code-frame": "^7.20.0", - "chalk": "^4.1.0", - "js-yaml": "^4.1.0" - }, - "bin": { - "excpretty": "build/cli.js" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@isaacs/ttlcache": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", - "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", - "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/create-cache-key-function": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", - "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@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==", - "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==", - "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==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tabs": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@react-native-async-storage/async-storage": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz", - "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==", - "license": "MIT", - "dependencies": { - "merge-options": "^3.0.4" - }, - "peerDependencies": { - "react-native": "^0.0.0-0 || >=0.65 <1.0" - } - }, - "node_modules/@react-native/assets-registry": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.5.tgz", - "integrity": "sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==", - "license": "MIT", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/babel-plugin-codegen": { - "version": "0.79.0-rc.4", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.79.0-rc.4.tgz", - "integrity": "sha512-vhYfoFdzz1D/bDAuGThtNia9bUpjtCo73IWfndNoKj+84SOGTtrKaZ/jOpXs5uMskpe1D4xaHuOHHFbzp/0E8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.3", - "@react-native/codegen": "0.79.0-rc.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/babel-preset": { - "version": "0.79.0-rc.4", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.79.0-rc.4.tgz", - "integrity": "sha512-0vyO3Ix2N0sI43w0XNJ2zzSWzX7no/JMrXnVzJEm9IhNHsuMmKaXQdRvijDZroepDINIbb6vaIWwzeWkqhiPAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.25.2", - "@babel/plugin-transform-react-jsx-self": "^7.24.7", - "@babel/plugin-transform-react-jsx-source": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.25.2", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/template": "^7.25.0", - "@react-native/babel-plugin-codegen": "0.79.0-rc.4", - "babel-plugin-syntax-hermes-parser": "0.25.1", - "babel-plugin-transform-flow-enums": "^0.0.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/@react-native/codegen": { - "version": "0.79.0-rc.4", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.79.0-rc.4.tgz", - "integrity": "sha512-62J5LVV0LBqyqSV1REin2+ciWamctlYMFyy216Gko/+aqMdoYVX/bM14pdPmqtPZRoMEgWQCgmfw+xc0yx9aPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob": "^7.1.1", - "hermes-parser": "0.25.1", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "yargs": "^17.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/@react-native/community-cli-plugin": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.81.5.tgz", - "integrity": "sha512-yWRlmEOtcyvSZ4+OvqPabt+NS36vg0K/WADTQLhrYrm9qdZSuXmq8PmdJWz/68wAqKQ+4KTILiq2kjRQwnyhQw==", - "license": "MIT", - "dependencies": { - "@react-native/dev-middleware": "0.81.5", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "metro": "^0.83.1", - "metro-config": "^0.83.1", - "metro-core": "^0.83.1", - "semver": "^7.1.3" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@react-native-community/cli": "*", - "@react-native/metro-config": "*" - }, - "peerDependenciesMeta": { - "@react-native-community/cli": { - "optional": true - }, - "@react-native/metro-config": { - "optional": true - } - } - }, - "node_modules/@react-native/community-cli-plugin/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@react-native/debugger-frontend": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.81.5.tgz", - "integrity": "sha512-bnd9FSdWKx2ncklOetCgrlwqSGhMHP2zOxObJbOWXoj7GHEmih4MKarBo5/a8gX8EfA1EwRATdfNBQ81DY+h+w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/dev-middleware": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.81.5.tgz", - "integrity": "sha512-WfPfZzboYgo/TUtysuD5xyANzzfka8Ebni6RIb2wDxhb56ERi7qDrE4xGhtPsjCL4pQBXSVxyIlCy0d8I6EgGA==", - "license": "MIT", - "dependencies": { - "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.81.5", - "chrome-launcher": "^0.15.2", - "chromium-edge-launcher": "^0.2.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "open": "^7.0.3", - "serve-static": "^1.16.2", - "ws": "^6.2.3" - }, - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/dev-middleware/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/@react-native/gradle-plugin": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.81.5.tgz", - "integrity": "sha512-hORRlNBj+ReNMLo9jme3yQ6JQf4GZpVEBLxmTXGGlIL78MAezDZr5/uq9dwElSbcGmLEgeiax6e174Fie6qPLg==", - "license": "MIT", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/js-polyfills": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.81.5.tgz", - "integrity": "sha512-fB7M1CMOCIUudTRuj7kzxIBTVw2KXnsgbQ6+4cbqSxo8NmRRhA0Ul4ZUzZj3rFd3VznTL4Brmocv1oiN0bWZ8w==", - "license": "MIT", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/normalize-colors": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.5.tgz", - "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", - "license": "MIT" - }, - "node_modules/@react-native/virtualized-lists": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz", - "integrity": "sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw==", - "license": "MIT", - "dependencies": { - "invariant": "^2.2.4", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@types/react": "^19.1.0", - "react": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@react-navigation/bottom-tabs": { - "version": "7.15.9", - "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.15.9.tgz", - "integrity": "sha512-Ou28A1aZLj5wiFQ3F93aIsrI4NCwn3IJzkkjNo9KLFXsc0Yks+UqrVaFlffHFLsrbajuGRG/OQpnMA1ljayY5Q==", - "license": "MIT", - "dependencies": { - "@react-navigation/elements": "^2.9.14", - "color": "^4.2.3", - "sf-symbols-typescript": "^2.1.0" - }, - "peerDependencies": { - "@react-navigation/native": "^7.2.2", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-screens": ">= 4.0.0" - } - }, - "node_modules/@react-navigation/core": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.17.2.tgz", - "integrity": "sha512-Rt2OZwcgOmjv401uLGAKaRM6xo0fiBce/A7LfRHI1oe5FV+KooWcgAoZ2XOtgKj6UzVMuQWt3b2e6rxo/mDJRA==", - "license": "MIT", - "dependencies": { - "@react-navigation/routers": "^7.5.3", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "query-string": "^7.1.3", - "react-is": "^19.1.0", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" - }, - "peerDependencies": { - "react": ">= 18.2.0" - } - }, - "node_modules/@react-navigation/elements": { - "version": "2.9.14", - "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.14.tgz", - "integrity": "sha512-lKqzu+su2pI/YIZmR7L7xdOs4UL+rVXKJAMpRMBrwInEy96SjIFst6QDGpE89Dunnu3VjVpjWfByo9f2GWBHDQ==", - "license": "MIT", - "dependencies": { - "color": "^4.2.3", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" - }, - "peerDependencies": { - "@react-native-masked-view/masked-view": ">= 0.2.0", - "@react-navigation/native": "^7.2.2", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0" - }, - "peerDependenciesMeta": { - "@react-native-masked-view/masked-view": { - "optional": true - } - } - }, - "node_modules/@react-navigation/native": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.2.2.tgz", - "integrity": "sha512-kem1Ko2BcbAjmbQIv66dNmr6EtfDut3QU0qjsVhMnLLhktwyXb6FzZYp8gTrUb6AvkAbaJoi+BF5Pl55pAUa5w==", - "license": "MIT", - "dependencies": { - "@react-navigation/core": "^7.17.2", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "use-latest-callback": "^0.2.4" - }, - "peerDependencies": { - "react": ">= 18.2.0", - "react-native": "*" - } - }, - "node_modules/@react-navigation/native-stack": { - "version": "7.14.11", - "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.14.11.tgz", - "integrity": "sha512-1ufBtJ7KbVFlQhXsYSYHqjgkmP30AzJSgW48YjWMQZ3NZGAyYe34w9Wd4KpdebQCfDClPe9maU+8crA/awa6lQ==", - "license": "MIT", - "dependencies": { - "@react-navigation/elements": "^2.9.14", - "color": "^4.2.3", - "sf-symbols-typescript": "^2.1.0", - "warn-once": "^0.1.1" - }, - "peerDependencies": { - "@react-navigation/native": "^7.2.2", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-screens": ">= 4.0.0" - } - }, - "node_modules/@react-navigation/routers": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.3.tgz", - "integrity": "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==", - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.19.0" - } - }, - "node_modules/@types/react": { - "version": "19.1.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", - "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@urql/core": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.2.0.tgz", - "integrity": "sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==", - "license": "MIT", - "dependencies": { - "@0no-co/graphql.web": "^1.0.13", - "wonka": "^6.3.2" - } - }, - "node_modules/@urql/exchange-retry": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.3.2.tgz", - "integrity": "sha512-TQMCz2pFJMfpNxmSfX1VSfTjwUIFx/mL+p1bnfM1xjjdla7Z+KnGMW/EhFbpckp3LyWAH4PgOsMwOMnIN+MBFg==", - "license": "MIT", - "dependencies": { - "@urql/core": "^5.1.2", - "wonka": "^6.3.2" - }, - "peerDependencies": { - "@urql/core": "^5.0.0" - } - }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz", - "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/anser": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", - "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", - "license": "MIT" - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/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==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "license": "MIT" - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT" - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "license": "MIT" - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", - "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.8", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", - "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.8" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-react-compiler": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz", - "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.26.0" - } - }, - "node_modules/babel-plugin-react-native-web": { - "version": "0.19.13", - "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.13.tgz", - "integrity": "sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.25.1.tgz", - "integrity": "sha512-IVNpGzboFLfXZUAwkLFcI/bnqVbwky0jP3eBno4HKtqvQJAHBLdgxiG6lQ4to0+Q/YCN3PO0od5NZwIKyY4REQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "hermes-parser": "0.25.1" - } - }, - "node_modules/babel-plugin-transform-flow-enums": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", - "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-flow": "^7.12.1" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-expo": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-13.0.0.tgz", - "integrity": "sha512-4NfamKh+BKu6v0VUtZ2wFuZ9VdaDnYOC+vsAHhUF3ks1jzFLo9TwBqsrkhD129DIHfdhVJnRJah2KRCXEjcrVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/plugin-proposal-decorators": "^7.12.9", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.23.0", - "@react-native/babel-preset": "0.79.0-rc.4", - "babel-plugin-react-native-web": "~0.19.13", - "babel-plugin-syntax-hermes-parser": "^0.25.1", - "babel-plugin-transform-flow-enums": "^0.0.2", - "debug": "^4.3.4", - "react-refresh": "^0.14.2", - "resolve-from": "^5.0.0" - }, - "peerDependencies": { - "babel-plugin-react-compiler": "^19.0.0-beta-9ee70a1-20241017", - "react-compiler-runtime": "^19.0.0-beta-8a03594-20241020" - }, - "peerDependenciesMeta": { - "babel-plugin-react-compiler": { - "optional": true - }, - "react-compiler-runtime": { - "optional": true - } - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.19.tgz", - "integrity": "sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/better-opn": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", - "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", - "license": "MIT", - "dependencies": { - "open": "^8.0.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/better-opn/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "license": "Unlicense", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bplist-creator": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", - "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", - "license": "MIT", - "dependencies": { - "stream-buffers": "2.2.x" - } - }, - "node_modules/bplist-parser": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", - "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", - "license": "MIT", - "dependencies": { - "big-integer": "1.6.x" - }, - "engines": { - "node": ">= 5.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "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/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001788", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", - "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", - "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/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "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==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/chrome-launcher": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", - "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0" - }, - "bin": { - "print-chrome-path": "bin/print-chrome-path.js" - }, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/chromium-edge-launcher": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.2.0.tgz", - "integrity": "sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" - } - }, - "node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/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": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/comment-json": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.6.2.tgz", - "integrity": "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==", - "license": "MIT", - "dependencies": { - "array-timsort": "^1.0.3", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.1.0", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/compression/node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", - "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "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/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dotenv-expand": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", - "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", - "license": "BSD-2-Clause", - "dependencies": { - "dotenv": "^16.4.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.339", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.339.tgz", - "integrity": "sha512-Is+0BBHJ4NrdpAYiperrmp53pLywG/yV/6lIMTAnhxvzj/Cmn5Q/ogSHC6AKe7X+8kPLxxFk0cs5oc/3j/fxIg==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/env-editor": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz", - "integrity": "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/expo": { - "version": "54.0.33", - "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.33.tgz", - "integrity": "sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.0", - "@expo/cli": "54.0.23", - "@expo/config": "~12.0.13", - "@expo/config-plugins": "~54.0.4", - "@expo/devtools": "0.1.8", - "@expo/fingerprint": "0.15.4", - "@expo/metro": "~54.2.0", - "@expo/metro-config": "54.0.14", - "@expo/vector-icons": "^15.0.3", - "@ungap/structured-clone": "^1.3.0", - "babel-preset-expo": "~54.0.10", - "expo-asset": "~12.0.12", - "expo-constants": "~18.0.13", - "expo-file-system": "~19.0.21", - "expo-font": "~14.0.11", - "expo-keep-awake": "~15.0.8", - "expo-modules-autolinking": "3.0.24", - "expo-modules-core": "3.0.29", - "pretty-format": "^29.7.0", - "react-refresh": "^0.14.2", - "whatwg-url-without-unicode": "8.0.0-3" - }, - "bin": { - "expo": "bin/cli", - "expo-modules-autolinking": "bin/autolinking", - "fingerprint": "bin/fingerprint" - }, - "peerDependencies": { - "@expo/dom-webview": "*", - "@expo/metro-runtime": "*", - "react": "*", - "react-native": "*", - "react-native-webview": "*" - }, - "peerDependenciesMeta": { - "@expo/dom-webview": { - "optional": true - }, - "@expo/metro-runtime": { - "optional": true - }, - "react-native-webview": { - "optional": true - } - } - }, - "node_modules/expo-asset": { - "version": "12.0.12", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-12.0.12.tgz", - "integrity": "sha512-CsXFCQbx2fElSMn0lyTdRIyKlSXOal6ilLJd+yeZ6xaC7I9AICQgscY5nj0QcwgA+KYYCCEQEBndMsmj7drOWQ==", - "license": "MIT", - "dependencies": { - "@expo/image-utils": "^0.8.8", - "expo-constants": "~18.0.12" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-camera": { - "version": "16.1.11", - "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-16.1.11.tgz", - "integrity": "sha512-etA5ZKoC6nPBnWWqiTmlX//zoFZ6cWQCCIdmpUHTGHAKd4qZNCkhPvBWbi8o32pDe57lix1V4+TPFgEcvPwsaA==", - "license": "MIT", - "dependencies": { - "invariant": "^2.2.4" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*", - "react-native-web": "*" - }, - "peerDependenciesMeta": { - "react-native-web": { - "optional": true - } - } - }, - "node_modules/expo-clipboard": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-8.0.8.tgz", - "integrity": "sha512-VKoBkHIpZZDJTB0jRO4/PZskHdMNOEz3P/41tmM6fDuODMpqhvyWK053X0ebspkxiawJX9lX33JXHBCvVsTTOA==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-constants": { - "version": "18.0.13", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.13.tgz", - "integrity": "sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ==", - "license": "MIT", - "dependencies": { - "@expo/config": "~12.0.13", - "@expo/env": "~2.0.8" - }, - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, - "node_modules/expo-crypto": { - "version": "14.1.5", - "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-14.1.5.tgz", - "integrity": "sha512-ZXJoUMoUeiMNEoSD4itItFFz3cKrit6YJ/BR0hjuwNC+NczbV9rorvhvmeJmrU9O2cFQHhJQQR1fjQnt45Vu4Q==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.0" - }, - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-document-picker": { - "version": "14.0.8", - "resolved": "https://registry.npmjs.org/expo-document-picker/-/expo-document-picker-14.0.8.tgz", - "integrity": "sha512-3tyQKpPqWWFlI8p9RiMX1+T1Zge5mEKeBuXWp1h8PEItFMUDSiOJbQ112sfdC6Hxt8wSxreV9bCRl/NgBdt+fA==", - "license": "MIT", - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-file-system": { - "version": "19.0.21", - "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.21.tgz", - "integrity": "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, - "node_modules/expo-font": { - "version": "14.0.11", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.11.tgz", - "integrity": "sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==", - "license": "MIT", - "dependencies": { - "fontfaceobserver": "^2.1.0" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-keep-awake": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.8.tgz", - "integrity": "sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*" - } - }, - "node_modules/expo-linking": { - "version": "8.0.11", - "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.11.tgz", - "integrity": "sha512-+VSaNL5om3kOp/SSKO5qe6cFgfSIWnnQDSbA7XLs3ECkYzXRquk5unxNS3pg7eK5kNUmQ4kgLI7MhTggAEUBLA==", - "license": "MIT", - "dependencies": { - "expo-constants": "~18.0.12", - "invariant": "^2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-modules-autolinking": { - "version": "3.0.24", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.24.tgz", - "integrity": "sha512-TP+6HTwhL7orDvsz2VzauyQlXJcAWyU3ANsZ7JGL4DQu8XaZv/A41ZchbtAYLfozNA2Ya1Hzmhx65hXryBMjaQ==", - "license": "MIT", - "dependencies": { - "@expo/spawn-async": "^1.7.2", - "chalk": "^4.1.0", - "commander": "^7.2.0", - "require-from-string": "^2.0.2", - "resolve-from": "^5.0.0" - }, - "bin": { - "expo-modules-autolinking": "bin/expo-modules-autolinking.js" - } - }, - "node_modules/expo-modules-core": { - "version": "3.0.29", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-3.0.29.tgz", - "integrity": "sha512-LzipcjGqk8gvkrOUf7O2mejNWugPkf3lmd9GkqL9WuNyeN2fRwU0Dn77e3ZUKI3k6sI+DNwjkq4Nu9fNN9WS7Q==", - "license": "MIT", - "dependencies": { - "invariant": "^2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-router": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-6.0.23.tgz", - "integrity": "sha512-qCxVAiCrCyu0npky6azEZ6dJDMt77OmCzEbpF6RbUTlfkaCA417LvY14SBkk0xyGruSxy/7pvJOI6tuThaUVCA==", - "license": "MIT", - "dependencies": { - "@expo/metro-runtime": "^6.1.2", - "@expo/schema-utils": "^0.1.8", - "@radix-ui/react-slot": "1.2.0", - "@radix-ui/react-tabs": "^1.1.12", - "@react-navigation/bottom-tabs": "^7.4.0", - "@react-navigation/native": "^7.1.8", - "@react-navigation/native-stack": "^7.3.16", - "client-only": "^0.0.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "expo-server": "^1.0.5", - "fast-deep-equal": "^3.1.3", - "invariant": "^2.2.4", - "nanoid": "^3.3.8", - "query-string": "^7.1.3", - "react-fast-compare": "^3.2.2", - "react-native-is-edge-to-edge": "^1.1.6", - "semver": "~7.6.3", - "server-only": "^0.0.1", - "sf-symbols-typescript": "^2.1.0", - "shallowequal": "^1.1.0", - "use-latest-callback": "^0.2.1", - "vaul": "^1.1.2" - }, - "peerDependencies": { - "@expo/metro-runtime": "^6.1.2", - "@react-navigation/drawer": "^7.5.0", - "@testing-library/react-native": ">= 12.0.0", - "expo": "*", - "expo-constants": "^18.0.13", - "expo-linking": "^8.0.11", - "react": "*", - "react-dom": "*", - "react-native": "*", - "react-native-gesture-handler": "*", - "react-native-reanimated": "*", - "react-native-safe-area-context": ">= 5.4.0", - "react-native-screens": "*", - "react-native-web": "*", - "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4" - }, - "peerDependenciesMeta": { - "@react-navigation/drawer": { - "optional": true - }, - "@testing-library/react-native": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native-gesture-handler": { - "optional": true - }, - "react-native-reanimated": { - "optional": true - }, - "react-native-web": { - "optional": true - }, - "react-server-dom-webpack": { - "optional": true - } - } - }, - "node_modules/expo-router/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/expo-secure-store": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-15.0.8.tgz", - "integrity": "sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw==", - "license": "MIT", - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-server": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.5.tgz", - "integrity": "sha512-IGR++flYH70rhLyeXF0Phle56/k4cee87WeQ4mamS+MkVAVP+dDlOHf2nN06Z9Y2KhU0Gp1k+y61KkghF7HdhA==", - "license": "MIT", - "engines": { - "node": ">=20.16.0" - } - }, - "node_modules/expo-sharing": { - "version": "14.0.8", - "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-14.0.8.tgz", - "integrity": "sha512-A1pPr2iBrxypFDCWVAESk532HK+db7MFXbvO2sCV9ienaFXAk7lIBm6bkqgE6vzRd9O3RGdEGzYx80cYlc089Q==", - "license": "MIT", - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-splash-screen": { - "version": "31.0.13", - "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-31.0.13.tgz", - "integrity": "sha512-1epJLC1cDlwwj089R2h8cxaU5uk4ONVAC+vzGiTZH4YARQhL4Stlz1MbR6yAS173GMosvkE6CAeihR7oIbCkDA==", - "license": "MIT", - "dependencies": { - "@expo/prebuild-config": "^54.0.8" - }, - "peerDependencies": { - "expo": "*" - } - }, - "node_modules/expo-status-bar": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-3.0.9.tgz", - "integrity": "sha512-xyYyVg6V1/SSOZWh4Ni3U129XHCnFHBTcUo0dhWtFDrZbNp/duw5AGsQfb2sVeU0gxWHXSY1+5F0jnKYC7WuOw==", - "license": "MIT", - "dependencies": { - "react-native-is-edge-to-edge": "^1.2.1" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-web-browser": { - "version": "15.0.10", - "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-15.0.10.tgz", - "integrity": "sha512-fvDhW4bhmXAeWFNFiInmsGCK83PAqAcQaFyp/3pE/jbdKmFKoRCWr46uZGIfN4msLK/OODhaQ/+US7GSJNDHJg==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, - "node_modules/expo/node_modules/@react-native/babel-plugin-codegen": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.81.5.tgz", - "integrity": "sha512-oF71cIH6je3fSLi6VPjjC3Sgyyn57JLHXs+mHWc9MoCiJJcM4nqsS5J38zv1XQ8d3zOW2JtHro+LF0tagj2bfQ==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.3", - "@react-native/codegen": "0.81.5" - }, - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/expo/node_modules/@react-native/babel-preset": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.81.5.tgz", - "integrity": "sha512-UoI/x/5tCmi+pZ3c1+Ypr1DaRMDLI3y+Q70pVLLVgrnC3DHsHRIbHcCHIeG/IJvoeFqFM2sTdhSOLJrf8lOPrA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.25.2", - "@babel/plugin-transform-react-jsx-self": "^7.24.7", - "@babel/plugin-transform-react-jsx-source": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.25.2", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/template": "^7.25.0", - "@react-native/babel-plugin-codegen": "0.81.5", - "babel-plugin-syntax-hermes-parser": "0.29.1", - "babel-plugin-transform-flow-enums": "^0.0.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/expo/node_modules/@react-native/codegen": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.81.5.tgz", - "integrity": "sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/parser": "^7.25.3", - "glob": "^7.1.1", - "hermes-parser": "0.29.1", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "yargs": "^17.6.2" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/expo/node_modules/babel-plugin-react-native-web": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.21.2.tgz", - "integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==", - "license": "MIT" - }, - "node_modules/expo/node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.29.1.tgz", - "integrity": "sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==", - "license": "MIT", - "dependencies": { - "hermes-parser": "0.29.1" - } - }, - "node_modules/expo/node_modules/babel-preset-expo": { - "version": "54.0.10", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-54.0.10.tgz", - "integrity": "sha512-wTt7POavLFypLcPW/uC5v8y+mtQKDJiyGLzYCjqr9tx0Qc3vCXcDKk1iCFIj/++Iy5CWhhTflEa7VvVPNWeCfw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/plugin-proposal-decorators": "^7.12.9", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.23.0", - "@react-native/babel-preset": "0.81.5", - "babel-plugin-react-compiler": "^1.0.0", - "babel-plugin-react-native-web": "~0.21.0", - "babel-plugin-syntax-hermes-parser": "^0.29.1", - "babel-plugin-transform-flow-enums": "^0.0.2", - "debug": "^4.3.4", - "resolve-from": "^5.0.0" - }, - "peerDependencies": { - "@babel/runtime": "^7.20.0", - "expo": "*", - "react-refresh": ">=0.14.0 <1.0.0" - }, - "peerDependenciesMeta": { - "@babel/runtime": { - "optional": true - }, - "expo": { - "optional": true - } - } - }, - "node_modules/expo/node_modules/hermes-estree": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", - "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", - "license": "MIT" - }, - "node_modules/expo/node_modules/hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", - "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", - "license": "MIT", - "dependencies": { - "hermes-estree": "0.29.1" - } - }, - "node_modules/exponential-backoff": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", - "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", - "license": "Apache-2.0" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "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==", - "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==", - "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==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flow-enums-runtime": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", - "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", - "license": "MIT" - }, - "node_modules/fontfaceobserver": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", - "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", - "license": "BSD-2-Clause" - }, - "node_modules/freeport-async": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", - "integrity": "sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/getenv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", - "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/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==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hermes-estree": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", - "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", - "dev": true, - "license": "MIT" - }, - "node_modules/hermes-parser": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", - "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hermes-estree": "0.25.1" - } - }, - "node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/image-size": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", - "license": "MIT", - "dependencies": { - "queue": "6.0.2" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT" - }, - "node_modules/is-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==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jimp-compact": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", - "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", - "license": "MIT" - }, - "node_modules/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==", - "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.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsc-safe-url": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", - "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", - "license": "0BSD" - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lan-network": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.1.7.tgz", - "integrity": "sha512-mnIlAEMu4OyEvUNdzco9xpuB9YVcPkQec+QsgycBCtPZvEqWPCDPfbAE4OJMdBBWpZWtpCn1xw9jJYlwjWI5zQ==", - "license": "MIT", - "bin": { - "lan-network": "dist/lan-network-cli.js" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lighthouse-logger": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", - "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", - "license": "Apache-2.0", - "dependencies": { - "debug": "^2.6.9", - "marky": "^1.2.2" - } - }, - "node_modules/lighthouse-logger/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/lighthouse-logger/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "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==", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT" - }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "license": "MIT", - "dependencies": { - "chalk": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/log-symbols/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/marky": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", - "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", - "license": "Apache-2.0" - }, - "node_modules/memoize-one": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", - "license": "MIT" - }, - "node_modules/merge-options": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", - "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", - "license": "MIT", - "dependencies": { - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/metro": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.3.tgz", - "integrity": "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", - "@babel/types": "^7.25.2", - "accepts": "^1.3.7", - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "error-stack-parser": "^2.0.6", - "flow-enums-runtime": "^0.0.6", - "graceful-fs": "^4.2.4", - "hermes-parser": "0.32.0", - "image-size": "^1.0.2", - "invariant": "^2.2.4", - "jest-worker": "^29.7.0", - "jsc-safe-url": "^0.2.2", - "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-config": "0.83.3", - "metro-core": "0.83.3", - "metro-file-map": "0.83.3", - "metro-resolver": "0.83.3", - "metro-runtime": "0.83.3", - "metro-source-map": "0.83.3", - "metro-symbolicate": "0.83.3", - "metro-transform-plugins": "0.83.3", - "metro-transform-worker": "0.83.3", - "mime-types": "^2.1.27", - "nullthrows": "^1.1.1", - "serialize-error": "^2.1.0", - "source-map": "^0.5.6", - "throat": "^5.0.0", - "ws": "^7.5.10", - "yargs": "^17.6.2" - }, - "bin": { - "metro": "src/cli.js" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-babel-transformer": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.3.tgz", - "integrity": "sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "hermes-parser": "0.32.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-babel-transformer/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", - "license": "MIT" - }, - "node_modules/metro-babel-transformer/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", - "license": "MIT", - "dependencies": { - "hermes-estree": "0.32.0" - } - }, - "node_modules/metro-cache": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.3.tgz", - "integrity": "sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q==", - "license": "MIT", - "dependencies": { - "exponential-backoff": "^3.1.1", - "flow-enums-runtime": "^0.0.6", - "https-proxy-agent": "^7.0.5", - "metro-core": "0.83.3" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-cache-key": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.3.tgz", - "integrity": "sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-config": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.3.tgz", - "integrity": "sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA==", - "license": "MIT", - "dependencies": { - "connect": "^3.6.5", - "flow-enums-runtime": "^0.0.6", - "jest-validate": "^29.7.0", - "metro": "0.83.3", - "metro-cache": "0.83.3", - "metro-core": "0.83.3", - "metro-runtime": "0.83.3", - "yaml": "^2.6.1" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-core": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.3.tgz", - "integrity": "sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6", - "lodash.throttle": "^4.1.1", - "metro-resolver": "0.83.3" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-file-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.3.tgz", - "integrity": "sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "fb-watchman": "^2.0.0", - "flow-enums-runtime": "^0.0.6", - "graceful-fs": "^4.2.4", - "invariant": "^2.2.4", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "nullthrows": "^1.1.1", - "walker": "^1.0.7" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-minify-terser": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.3.tgz", - "integrity": "sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6", - "terser": "^5.15.0" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-resolver": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.3.tgz", - "integrity": "sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-runtime": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.3.tgz", - "integrity": "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.0", - "flow-enums-runtime": "^0.0.6" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-source-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.3.tgz", - "integrity": "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.3", - "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", - "@babel/types": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "invariant": "^2.2.4", - "metro-symbolicate": "0.83.3", - "nullthrows": "^1.1.1", - "ob1": "0.83.3", - "source-map": "^0.5.6", - "vlq": "^1.0.0" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-symbolicate": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.3.tgz", - "integrity": "sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6", - "invariant": "^2.2.4", - "metro-source-map": "0.83.3", - "nullthrows": "^1.1.1", - "source-map": "^0.5.6", - "vlq": "^1.0.0" - }, - "bin": { - "metro-symbolicate": "src/index.js" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-transform-plugins": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.3.tgz", - "integrity": "sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", - "flow-enums-runtime": "^0.0.6", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro-transform-worker": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.3.tgz", - "integrity": "sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/types": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "metro": "0.83.3", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-minify-terser": "0.83.3", - "metro-source-map": "0.83.3", - "metro-transform-plugins": "0.83.3", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/metro/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "license": "MIT" - }, - "node_modules/metro/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", - "license": "MIT" - }, - "node_modules/metro/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", - "license": "MIT", - "dependencies": { - "hermes-estree": "0.32.0" - } - }, - "node_modules/metro/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/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==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/nativewind": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/nativewind/-/nativewind-4.2.3.tgz", - "integrity": "sha512-HglF1v6A8CqBFpXWs0d3yf4qQGurrreLuyE8FTRI/VDH8b0npZa2SDG5tviTkLiBg0s5j09mQALZOjxuocgMLA==", - "license": "MIT", - "dependencies": { - "comment-json": "^4.2.5", - "debug": "^4.3.7", - "react-native-css-interop": "0.2.3" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "tailwindcss": ">3.3.0" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nested-error-stacks": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", - "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", - "license": "MIT" - }, - "node_modules/node-forge": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", - "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-package-arg": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", - "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", - "license": "ISC", - "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-package-arg/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/nullthrows": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", - "license": "MIT" - }, - "node_modules/ob1": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.3.tgz", - "integrity": "sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==", - "license": "MIT", - "dependencies": { - "flow-enums-runtime": "^0.0.6" - }, - "engines": { - "node": ">=20.19.4" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", - "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", - "license": "MIT", - "dependencies": { - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-spinners": "^2.0.0", - "log-symbols": "^2.2.0", - "strip-ansi": "^5.2.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/ora/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ora/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-png": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", - "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", - "license": "MIT", - "dependencies": { - "pngjs": "^3.3.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", - "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "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": "3.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.2.tgz", - "integrity": "sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "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==", - "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==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/plist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", - "license": "MIT", - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" - }, - "engines": { - "node": ">=10.4.0" - } - }, - "node_modules/pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "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-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "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==", - "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==", - "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==", - "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==", - "license": "MIT" - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "license": "MIT", - "dependencies": { - "asap": "~2.0.6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qrcode-terminal": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", - "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==", - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, - "node_modules/query-string": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", - "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", - "license": "MIT", - "dependencies": { - "decode-uri-component": "^0.2.2", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "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/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-devtools-core": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", - "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", - "license": "MIT", - "dependencies": { - "shell-quote": "^1.6.1", - "ws": "^7" - } - }, - "node_modules/react-devtools-core/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", - "license": "MIT" - }, - "node_modules/react-freeze": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", - "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=17.0.0" - } - }, - "node_modules/react-is": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz", - "integrity": "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ==", - "license": "MIT" - }, - "node_modules/react-native": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", - "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", - "license": "MIT", - "dependencies": { - "@jest/create-cache-key-function": "^29.7.0", - "@react-native/assets-registry": "0.81.5", - "@react-native/codegen": "0.81.5", - "@react-native/community-cli-plugin": "0.81.5", - "@react-native/gradle-plugin": "0.81.5", - "@react-native/js-polyfills": "0.81.5", - "@react-native/normalize-colors": "0.81.5", - "@react-native/virtualized-lists": "0.81.5", - "abort-controller": "^3.0.0", - "anser": "^1.4.9", - "ansi-regex": "^5.0.0", - "babel-jest": "^29.7.0", - "babel-plugin-syntax-hermes-parser": "0.29.1", - "base64-js": "^1.5.1", - "commander": "^12.0.0", - "flow-enums-runtime": "^0.0.6", - "glob": "^7.1.1", - "invariant": "^2.2.4", - "jest-environment-node": "^29.7.0", - "memoize-one": "^5.0.0", - "metro-runtime": "^0.83.1", - "metro-source-map": "^0.83.1", - "nullthrows": "^1.1.1", - "pretty-format": "^29.7.0", - "promise": "^8.3.0", - "react-devtools-core": "^6.1.5", - "react-refresh": "^0.14.0", - "regenerator-runtime": "^0.13.2", - "scheduler": "0.26.0", - "semver": "^7.1.3", - "stacktrace-parser": "^0.1.10", - "whatwg-fetch": "^3.0.0", - "ws": "^6.2.3", - "yargs": "^17.6.2" - }, - "bin": { - "react-native": "cli.js" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@types/react": "^19.1.0", - "react": "^19.1.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-native-css-interop": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.2.3.tgz", - "integrity": "sha512-wc+JI7iUfdFBqnE18HhMTtD0q9vkhuMczToA87UdHGWwMyxdT5sCcNy+i4KInPCE855IY0Ic8kLQqecAIBWz7w==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/traverse": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.3.7", - "lightningcss": "~1.27.0", - "semver": "^7.6.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": ">=18", - "react-native": "*", - "react-native-reanimated": ">=3.6.2", - "tailwindcss": "~3" - }, - "peerDependenciesMeta": { - "react-native-safe-area-context": { - "optional": true - }, - "react-native-svg": { - "optional": true - } - } - }, - "node_modules/react-native-css-interop/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.27.0.tgz", - "integrity": "sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.27.0", - "lightningcss-darwin-x64": "1.27.0", - "lightningcss-freebsd-x64": "1.27.0", - "lightningcss-linux-arm-gnueabihf": "1.27.0", - "lightningcss-linux-arm64-gnu": "1.27.0", - "lightningcss-linux-arm64-musl": "1.27.0", - "lightningcss-linux-x64-gnu": "1.27.0", - "lightningcss-linux-x64-musl": "1.27.0", - "lightningcss-win32-arm64-msvc": "1.27.0", - "lightningcss-win32-x64-msvc": "1.27.0" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-darwin-arm64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.27.0.tgz", - "integrity": "sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-darwin-x64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.27.0.tgz", - "integrity": "sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-freebsd-x64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.27.0.tgz", - "integrity": "sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.27.0.tgz", - "integrity": "sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.27.0.tgz", - "integrity": "sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm64-musl": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.27.0.tgz", - "integrity": "sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-x64-gnu": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.27.0.tgz", - "integrity": "sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-linux-x64-musl": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.27.0.tgz", - "integrity": "sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.27.0.tgz", - "integrity": "sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/lightningcss-win32-x64-msvc": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.27.0.tgz", - "integrity": "sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/react-native-css-interop/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-native-is-edge-to-edge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.3.1.tgz", - "integrity": "sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==", - "license": "MIT", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-reanimated": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.17.5.tgz", - "integrity": "sha512-SxBK7wQfJ4UoWoJqQnmIC7ZjuNgVb9rcY5Xc67upXAFKftWg0rnkknTw6vgwnjRcvYThrjzUVti66XoZdDJGtw==", - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-arrow-functions": "^7.0.0-0", - "@babel/plugin-transform-class-properties": "^7.0.0-0", - "@babel/plugin-transform-classes": "^7.0.0-0", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", - "@babel/plugin-transform-optional-chaining": "^7.0.0-0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", - "@babel/plugin-transform-template-literals": "^7.0.0-0", - "@babel/plugin-transform-unicode-regex": "^7.0.0-0", - "@babel/preset-typescript": "^7.16.7", - "convert-source-map": "^2.0.0", - "invariant": "^2.2.4", - "react-native-is-edge-to-edge": "1.1.7" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0", - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-reanimated/node_modules/react-native-is-edge-to-edge": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz", - "integrity": "sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w==", - "license": "MIT", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-safe-area-context": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", - "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", - "license": "MIT", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-screens": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", - "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", - "license": "MIT", - "dependencies": { - "react-freeze": "^1.0.0", - "react-native-is-edge-to-edge": "^1.2.1", - "warn-once": "^0.1.0" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-worklets": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.8.1.tgz", - "integrity": "sha512-oWP/lStsAHU6oYCaWDXrda/wOHVdhusQJz1e6x9gPnXdFf4ndNDAOtWCmk2zGrAnlapfyA3rM6PCQq94mPg9cw==", - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-classes": "^7.28.4", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/preset-typescript": "^7.27.1", - "convert-source-map": "^2.0.0", - "semver": "^7.7.3" - }, - "peerDependencies": { - "@babel/core": "*", - "@react-native/metro-config": "*", - "react": "*", - "react-native": "0.81 - 0.85" - } - }, - "node_modules/react-native-worklets/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-native/node_modules/@react-native/codegen": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.81.5.tgz", - "integrity": "sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/parser": "^7.25.3", - "glob": "^7.1.1", - "hermes-parser": "0.29.1", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "yargs": "^17.6.2" - }, - "engines": { - "node": ">= 20.19.4" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/react-native/node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.29.1.tgz", - "integrity": "sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==", - "license": "MIT", - "dependencies": { - "hermes-parser": "0.29.1" - } - }, - "node_modules/react-native/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/react-native/node_modules/hermes-estree": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", - "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", - "license": "MIT" - }, - "node_modules/react-native/node_modules/hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", - "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", - "license": "MIT", - "dependencies": { - "hermes-estree": "0.29.1" - } - }, - "node_modules/react-native/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-native/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-remove-scroll": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/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==", - "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==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/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==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", - "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT" - }, - "node_modules/regexpu-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", - "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.2", - "regjsgen": "^0.8.0", - "regjsparser": "^0.13.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.2.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", - "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.1.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requireg": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz", - "integrity": "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==", - "dependencies": { - "nested-error-stacks": "~2.0.1", - "rc": "~1.2.7", - "resolve": "~1.7.1" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/requireg/node_modules/resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", - "license": "MIT", - "dependencies": { - "path-parse": "^1.0.5" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "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/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-workspace-root": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.1.tgz", - "integrity": "sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==", - "license": "MIT" - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "license": "MIT", - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "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", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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==", - "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-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serialize-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", - "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-static/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/server-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", - "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sf-symbols-typescript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz", - "integrity": "sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/simple-plist": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", - "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", - "license": "MIT", - "dependencies": { - "bplist-creator": "0.1.0", - "bplist-parser": "0.3.1", - "plist": "^3.0.5" - } - }, - "node_modules/simple-plist/node_modules/bplist-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", - "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", - "license": "MIT", - "dependencies": { - "big-integer": "1.6.x" - }, - "engines": { - "node": ">= 5.10.0" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/slugify": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.9.tgz", - "integrity": "sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "license": "MIT" - }, - "node_modules/stacktrace-parser": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", - "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/stream-buffers": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", - "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", - "license": "Unlicense", - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/structured-headers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", - "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", - "license": "MIT" - }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwind-merge": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz", - "integrity": "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "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==", - "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/tar": { - "version": "7.5.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", - "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", - "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "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/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/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/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "license": "Unlicense" - }, - "node_modules/tweetnacl-util": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", - "license": "Unlicense" - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/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/undici": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", - "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, - "node_modules/undici-types": { - "version": "7.19.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", - "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", - "license": "MIT" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", - "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", - "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-latest-callback": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.6.tgz", - "integrity": "sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vaul": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", - "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-dialog": "^1.1.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/vlq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", - "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", - "license": "MIT" - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/warn-once": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz", - "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==", - "license": "MIT" - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", - "license": "MIT" - }, - "node_modules/whatwg-url-without-unicode": { - "version": "8.0.0-3", - "resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz", - "integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==", - "license": "MIT", - "dependencies": { - "buffer": "^5.4.3", - "punycode": "^2.1.1", - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wonka": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.6.tgz", - "integrity": "sha512-MXH+6mDHAZ2GuMpgKS055FR6v0xVP3XwquxIMYXgiW+FejHQlMGlvVRZT4qMCxR+bEo/FCtIdKxwej9WV3YQag==", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xcode": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", - "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", - "license": "Apache-2.0", - "dependencies": { - "simple-plist": "^1.1.0", - "uuid": "^7.0.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/xml2js": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", - "integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xml2js/node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "license": "MIT", - "engines": { - "node": ">=8.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zustand": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", - "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", - "license": "MIT", - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=18.0.0", - "immer": ">=9.0.6", - "react": ">=18.0.0", - "use-sync-external-store": ">=1.2.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - }, - "use-sync-external-store": { - "optional": true - } - } - } - } -} diff --git a/client-app/package.json b/client-app/package.json deleted file mode 100644 index d3ff6ab..0000000 --- a/client-app/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "dchain-messenger", - "version": "1.0.0", - "main": "expo-router/entry", - "scripts": { - "start": "expo start", - "android": "expo start --android", - "ios": "expo start --ios", - "web": "expo start --web", - "lint": "eslint ." - }, - "dependencies": { - "@react-native-async-storage/async-storage": "2.2.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "expo": "~54.0.0", - "expo-asset": "~12.0.12", - "expo-camera": "~16.1.6", - "expo-crypto": "~14.1.4", - "expo-clipboard": "~8.0.8", - "expo-constants": "~18.0.13", - "expo-document-picker": "~14.0.8", - "expo-file-system": "~19.0.21", - "expo-font": "~14.0.11", - "expo-linking": "~8.0.11", - "expo-router": "~6.0.23", - "expo-secure-store": "~15.0.8", - "expo-sharing": "~14.0.8", - "expo-splash-screen": "~31.0.13", - "expo-status-bar": "~3.0.9", - "expo-web-browser": "~15.0.10", - "nativewind": "^4.1.23", - "react": "19.1.0", - "react-native": "0.81.5", - "react-native-reanimated": "~3.17.0", - "react-native-safe-area-context": "~5.6.0", - "react-native-screens": "~4.16.0", - "react-native-worklets": "~0.8.1", - "tailwind-merge": "^2.6.0", - "tailwindcss": "^3.4.17", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1", - "zustand": "^5.0.3" - }, - "devDependencies": { - "@babel/core": "^7.25.2", - "@types/react": "~19.1.0", - "babel-preset-expo": "~13.0.0", - "typescript": "^5.3.3" - } -} diff --git a/client-app/tailwind.config.js b/client-app/tailwind.config.js deleted file mode 100644 index 35bef05..0000000 --- a/client-app/tailwind.config.js +++ /dev/null @@ -1,28 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - './app/**/*.{js,jsx,ts,tsx}', - './components/**/*.{js,jsx,ts,tsx}', - './hooks/**/*.{js,jsx,ts,tsx}', - './lib/**/*.{js,jsx,ts,tsx}', - ], - presets: [require('nativewind/preset')], - theme: { - extend: { - colors: { - // DChain brand — deep navy + teal accent - background: '#0d1117', - surface: '#161b22', - surfaceHigh: '#21262d', - border: '#30363d', - primary: '#2563eb', - primaryFg: '#ffffff', - accent: '#22d3ee', - muted: '#8b949e', - destructive: '#f85149', - success: '#3fb950', - }, - }, - }, - plugins: [], -}; diff --git a/client-app/tsconfig.json b/client-app/tsconfig.json deleted file mode 100644 index 140c044..0000000 --- a/client-app/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "expo/tsconfig.base", - "compilerOptions": { - "strict": true, - "paths": { - "@/*": ["./*"] - } - } -} diff --git a/contracts/hello_go/hello_go_abi.json b/contracts/hello_go/hello_go_abi.json deleted file mode 100644 index 6efecaf..0000000 --- a/contracts/hello_go/hello_go_abi.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "contract": "hello_go", - "version": "1.0.0", - "description": "Example DChain contract written in Go (TinyGo SDK). Demonstrates counter, owner-gated reset, string args, and inter-contract calls.", - "build": "tinygo build -o hello_go.wasm -target wasip1 -no-debug .", - "methods": [ - { - "name": "increment", - "description": "Add 1 to the counter. Logs 'counter: N'.", - "args": [] - }, - { - "name": "get", - "description": "Log the current counter value without changing state.", - "args": [] - }, - { - "name": "reset", - "description": "Reset counter to 0. First caller becomes the owner; only owner can reset.", - "args": [] - }, - { - "name": "greet", - "description": "Log a greeting. Logs 'hello, ! block=N'.", - "args": [ - {"name": "name", "type": "string", "description": "Name to greet (defaults to 'world')"} - ] - }, - { - "name": "ping", - "description": "Call a method on another contract (inter-contract call demo).", - "args": [ - {"name": "contract_id", "type": "string", "description": "Target contract ID"}, - {"name": "method", "type": "string", "description": "Method to call on target"} - ] - } - ] -} diff --git a/contracts/hello_go/main.go b/contracts/hello_go/main.go deleted file mode 100644 index e6f1e83..0000000 --- a/contracts/hello_go/main.go +++ /dev/null @@ -1,104 +0,0 @@ -// Package main is an example DChain smart contract written in Go. -// -// # Build -// -// tinygo build -o hello_go.wasm -target wasip1 -no-debug . -// -// # Deploy -// -// client deploy-contract --key /keys/node1.json \ -// --wasm hello_go.wasm --abi hello_go_abi.json \ -// --node http://localhost:8081 -// -// # Use -// -// client call-contract --key /keys/node1.json --contract $ID \ -// --method increment --gas 5000 --node http://localhost:8081 -// -// The contract implements a simple counter with owner-gated reset. -// It demonstrates every SDK primitive: arguments, state, caller, logging, -// and inter-contract calls. -package main - -import ( - "strconv" - - dc "go-blockchain/contracts/sdk" -) - -// increment adds 1 to the counter and logs the new value. -// -//export increment -func increment() { - v := dc.GetU64("counter") - v++ - dc.PutU64("counter", v) - dc.Log("counter: " + strconv.FormatUint(v, 10)) -} - -// get logs the current counter value without changing state. -// -//export get -func get() { - v := dc.GetU64("counter") - dc.Log("counter: " + strconv.FormatUint(v, 10)) -} - -// reset sets the counter to 0. On first call the caller becomes the owner. -// Subsequent calls are restricted to the owner. -// -//export reset -func reset() { - owner := dc.GetStateStr("owner") - caller := dc.Caller() - if owner == "" { - dc.SetStateStr("owner", caller) - dc.PutU64("counter", 0) - dc.Log("initialized — owner: " + caller[:min8(caller)]) - return - } - if caller != owner { - dc.Log("unauthorized: " + caller[:min8(caller)]) - return - } - dc.PutU64("counter", 0) - dc.Log("reset by owner") -} - -// greet logs a personalised greeting using the first call argument. -// -//export greet -func greet() { - name := dc.ArgStr(0, 64) - if name == "" { - name = "world" - } - dc.Log("hello, " + name + "! block=" + strconv.FormatUint(dc.BlockHeight(), 10)) -} - -// ping calls another contract's method (demonstrates inter-contract calls). -// Args: contract_id (string), method (string) -// -//export ping -func ping() { - target := dc.ArgStr(0, 64) - method := dc.ArgStr(1, 64) - if target == "" || method == "" { - dc.Log("ping: target and method required") - return - } - if dc.CallContract(target, method, "[]") { - dc.Log("ping: " + method + " ok") - } else { - dc.Log("ping: " + method + " failed") - } -} - -func min8(s string) int { - if len(s) < 8 { - return len(s) - } - return 8 -} - -func main() {} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..5b5f2e8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,34 @@ +# DChain documentation + +Справочник по блокчейн-ноде DChain. Читается в любом порядке, но если в первый +раз — идите по разделам сверху вниз. + +## Оглавление + +| Документ | О чём | +|----------|-------| +| [quickstart.md](quickstart.md) | Поднять ноду локально за 5 минут | +| [architecture.md](architecture.md) | 4 слоя стека: network / chain / transport / app | +| [node/README.md](node/README.md) | Запуск ноды (docker, native, deployment) | +| [update-system.md](update-system.md) | Версионирование, `/api/update-check`, auto-update от Gitea | +| [api/README.md](api/README.md) | REST + WebSocket API endpoints | +| [cli/README.md](cli/README.md) | CLI `client` — команды, флаги, примеры | +| [contracts/README.md](contracts/README.md) | Системные контракты (native + WASM) | +| [development/README.md](development/README.md) | SDK для написания своих контрактов (TinyGo) | +| [node/governance.md](node/governance.md) | On-chain governance, голосование параметров | +| [node/multi-server.md](node/multi-server.md) | Multi-validator federation deploy | + +## Внешние ссылки из репо + +- [README.md](../README.md) — обзор проекта +- [deploy/single/README.md](../deploy/single/README.md) — operator runbook для single-node +- [deploy/prod/README.md](../deploy/prod/README.md) — operator runbook для multi-validator +- [deploy/UPDATE_STRATEGY.md](../deploy/UPDATE_STRATEGY.md) — дизайн forward-compat обновлений (4 слоя) + +## Соглашения + +- **Единицы:** микро-токены (`µT`). 1 T = 1,000,000 µT. MinFee = 1000 µT = 0.001 T. +- **Pubkey:** 32-байтный Ed25519 (hex, 64 символа). +- **Address:** `DC` + первые 24 hex-символа SHA-256(pubkey). +- **ChainID:** `dchain-` + первые 12 hex-символов SHA-256 genesis-блока. +- **Времена:** RFC 3339 (UTC) везде, кроме internal-счётчиков в Prometheus (unix seconds). diff --git a/docs/api/README.md b/docs/api/README.md index 13f7153..c967fd2 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -1,45 +1,72 @@ -# REST API +# REST + WebSocket API -DChain-нода предоставляет HTTP API на порту `--stats-addr` (по умолчанию `:8080`). +DChain-нода обслуживает HTTP + WebSocket на `--stats-addr` (по умолчанию `:8080`). +JSON везде; ошибки в формате `{"error": "описание"}`, с 4xx для клиентских +проблем и 5xx для серверных. -## Базовые URL +Полная OpenAPI-спека живёт в бинаре (`/swagger/openapi.json`) и рендерится +Swagger UI на `/swagger`. Эти два эндпоинта можно выключить через +`DCHAIN_DISABLE_SWAGGER=true` — см. [deploy/single/README.md](../../deploy/single/README.md). -| Окружение | URL | -|---------|-----| -| Локально | `http://localhost:8081` | -| Docker node1 | `http://node1:8080` | -| Docker node2 | `http://node2:8080` | -| Docker node3 | `http://node3:8080` | - -## Разделы API +## Разделы | Документ | Эндпоинты | |---------|----------| -| [Chain API](chain.md) | Блоки, транзакции, балансы, адреса, stats | -| [Contracts API](contracts.md) | Деплой, вызов, state, логи | -| [Relay API](relay.md) | Отправка сообщений, inbox, контакты | +| [chain.md](chain.md) | Блоки, транзакции, балансы, адреса, netstats, validators | +| [contracts.md](contracts.md) | Деплой, вызов, state, логи контрактов | +| [relay.md](relay.md) | Relay-mailbox: отправка/приём encrypted envelopes | -## Формат ошибок +## Discovery / metadata (always on) -```json -{"error": "описание ошибки"} -``` +| Endpoint | Возврат | +|----------|---------| +| `GET /api/netstats` | height, total_txs, supply, validator_count | +| `GET /api/network-info` | chain_id, genesis_hash, peers[], validators[], contracts | +| `GET /api/well-known-version` | node_version, build{}, protocol_version, features[], chain_id | +| `GET /api/well-known-contracts` | canonical contract_id → name mapping | +| `GET /api/update-check` | comparison with upstream Gitea release (см. [../update-system.md](../update-system.md)) | +| `GET /api/peers` | connected peer IDs + их версии (из gossip `dchain/version/v1`) | +| `GET /api/validators` | active validator set | +| `GET /metrics` | Prometheus exposition | -HTTP-статус: 400 для клиентских ошибок, 500 для серверных. +## Real-time (push) + +- **`GET /api/ws`** — WebSocket, рекомендованный транспорт. Поддерживает + `submit_tx`, `subscribe`, `unsubscribe`, `typing`, `ping`. Push-события: + `block`, `tx`, `inbox`, `typing`, `submit_ack`. +- `GET /api/events` — SSE, legacy одностороннее streaming (для старых клиентов). + +Protocol reference — `node/ws.go` + [../architecture.md](../architecture.md). ## Аутентификация -REST API не требует аутентификации. Транзакции подписываются на стороне клиента (CLI-командами) и отправляются как подписанные JSON-объекты. API не имеет admin-эндпоинтов требующих токенов. +По умолчанию API **публичен**. Три режима access-control настраиваются через +env: -## Пример +| Режим | `DCHAIN_API_TOKEN` | `DCHAIN_API_PRIVATE` | Эффект | +|-------|:------------------:|:--------------------:|--------| +| Public | не задан | — | Все могут читать и submit'ить tx | +| Token writes | задан | `false` | Читать — любой; `POST /api/tx` и WS `submit_tx` — только с `Authorization: Bearer ` | +| Fully private | задан | `true` | Все эндпоинты требуют token (включая `/api/netstats`) | + +WebSocket: токен передаётся query-параметром `?token=<...>`. + +## Примеры ```bash -# Статистика сети -curl http://localhost:8081/api/stats +# Базовый health +curl -s http://localhost:8080/api/netstats | jq . -# Баланс адреса -curl http://localhost:8081/api/balance/03a1b2c3... +# Версия ноды +curl -s http://localhost:8080/api/well-known-version | jq '{tag: .build.tag, features: .features}' -# Последние блоки -curl http://localhost:8081/api/blocks?limit=10 +# Список пиров с их версиями +curl -s http://localhost:8080/api/peers | jq '.peers[].version' + +# С токеном (private-режим) +curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/netstats + +# Submit tx (public-режим) +curl -s -X POST -H "Content-Type: application/json" \ + -d @tx.json http://localhost:8080/api/tx ``` diff --git a/docs/contracts/README.md b/docs/contracts/README.md index a605071..fcc8b74 100644 --- a/docs/contracts/README.md +++ b/docs/contracts/README.md @@ -1,59 +1,85 @@ -# Production Smart Contracts +# Smart contracts -DChain поставляется с четырьмя production-контрактами, задеплоенными из genesis-кошелька. +DChain поддерживает **два типа** контрактов — native Go (запечены в +бинарь ноды) и WASM (деплоятся on-chain через `DEPLOY_CONTRACT` tx). -## Обзор +## Native (Go, скомпилированы в бинарь) -| Контракт | Назначение | Ключевые фичи | -|---------|-----------|--------------| -| [username_registry](username_registry.md) | Username ↔ адрес | Тарифная сетка, treasury fees, reverse lookup | +Живут в `blockchain/native*.go`. Видны как `native:`, работают на +каждой ноде идентично (тот же Go-код — нет WASM-drift'а). + +| Контракт | ID | Назначение | +|----------|----|-----------| +| [username_registry](username_registry.md) | `native:username_registry` | `@username` ↔ pubkey, плата 10_000 µT, min 4 символа | + +Клиент находит `native:username_registry` автоматически через +`/api/well-known-contracts` — без конфигурирования. + +## WASM (user-deployable) + +Деплоятся любым обладателем баланса через `client deploy-contract`. +Исполняются в wazero runtime с gas limit и set'ом host-функций. + +| Контракт | Назначение | Key features | +|----------|------------|--------------| | [governance](governance.md) | On-chain параметры | Propose/approve workflow, admin role | | [auction](auction.md) | English auction | Token escrow, автоматический refund, settle | | [escrow](escrow.md) | Двусторонний escrow | Dispute/resolve, admin arbitration | -## Деплой +Исходники + WASM-артефакты — в `contracts//`. SDK для написания +новых контрактов — [../development/README.md](../development/README.md). + +## Деплой WASM + +Контракты **не деплоятся автоматически** — это выбор оператора. +Скрипт-хелпер: ```bash -docker compose --profile deploy run --rm deploy +./scripts/deploy_contracts.sh # деплоит auction + escrow + governance из CWD ``` -Все 4 контракта деплоятся автоматически. ID сохраняются в `/tmp/contracts.env`. - -## Вызов контракта +Или вручную: ```bash -docker exec node1 client call-contract \ - --key /keys/node1.json \ - --contract \ - --method \ - --arg # строковый аргумент (можно несколько) - --arg64 # числовой аргумент uint64 - --gas # рекомендуется 20000 для записи, 5000 для чтения - --node http://node1:8080 +client deploy-contract \ + --key node.json \ + --wasm contracts/auction/auction.wasm \ + --abi contracts/auction/auction_abi.json \ + --node http://localhost:8080 ``` -## Contract Treasury +После деплоя `contract_id` печатается в stdout — сохраните его. -У каждого контракта есть **ownerless treasury address** — `hex(sha256(contractID + ":treasury"))`. -Это эскроу-адрес без private key. Только сам контракт может снять с него деньги через host function `transfer`. - -Используется в `auction` и `escrow` для хранения заблокированных токенов. - -## Просмотр состояния +## Вызов контракта (любой типа) ```bash -# Через REST API -curl http://localhost:8081/api/contracts//state/ - -# Через Explorer -open http://localhost:8081/contract?id= +client call-contract \ + --key node.json \ + --contract \ + --method \ + --arg # можно повторять + --arg64 # числовые аргументы + --gas 20000 # reasonable default; read-only методы: 5000 + --amount 0 # tx.Amount (для методов с платой — см. username_registry) + --node http://localhost:8080 ``` -## Логи контракта +## Contract treasury + +У каждого WASM-контракта есть **ownerless treasury** по адресу +`hex(sha256(contractID + ":treasury"))`. Private-key не существует, +списывать можно только через host function `transfer` из самого +контракта. Используется `auction` (escrow bids) и `escrow` (held amount). + +## Просмотр state / логов ```bash -# REST -curl "http://localhost:8081/api/contracts//logs?limit=20" +# Конкретный ключ state +curl -s http://localhost:8080/api/contracts//state/ -# Explorer → вкладка Logs +# Последние N логов контракта +curl -s "http://localhost:8080/api/contracts//logs?limit=20" | jq . + +# Или через Explorer UI +open http://localhost:8080/contract?id= ``` diff --git a/docs/development/README.md b/docs/development/README.md index 86d6898..1e0a617 100644 --- a/docs/development/README.md +++ b/docs/development/README.md @@ -85,19 +85,20 @@ contracts/ ## Деплой контракта ```bash -# Локально client deploy-contract \ - --key key.json \ + --key node.json \ --wasm mycontract.wasm \ --abi mycontract_abi.json \ - --node http://localhost:8081 + --node http://localhost:8080 +``` -# В Docker -docker exec node1 client deploy-contract \ - --key /keys/node1.json \ +В Docker (single-node): +```bash +docker exec dchain /usr/local/bin/client deploy-contract \ + --key /keys/node.json \ --wasm /path/to/mycontract.wasm \ --abi /path/to/mycontract_abi.json \ - --node http://node1:8080 + --node http://localhost:8080 ``` Успешный деплой логирует `contract_id: `. diff --git a/docs/node/README.md b/docs/node/README.md index fa5db72..c676262 100644 --- a/docs/node/README.md +++ b/docs/node/README.md @@ -1,219 +1,182 @@ # Запуск ноды -## Быстрый старт (Docker) +Три основных сценария — по возрастанию сложности. + +| Сценарий | Где подробно | +|----------|--------------| +| **Одна нода локально (dev, genesis)** | [../quickstart.md](../quickstart.md) → Путь 1 | +| **Одна нода с TLS и доменом (production personal)** | [../../deploy/single/README.md](../../deploy/single/README.md) | +| **Multi-validator federation (3+ нод, PBFT quorum)** | [../../deploy/prod/README.md](../../deploy/prod/README.md) + [multi-server.md](multi-server.md) | + +Этот документ — про native запуск (не через docker) и обзор всех флагов. + +## Native build + +Требования: +- Go 1.24+ +- BadgerDB + libp2p встроены в бинарь (no-cgo, `CGO_ENABLED=0`) ```bash -git clone https://github.com/your/go-blockchain -cd go-blockchain -docker compose up -d +go build -ldflags "-s -w \ + -X go-blockchain/node/version.Tag=$(git describe --tags --always --dirty) \ + -X go-blockchain/node/version.Commit=$(git rev-parse HEAD) \ + -X go-blockchain/node/version.Date=$(date -u +%Y-%m-%dT%H:%M:%SZ) \ + -X go-blockchain/node/version.Dirty=$(git diff --quiet HEAD -- && echo false || echo true)" \ + -o node ./cmd/node + +./node --version +# dchain-node v0.0.1 (commit=abc1234 date=… dirty=false) ``` -Запускает 3 ноды: `node1` (8081), `node2` (8082), `node3` (8083). +Ldflags не обязательны — без них версия будет "dev". Makefile в корне +инкапсулирует это: `make build`. -Деплой контрактов: -```bash -docker exec node1 /scripts/deploy_contracts.sh -``` +## Single node (genesis) -Explorer: http://localhost:8081 - ---- - -## Запуск вручную - -### Требования - -- Go 1.21+ -- BadgerDB (встроен) -- libp2p (встроен) - -### Сборка +Сгенерировать ключ и поднять блок 0 с собой как единственным валидатором: ```bash -cd go-blockchain -go build ./cmd/node/ -go build ./cmd/client/ -``` +./node --version # проверка +./client keygen --out node.json -### Genesis нода - -```bash -# Первый запуск — создать genesis ./node \ - --key node1.json \ + --key node.json \ + --db ./data \ --genesis \ - --validators "$(cat node1.json | jq -r .pub_key),$(cat node2.json | jq -r .pub_key),$(cat node3.json | jq -r .pub_key)" \ - --stats-addr :8081 \ --listen /ip4/0.0.0.0/tcp/4001 \ - --db ./data/node1 + --stats-addr :8080 ``` -### Peer нода +После первого успешного запуска удалите `--genesis` (no-op, но шумит в логах). + +## Join existing network + +Зная URL хотя бы одной живой ноды: ```bash ./node \ - --key node2.json \ - --peers /ip4/127.0.0.1/tcp/4001/p2p/ \ - --stats-addr :8082 \ - --listen /ip4/0.0.0.0/tcp/4002 \ - --db ./data/node2 + --key node.json \ + --db ./data \ + --join http://seed1.example.com:8080,http://seed2.example.com:8080 \ + --listen /ip4/0.0.0.0/tcp/4001 \ + --stats-addr :8080 ``` ---- +Нода скачает `/api/network-info`, подхватит chain_id, genesis_hash, список +валидаторов и peer multiaddrs. По умолчанию запускается как **observer** +(применяет блоки, принимает tx, но не голосует). Чтобы стать валидатором, +существующий валидатор должен подать `ADD_VALIDATOR` tx с multi-sig. -## Флаги командной строки +## Все флаги -| Флаг | По умолчанию | Описание | -|------|------------|---------| -| `--key` | `node.json` | Файл Ed25519 + X25519 идентичности | -| `--db` | `chaindata` | Директория BadgerDB | -| `--listen` | `/ip4/0.0.0.0/tcp/4001` | libp2p адрес | -| `--peers` | — | Bootstrap peer multiaddrs (через запятую) | -| `--validators` | — | Pubkeys валидаторов (через запятую, только для `--genesis`) | -| `--genesis` | false | Создать genesis блок при первом старте | -| `--stats-addr` | `:8080` | HTTP API/Explorer порт | -| `--wallet` | — | Payout кошелёк (hex pubkey) | -| `--wallet-pass` | — | Пароль к кошельку | -| `--heartbeat` | false | Отправлять heartbeat каждые 60 минут | -| `--register-relay` | false | Зарегистрироваться как relay-провайдер | -| `--relay-fee` | 0 | Fee за relay сообщение в µT | -| `--relay-key` | `relay.json` | X25519 ключ для relay шифрования | -| `--mailbox-db` | — | Директория для relay mailbox | -| `--governance-contract` | — | ID governance контракта для динамических параметров | +Каждый флаг имеет env fallback (`DCHAIN_` в UPPERCASE с +заменой `-` на `_`). ---- +### Идентичность +| Флаг | env | По умолчанию | Описание | +|------|-----|--------------|----------| +| `--key` | `DCHAIN_KEY` | `node.json` | Ed25519 + X25519 identity file | +| `--db` | `DCHAIN_DB` | `chaindata` | BadgerDB directory | -## Docker Compose +### Сеть +| Флаг | env | По умолчанию | Описание | +|------|-----|--------------|----------| +| `--listen` | `DCHAIN_LISTEN` | `/ip4/0.0.0.0/tcp/4001` | libp2p listen multiaddr | +| `--announce` | `DCHAIN_ANNOUNCE` | auto | Что анонсировать пирам (публичный IP!) | +| `--peers` | `DCHAIN_PEERS` | — | Bootstrap peers (comma-sep multiaddrs) | +| `--join` | `DCHAIN_JOIN` | — | Seed HTTP URLs для onboarding | -### docker-compose.yml структура +### Консенсус +| Флаг | env | По умолчанию | Описание | +|------|-----|--------------|----------| +| `--genesis` | `DCHAIN_GENESIS` | false | Создать block 0 при первом старте | +| `--validators` | `DCHAIN_VALIDATORS` | — | Initial validator set (для `--genesis`) | +| `--observer` | `DCHAIN_OBSERVER` | false | Observer mode (не голосовать) | +| `--allow-genesis-mismatch` | — | false | Пропустить safety check (опасно) | -```yaml -services: - node1: - build: . - ports: - - "8081:8080" # HTTP API - - "4001:4001" # libp2p - volumes: - - node1_data:/chaindata - - ./keys:/keys:ro - command: > - node - --key /keys/node1.json - --genesis - --validators "$VALIDATORS" - --stats-addr :8080 - --listen /ip4/0.0.0.0/tcp/4001 - --governance-contract "$GOV_ID" - environment: - - VALIDATORS=03...,04...,05... +### Relay +| Флаг | env | По умолчанию | Описание | +|------|-----|--------------|----------| +| `--register-relay` | `DCHAIN_REGISTER_RELAY` | false | Подать REGISTER_RELAY tx при старте | +| `--relay-fee` | — | 1000 | Fee за relay envelope, µT | +| `--relay-key` | — | `relay.json` | X25519 key для E2E шифрования envelope | +| `--mailbox-db` | — | — | Badger dir для offline-получателей | - node2: - build: . - ports: - - "8082:8080" - command: > - node - --key /keys/node2.json - --peers /dns4/node1/tcp/4001/p2p/$NODE1_PEER_ID - --stats-addr :8080 - --listen /ip4/0.0.0.0/tcp/4001 -``` +### Governance / misc +| Флаг | env | По умолчанию | Описание | +|------|-----|--------------|----------| +| `--governance-contract` | `DCHAIN_GOVERNANCE_CONTRACT` | — | ID governance контракта | +| `--heartbeat` | — | false | Слать heartbeat tx каждые 60 мин | +| `--log-format` | `DCHAIN_LOG_FORMAT` | `text` | `text` или `json` | -### Управление +### HTTP / WebSocket / UI +| Флаг | env | По умолчанию | Описание | +|------|-----|--------------|----------| +| `--stats-addr` | — | `:8080` | HTTP + WS listen address | +| `--disable-ui` | `DCHAIN_DISABLE_UI` | false | Выключить блок-эксплорер HTML-страницы | +| `--disable-swagger` | `DCHAIN_DISABLE_SWAGGER` | false | Выключить `/swagger` + `/swagger/openapi.json` | +| `--api-token` | `DCHAIN_API_TOKEN` | — | Bearer token для submit (пусто = public) | +| `--api-private` | `DCHAIN_API_PRIVATE` | false | Требовать token и для чтения | -```bash -# Запустить -docker compose up -d - -# Логи -docker compose logs -f node1 - -# Остановить -docker compose down - -# Сбросить данные -docker compose down -v -``` - ---- +### Update system +| Флаг | env | По умолчанию | Описание | +|------|-----|--------------|----------| +| `--update-source-url` | `DCHAIN_UPDATE_SOURCE_URL` | — | Gitea `/api/v1/.../releases/latest` URL | +| `--update-source-token` | `DCHAIN_UPDATE_SOURCE_TOKEN` | — | PAT для приватных repo | ## Файл ключа -Каждая нода использует один файл с обоими ключами: - ```json { - "pub_key": "03abcd...", // Ed25519 pubkey (hex, 66 символов) - "priv_key": "...", // Ed25519 privkey (hex) - "x25519_pub": "...", // X25519 pubkey для E2E relay (hex, 64 символа) - "x25519_priv": "..." // X25519 privkey + "pub_key": "26018d40...", // Ed25519 public (64 hex chars) + "priv_key": "16aba1d2...", // Ed25519 private (128 hex chars, priv||pub) + "x25519_pub": "baada10a...", // X25519 public для relay E2E + "x25519_priv":"a814c191..." // X25519 private } ``` -Генерация: -```bash -client keygen --out node1.json +`client keygen --out node.json` — создаёт валидный файл. + +## Схема ключей BadgerDB + +``` +height → uint64 (tip) +block: → JSON Block +tx: → JSON TxRecord +txchron:: → tx_id (recent-tx index) +balance: → uint64 (µT) +stake: → uint64 (µT) +id: → JSON RegisterKeyPayload +chan: → JSON CreateChannelPayload +chan-member:: → "" +contract: → JSON ContractRecord +cstate:: → bytes +clog::: → JSON ContractLogEntry +relay: → JSON RegisterRelayPayload +validator: → "" (presence = active) +schema:ver → uint32 (migration version) ``` ---- +## Метрики + healthcheck -## Мониторинг +- **Healthcheck:** `curl http://localhost:8080/api/netstats` возвращает + 200 с JSON, если нода живая. +- **Prometheus:** `GET /metrics` — см. [../api/README.md](../api/README.md). +- **Время блока:** норма ~5 сек (константа в `consensus/pbft.go`). -### HTTP healthcheck +## Сброс данных ```bash -curl http://localhost:8081/api/netstats -``` +# Native +rm -rf ./data ./mailbox -### Logs - -```bash # Docker -docker compose logs -f node1 | grep -E "block|error|warn" - -# Прямой запуск -./node ... 2>&1 | tee node.log -``` - -### Метрики производительности - -| Показатель | Норма | -|-----------|------| -| Время блока | ~3 с | -| Блоков в минуту | ~20 | -| PBFT фазы | prepare → commit → finalize | - ---- - -## Структура данных BadgerDB - -``` -balance: → uint64 (µT) -identity: → JSON RegisterKeyPayload -stake: → uint64 (µT) -block: → JSON Block -tx: → JSON TxRecord -txidx::: → txid (индекс по адресу) -contract: → JSON ContractRecord -cstate:: → []byte (state контракта) -clog:: → JSON ContractLogEntry -relay: → JSON RegisteredRelayInfo -``` - ---- - -## Сброс и восстановление - -```bash -# Полный сброс docker compose down -v -docker compose up -d -# Только данные одной ноды -docker compose stop node1 -docker volume rm go-blockchain_node1_data -docker compose up -d node1 +# Single-node container +docker stop dchain && docker rm dchain +docker volume rm dchain_data ``` -После сброса нужно заново задеплоить контракты и залинковать governance. +После сброса нода начнёт с пустого стейта; c `--genesis` — создаст новый +chain_id, с `--join` — синкается с сетью. diff --git a/docs/quickstart.md b/docs/quickstart.md index 04d3f58..08e6d37 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -1,177 +1,85 @@ -# Быстрый старт +# Quickstart + +Три пути в зависимости от того, что вам нужно. + +## Путь 1: локальная single-node за 5 минут + +Самый быстрый способ потрогать блокчейн — одна нода в Docker, без TLS, без +федерации, с HTTP-API на `localhost:8080`. + +```bash +git clone https://git.vsecoder.vodka/vsecoder/dchain.git +cd dchain + +# 1. Собираем образ +docker build -t dchain-node-slim -f deploy/prod/Dockerfile.slim . + +# 2. Генерим ключ ноды (один раз) +mkdir -p keys +docker run --rm --entrypoint /usr/local/bin/client \ + -v "$PWD/keys:/out" dchain-node-slim \ + keygen --out /out/node.json + +# 3. Запускаем как genesis (блок 0 = эта нода — единственный валидатор) +docker run -d --name dchain --restart unless-stopped \ + -p 4001:4001 -p 8080:8080 \ + -v dchain_data:/data \ + -v "$PWD/keys:/keys:ro" \ + -e DCHAIN_GENESIS=true \ + -e DCHAIN_ANNOUNCE=/ip4/127.0.0.1/tcp/4001 \ + dchain-node-slim \ + --db=/data/chain --mailbox-db=/data/mailbox --key=/keys/node.json \ + --relay-key=/data/relay.json --listen=/ip4/0.0.0.0/tcp/4001 --stats-addr=:8080 + +# 4. Проверяем +curl -s http://localhost:8080/api/netstats +curl -s http://localhost:8080/api/well-known-version +open http://localhost:8080/ # Explorer UI +open http://localhost:8080/swagger # Swagger UI +``` + +Блоки коммитятся каждые ~5 секунд. Что дальше: + +- Первая отправка tx — [cli/README.md](cli/README.md) +- API endpoint reference — [api/README.md](api/README.md) +- Выключить UI / Swagger — см. `DCHAIN_DISABLE_UI` / `DCHAIN_DISABLE_SWAGGER` в [deploy/single/README.md](../deploy/single/README.md) + +## Путь 2: single-node с TLS и доменом + +Полный operator runbook — [deploy/single/README.md](../deploy/single/README.md). +Там 6 готовых сценариев: публичная с UI, headless API, полностью приватная, +genesis, joiner, auto-update от Gitea. + +## Путь 3: multi-validator federation + +3 ноды в PBFT quorum, Caddy LB с ip_hash для WS — [deploy/prod/README.md](../deploy/prod/README.md). ## Требования -- Docker Desktop (или Docker Engine + Compose v2) -- 4 GB RAM, 2 CPU +- Docker Desktop / Docker Engine + Compose v2 +- 2 GB RAM (одна нода), 4 GB (3-node dev cluster) +- 1 CPU -Для разработки контрактов дополнительно: -- Go 1.21+ -- TinyGo 0.30+ (только для TinyGo-контрактов) +Для разработки из исходников: ---- +- Go 1.24+ (модуль использует новые фичи) +- TinyGo 0.30+ — только если собираете WASM-контракты вручную -## 1. Запустить сеть +## Проверка жизни ```bash -git clone -cd go-blockchain +# высота тип +curl -s http://localhost:8080/api/netstats | jq '.total_blocks' -docker compose up --build -d +# версия бинаря +docker exec dchain /usr/local/bin/node --version +# → dchain-node v0.5.0-dev (commit=abc1234 date=… dirty=false) + +# /api/well-known-version — те же данные + features[] +curl -s http://localhost:8080/api/well-known-version | jq . + +# лист возможностей +curl -s http://localhost:8080/api/well-known-version | jq '.features' ``` -Запускается три ноды: - -| Контейнер | Роль | Explorer | -|-----------|------|---------| -| node1 | genesis + validator + relay | http://localhost:8081 | -| node2 | validator + relay | http://localhost:8082 | -| node3 | relay-only observer | http://localhost:8083 | - -Дождитесь пока в Explorer появятся блоки (~10 секунд). - -Swagger: http://localhost:8081/swagger - ---- - -## 2. Задеплоить контракты - -```bash -docker compose --profile deploy run --rm deploy -``` - -Скрипт: -1. Ждёт готовности node1 -2. Деплоит 4 контракта из genesis-ключа `/keys/node1.json` -3. Вызывает `init` на governance и escrow -4. Привязывает governance к нодам через `/api/governance/link` -5. Выводит contract ID и сохраняет в `/tmp/contracts.env` - -Пример вывода: -``` -══════════════════════════════════════════════════ - DChain — деплой production-контрактов -══════════════════════════════════════════════════ - -▶ Деплой username_registry - ✓ username_registry contract_id: a1b2c3d4e5f60718 - -▶ Деплой governance - ✓ governance contract_id: 9f8e7d6c5b4a3210 - -▶ Деплой auction - ✓ auction contract_id: 1a2b3c4d5e6f7089 - -▶ Деплой escrow - ✓ escrow contract_id: fedcba9876543210 - - ✓ username_registry : a1b2c3d4e5f60718 - ✓ governance : 9f8e7d6c5b4a3210 - ✓ auction : 1a2b3c4d5e6f7089 - ✓ escrow : fedcba9876543210 -``` - -Сохраните ID для последующего использования: -```bash -# Запомнить ID -export UR_ID=a1b2c3d4e5f60718 -export GOV_ID=9f8e7d6c5b4a3210 -export AUC_ID=1a2b3c4d5e6f7089 -export ESC_ID=fedcba9876543210 -``` - ---- - -## 3. Первые операции - -### Проверить баланс genesis-кошелька - -```bash -docker exec node1 client balance \ - --key /keys/node1.json \ - --node http://node1:8080 -``` - -### Создать новый кошелёк - -```bash -docker exec node1 wallet keygen --out /tmp/alice.json -docker exec node1 client balance \ - --key /tmp/alice.json \ - --node http://node1:8080 -``` - -### Перевести токены - -```bash -# Получить pubkey Alice -ALICE_PUB=$(docker exec node1 sh -c 'cat /tmp/alice.json | grep pub_key' | grep -oP '"pub_key":\s*"\K[^"]+') - -docker exec node1 client transfer \ - --key /keys/node1.json \ - --to $ALICE_PUB \ - --amount 1000000 \ - --node http://node1:8080 -``` - -### Зарегистрировать username - -```bash -docker exec node1 client call-contract \ - --key /keys/node1.json \ - --contract $UR_ID \ - --method register \ - --arg alice \ - --gas 20000 \ - --node http://node1:8080 -``` - -### Отправить сообщение (по username) - -```bash -docker exec node1 client send-msg \ - --key /keys/node1.json \ - --to @alice \ - --registry $UR_ID \ - --msg "Привет!" \ - --node http://node1:8080 -``` - ---- - -## 4. Explorer - -После деплоя контракты видны в Explorer: - -``` -http://localhost:8081/contracts — все контракты -http://localhost:8081/contract?id=$UR_ID — username_registry -http://localhost:8081/contract?id=$GOV_ID — governance -http://localhost:8081/contract?id=$AUC_ID — auction -http://localhost:8081/contract?id=$ESC_ID — escrow -``` - -Вкладки в Explorer на странице контракта: -- **Overview** — метаданные, ABI-методы -- **State** — query state по ключу -- **Logs** — history вызовов с логами -- **Raw** — сырой JSON ContractRecord - ---- - -## 5. Полный сброс - -```bash -docker compose down -v && docker compose up --build -d -``` - -Флаг `-v` удаляет тома BadgerDB. После пересборки сеть стартует с чистого genesis. - ---- - -## Следующие шаги - -- [Контракты](contracts/README.md) — использование всех 4 контрактов -- [Разработка контрактов](development/README.md) — написать свой контракт -- [CLI](cli/README.md) — все команды клиента -- [API](api/README.md) — REST-интерфейс +Если `total_blocks` растёт каждые ~5 сек — всё ОК. diff --git a/docs/update-system.md b/docs/update-system.md new file mode 100644 index 0000000..bce673f --- /dev/null +++ b/docs/update-system.md @@ -0,0 +1,201 @@ +# Система обновлений и версионирование + +DChain поставляется с полноценной системой update-detection, которая +включает **пять слоёв**: build-time версия в бинаре, HTTP-эндпоинты для +обнаружения, peer-gossip версий соседей, `update-check` от Gitea, и +rolling-restart скрипт. Ниже — как это работает и как использовать. + +## Слой 1. Build-time версия + +Бинарь хранит 4 поля, инжектимые через `-ldflags -X`: + +- `Tag` — человекочитаемый тег, обычно `git describe --tags --always --dirty` +- `Commit` — полный 40-символьный SHA коммита +- `Date` — RFC 3339 timestamp сборки (UTC) +- `Dirty` — `"true"` если сборка была из грязного worktree + +Печать: + +```bash +node --version +# dchain-node v0.0.1 (commit=abc1234 date=2026-04-17T10:00:00Z dirty=false) + +client --version +# тот же формат +``` + +Все 4 образа (`Dockerfile` и `deploy/prod/Dockerfile.slim`) принимают эти +значения через `--build-arg VERSION_TAG=…` и т.д. `update.sh` +вычисляет их автоматически перед ребилдом. + +## Слой 2. `/api/well-known-version` + +```bash +curl -s http://localhost:8080/api/well-known-version | jq . +``` +```json +{ + "node_version": "v0.0.1", + "build": { + "tag": "v0.0.1", + "commit": "abc1234…", + "date": "2026-04-17T10:00:00Z", + "dirty": "false" + }, + "protocol_version": 1, + "features": [ + "access_token", "chain_id", "channels_v1", "contract_logs", + "fan_out", "identity_registry", "native_username_registry", + "onboarding_api", "payment_channels", "relay_mailbox", + "ws_submit_tx" + ], + "chain_id": "dchain-ddb9a7e37fc8" +} +``` + +Клиент использует это для feature-detection: зная `features[]`, он знает +какие экраны/флоу рендерить. Пример в `client-app/lib/api.ts` — +функция `checkNodeVersion()`. + +**Protocol version** — отдельная ось от `node_version`. Меняется только +при несовместимых изменениях wire-протокола (новый формат PBFT-message, +breaking change в tx encoding). Клиент, который ожидает `protocol_version: 1`, +не должен работать с нодой `protocol_version: 2`. + +## Слой 3. Peer-gossip версий + +Каждая нода публикует своё `{peer_id, tag, commit, protocol_version, +timestamp}` в gossipsub-топик `dchain/version/v1` раз в 60 секунд. Другие +ноды эту информацию получают, хранят 15 минут, и отдают через +`/api/peers`: + +```bash +curl -s http://localhost:8080/api/peers | jq . +``` +```json +{ + "peers": [ + { + "id": "12D3KooW…", + "addrs": ["/ip4/…/tcp/4001/p2p/12D3KooW…"], + "version": { + "tag": "v0.0.1", + "commit": "abc1234…", + "protocol_version": 1, + "timestamp": 1745000000, + "received_at": "2026-04-17T10:01:00Z" + } + } + ] +} +``` + +Зачем это: + +- Operator видит «какая версия у моих соседей» одним запросом. +- Client видит «стоит ли переключиться на другую ноду». +- Feature-flag activation (например, `EventChannelBan`) можно запускать + только когда ≥80% сети на новой версии. + +## Слой 4. `/api/update-check` + +Оператор настраивает `DCHAIN_UPDATE_SOURCE_URL` на ссылку вида: + +``` +https:///api/v1/repos///releases/latest +``` + +Нода опрашивает этот URL (15 мин cache, 5 сек timeout) и возвращает: + +```bash +curl -s http://localhost:8080/api/update-check | jq . +``` +```json +{ + "current": { "tag": "v0.0.1", "commit": "…", "date": "…", "dirty": "false" }, + "latest": { "tag": "v0.0.2", "commit": "…", "url": "https://…", "published_at": "…" }, + "update_available": true, + "checked_at": "2026-04-17T11:00:00Z", + "source": "https://git.vsecoder.vodka/api/v1/repos/vsecoder/dchain/releases/latest" +} +``` + +- 503 — не настроен `DCHAIN_UPDATE_SOURCE_URL`. +- 502 — upstream (Gitea) недоступен или ответил non-2xx. +- `update_available: false` — либо HEAD совпадает, либо Gitea вернула + draft/prerelease (оба игнорируются). + +Токен для приватного репо: `DCHAIN_UPDATE_SOURCE_TOKEN=` +(scope: `read:repository` достаточно). + +## Слой 5. `update.sh` + systemd timer + +Скрипт в `deploy/single/update.sh`. Флоу: + +1. Если `DCHAIN_UPDATE_SOURCE_URL` задан — сначала спрашивает + `/api/update-check`. Если `update_available: false` — выход с кодом 0. +2. `git fetch --tags`. +3. **Semver guard:** если `UPDATE_ALLOW_MAJOR != true` и major-версия + меняется (v1.x → v2.y) — блокирует обновление с exit code 4. +4. `git checkout ` (в detached HEAD) или ff-merge на `origin/main`. +5. Ребилд образа с правильными `--build-arg VERSION_*`. +6. Smoke-test: `docker run --rm … node --version` — должен напечатать + новую версию без ошибок. +7. `docker compose up -d --force-recreate node`. +8. Polling `/api/netstats` до 60 сек — если не ожил, `exit 1`. + +Systemd-интеграция — в `deploy/single/systemd/`: + +```bash +sudo cp deploy/single/systemd/dchain-update.{service,timer} /etc/systemd/system/ +sudo systemctl daemon-reload +sudo systemctl enable --now dchain-update.timer +``` + +Таймер: `OnUnitActiveSec=1h` + `RandomizedDelaySec=15min` — чтобы +федерация не рестартовала вся одновременно и не уронила PBFT quorum. + +## Schema migrations (BadgerDB) + +Отдельный слой, относящийся к on-disk формату данных. См. +`blockchain/schema_migrations.go`: + +- `CurrentSchemaVersion` — const в Go-коде, bumpается с каждой + миграцией. +- `schemaMetaKey = "schema:ver"` — ключ в BadgerDB хранит фактическую + версию данных. +- `runMigrations(db)` вызывается при `NewChain()`, применяет каждый + шаг форвард-миграций атомарно (data rewrite + version bump в одной + badger.Update транзакции). +- Если stored version > CurrentSchemaVersion — ошибка (запускаете + старый бинарь на новом DB). Fix: обновите бинарь или восстановите + из бэкапа. + +На текущий релиз `CurrentSchemaVersion = 0`, миграций нет — scaffold +живёт и готов к первому реальному изменению формата. + +## Forward-compat для EventType + +В `blockchain/chain.go` → `applyTx()` добавлен `default:` case для +неизвестных `EventType`: + +- Fee дебитуется с отправителя (не спам-вектор). +- Tx применяется как **no-op**. +- В логе warning «unknown event type … — binary is older than this tx». + +Это значит: новую `EventType` можно подать в сеть, она будет +обработана на новых нодах и проигнорирована на старых, без split'а +консенсуса. Full design — `deploy/UPDATE_STRATEGY.md` → §5.1. + +## Checklist при релизе + +1. В Git `git tag -a vX.Y.Z -m "release notes"` + `git push origin vX.Y.Z`. +2. В Gitea UI: `Releases → New Release → Tag: vX.Y.Z → Publish`. +3. На всех нодах с настроенным `update.sh`: + - systemd-таймер подхватит через ~1 час (± 15 мин jitter). + - Operator может форсить: `sudo systemctl start dchain-update.service`. +4. Проверка: + ```bash + curl -s https:///api/well-known-version | jq .build.tag + # должно вернуть vX.Y.Z + ```